From 798ec96e1447f8c21b4b774d3be9c50ed30a9318 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 30 Sep 2017 16:19:16 +1000 Subject: [PATCH 01/16] D3D: Make state cache part of Renderer and not static --- Source/Core/VideoBackends/D3D/D3DState.cpp | 42 ++++++++------------ Source/Core/VideoBackends/D3D/D3DState.h | 5 +-- Source/Core/VideoBackends/D3D/Render.cpp | 45 ++++++++-------------- Source/Core/VideoBackends/D3D/Render.h | 13 +++++++ 4 files changed, 46 insertions(+), 59 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/D3DState.cpp b/Source/Core/VideoBackends/D3D/D3DState.cpp index 5732ffd21b..41412342cc 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.cpp +++ b/Source/Core/VideoBackends/D3D/D3DState.cpp @@ -263,6 +263,21 @@ void StateManager::SetTextureByMask(u32 textureSlotMask, ID3D11ShaderResourceVie } // namespace D3D +StateCache::~StateCache() +{ + for (auto& it : m_depth) + SAFE_RELEASE(it.second); + + for (auto& it : m_raster) + SAFE_RELEASE(it.second); + + for (auto& it : m_blend) + SAFE_RELEASE(it.second); + + for (auto& it : m_sampler) + SAFE_RELEASE(it.second); +} + ID3D11SamplerState* StateCache::Get(SamplerState state) { auto it = m_sampler.find(state.hex); @@ -471,33 +486,6 @@ ID3D11DepthStencilState* StateCache::Get(DepthState state) return res; } -void StateCache::Clear() -{ - for (auto it : m_depth) - { - SAFE_RELEASE(it.second); - } - m_depth.clear(); - - for (auto it : m_raster) - { - SAFE_RELEASE(it.second); - } - m_raster.clear(); - - for (auto it : m_blend) - { - SAFE_RELEASE(it.second); - } - m_blend.clear(); - - for (auto it : m_sampler) - { - SAFE_RELEASE(it.second); - } - m_sampler.clear(); -} - D3D11_PRIMITIVE_TOPOLOGY StateCache::GetPrimitiveTopology(PrimitiveType primitive) { static constexpr std::array primitives = { diff --git a/Source/Core/VideoBackends/D3D/D3DState.h b/Source/Core/VideoBackends/D3D/D3DState.h index 0c083f7c1b..6d2f72b17f 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.h +++ b/Source/Core/VideoBackends/D3D/D3DState.h @@ -23,6 +23,8 @@ namespace DX11 class StateCache { public: + ~StateCache(); + // Get existing or create new render state. // Returned objects is owned by the cache and does not need to be released. ID3D11SamplerState* Get(SamplerState state); @@ -30,9 +32,6 @@ public: ID3D11RasterizerState* Get(RasterizationState state); ID3D11DepthStencilState* Get(DepthState state); - // Release all cached states and clear hash tables. - void Clear(); - // Convert RasterState primitive type to D3D11 primitive topology. static D3D11_PRIMITIVE_TOPOLOGY GetPrimitiveTopology(PrimitiveType primitive); diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 4fc2b1f084..41a48e9a8d 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -53,14 +53,6 @@ typedef struct _Nv_Stereo_Image_Header #define NVSTEREO_IMAGE_SIGNATURE 0x4433564e -struct GXPipelineState -{ - std::array samplers; - BlendingState blend; - DepthState zmode; - RasterizationState raster; -}; - static u32 s_last_multisamples = 1; static bool s_last_stereo_mode = false; static bool s_last_fullscreen_mode = false; @@ -74,9 +66,6 @@ static ID3D11RasterizerState* s_reset_rast_state = nullptr; static ID3D11Texture2D* s_screenshot_texture = nullptr; static D3DTexture2D* s_3d_vision_texture = nullptr; -static GXPipelineState s_gx_state; -static StateCache s_gx_state_cache; - static void SetupDeviceObjects() { HRESULT hr; @@ -173,8 +162,6 @@ static void TeardownDeviceObjects() SAFE_RELEASE(s_reset_rast_state); SAFE_RELEASE(s_screenshot_texture); SAFE_RELEASE(s_3d_vision_texture); - - s_gx_state_cache.Clear(); } static void Create3DVisionTexture(int width, int height) @@ -212,13 +199,13 @@ Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferH SetupDeviceObjects(); // Setup GX pipeline state - for (auto& sampler : s_gx_state.samplers) + for (auto& sampler : m_gx_state.samplers) sampler.hex = RenderState::GetPointSamplerState().hex; - s_gx_state.zmode.testenable = false; - s_gx_state.zmode.updateenable = false; - s_gx_state.zmode.func = ZMode::NEVER; - s_gx_state.raster.cullmode = GenMode::CULL_NONE; + m_gx_state.zmode.testenable = false; + m_gx_state.zmode.updateenable = false; + m_gx_state.zmode.func = ZMode::NEVER; + m_gx_state.raster.cullmode = GenMode::CULL_NONE; // Clear EFB textures constexpr std::array clear_color{{0.f, 0.f, 0.f, 1.f}}; @@ -600,7 +587,7 @@ void Renderer::ReinterpretPixelData(unsigned int convtype) void Renderer::SetBlendingState(const BlendingState& state) { - s_gx_state.blend.hex = state.hex; + m_gx_state.blend.hex = state.hex; } // This function has the final picture. We adjust the aspect ratio here. @@ -719,15 +706,15 @@ void Renderer::RestoreAPIState() void Renderer::ApplyState() { - D3D::stateman->PushBlendState(s_gx_state_cache.Get(s_gx_state.blend)); - D3D::stateman->PushDepthState(s_gx_state_cache.Get(s_gx_state.zmode)); - D3D::stateman->PushRasterizerState(s_gx_state_cache.Get(s_gx_state.raster)); + D3D::stateman->PushBlendState(m_state_cache.Get(m_gx_state.blend)); + D3D::stateman->PushDepthState(m_state_cache.Get(m_gx_state.zmode)); + D3D::stateman->PushRasterizerState(m_state_cache.Get(m_gx_state.raster)); D3D::stateman->SetPrimitiveTopology( - StateCache::GetPrimitiveTopology(s_gx_state.raster.primitive)); - FramebufferManager::SetIntegerEFBRenderTarget(s_gx_state.blend.logicopenable); + StateCache::GetPrimitiveTopology(m_gx_state.raster.primitive)); + FramebufferManager::SetIntegerEFBRenderTarget(m_gx_state.blend.logicopenable); - for (u32 stage = 0; stage < static_cast(s_gx_state.samplers.size()); stage++) - D3D::stateman->SetSampler(stage, s_gx_state_cache.Get(s_gx_state.samplers[stage])); + for (u32 stage = 0; stage < static_cast(m_gx_state.samplers.size()); stage++) + D3D::stateman->SetSampler(stage, m_state_cache.Get(m_gx_state.samplers[stage])); ID3D11Buffer* vertexConstants = VertexShaderCache::GetConstantBuffer(); @@ -746,17 +733,17 @@ void Renderer::RestoreState() void Renderer::SetRasterizationState(const RasterizationState& state) { - s_gx_state.raster.hex = state.hex; + m_gx_state.raster.hex = state.hex; } void Renderer::SetDepthState(const DepthState& state) { - s_gx_state.zmode.hex = state.hex; + m_gx_state.zmode.hex = state.hex; } void Renderer::SetSamplerState(u32 index, const SamplerState& state) { - s_gx_state.samplers[index].hex = state.hex; + m_gx_state.samplers[index].hex = state.hex; } void Renderer::SetInterlacingMode() diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index b35e0f9d20..340802f02f 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -5,6 +5,7 @@ #pragma once #include +#include "VideoBackends/D3D/D3DState.h" #include "VideoCommon/RenderBase.h" enum class EFBAccessType; @@ -19,6 +20,7 @@ public: Renderer(); ~Renderer() override; + StateCache& GetStateCache() { return m_state_cache; } void SetBlendingState(const BlendingState& state) override; void SetScissorRect(const EFBRectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; @@ -56,7 +58,18 @@ public: bool CheckForResize(); private: + struct GXPipelineState + { + std::array samplers; + BlendingState blend; + DepthState zmode; + RasterizationState raster; + }; + void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture, u32 src_width, u32 src_height, float Gamma); + + StateCache m_state_cache; + GXPipelineState m_gx_state; }; } From d9400f708a5af50a38273e25c103143968e16b67 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 21 Nov 2017 19:53:38 +1000 Subject: [PATCH 02/16] D3D: Move remaining static variables from Render.cpp to Renderer class --- Source/Core/VideoBackends/D3D/Render.cpp | 309 +++++++++++------------ Source/Core/VideoBackends/D3D/Render.h | 18 ++ 2 files changed, 166 insertions(+), 161 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 41a48e9a8d..762404d581 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -53,147 +53,11 @@ typedef struct _Nv_Stereo_Image_Header #define NVSTEREO_IMAGE_SIGNATURE 0x4433564e -static u32 s_last_multisamples = 1; -static bool s_last_stereo_mode = false; -static bool s_last_fullscreen_mode = false; - -static std::array s_clear_blend_states{}; -static std::array s_clear_depth_states{}; -static ID3D11BlendState* s_reset_blend_state = nullptr; -static ID3D11DepthStencilState* s_reset_depth_state = nullptr; -static ID3D11RasterizerState* s_reset_rast_state = nullptr; - -static ID3D11Texture2D* s_screenshot_texture = nullptr; -static D3DTexture2D* s_3d_vision_texture = nullptr; - -static void SetupDeviceObjects() -{ - HRESULT hr; - - D3D11_DEPTH_STENCIL_DESC ddesc; - ddesc.DepthEnable = FALSE; - ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; - ddesc.DepthFunc = D3D11_COMPARISON_ALWAYS; - ddesc.StencilEnable = FALSE; - ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; - ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; - hr = D3D::device->CreateDepthStencilState(&ddesc, &s_clear_depth_states[0]); - CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen"); - ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; - ddesc.DepthEnable = TRUE; - hr = D3D::device->CreateDepthStencilState(&ddesc, &s_clear_depth_states[1]); - CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen"); - ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; - hr = D3D::device->CreateDepthStencilState(&ddesc, &s_clear_depth_states[2]); - CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen"); - D3D::SetDebugObjectName(s_clear_depth_states[0], - "depth state for Renderer::ClearScreen (depth buffer disabled)"); - D3D::SetDebugObjectName( - s_clear_depth_states[1], - "depth state for Renderer::ClearScreen (depth buffer enabled, writing enabled)"); - D3D::SetDebugObjectName( - s_clear_depth_states[2], - "depth state for Renderer::ClearScreen (depth buffer enabled, writing disabled)"); - - D3D11_BLEND_DESC blenddesc; - blenddesc.AlphaToCoverageEnable = FALSE; - blenddesc.IndependentBlendEnable = FALSE; - blenddesc.RenderTarget[0].BlendEnable = FALSE; - blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; - blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; - blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; - blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; - blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; - blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; - hr = D3D::device->CreateBlendState(&blenddesc, &s_reset_blend_state); - CHECK(hr == S_OK, "Create blend state for Renderer::ResetAPIState"); - D3D::SetDebugObjectName(s_reset_blend_state, "blend state for Renderer::ResetAPIState"); - - s_clear_blend_states[0] = s_reset_blend_state; - s_reset_blend_state->AddRef(); - - blenddesc.RenderTarget[0].RenderTargetWriteMask = - D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN | D3D11_COLOR_WRITE_ENABLE_BLUE; - hr = D3D::device->CreateBlendState(&blenddesc, &s_clear_blend_states[1]); - CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen"); - - blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALPHA; - hr = D3D::device->CreateBlendState(&blenddesc, &s_clear_blend_states[2]); - CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen"); - - blenddesc.RenderTarget[0].RenderTargetWriteMask = 0; - hr = D3D::device->CreateBlendState(&blenddesc, &s_clear_blend_states[3]); - CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen"); - - ddesc.DepthEnable = FALSE; - ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; - ddesc.DepthFunc = D3D11_COMPARISON_LESS; - ddesc.StencilEnable = FALSE; - ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; - ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; - hr = D3D::device->CreateDepthStencilState(&ddesc, &s_reset_depth_state); - CHECK(hr == S_OK, "Create depth state for Renderer::ResetAPIState"); - D3D::SetDebugObjectName(s_reset_depth_state, "depth stencil state for Renderer::ResetAPIState"); - - D3D11_RASTERIZER_DESC rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, - 0, 0.f, 0.f, false, false, false, false); - hr = D3D::device->CreateRasterizerState(&rastdesc, &s_reset_rast_state); - CHECK(hr == S_OK, "Create rasterizer state for Renderer::ResetAPIState"); - D3D::SetDebugObjectName(s_reset_rast_state, "rasterizer state for Renderer::ResetAPIState"); - - s_screenshot_texture = nullptr; -} - -// Kill off all device objects -static void TeardownDeviceObjects() -{ - g_framebuffer_manager.reset(); - - SAFE_RELEASE(s_clear_blend_states[0]); - SAFE_RELEASE(s_clear_blend_states[1]); - SAFE_RELEASE(s_clear_blend_states[2]); - SAFE_RELEASE(s_clear_blend_states[3]); - SAFE_RELEASE(s_clear_depth_states[0]); - SAFE_RELEASE(s_clear_depth_states[1]); - SAFE_RELEASE(s_clear_depth_states[2]); - SAFE_RELEASE(s_reset_blend_state); - SAFE_RELEASE(s_reset_depth_state); - SAFE_RELEASE(s_reset_rast_state); - SAFE_RELEASE(s_screenshot_texture); - SAFE_RELEASE(s_3d_vision_texture); -} - -static void Create3DVisionTexture(int width, int height) -{ - // Create a staging texture for 3D vision with signature information in the last row. - // Nvidia 3D Vision supports full SBS, so there is no loss in resolution during this process. - NVSTEREOIMAGEHEADER header; - header.dwSignature = NVSTEREO_IMAGE_SIGNATURE; - header.dwWidth = static_cast(width * 2); - header.dwHeight = static_cast(height + 1); - header.dwBPP = 32; - header.dwFlags = 0; - - const u32 pitch = static_cast(4 * width * 2); - const auto memory = std::make_unique((height + 1) * pitch); - u8* image_header_location = &memory[height * pitch]; - std::memcpy(image_header_location, &header, sizeof(header)); - - D3D11_SUBRESOURCE_DATA sys_data; - sys_data.SysMemPitch = pitch; - sys_data.pSysMem = memory.get(); - - s_3d_vision_texture = - D3DTexture2D::Create(width * 2, height + 1, D3D11_BIND_RENDER_TARGET, D3D11_USAGE_DEFAULT, - DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, &sys_data); -} - Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferHeight()) { - s_last_multisamples = g_ActiveConfig.iMultisamples; - s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; - s_last_fullscreen_mode = D3D::GetFullscreenState(); + m_last_multisamples = g_ActiveConfig.iMultisamples; + m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; + m_last_fullscreen_mode = D3D::GetFullscreenState(); g_framebuffer_manager = std::make_unique(m_target_width, m_target_height); SetupDeviceObjects(); @@ -228,6 +92,129 @@ Renderer::~Renderer() D3D::Close(); } +void Renderer::SetupDeviceObjects() +{ + HRESULT hr; + + D3D11_DEPTH_STENCIL_DESC ddesc; + ddesc.DepthEnable = FALSE; + ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + ddesc.DepthFunc = D3D11_COMPARISON_ALWAYS; + ddesc.StencilEnable = FALSE; + ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; + ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; + hr = D3D::device->CreateDepthStencilState(&ddesc, &m_clear_depth_states[0]); + CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen"); + ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + ddesc.DepthEnable = TRUE; + hr = D3D::device->CreateDepthStencilState(&ddesc, &m_clear_depth_states[1]); + CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen"); + ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + hr = D3D::device->CreateDepthStencilState(&ddesc, &m_clear_depth_states[2]); + CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen"); + D3D::SetDebugObjectName(m_clear_depth_states[0], + "depth state for Renderer::ClearScreen (depth buffer disabled)"); + D3D::SetDebugObjectName( + m_clear_depth_states[1], + "depth state for Renderer::ClearScreen (depth buffer enabled, writing enabled)"); + D3D::SetDebugObjectName( + m_clear_depth_states[2], + "depth state for Renderer::ClearScreen (depth buffer enabled, writing disabled)"); + + D3D11_BLEND_DESC blenddesc; + blenddesc.AlphaToCoverageEnable = FALSE; + blenddesc.IndependentBlendEnable = FALSE; + blenddesc.RenderTarget[0].BlendEnable = FALSE; + blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + hr = D3D::device->CreateBlendState(&blenddesc, &m_reset_blend_state); + CHECK(hr == S_OK, "Create blend state for Renderer::ResetAPIState"); + D3D::SetDebugObjectName(m_reset_blend_state, "blend state for Renderer::ResetAPIState"); + + m_clear_blend_states[0] = m_reset_blend_state; + m_reset_blend_state->AddRef(); + + blenddesc.RenderTarget[0].RenderTargetWriteMask = + D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN | D3D11_COLOR_WRITE_ENABLE_BLUE; + hr = D3D::device->CreateBlendState(&blenddesc, &m_clear_blend_states[1]); + CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen"); + + blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALPHA; + hr = D3D::device->CreateBlendState(&blenddesc, &m_clear_blend_states[2]); + CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen"); + + blenddesc.RenderTarget[0].RenderTargetWriteMask = 0; + hr = D3D::device->CreateBlendState(&blenddesc, &m_clear_blend_states[3]); + CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen"); + + ddesc.DepthEnable = FALSE; + ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + ddesc.DepthFunc = D3D11_COMPARISON_LESS; + ddesc.StencilEnable = FALSE; + ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; + ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; + hr = D3D::device->CreateDepthStencilState(&ddesc, &m_reset_depth_state); + CHECK(hr == S_OK, "Create depth state for Renderer::ResetAPIState"); + D3D::SetDebugObjectName(m_reset_depth_state, "depth stencil state for Renderer::ResetAPIState"); + + D3D11_RASTERIZER_DESC rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, + 0, 0.f, 0.f, false, false, false, false); + hr = D3D::device->CreateRasterizerState(&rastdesc, &m_reset_rast_state); + CHECK(hr == S_OK, "Create rasterizer state for Renderer::ResetAPIState"); + D3D::SetDebugObjectName(m_reset_rast_state, "rasterizer state for Renderer::ResetAPIState"); + + m_screenshot_texture = nullptr; +} + +// Kill off all device objects +void Renderer::TeardownDeviceObjects() +{ + g_framebuffer_manager.reset(); + + SAFE_RELEASE(m_clear_blend_states[0]); + SAFE_RELEASE(m_clear_blend_states[1]); + SAFE_RELEASE(m_clear_blend_states[2]); + SAFE_RELEASE(m_clear_blend_states[3]); + SAFE_RELEASE(m_clear_depth_states[0]); + SAFE_RELEASE(m_clear_depth_states[1]); + SAFE_RELEASE(m_clear_depth_states[2]); + SAFE_RELEASE(m_reset_blend_state); + SAFE_RELEASE(m_reset_depth_state); + SAFE_RELEASE(m_reset_rast_state); + SAFE_RELEASE(m_screenshot_texture); + SAFE_RELEASE(m_3d_vision_texture); +} + +void Renderer::Create3DVisionTexture(int width, int height) +{ + // Create a staging texture for 3D vision with signature information in the last row. + // Nvidia 3D Vision supports full SBS, so there is no loss in resolution during this process. + NVSTEREOIMAGEHEADER header; + header.dwSignature = NVSTEREO_IMAGE_SIGNATURE; + header.dwWidth = static_cast(width * 2); + header.dwHeight = static_cast(height + 1); + header.dwBPP = 32; + header.dwFlags = 0; + + const u32 pitch = static_cast(4 * width * 2); + const auto memory = std::make_unique((height + 1) * pitch); + u8* image_header_location = &memory[height * pitch]; + std::memcpy(image_header_location, &header, sizeof(header)); + + D3D11_SUBRESOURCE_DATA sys_data; + sys_data.SysMemPitch = pitch; + sys_data.pSysMem = memory.get(); + + m_3d_vision_texture = + D3DTexture2D::Create(width * 2, height + 1, D3D11_BIND_RENDER_TARGET, D3D11_USAGE_DEFAULT, + DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, &sys_data); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { D3D::DrawTextScaled(static_cast(left + 1), static_cast(top + 1), 20.f, 0.0f, @@ -430,8 +417,8 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num } else // if (type == EFBAccessType::PokeZ) { - D3D::stateman->PushBlendState(s_clear_blend_states[3]); - D3D::stateman->PushDepthState(s_clear_depth_states[1]); + D3D::stateman->PushBlendState(m_clear_blend_states[3]); + D3D::stateman->PushDepthState(m_clear_depth_states[1]); D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.0f, 0.0f, (float)GetTargetWidth(), (float)GetTargetHeight()); @@ -513,21 +500,21 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaE ResetAPIState(); if (colorEnable && alphaEnable) - D3D::stateman->PushBlendState(s_clear_blend_states[0]); + D3D::stateman->PushBlendState(m_clear_blend_states[0]); else if (colorEnable) - D3D::stateman->PushBlendState(s_clear_blend_states[1]); + D3D::stateman->PushBlendState(m_clear_blend_states[1]); else if (alphaEnable) - D3D::stateman->PushBlendState(s_clear_blend_states[2]); + D3D::stateman->PushBlendState(m_clear_blend_states[2]); else - D3D::stateman->PushBlendState(s_clear_blend_states[3]); + D3D::stateman->PushBlendState(m_clear_blend_states[3]); // TODO: Should we enable Z testing here? // if (!bpmem.zmode.testenable) D3D::stateman->PushDepthState(s_clear_depth_states[0]); // else if (zEnable) - D3D::stateman->PushDepthState(s_clear_depth_states[1]); + D3D::stateman->PushDepthState(m_clear_depth_states[1]); else /*if (!zEnable)*/ - D3D::stateman->PushDepthState(s_clear_depth_states[2]); + D3D::stateman->PushDepthState(m_clear_depth_states[2]); // Update the view port for clearing the picture TargetRectangle targetRc = Renderer::ConvertEFBRectangle(rc); @@ -633,33 +620,33 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region const bool window_resized = CheckForResize(); const bool fullscreen = D3D::GetFullscreenState(); - const bool fs_changed = s_last_fullscreen_mode != fullscreen; + const bool fs_changed = m_last_fullscreen_mode != fullscreen; // Flip/present backbuffer to frontbuffer here D3D::Present(); // Resize the back buffers NOW to avoid flickering if (CalculateTargetSize() || window_resized || fs_changed || - s_last_multisamples != g_ActiveConfig.iMultisamples || - s_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off)) + m_last_multisamples != g_ActiveConfig.iMultisamples || + m_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off)) { - s_last_multisamples = g_ActiveConfig.iMultisamples; - s_last_fullscreen_mode = fullscreen; + m_last_multisamples = g_ActiveConfig.iMultisamples; + m_last_fullscreen_mode = fullscreen; PixelShaderCache::InvalidateMSAAShaders(); if (window_resized || fs_changed) { // TODO: Aren't we still holding a reference to the back buffer right now? D3D::Reset(); - SAFE_RELEASE(s_screenshot_texture); - SAFE_RELEASE(s_3d_vision_texture); + SAFE_RELEASE(m_screenshot_texture); + SAFE_RELEASE(m_3d_vision_texture); m_backbuffer_width = D3D::GetBackBufferWidth(); m_backbuffer_height = D3D::GetBackBufferHeight(); } UpdateDrawRectangle(); - s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; + m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr); @@ -689,9 +676,9 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // ALWAYS call RestoreAPIState for each ResetAPIState call you're doing void Renderer::ResetAPIState() { - D3D::stateman->PushBlendState(s_reset_blend_state); - D3D::stateman->PushDepthState(s_reset_depth_state); - D3D::stateman->PushRasterizerState(s_reset_rast_state); + D3D::stateman->PushBlendState(m_reset_blend_state); + D3D::stateman->PushDepthState(m_reset_depth_state); + D3D::stateman->PushRasterizerState(m_reset_rast_state); } void Renderer::RestoreAPIState() @@ -818,7 +805,7 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D } else if (g_ActiveConfig.stereo_mode == StereoMode::Nvidia3DVision) { - if (!s_3d_vision_texture) + if (!m_3d_vision_texture) Create3DVisionTexture(m_backbuffer_width, m_backbuffer_height); D3D11_VIEWPORT leftVp = CD3D11_VIEWPORT((float)dst.left, (float)dst.top, (float)dst.GetWidth(), @@ -827,7 +814,7 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D (float)dst.GetWidth(), (float)dst.GetHeight()); // Render to staging texture which is double the width of the backbuffer - D3D::context->OMSetRenderTargets(1, &s_3d_vision_texture->GetRTV(), nullptr); + D3D::context->OMSetRenderTargets(1, &m_3d_vision_texture->GetRTV(), nullptr); D3D::context->RSSetViewports(1, &leftVp); D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, @@ -845,7 +832,7 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D // recognize the signature and automatically include the right eye frame. D3D11_BOX box = CD3D11_BOX(0, 0, 0, m_backbuffer_width, m_backbuffer_height, 1); D3D::context->CopySubresourceRegion(D3D::GetBackBuffer()->GetTex(), 0, 0, 0, 0, - s_3d_vision_texture->GetTex(), 0, &box); + m_3d_vision_texture->GetTex(), 0, &box); // Restore render target to backbuffer D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr); diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index 340802f02f..333ff9a80c 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "VideoBackends/D3D/D3DState.h" #include "VideoCommon/RenderBase.h" @@ -66,10 +67,27 @@ private: RasterizationState raster; }; + void SetupDeviceObjects(); + void TeardownDeviceObjects(); + void Create3DVisionTexture(int width, int height); + void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture, u32 src_width, u32 src_height, float Gamma); StateCache m_state_cache; GXPipelineState m_gx_state; + + std::array m_clear_blend_states{}; + std::array m_clear_depth_states{}; + ID3D11BlendState* m_reset_blend_state = nullptr; + ID3D11DepthStencilState* m_reset_depth_state = nullptr; + ID3D11RasterizerState* m_reset_rast_state = nullptr; + + ID3D11Texture2D* m_screenshot_texture = nullptr; + D3DTexture2D* m_3d_vision_texture = nullptr; + + u32 m_last_multisamples = 1; + bool m_last_stereo_mode = false; + bool m_last_fullscreen_mode = false; }; } From c5a89b648383ee5ef3f4fe8285fbd2fcf71e3aad Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 21 Nov 2017 19:55:14 +1000 Subject: [PATCH 03/16] D3D: Remove BeginFrame/EndFrame These functions did not do anything anyway. There is also no need to present the backbuffer when shutting down. --- Source/Core/VideoBackends/D3D/D3DBase.cpp | 23 ----------------------- Source/Core/VideoBackends/D3D/D3DBase.h | 2 -- Source/Core/VideoBackends/D3D/Render.cpp | 5 ----- 3 files changed, 30 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/D3DBase.cpp b/Source/Core/VideoBackends/D3D/D3DBase.cpp index 4cf5fb7524..e32ef95576 100644 --- a/Source/Core/VideoBackends/D3D/D3DBase.cpp +++ b/Source/Core/VideoBackends/D3D/D3DBase.cpp @@ -52,8 +52,6 @@ const D3D_FEATURE_LEVEL supported_feature_levels[NUM_SUPPORTED_FEATURE_LEVELS] = unsigned int xres, yres; -bool bFrameInProgress = false; - HRESULT LoadDXGI() { if (dxgi_dll_ref++ > 0) @@ -602,27 +600,6 @@ void Reset() SetDebugObjectName(backbuf->GetRTV(), "backbuffer render target view"); } -bool BeginFrame() -{ - if (bFrameInProgress) - { - PanicAlert("BeginFrame called although a frame is already in progress"); - return false; - } - bFrameInProgress = true; - return (device != nullptr); -} - -void EndFrame() -{ - if (!bFrameInProgress) - { - PanicAlert("EndFrame called although no frame is in progress"); - return; - } - bFrameInProgress = false; -} - void Present() { UINT present_flags = 0; diff --git a/Source/Core/VideoBackends/D3D/D3DBase.h b/Source/Core/VideoBackends/D3D/D3DBase.h index 36db9fae3d..6d5e9cc8c9 100644 --- a/Source/Core/VideoBackends/D3D/D3DBase.h +++ b/Source/Core/VideoBackends/D3D/D3DBase.h @@ -63,8 +63,6 @@ extern HWND hWnd; extern bool bFrameInProgress; void Reset(); -bool BeginFrame(); -void EndFrame(); void Present(); unsigned int GetBackBufferWidth(); diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 762404d581..a14a71a24a 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -81,14 +81,11 @@ Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferH D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)m_target_width, (float)m_target_height); D3D::context->RSSetViewports(1, &vp); FramebufferManager::BindEFBRenderTarget(); - D3D::BeginFrame(); } Renderer::~Renderer() { TeardownDeviceObjects(); - D3D::EndFrame(); - D3D::Present(); D3D::Close(); } @@ -607,7 +604,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region Renderer::DrawDebugText(); OSD::DrawMessages(); - D3D::EndFrame(); g_texture_cache->Cleanup(frameCount); @@ -668,7 +664,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // begin next frame RestoreAPIState(); - D3D::BeginFrame(); FramebufferManager::BindEFBRenderTarget(); SetViewport(); } From 5860c97144a01c4203e98671e158086c672bcf97 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 21 Nov 2017 19:56:22 +1000 Subject: [PATCH 04/16] D3D: Move device release from Renderer destructor to shutdown Necessary if we wish to have GPU objects in our base class, as otherwise the device will be released before the objects. --- Source/Core/VideoBackends/D3D/Render.cpp | 1 - Source/Core/VideoBackends/D3D/main.cpp | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index a14a71a24a..3c27a41d85 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -86,7 +86,6 @@ Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferH Renderer::~Renderer() { TeardownDeviceObjects(); - D3D::Close(); } void Renderer::SetupDeviceObjects() diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp index a146a4d25a..f29e079d61 100644 --- a/Source/Core/VideoBackends/D3D/main.cpp +++ b/Source/Core/VideoBackends/D3D/main.cpp @@ -181,6 +181,8 @@ void VideoBackend::Shutdown() g_texture_cache.reset(); g_renderer.reset(); + D3D::Close(); + ShutdownShared(); } From 49a9c33bd75df2b8cebd58eab10f140c79a8f65c Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 30 Sep 2017 16:25:36 +1000 Subject: [PATCH 05/16] VideoCommon: Move abstract texture creation function to Renderer --- Source/Core/VideoBackends/D3D/Render.cpp | 5 +++++ Source/Core/VideoBackends/D3D/Render.h | 2 ++ Source/Core/VideoBackends/D3D/TextureCache.cpp | 5 ----- Source/Core/VideoBackends/D3D/TextureCache.h | 2 -- Source/Core/VideoBackends/Null/Render.cpp | 6 ++++++ Source/Core/VideoBackends/Null/Render.h | 2 ++ Source/Core/VideoBackends/Null/TextureCache.h | 6 ------ Source/Core/VideoBackends/OGL/OGLTexture.cpp | 1 - Source/Core/VideoBackends/OGL/Render.cpp | 5 +++++ Source/Core/VideoBackends/OGL/Render.h | 2 ++ Source/Core/VideoBackends/OGL/TextureCache.cpp | 5 ----- Source/Core/VideoBackends/OGL/TextureCache.h | 1 - Source/Core/VideoBackends/Software/SWRenderer.cpp | 6 ++++++ Source/Core/VideoBackends/Software/SWRenderer.h | 2 ++ Source/Core/VideoBackends/Software/TextureCache.h | 5 ----- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 5 +++++ Source/Core/VideoBackends/Vulkan/Renderer.h | 2 ++ Source/Core/VideoBackends/Vulkan/TextureCache.cpp | 5 ----- Source/Core/VideoBackends/Vulkan/TextureCache.h | 2 -- Source/Core/VideoCommon/RenderBase.cpp | 2 +- Source/Core/VideoCommon/RenderBase.h | 3 +++ Source/Core/VideoCommon/TextureCacheBase.cpp | 2 +- Source/Core/VideoCommon/TextureCacheBase.h | 2 -- Source/Core/VideoCommon/TextureConfig.h | 7 +++++++ 24 files changed, 49 insertions(+), 36 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 3c27a41d85..0bddc4cd40 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -211,6 +211,11 @@ void Renderer::Create3DVisionTexture(int width, int height) DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, &sys_data); } +std::unique_ptr Renderer::CreateTexture(const TextureConfig& config) +{ + return std::make_unique(config); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { D3D::DrawTextScaled(static_cast(left + 1), static_cast(top + 1), 20.f, 0.0f, diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index 333ff9a80c..527fd1f4fc 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -22,6 +22,8 @@ public: ~Renderer() override; StateCache& GetStateCache() { return m_state_cache; } + std::unique_ptr CreateTexture(const TextureConfig& config) override; + void SetBlendingState(const BlendingState& state) override; void SetScissorRect(const EFBRectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; diff --git a/Source/Core/VideoBackends/D3D/TextureCache.cpp b/Source/Core/VideoBackends/D3D/TextureCache.cpp index 578a488bde..7d9d62cf54 100644 --- a/Source/Core/VideoBackends/D3D/TextureCache.cpp +++ b/Source/Core/VideoBackends/D3D/TextureCache.cpp @@ -33,11 +33,6 @@ static const size_t MAX_COPY_BUFFERS = 32; static ID3D11Buffer* s_efbcopycbuf[MAX_COPY_BUFFERS] = {0}; static std::unique_ptr g_encoder; -std::unique_ptr TextureCache::CreateTexture(const TextureConfig& config) -{ - return std::make_unique(config); -} - void TextureCache::CopyEFB(u8* dst, const EFBCopyParams& params, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect, bool scale_by_half) diff --git a/Source/Core/VideoBackends/D3D/TextureCache.h b/Source/Core/VideoBackends/D3D/TextureCache.h index afb3a1421d..cfe2efdf65 100644 --- a/Source/Core/VideoBackends/D3D/TextureCache.h +++ b/Source/Core/VideoBackends/D3D/TextureCache.h @@ -19,8 +19,6 @@ public: ~TextureCache(); private: - std::unique_ptr CreateTexture(const TextureConfig& config) override; - u64 EncodeToRamFromTexture(u32 address, void* source_texture, u32 SourceW, u32 SourceH, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) diff --git a/Source/Core/VideoBackends/Null/Render.cpp b/Source/Core/VideoBackends/Null/Render.cpp index b1a88f7a80..6b045a0c6a 100644 --- a/Source/Core/VideoBackends/Null/Render.cpp +++ b/Source/Core/VideoBackends/Null/Render.cpp @@ -4,6 +4,7 @@ #include "Common/Logging/Log.h" +#include "VideoBackends/Null/NullTexture.h" #include "VideoBackends/Null/Render.h" #include "VideoCommon/VideoConfig.h" @@ -21,6 +22,11 @@ Renderer::~Renderer() UpdateActiveConfig(); } +std::unique_ptr Renderer::CreateTexture(const TextureConfig& config) +{ + return std::make_unique(config); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { NOTICE_LOG(VIDEO, "RenderText: %s", text.c_str()); diff --git a/Source/Core/VideoBackends/Null/Render.h b/Source/Core/VideoBackends/Null/Render.h index 2a21918f81..9ba2ef575b 100644 --- a/Source/Core/VideoBackends/Null/Render.h +++ b/Source/Core/VideoBackends/Null/Render.h @@ -14,6 +14,8 @@ public: Renderer(); ~Renderer() override; + std::unique_ptr CreateTexture(const TextureConfig& config) override; + void RenderText(const std::string& pstr, int left, int top, u32 color) override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override { return 0; } void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {} diff --git a/Source/Core/VideoBackends/Null/TextureCache.h b/Source/Core/VideoBackends/Null/TextureCache.h index 2b3283c056..6f09108392 100644 --- a/Source/Core/VideoBackends/Null/TextureCache.h +++ b/Source/Core/VideoBackends/Null/TextureCache.h @@ -35,12 +35,6 @@ public: bool scale_by_half, unsigned int cbuf_id, const float* colmat) override { } - -private: - std::unique_ptr CreateTexture(const TextureConfig& config) override - { - return std::make_unique(config); - } }; } // Null name space diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.cpp b/Source/Core/VideoBackends/OGL/OGLTexture.cpp index 659cc9764b..40295f6f28 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.cpp +++ b/Source/Core/VideoBackends/OGL/OGLTexture.cpp @@ -9,7 +9,6 @@ #include "VideoBackends/OGL/FramebufferManager.h" #include "VideoBackends/OGL/OGLTexture.h" -#include "VideoBackends/OGL/Render.h" #include "VideoBackends/OGL/SamplerCache.h" #include "VideoBackends/OGL/TextureCache.h" diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index ac662e456b..fff40f7abf 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -814,6 +814,11 @@ void Renderer::Init() OpenGL_CreateAttributelessVAO(); } +std::unique_ptr Renderer::CreateTexture(const TextureConfig& config) +{ + return std::make_unique(config); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { u32 backbuffer_width = std::max(GLInterface->GetBackBufferWidth(), 1u); diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index 725cc7a639..2e60df3f6d 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -77,6 +77,8 @@ public: void Init(); void Shutdown(); + std::unique_ptr CreateTexture(const TextureConfig& config) override; + void SetBlendingState(const BlendingState& state) override; void SetScissorRect(const EFBRectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp index 8e4811bbc1..ba945f8043 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.cpp +++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp @@ -33,11 +33,6 @@ namespace OGL { //#define TIME_TEXTURE_DECODING 1 -std::unique_ptr TextureCache::CreateTexture(const TextureConfig& config) -{ - return std::make_unique(config); -} - void TextureCache::CopyEFB(u8* dst, const EFBCopyParams& params, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect, bool scale_by_half) diff --git a/Source/Core/VideoBackends/OGL/TextureCache.h b/Source/Core/VideoBackends/OGL/TextureCache.h index ca9038f296..ae848c8c9c 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.h +++ b/Source/Core/VideoBackends/OGL/TextureCache.h @@ -59,7 +59,6 @@ private: bool valid = false; }; - std::unique_ptr CreateTexture(const TextureConfig& config) override; void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, const void* palette, TLUTFormat format) override; diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index 69523efe78..ea7ccaba7c 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -14,6 +14,7 @@ #include "VideoBackends/Software/EfbCopy.h" #include "VideoBackends/Software/EfbInterface.h" #include "VideoBackends/Software/SWOGLWindow.h" +#include "VideoBackends/Software/SWTexture.h" #include "VideoCommon/BoundingBox.h" #include "VideoCommon/OnScreenDisplay.h" @@ -34,6 +35,11 @@ void SWRenderer::Shutdown() UpdateActiveConfig(); } +std::unique_ptr SWRenderer::CreateTexture(const TextureConfig& config) +{ + return std::make_unique(config); +} + void SWRenderer::RenderText(const std::string& pstr, int left, int top, u32 color) { SWOGLWindow::s_instance->PrintText(pstr, left, top, color); diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h index a87531fcab..ac8764e0c7 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -16,6 +16,8 @@ public: static void Init(); static void Shutdown(); + std::unique_ptr CreateTexture(const TextureConfig& config) override; + void RenderText(const std::string& pstr, int left, int top, u32 color) override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {} diff --git a/Source/Core/VideoBackends/Software/TextureCache.h b/Source/Core/VideoBackends/Software/TextureCache.h index 3573d36ebb..49612394c2 100644 --- a/Source/Core/VideoBackends/Software/TextureCache.h +++ b/Source/Core/VideoBackends/Software/TextureCache.h @@ -25,11 +25,6 @@ public: } private: - std::unique_ptr CreateTexture(const TextureConfig& config) override - { - return std::make_unique(config); - } - void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect, bool scale_by_half, unsigned int cbuf_id, const float* colmat) override { diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 8f83e2b202..0bb1640f47 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -159,6 +159,11 @@ void Renderer::DestroySemaphores() } } +std::unique_ptr Renderer::CreateTexture(const TextureConfig& config) +{ + return VKTexture::Create(config); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { u32 backbuffer_width = m_swap_chain->GetWidth(); diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index e8b7952e7d..250e76634d 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -32,6 +32,8 @@ public: static Renderer* GetInstance(); + std::unique_ptr CreateTexture(const TextureConfig& config) override; + SwapChain* GetSwapChain() const { return m_swap_chain.get(); } BoundingBox* GetBoundingBox() const { return m_bounding_box.get(); } bool Initialize(); diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index 1d8221f646..5a396ee4b2 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -178,11 +178,6 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u } } -std::unique_ptr TextureCache::CreateTexture(const TextureConfig& config) -{ - return VKTexture::Create(config); -} - bool TextureCache::CreateRenderPasses() { static constexpr VkAttachmentDescription update_attachment = { diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.h b/Source/Core/VideoBackends/Vulkan/TextureCache.h index 9bfa2da04e..12bf088ad1 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.h +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.h @@ -31,8 +31,6 @@ public: bool CompileShaders() override; void DeleteShaders() override; - std::unique_ptr CreateTexture(const TextureConfig& config) override; - void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, const void* palette, TLUTFormat format) override; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 4a46475344..6f3e23923c 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -731,7 +731,7 @@ void Renderer::UpdateFrameDumpTexture() config.width = target_width; config.height = target_height; config.rendertarget = true; - m_dump_texture = g_texture_cache->CreateTexture(config); + m_dump_texture = CreateTexture(config); } m_dump_texture->CopyRectangleFromTexture(m_last_xfb_texture, m_last_xfb_region, EFBRectangle{0, 0, target_width, target_height}); diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index b622372b90..7158f3f18e 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -35,6 +35,7 @@ class AbstractRawTexture; class AbstractTexture; class PostProcessingShaderImplementation; +struct TextureConfig; enum class EFBAccessType; struct EfbPokeData @@ -79,6 +80,8 @@ public: virtual void RestoreState() {} virtual void ResetAPIState() {} virtual void RestoreAPIState() {} + virtual std::unique_ptr CreateTexture(const TextureConfig& config) = 0; + // Ideal internal resolution - multiple of the native EFB resolution int GetTargetWidth() const { return m_target_width; } int GetTargetHeight() const { return m_target_height; } diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 7e3f7b25c0..521f5816f1 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -2090,7 +2090,7 @@ std::unique_ptr TextureCacheBase::AllocateTexture(const Texture } else { - entry = CreateTexture(config); + entry = g_renderer->CreateTexture(config); if (!entry) return nullptr; diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index a9700ad68c..38666faf77 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -273,8 +273,6 @@ public: void ScaleTextureCacheEntryTo(TCacheEntry* entry, u32 new_width, u32 new_height); - virtual std::unique_ptr CreateTexture(const TextureConfig& config) = 0; - protected: TextureCacheBase(); diff --git a/Source/Core/VideoCommon/TextureConfig.h b/Source/Core/VideoCommon/TextureConfig.h index 86ea88f651..fa2d60c494 100644 --- a/Source/Core/VideoCommon/TextureConfig.h +++ b/Source/Core/VideoCommon/TextureConfig.h @@ -22,6 +22,13 @@ enum class AbstractTextureFormat : u32 struct TextureConfig { constexpr TextureConfig() = default; + constexpr TextureConfig(u32 width_, u32 height_, u32 levels_, u32 layers_, + AbstractTextureFormat format_, bool rendertarget_) + : width(width_), height(height_), levels(levels_), layers(layers_), format(format_), + rendertarget(rendertarget_) + { + } + bool operator==(const TextureConfig& o) const; MathUtil::Rectangle GetRect() const; From a584ccc7d882213d93586cae9063d9865311aaad Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 9 Sep 2017 15:24:41 +1000 Subject: [PATCH 06/16] AbstractTexture: Support BGRA8 formats Used for some driver's swap chains, and EFB to RAM. --- Source/Core/VideoBackends/D3D/DXTexture.cpp | 4 ++++ Source/Core/VideoBackends/OGL/OGLTexture.cpp | 7 +++++++ Source/Core/VideoBackends/Vulkan/Util.cpp | 6 ++++++ Source/Core/VideoCommon/AbstractTexture.cpp | 20 ++++++++++++++++---- Source/Core/VideoCommon/TextureConfig.h | 4 +++- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/DXTexture.cpp b/Source/Core/VideoBackends/D3D/DXTexture.cpp index 5f7a4e39eb..30f664b5b4 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.cpp +++ b/Source/Core/VideoBackends/D3D/DXTexture.cpp @@ -40,7 +40,11 @@ DXGI_FORMAT GetDXGIFormatForHostFormat(AbstractTextureFormat format) case AbstractTextureFormat::BPTC: return DXGI_FORMAT_BC7_UNORM; case AbstractTextureFormat::RGBA8: + return DXGI_FORMAT_R8G8B8A8_UNORM; + case AbstractTextureFormat::BGRA8: + return DXGI_FORMAT_B8G8R8A8_UNORM; default: + PanicAlert("Unhandled texture format."); return DXGI_FORMAT_R8G8B8A8_UNORM; } } diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.cpp b/Source/Core/VideoBackends/OGL/OGLTexture.cpp index 40295f6f28..272a879090 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.cpp +++ b/Source/Core/VideoBackends/OGL/OGLTexture.cpp @@ -35,7 +35,11 @@ GLenum GetGLInternalFormatForTextureFormat(AbstractTextureFormat format, bool st case AbstractTextureFormat::BPTC: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; case AbstractTextureFormat::RGBA8: + return storage ? GL_RGBA8 : GL_RGBA; + case AbstractTextureFormat::BGRA8: + return GL_BGRA; default: + PanicAlert("Unhandled texture format."); return storage ? GL_RGBA8 : GL_RGBA; } } @@ -46,6 +50,8 @@ GLenum GetGLFormatForTextureFormat(AbstractTextureFormat format) { case AbstractTextureFormat::RGBA8: return GL_RGBA; + case AbstractTextureFormat::BGRA8: + return GL_BGRA; // Compressed texture formats don't use this parameter. default: return GL_UNSIGNED_BYTE; @@ -57,6 +63,7 @@ GLenum GetGLTypeForTextureFormat(AbstractTextureFormat format) switch (format) { case AbstractTextureFormat::RGBA8: + case AbstractTextureFormat::BGRA8: return GL_UNSIGNED_BYTE; // Compressed texture formats don't use this parameter. default: diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp index 221fa97cda..e74435e6d6 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.cpp +++ b/Source/Core/VideoBackends/Vulkan/Util.cpp @@ -107,7 +107,13 @@ VkFormat GetVkFormatForHostTextureFormat(AbstractTextureFormat format) return VK_FORMAT_BC7_UNORM_BLOCK; case AbstractTextureFormat::RGBA8: + return VK_FORMAT_R8G8B8A8_UNORM; + + case AbstractTextureFormat::BGRA8: + return VK_FORMAT_B8G8R8A8_UNORM; + default: + PanicAlert("Unhandled texture format."); return VK_FORMAT_R8G8B8A8_UNORM; } } diff --git a/Source/Core/VideoCommon/AbstractTexture.cpp b/Source/Core/VideoCommon/AbstractTexture.cpp index 7e4f493601..9fa099e602 100644 --- a/Source/Core/VideoCommon/AbstractTexture.cpp +++ b/Source/Core/VideoCommon/AbstractTexture.cpp @@ -5,7 +5,7 @@ #include #include "Common/Assert.h" - +#include "Common/MsgHandler.h" #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/ImageWrite.h" @@ -102,8 +102,17 @@ AbstractTexture::MapRegionImpl(u32 level, u32 x, u32 y, u32 width, u32 height) bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format) { - // This will need to be changed if we add any other uncompressed formats. - return format != AbstractTextureFormat::RGBA8; + switch (format) + { + case AbstractTextureFormat::DXT1: + case AbstractTextureFormat::DXT3: + case AbstractTextureFormat::DXT5: + case AbstractTextureFormat::BPTC: + return true; + + default: + return false; + } } size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length) @@ -117,8 +126,11 @@ size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat for case AbstractTextureFormat::BPTC: return static_cast(std::max(1u, row_length / 4)) * 16; case AbstractTextureFormat::RGBA8: - default: + case AbstractTextureFormat::BGRA8: return static_cast(row_length) * 4; + default: + PanicAlert("Unhandled texture format."); + return 0; } } diff --git a/Source/Core/VideoCommon/TextureConfig.h b/Source/Core/VideoCommon/TextureConfig.h index fa2d60c494..ae6e54de5e 100644 --- a/Source/Core/VideoCommon/TextureConfig.h +++ b/Source/Core/VideoCommon/TextureConfig.h @@ -13,10 +13,12 @@ enum class AbstractTextureFormat : u32 { RGBA8, + BGRA8, DXT1, DXT3, DXT5, - BPTC + BPTC, + Undefined }; struct TextureConfig From f43d85921dc85ae586d69b21d28a7578835a55bd Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 22 Oct 2017 00:49:40 +1000 Subject: [PATCH 07/16] VideoBackends: Add AbstractStagingTexture class Can be used for asynchronous readback or upload of textures. --- .../Common/GL/GLExtensions/GLExtensions.cpp | 3 + Source/Core/VideoBackends/D3D/DXTexture.cpp | 134 ++++++++- Source/Core/VideoBackends/D3D/DXTexture.h | 27 ++ Source/Core/VideoBackends/D3D/Render.cpp | 6 + Source/Core/VideoBackends/D3D/Render.h | 2 + .../Core/VideoBackends/Null/NullTexture.cpp | 39 +++ Source/Core/VideoBackends/Null/NullTexture.h | 24 ++ Source/Core/VideoBackends/Null/Render.cpp | 6 + Source/Core/VideoBackends/Null/Render.h | 2 + Source/Core/VideoBackends/OGL/OGLTexture.cpp | 277 +++++++++++++++++- Source/Core/VideoBackends/OGL/OGLTexture.h | 32 ++ Source/Core/VideoBackends/OGL/Render.cpp | 10 +- Source/Core/VideoBackends/OGL/Render.h | 4 +- .../VideoBackends/Software/SWRenderer.cpp | 6 + .../Core/VideoBackends/Software/SWRenderer.h | 2 + .../Core/VideoBackends/Software/SWTexture.cpp | 70 +++++ .../Core/VideoBackends/Software/SWTexture.h | 22 ++ .../Vulkan/CommandBufferManager.cpp | 6 + Source/Core/VideoBackends/Vulkan/Renderer.cpp | 6 + Source/Core/VideoBackends/Vulkan/Renderer.h | 2 + .../Core/VideoBackends/Vulkan/StagingBuffer.h | 2 +- .../Core/VideoBackends/Vulkan/VKTexture.cpp | 222 +++++++++++++- Source/Core/VideoBackends/Vulkan/VKTexture.h | 35 +++ .../VideoCommon/AbstractStagingTexture.cpp | 133 +++++++++ .../Core/VideoCommon/AbstractStagingTexture.h | 86 ++++++ Source/Core/VideoCommon/AbstractTexture.cpp | 23 +- Source/Core/VideoCommon/AbstractTexture.h | 5 +- Source/Core/VideoCommon/CMakeLists.txt | 1 + Source/Core/VideoCommon/RenderBase.h | 4 + Source/Core/VideoCommon/TextureConfig.cpp | 22 ++ Source/Core/VideoCommon/TextureConfig.h | 11 + Source/Core/VideoCommon/VideoCommon.vcxproj | 2 + .../VideoCommon/VideoCommon.vcxproj.filters | 6 + 33 files changed, 1220 insertions(+), 12 deletions(-) create mode 100644 Source/Core/VideoCommon/AbstractStagingTexture.cpp create mode 100644 Source/Core/VideoCommon/AbstractStagingTexture.h diff --git a/Source/Core/Common/GL/GLExtensions/GLExtensions.cpp b/Source/Core/Common/GL/GLExtensions/GLExtensions.cpp index 80670b981d..f326e46365 100644 --- a/Source/Core/Common/GL/GLExtensions/GLExtensions.cpp +++ b/Source/Core/Common/GL/GLExtensions/GLExtensions.cpp @@ -1870,6 +1870,9 @@ const GLFunc gl_function_array[] = { GLFUNC_REQUIRES(glDispatchCompute, "GL_ARB_compute_shader !VERSION_4_3 |VERSION_GLES_3_1"), GLFUNC_REQUIRES(glDispatchComputeIndirect, "GL_ARB_compute_shader !VERSION_4_3 |VERSION_GLES_3_1"), + + // ARB_get_texture_sub_image + GLFUNC_REQUIRES(glGetTextureSubImage, "GL_ARB_get_texture_sub_image !VERSION_4_5"), }; namespace GLExtensions diff --git a/Source/Core/VideoBackends/D3D/DXTexture.cpp b/Source/Core/VideoBackends/D3D/DXTexture.cpp index 30f664b5b4..92d3d112e9 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.cpp +++ b/Source/Core/VideoBackends/D3D/DXTexture.cpp @@ -217,8 +217,140 @@ void DXTexture::CopyRectangleFromTexture(const AbstractTexture* source, void DXTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) { - size_t src_pitch = CalculateHostTextureLevelPitch(m_config.format, row_length); + size_t src_pitch = CalculateStrideForFormat(m_config.format, row_length); D3D::context->UpdateSubresource(m_texture->GetTex(), level, nullptr, buffer, static_cast(src_pitch), 0); } + +DXStagingTexture::DXStagingTexture(StagingTextureType type, const TextureConfig& config, + ID3D11Texture2D* tex) + : AbstractStagingTexture(type, config), m_tex(tex) +{ +} + +DXStagingTexture::~DXStagingTexture() +{ + if (IsMapped()) + DXStagingTexture::Unmap(); + SAFE_RELEASE(m_tex); +} + +std::unique_ptr DXStagingTexture::Create(StagingTextureType type, + const TextureConfig& config) +{ + D3D11_USAGE usage; + UINT cpu_flags; + if (type == StagingTextureType::Readback) + { + usage = D3D11_USAGE_STAGING; + cpu_flags = D3D11_CPU_ACCESS_READ; + } + else if (type == StagingTextureType::Upload) + { + usage = D3D11_USAGE_DYNAMIC; + cpu_flags = D3D11_CPU_ACCESS_WRITE; + } + else + { + usage = D3D11_USAGE_STAGING; + cpu_flags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; + } + + CD3D11_TEXTURE2D_DESC desc(GetDXGIFormatForHostFormat(config.format), config.width, config.height, + 1, 1, 0, usage, cpu_flags); + + ID3D11Texture2D* texture; + HRESULT hr = D3D::device->CreateTexture2D(&desc, nullptr, &texture); + CHECK(SUCCEEDED(hr), "Create staging texture"); + if (FAILED(hr)) + return nullptr; + + return std::unique_ptr(new DXStagingTexture(type, config, texture)); +} + +void DXStagingTexture::CopyFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect) +{ + _assert_(m_type == StagingTextureType::Readback); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); + _assert_(src_rect.left >= 0 && static_cast(src_rect.right) <= src->GetConfig().width && + src_rect.top >= 0 && static_cast(src_rect.bottom) <= src->GetConfig().height); + _assert_(dst_rect.left >= 0 && static_cast(dst_rect.right) <= m_config.width && + dst_rect.top >= 0 && static_cast(dst_rect.bottom) <= m_config.height); + + if (IsMapped()) + DXStagingTexture::Unmap(); + + CD3D11_BOX src_box(src_rect.left, src_rect.top, 0, src_rect.right, src_rect.bottom, 1); + D3D::context->CopySubresourceRegion( + m_tex, 0, static_cast(dst_rect.left), static_cast(dst_rect.top), 0, + static_cast(src)->GetRawTexIdentifier()->GetTex(), + D3D11CalcSubresource(src_level, src_layer, src->GetConfig().levels), &src_box); + + m_needs_flush = true; +} + +void DXStagingTexture::CopyToTexture(const MathUtil::Rectangle& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) +{ + _assert_(m_type == StagingTextureType::Upload); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); + _assert_(src_rect.left >= 0 && static_cast(src_rect.right) <= m_config.width && + src_rect.top >= 0 && static_cast(src_rect.bottom) <= m_config.height); + _assert_(dst_rect.left >= 0 && static_cast(dst_rect.right) <= dst->GetConfig().width && + dst_rect.top >= 0 && static_cast(dst_rect.bottom) <= dst->GetConfig().height); + + if (IsMapped()) + DXStagingTexture::Unmap(); + + CD3D11_BOX src_box(src_rect.left, src_rect.top, 0, src_rect.right, src_rect.bottom, 1); + D3D::context->CopySubresourceRegion( + static_cast(dst)->GetRawTexIdentifier()->GetTex(), + D3D11CalcSubresource(dst_level, dst_layer, dst->GetConfig().levels), + static_cast(dst_rect.left), static_cast(dst_rect.top), 0, m_tex, 0, &src_box); +} + +bool DXStagingTexture::Map() +{ + if (m_map_pointer) + return true; + + D3D11_MAP map_type; + if (m_type == StagingTextureType::Readback) + map_type = D3D11_MAP_READ; + else if (m_type == StagingTextureType::Upload) + map_type = D3D11_MAP_WRITE; + else + map_type = D3D11_MAP_READ_WRITE; + + D3D11_MAPPED_SUBRESOURCE sr; + HRESULT hr = D3D::context->Map(m_tex, 0, map_type, 0, &sr); + CHECK(SUCCEEDED(hr), "Map readback texture"); + if (FAILED(hr)) + return false; + + m_map_pointer = reinterpret_cast(sr.pData); + m_map_stride = sr.RowPitch; + return true; +} + +void DXStagingTexture::Unmap() +{ + if (!m_map_pointer) + return; + + D3D::context->Unmap(m_tex, 0); + m_map_pointer = nullptr; +} + +void DXStagingTexture::Flush() +{ + // Flushing is handled by the API. + m_needs_flush = false; +} + } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/DXTexture.h b/Source/Core/VideoBackends/D3D/DXTexture.h index 8dfffd38d5..4403eb2d43 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.h +++ b/Source/Core/VideoBackends/D3D/DXTexture.h @@ -6,6 +6,7 @@ #include "Common/CommonTypes.h" +#include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" class D3DTexture2D; @@ -38,4 +39,30 @@ private: ID3D11Texture2D* m_staging_texture = nullptr; }; +class DXStagingTexture final : public AbstractStagingTexture +{ +public: + DXStagingTexture() = delete; + ~DXStagingTexture(); + + void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, + u32 src_layer, u32 src_level, + const MathUtil::Rectangle& dst_rect) override; + void CopyToTexture(const MathUtil::Rectangle& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) override; + + bool Map() override; + void Unmap() override; + void Flush() override; + + static std::unique_ptr Create(StagingTextureType type, + const TextureConfig& config); + +private: + DXStagingTexture(StagingTextureType type, const TextureConfig& config, ID3D11Texture2D* tex); + + ID3D11Texture2D* m_tex = nullptr; +}; + } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 0bddc4cd40..a7518cdf64 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -216,6 +216,12 @@ std::unique_ptr Renderer::CreateTexture(const TextureConfig& co return std::make_unique(config); } +std::unique_ptr Renderer::CreateStagingTexture(StagingTextureType type, + const TextureConfig& config) +{ + return DXStagingTexture::Create(type, config); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { D3D::DrawTextScaled(static_cast(left + 1), static_cast(top + 1), 20.f, 0.0f, diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index 527fd1f4fc..4668d54a92 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -23,6 +23,8 @@ public: StateCache& GetStateCache() { return m_state_cache; } std::unique_ptr CreateTexture(const TextureConfig& config) override; + std::unique_ptr + CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; void SetBlendingState(const BlendingState& state) override; void SetScissorRect(const EFBRectangle& rc) override; diff --git a/Source/Core/VideoBackends/Null/NullTexture.cpp b/Source/Core/VideoBackends/Null/NullTexture.cpp index 7d852f7ddf..b5363d3918 100644 --- a/Source/Core/VideoBackends/Null/NullTexture.cpp +++ b/Source/Core/VideoBackends/Null/NullTexture.cpp @@ -25,4 +25,43 @@ void NullTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u { } +NullStagingTexture::NullStagingTexture(StagingTextureType type, const TextureConfig& config) + : AbstractStagingTexture(type, config) +{ + m_texture_buf.resize(m_texel_size * config.width * config.height); + m_map_pointer = reinterpret_cast(m_texture_buf.data()); + m_map_stride = m_texel_size * config.width; +} + +NullStagingTexture::~NullStagingTexture() = default; + +void NullStagingTexture::CopyFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect) +{ + m_needs_flush = true; +} + +void NullStagingTexture::CopyToTexture(const MathUtil::Rectangle& src_rect, + AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) +{ + m_needs_flush = true; +} + +bool NullStagingTexture::Map() +{ + return true; +} + +void NullStagingTexture::Unmap() +{ +} + +void NullStagingTexture::Flush() +{ + m_needs_flush = false; +} + } // namespace Null diff --git a/Source/Core/VideoBackends/Null/NullTexture.h b/Source/Core/VideoBackends/Null/NullTexture.h index 65f4252050..81227e162f 100644 --- a/Source/Core/VideoBackends/Null/NullTexture.h +++ b/Source/Core/VideoBackends/Null/NullTexture.h @@ -4,8 +4,11 @@ #pragma once +#include + #include "Common/CommonTypes.h" +#include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" namespace Null @@ -25,4 +28,25 @@ public: size_t buffer_size) override; }; +class NullStagingTexture final : public AbstractStagingTexture +{ +public: + explicit NullStagingTexture(StagingTextureType type, const TextureConfig& config); + ~NullStagingTexture(); + + void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, + u32 src_layer, u32 src_level, + const MathUtil::Rectangle& dst_rect) override; + void CopyToTexture(const MathUtil::Rectangle& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) override; + + bool Map() override; + void Unmap() override; + void Flush() override; + +private: + std::vector m_texture_buf; +}; + } // namespace Null diff --git a/Source/Core/VideoBackends/Null/Render.cpp b/Source/Core/VideoBackends/Null/Render.cpp index 6b045a0c6a..c589c8278f 100644 --- a/Source/Core/VideoBackends/Null/Render.cpp +++ b/Source/Core/VideoBackends/Null/Render.cpp @@ -27,6 +27,12 @@ std::unique_ptr Renderer::CreateTexture(const TextureConfig& co return std::make_unique(config); } +std::unique_ptr Renderer::CreateStagingTexture(StagingTextureType type, + const TextureConfig& config) +{ + return std::make_unique(type, config); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { NOTICE_LOG(VIDEO, "RenderText: %s", text.c_str()); diff --git a/Source/Core/VideoBackends/Null/Render.h b/Source/Core/VideoBackends/Null/Render.h index 9ba2ef575b..32b1d560e5 100644 --- a/Source/Core/VideoBackends/Null/Render.h +++ b/Source/Core/VideoBackends/Null/Render.h @@ -15,6 +15,8 @@ public: ~Renderer() override; std::unique_ptr CreateTexture(const TextureConfig& config) override; + std::unique_ptr + CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; void RenderText(const std::string& pstr, int left, int top, u32 color) override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override { return 0; } diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.cpp b/Source/Core/VideoBackends/OGL/OGLTexture.cpp index 272a879090..f6390381c6 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.cpp +++ b/Source/Core/VideoBackends/OGL/OGLTexture.cpp @@ -37,7 +37,7 @@ GLenum GetGLInternalFormatForTextureFormat(AbstractTextureFormat format, bool st case AbstractTextureFormat::RGBA8: return storage ? GL_RGBA8 : GL_RGBA; case AbstractTextureFormat::BGRA8: - return GL_BGRA; + return storage ? GL_RGBA8 : GL_BGRA; default: PanicAlert("Unhandled texture format."); return storage ? GL_RGBA8 : GL_RGBA; @@ -70,6 +70,15 @@ GLenum GetGLTypeForTextureFormat(AbstractTextureFormat format) return GL_UNSIGNED_BYTE; } } + +bool UsePersistentStagingBuffers() +{ + // We require ARB_buffer_storage to create the persistent mapped buffer, + // ARB_shader_image_load_store for glMemoryBarrier, and ARB_sync to ensure + // the GPU has finished the copy before reading the buffer from the CPU. + return g_ogl_config.bSupportsGLBufferStorage && g_ogl_config.bSupportsImageLoadStore && + g_ogl_config.bSupportsGLSync; +} } // Anonymous namespace OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config) @@ -91,7 +100,7 @@ OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_co if (m_config.rendertarget) { // We can't render to compressed formats. - _assert_(!IsCompressedHostTextureFormat(m_config.format)); + _assert_(!IsCompressedFormat(m_config.format)); if (!g_ogl_config.bSupportsTextureStorage) { @@ -106,6 +115,10 @@ OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_co FramebufferManager::SetFramebuffer(m_framebuffer); FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_ARRAY, m_texId, 0); + + // We broke the framebuffer binding here, and need to restore it, as the CreateTexture + // method is in the base renderer class and can be called by VideoCommon. + FramebufferManager::SetFramebuffer(0); } SetStage(); @@ -275,7 +288,7 @@ void OGLTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8 glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length); GLenum gl_internal_format = GetGLInternalFormatForTextureFormat(m_config.format, false); - if (IsCompressedHostTextureFormat(m_config.format)) + if (IsCompressedFormat(m_config.format)) { if (g_ogl_config.bSupportsTextureStorage) { @@ -321,4 +334,262 @@ void OGLTexture::SetStage() glActiveTexture(GL_TEXTURE0 + s_ActiveTexture); } +OGLStagingTexture::OGLStagingTexture(StagingTextureType type, const TextureConfig& config, + GLenum target, GLuint buffer_name, size_t buffer_size, + char* map_ptr, size_t map_stride) + : AbstractStagingTexture(type, config), m_target(target), m_buffer_name(buffer_name), + m_buffer_size(buffer_size) +{ + m_map_pointer = map_ptr; + m_map_stride = map_stride; +} + +OGLStagingTexture::~OGLStagingTexture() +{ + if (m_fence != 0) + glDeleteSync(m_fence); + if (m_map_pointer) + { + glBindBuffer(GL_PIXEL_PACK_BUFFER, m_buffer_name); + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + } + if (m_buffer_name != 0) + glDeleteBuffers(1, &m_buffer_name); +} + +std::unique_ptr OGLStagingTexture::Create(StagingTextureType type, + const TextureConfig& config) +{ + size_t stride = config.GetStride(); + size_t buffer_size = stride * config.height; + GLenum target = + type == StagingTextureType::Readback ? GL_PIXEL_PACK_BUFFER : GL_PIXEL_UNPACK_BUFFER; + GLuint buffer; + glGenBuffers(1, &buffer); + glBindBuffer(target, buffer); + + // Prefer using buffer_storage where possible. This allows us to skip the map/unmap steps. + char* buffer_ptr; + if (UsePersistentStagingBuffers()) + { + GLenum buffer_flags; + GLenum map_flags; + if (type == StagingTextureType::Readback) + { + buffer_flags = GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT; + map_flags = GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT; + } + else if (type == StagingTextureType::Upload) + { + buffer_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT; + map_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; + } + else + { + buffer_flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT; + map_flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT; + } + + glBufferStorage(target, buffer_size, nullptr, buffer_flags); + buffer_ptr = + reinterpret_cast(glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, buffer_size, map_flags)); + _assert_(buffer_ptr != nullptr); + } + else + { + // Otherwise, fallback to mapping the buffer each time. + glBufferData(target, buffer_size, nullptr, + type == StagingTextureType::Readback ? GL_STREAM_READ : GL_STREAM_DRAW); + buffer_ptr = nullptr; + } + glBindBuffer(target, 0); + + return std::unique_ptr( + new OGLStagingTexture(type, config, target, buffer, buffer_size, buffer_ptr, stride)); +} + +void OGLStagingTexture::CopyFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect) +{ + _assert_(m_type == StagingTextureType::Readback); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); + _assert_(src_rect.left >= 0 && static_cast(src_rect.right) <= src->GetConfig().width && + src_rect.top >= 0 && static_cast(src_rect.bottom) <= src->GetConfig().height); + _assert_(dst_rect.left >= 0 && static_cast(dst_rect.right) <= m_config.width && + dst_rect.top >= 0 && static_cast(dst_rect.bottom) <= m_config.height); + + // Unmap the buffer before writing when not using persistent mappings. + if (!UsePersistentStagingBuffers()) + OGLStagingTexture::Unmap(); + + // Copy from the texture object to the staging buffer. + glBindBuffer(GL_PIXEL_PACK_BUFFER, m_buffer_name); + glPixelStorei(GL_PACK_ROW_LENGTH, m_config.width); + + const OGLTexture* gltex = static_cast(src); + size_t dst_offset = dst_rect.top * m_config.GetStride() + dst_rect.left * m_texel_size; + + // If we don't have a FBO associated with this texture, we need to use a slow path. + if (gltex->GetFramebuffer() != 0 && src_layer == 0 && src_level == 0) + { + // This texture has a framebuffer, so we can use glReadPixels(). + glBindFramebuffer(GL_READ_FRAMEBUFFER, gltex->GetFramebuffer()); + glReadPixels(src_rect.left, src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(), + GetGLFormatForTextureFormat(m_config.format), + GetGLTypeForTextureFormat(m_config.format), reinterpret_cast(dst_offset)); + + // Reset both read/draw framebuffers. + glBindFramebuffer(GL_FRAMEBUFFER, FramebufferManager::GetEFBFramebuffer()); + } + else + { + glActiveTexture(GL_TEXTURE9); + glBindTexture(GL_TEXTURE_2D_ARRAY, gltex->GetRawTexIdentifier()); + if (g_ogl_config.bSupportsTextureSubImage) + { + glGetTextureSubImage( + GL_TEXTURE_2D_ARRAY, src_level, src_rect.left, src_rect.top, src_layer, + src_rect.GetWidth(), src_rect.GetHeight(), 1, + GetGLFormatForTextureFormat(m_config.format), GetGLTypeForTextureFormat(m_config.format), + static_cast(m_buffer_size - dst_offset), reinterpret_cast(dst_offset)); + } + else + { + // TODO: Investigate whether it's faster to use glReadPixels() with a framebuffer, since we're + // copying the whole texture, which may waste bandwidth. So we're trading CPU work in creating + // the framebuffer for GPU work in copying potentially redundant texels. + glGetTexImage(GL_TEXTURE_2D_ARRAY, src_level, GetGLFormatForTextureFormat(m_config.format), + GetGLTypeForTextureFormat(m_config.format), nullptr); + } + + OGLTexture::SetStage(); + } + + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + + // If we support buffer storage, create a fence for synchronization. + if (UsePersistentStagingBuffers()) + { + if (m_fence != 0) + glDeleteSync(m_fence); + + glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT); + m_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + + m_needs_flush = true; +} + +void OGLStagingTexture::CopyToTexture(const MathUtil::Rectangle& src_rect, + AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) +{ + _assert_(m_type == StagingTextureType::Upload); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); + _assert_(src_rect.left >= 0 && static_cast(src_rect.right) <= m_config.width && + src_rect.top >= 0 && static_cast(src_rect.bottom) <= m_config.height); + _assert_(dst_rect.left >= 0 && static_cast(dst_rect.right) <= dst->GetConfig().width && + dst_rect.top >= 0 && static_cast(dst_rect.bottom) <= dst->GetConfig().height); + + size_t src_offset = src_rect.top * m_config.GetStride() + src_rect.left * m_texel_size; + size_t copy_size = src_rect.GetHeight() * m_config.GetStride(); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_buffer_name); + glPixelStorei(GL_UNPACK_ROW_LENGTH, m_config.width); + + if (!UsePersistentStagingBuffers()) + { + // Unmap the buffer before writing when not using persistent mappings. + if (m_map_pointer) + { + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + m_map_pointer = nullptr; + } + } + else + { + // Since we're not using coherent mapping, we must flush the range explicitly. + if (m_type == StagingTextureType::Upload) + glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, src_offset, copy_size); + glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT); + } + + // Copy from the staging buffer to the texture object. + glActiveTexture(GL_TEXTURE9); + glBindTexture(GL_TEXTURE_2D_ARRAY, static_cast(dst)->GetRawTexIdentifier()); + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, dst_rect.left, dst_rect.top, dst_layer, + dst_rect.GetWidth(), dst_rect.GetHeight(), 1, + GetGLFormatForTextureFormat(m_config.format), + GetGLTypeForTextureFormat(m_config.format), reinterpret_cast(src_offset)); + OGLTexture::SetStage(); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + // If we support buffer storage, create a fence for synchronization. + if (UsePersistentStagingBuffers()) + { + if (m_fence != 0) + glDeleteSync(m_fence); + + m_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + + m_needs_flush = true; +} + +void OGLStagingTexture::Flush() +{ + // No-op when not using buffer storage, as the transfers happen on Map(). + // m_fence will always be zero in this case. + if (m_fence == 0) + { + m_needs_flush = false; + return; + } + + glClientWaitSync(m_fence, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); + glDeleteSync(m_fence); + m_fence = 0; + m_needs_flush = false; +} + +bool OGLStagingTexture::Map() +{ + if (m_map_pointer) + return true; + + // Slow path, map the texture, unmap it later. + GLenum flags; + if (m_type == StagingTextureType::Readback) + flags = GL_MAP_READ_BIT; + else if (m_type == StagingTextureType::Upload) + flags = GL_MAP_WRITE_BIT; + else + flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT; + glBindBuffer(m_target, m_buffer_name); + m_map_pointer = reinterpret_cast(glMapBufferRange(m_target, 0, m_buffer_size, flags)); + if (!m_map_pointer) + return false; + + return true; +} + +void OGLStagingTexture::Unmap() +{ + // No-op with persistent mapped buffers. + if (!m_map_pointer || UsePersistentStagingBuffers()) + return; + + glBindBuffer(m_target, m_buffer_name); + glUnmapBuffer(m_target); + glBindBuffer(m_target, 0); + m_map_pointer = nullptr; +} + } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.h b/Source/Core/VideoBackends/OGL/OGLTexture.h index 91cb09bbca..064006fead 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.h +++ b/Source/Core/VideoBackends/OGL/OGLTexture.h @@ -8,6 +8,7 @@ #include "Common/GL/GLUtil.h" +#include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" namespace OGL @@ -43,4 +44,35 @@ private: std::vector m_staging_data; }; +class OGLStagingTexture final : public AbstractStagingTexture +{ +public: + OGLStagingTexture() = delete; + ~OGLStagingTexture(); + + void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, + u32 src_layer, u32 src_level, + const MathUtil::Rectangle& dst_rect) override; + void CopyToTexture(const MathUtil::Rectangle& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) override; + + bool Map() override; + void Unmap() override; + void Flush() override; + + static std::unique_ptr Create(StagingTextureType type, + const TextureConfig& config); + +private: + OGLStagingTexture(StagingTextureType type, const TextureConfig& config, GLenum target, + GLuint buffer_name, size_t buffer_size, char* map_ptr, size_t map_stride); + +private: + GLenum m_target; + GLuint m_buffer_name; + size_t m_buffer_size; + GLsync m_fence = 0; +}; + } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index fff40f7abf..780f4fa199 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -458,7 +458,7 @@ Renderer::Renderer() GLExtensions::Supports("GL_EXT_copy_image") || GLExtensions::Supports("GL_OES_copy_image")) && !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_COPYIMAGE); - g_ogl_config.bSupportTextureSubImage = GLExtensions::Supports("ARB_get_texture_sub_image"); + g_ogl_config.bSupportsTextureSubImage = GLExtensions::Supports("ARB_get_texture_sub_image"); // Desktop OpenGL supports the binding layout if it supports 420pack // OpenGL ES 3.1 supports it implicitly without an extension @@ -623,6 +623,8 @@ Renderer::Renderer() // Compute shaders are core in GL4.3. g_Config.backend_info.bSupportsComputeShaders = true; + if (GLExtensions::Version() >= 450) + g_ogl_config.bSupportsTextureSubImage = true; } else { @@ -819,6 +821,12 @@ std::unique_ptr Renderer::CreateTexture(const TextureConfig& co return std::make_unique(config); } +std::unique_ptr Renderer::CreateStagingTexture(StagingTextureType type, + const TextureConfig& config) +{ + return OGLStagingTexture::Create(type, config); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { u32 backbuffer_width = std::max(GLInterface->GetBackBufferWidth(), 1u); diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index 2e60df3f6d..e787828d53 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -58,7 +58,7 @@ struct VideoConfig bool bSupportsImageLoadStore; bool bSupportsAniso; bool bSupportsBitfield; - bool bSupportTextureSubImage; + bool bSupportsTextureSubImage; const char* gl_vendor; const char* gl_renderer; @@ -78,6 +78,8 @@ public: void Shutdown(); std::unique_ptr CreateTexture(const TextureConfig& config) override; + std::unique_ptr + CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; void SetBlendingState(const BlendingState& state) override; void SetScissorRect(const EFBRectangle& rc) override; diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index ea7ccaba7c..ee4b644ca7 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -40,6 +40,12 @@ std::unique_ptr SWRenderer::CreateTexture(const TextureConfig& return std::make_unique(config); } +std::unique_ptr +SWRenderer::CreateStagingTexture(StagingTextureType type, const TextureConfig& config) +{ + return std::make_unique(type, config); +} + void SWRenderer::RenderText(const std::string& pstr, int left, int top, u32 color) { SWOGLWindow::s_instance->PrintText(pstr, left, top, color); diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h index ac8764e0c7..312acd1991 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -17,6 +17,8 @@ public: static void Shutdown(); std::unique_ptr CreateTexture(const TextureConfig& config) override; + std::unique_ptr + CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; void RenderText(const std::string& pstr, int left, int top, u32 color) override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; diff --git a/Source/Core/VideoBackends/Software/SWTexture.cpp b/Source/Core/VideoBackends/Software/SWTexture.cpp index aa279b520b..184a223392 100644 --- a/Source/Core/VideoBackends/Software/SWTexture.cpp +++ b/Source/Core/VideoBackends/Software/SWTexture.cpp @@ -5,6 +5,7 @@ #include "VideoBackends/Software/SWTexture.h" #include +#include "Common/Assert.h" #include "VideoBackends/Software/CopyRegion.h" @@ -21,7 +22,31 @@ struct Pixel u8 a; }; #pragma pack(pop) + +void CopyTextureData(const TextureConfig& src_config, const u8* src_ptr, u32 src_x, u32 src_y, + u32 width, u32 height, const TextureConfig& dst_config, u8* dst_ptr, u32 dst_x, + u32 dst_y) +{ + size_t texel_size = AbstractTexture::GetTexelSizeForFormat(src_config.format); + size_t src_stride = src_config.GetStride(); + size_t src_offset = + static_cast(src_y) * src_stride + static_cast(src_x) * texel_size; + size_t dst_stride = dst_config.GetStride(); + size_t dst_offset = + static_cast(dst_y) * dst_stride + static_cast(dst_x) * texel_size; + size_t copy_len = static_cast(width) * texel_size; + + src_ptr += src_offset; + dst_ptr += dst_offset; + for (u32 i = 0; i < height; i++) + { + std::memcpy(dst_ptr, src_ptr, copy_len); + src_ptr += src_stride; + dst_ptr += dst_stride; + } } +} + SWTexture::SWTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config) { m_data.resize(tex_config.width * tex_config.height * 4); @@ -78,4 +103,49 @@ std::optional SWTexture::MapFullImpl() m_config.height}; } +SWStagingTexture::SWStagingTexture(StagingTextureType type, const TextureConfig& config) + : AbstractStagingTexture(type, config) +{ + m_data.resize(m_texel_size * config.width * config.height); + m_map_pointer = reinterpret_cast(m_data.data()); + m_map_stride = m_texel_size * config.width; +} + +SWStagingTexture::~SWStagingTexture() = default; + +void SWStagingTexture::CopyFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect) +{ + _assert_(src_level == 0 && src_layer == 0); + CopyTextureData(src->GetConfig(), static_cast(src)->GetData(), src_rect.left, + src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(), m_config, m_data.data(), + dst_rect.left, dst_rect.top); + m_needs_flush = true; +} + +void SWStagingTexture::CopyToTexture(const MathUtil::Rectangle& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) +{ + _assert_(dst_level == 0 && dst_layer == 0); + CopyTextureData(m_config, m_data.data(), src_rect.left, src_rect.top, src_rect.GetWidth(), + src_rect.GetHeight(), dst->GetConfig(), static_cast(dst)->GetData(), + dst_rect.left, dst_rect.top); + m_needs_flush = true; +} + +bool SWStagingTexture::Map() +{ + return true; +} + +void SWStagingTexture::Unmap() +{ +} + +void SWStagingTexture::Flush() +{ + m_needs_flush = false; +} } // namespace SW diff --git a/Source/Core/VideoBackends/Software/SWTexture.h b/Source/Core/VideoBackends/Software/SWTexture.h index fa7fea5308..2dbec7d0ab 100644 --- a/Source/Core/VideoBackends/Software/SWTexture.h +++ b/Source/Core/VideoBackends/Software/SWTexture.h @@ -8,6 +8,7 @@ #include "Common/CommonTypes.h" +#include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" namespace SW @@ -35,4 +36,25 @@ private: std::vector m_data; }; +class SWStagingTexture final : public AbstractStagingTexture +{ +public: + explicit SWStagingTexture(StagingTextureType type, const TextureConfig& config); + ~SWStagingTexture(); + + void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, + u32 src_layer, u32 src_level, + const MathUtil::Rectangle& dst_rect) override; + void CopyToTexture(const MathUtil::Rectangle& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) override; + + bool Map() override; + void Unmap() override; + void Flush() override; + +private: + std::vector m_data; +}; + } // namespace SW diff --git a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp index 51706fc33f..5d70d38584 100644 --- a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp @@ -371,6 +371,12 @@ void CommandBufferManager::OnCommandBufferExecuted(size_t index) FrameResources& resources = m_frame_resources[index]; // Fire fence tracking callbacks. + for (auto iter = m_fence_point_callbacks.begin(); iter != m_fence_point_callbacks.end();) + { + auto backup_iter = iter++; + backup_iter->second.second(resources.fence); + } + for (const auto& iter : m_fence_point_callbacks) iter.second.second(resources.fence); diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 0bb1640f47..af19ad4043 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -164,6 +164,12 @@ std::unique_ptr Renderer::CreateTexture(const TextureConfig& co return VKTexture::Create(config); } +std::unique_ptr Renderer::CreateStagingTexture(StagingTextureType type, + const TextureConfig& config) +{ + return VKStagingTexture::Create(type, config); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { u32 backbuffer_width = m_swap_chain->GetWidth(); diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index 250e76634d..b3f8168347 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -33,6 +33,8 @@ public: static Renderer* GetInstance(); std::unique_ptr CreateTexture(const TextureConfig& config) override; + std::unique_ptr + CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; SwapChain* GetSwapChain() const { return m_swap_chain.get(); } BoundingBox* GetBoundingBox() const { return m_bounding_box.get(); } diff --git a/Source/Core/VideoBackends/Vulkan/StagingBuffer.h b/Source/Core/VideoBackends/Vulkan/StagingBuffer.h index 65cfb1c5c7..2ecb21cb22 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingBuffer.h +++ b/Source/Core/VideoBackends/Vulkan/StagingBuffer.h @@ -59,11 +59,11 @@ public: static std::unique_ptr Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage); -protected: // Allocates the resources needed to create a staging buffer. static bool AllocateBuffer(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage, VkBuffer* out_buffer, VkDeviceMemory* out_memory, bool* out_coherent); +protected: STAGING_BUFFER_TYPE m_type; VkBuffer m_buffer; VkDeviceMemory m_memory; diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index cbb6e0c063..cfea34ff8e 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -291,7 +291,7 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* u32 upload_alignment = static_cast(g_vulkan_context->GetBufferImageGranularity()); u32 block_size = Util::GetBlockSize(m_texture->GetFormat()); u32 num_rows = Common::AlignUp(height, block_size) / block_size; - size_t source_pitch = CalculateHostTextureLevelPitch(m_config.format, row_length); + size_t source_pitch = CalculateStrideForFormat(m_config.format, row_length); size_t upload_size = source_pitch * num_rows; std::unique_ptr temp_buffer; VkBuffer upload_buffer; @@ -356,4 +356,224 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* } } +VKStagingTexture::VKStagingTexture(StagingTextureType type, const TextureConfig& config, + std::unique_ptr buffer) + : AbstractStagingTexture(type, config), m_staging_buffer(std::move(buffer)) +{ +} + +VKStagingTexture::~VKStagingTexture() +{ + if (m_needs_flush) + VKStagingTexture::Flush(); +} + +std::unique_ptr VKStagingTexture::Create(StagingTextureType type, + const TextureConfig& config) +{ + size_t stride = config.GetStride(); + size_t buffer_size = stride * static_cast(config.height); + + STAGING_BUFFER_TYPE buffer_type; + VkImageUsageFlags buffer_usage; + if (type == StagingTextureType::Readback) + { + buffer_type = STAGING_BUFFER_TYPE_READBACK; + buffer_usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + else if (type == StagingTextureType::Upload) + { + buffer_type = STAGING_BUFFER_TYPE_UPLOAD; + buffer_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + else + { + buffer_type = STAGING_BUFFER_TYPE_READBACK; + buffer_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + + VkBuffer buffer; + VkDeviceMemory memory; + bool coherent; + if (!StagingBuffer::AllocateBuffer(buffer_type, buffer_size, buffer_usage, &buffer, &memory, + &coherent)) + { + return nullptr; + } + + std::unique_ptr staging_buffer = + std::make_unique(buffer_type, buffer, memory, buffer_size, coherent); + std::unique_ptr staging_tex = std::unique_ptr( + new VKStagingTexture(type, config, std::move(staging_buffer))); + + // Use persistent mapping. + if (!staging_tex->m_staging_buffer->Map()) + return nullptr; + staging_tex->m_map_pointer = staging_tex->m_staging_buffer->GetMapPointer(); + staging_tex->m_map_stride = stride; + return staging_tex; +} + +void VKStagingTexture::CopyFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect) +{ + _assert_(m_type == StagingTextureType::Readback); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); + _assert_(src_rect.left >= 0 && static_cast(src_rect.right) <= src->GetConfig().width && + src_rect.top >= 0 && static_cast(src_rect.bottom) <= src->GetConfig().height); + _assert_(dst_rect.left >= 0 && static_cast(dst_rect.right) <= m_config.width && + dst_rect.top >= 0 && static_cast(dst_rect.bottom) <= m_config.height); + + Texture2D* src_tex = static_cast(src)->GetRawTexIdentifier(); + CopyFromTexture(src_tex, src_rect, src_layer, src_level, dst_rect); +} + +void VKStagingTexture::CopyFromTexture(Texture2D* src, const MathUtil::Rectangle& src_rect, + u32 src_layer, u32 src_level, + const MathUtil::Rectangle& dst_rect) +{ + if (m_needs_flush) + { + // Drop copy before reusing it. + g_command_buffer_mgr->RemoveFencePointCallback(this); + m_flush_fence = VK_NULL_HANDLE; + m_needs_flush = false; + } + + VkImageLayout old_layout = src->GetLayout(); + src->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + // Issue the image->buffer copy, but delay it for now. + VkBufferImageCopy image_copy = {}; + VkImageAspectFlags aspect = + Util::IsDepthFormat(src->GetFormat()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; + image_copy.bufferOffset = + static_cast(static_cast(dst_rect.top) * m_config.GetStride() + + static_cast(dst_rect.left) * m_texel_size); + image_copy.bufferRowLength = static_cast(m_config.width); + image_copy.bufferImageHeight = 0; + image_copy.imageSubresource = {aspect, src_level, src_layer, 1}; + image_copy.imageOffset = {src_rect.left, src_rect.top, 0}; + image_copy.imageExtent = {static_cast(src_rect.GetWidth()), + static_cast(src_rect.GetHeight()), 1u}; + vkCmdCopyImageToBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), src->GetImage(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_staging_buffer->GetBuffer(), 1, + &image_copy); + + // Restore old source texture layout. + src->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_layout); + + m_needs_flush = true; + g_command_buffer_mgr->AddFencePointCallback(this, + [this](VkCommandBuffer buf, VkFence fence) { + _assert_(m_needs_flush); + m_flush_fence = fence; + }, + [this](VkFence fence) { + m_flush_fence = VK_NULL_HANDLE; + m_needs_flush = false; + g_command_buffer_mgr->RemoveFencePointCallback( + this); + }); +} + +void VKStagingTexture::CopyToTexture(const MathUtil::Rectangle& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) +{ + _assert_(m_type == StagingTextureType::Upload); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); + _assert_(src_rect.left >= 0 && static_cast(src_rect.right) <= m_config.width && + src_rect.top >= 0 && static_cast(src_rect.bottom) <= m_config.height); + _assert_(dst_rect.left >= 0 && static_cast(dst_rect.right) <= dst->GetConfig().width && + dst_rect.top >= 0 && static_cast(dst_rect.bottom) <= dst->GetConfig().height); + + if (m_needs_flush) + { + // Drop copy before reusing it. + g_command_buffer_mgr->RemoveFencePointCallback(this); + m_flush_fence = VK_NULL_HANDLE; + m_needs_flush = false; + } + + // Flush caches before copying. + m_staging_buffer->FlushCPUCache(); + + Texture2D* dst_tex = static_cast(dst)->GetRawTexIdentifier(); + VkImageLayout old_layout = dst_tex->GetLayout(); + dst_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // Issue the image->buffer copy, but delay it for now. + VkBufferImageCopy image_copy = {}; + image_copy.bufferOffset = + static_cast(static_cast(src_rect.top) * m_config.GetStride() + + static_cast(src_rect.left) * m_texel_size); + image_copy.bufferRowLength = static_cast(m_config.width); + image_copy.bufferImageHeight = 0; + image_copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, 1}; + image_copy.imageOffset = {dst_rect.left, dst_rect.top, 0}; + image_copy.imageExtent = {static_cast(dst_rect.GetWidth()), + static_cast(dst_rect.GetHeight()), 1u}; + vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), + m_staging_buffer->GetBuffer(), dst_tex->GetImage(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + + // Restore old source texture layout. + dst_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_layout); + + m_needs_flush = true; + g_command_buffer_mgr->AddFencePointCallback(this, + [this](VkCommandBuffer buf, VkFence fence) { + _assert_(m_needs_flush); + m_flush_fence = fence; + }, + [this](VkFence fence) { + m_flush_fence = VK_NULL_HANDLE; + m_needs_flush = false; + g_command_buffer_mgr->RemoveFencePointCallback( + this); + }); +} + +bool VKStagingTexture::Map() +{ + // Always mapped. + return true; +} + +void VKStagingTexture::Unmap() +{ + // Always mapped. +} + +void VKStagingTexture::Flush() +{ + if (!m_needs_flush) + return; + + // Either of the below two calls will cause the callback to fire. + g_command_buffer_mgr->RemoveFencePointCallback(this); + if (m_flush_fence != VK_NULL_HANDLE) + { + // WaitForFence should fire the callback. + g_command_buffer_mgr->WaitForFence(m_flush_fence); + } + else + { + // We don't have a fence, and are pending. That means the readback is in the current + // command buffer, and must execute it to populate the staging texture. + Util::ExecuteCurrentCommandsAndRestoreState(false, true); + } + m_needs_flush = false; + + // For readback textures, invalidate the CPU cache as there is new data there. + if (m_type == StagingTextureType::Readback) + m_staging_buffer->InvalidateCPUCache(); +} + } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.h b/Source/Core/VideoBackends/Vulkan/VKTexture.h index 61ffae242c..5f2df0c26b 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.h +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.h @@ -7,10 +7,12 @@ #include #include +#include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" namespace Vulkan { +class StagingBuffer; class Texture2D; class VKTexture final : public AbstractTexture @@ -56,4 +58,37 @@ private: VkFramebuffer m_framebuffer; }; +class VKStagingTexture final : public AbstractStagingTexture +{ +public: + VKStagingTexture() = delete; + ~VKStagingTexture(); + + void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, + u32 src_layer, u32 src_level, + const MathUtil::Rectangle& dst_rect) override; + void CopyToTexture(const MathUtil::Rectangle& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) override; + + bool Map() override; + void Unmap() override; + void Flush() override; + + // This overload is provided for compatibility as we dropped StagingTexture2D. + // For now, FramebufferManager relies on them. But we can drop it once we move that to common. + void CopyFromTexture(Texture2D* src, const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect); + + static std::unique_ptr Create(StagingTextureType type, + const TextureConfig& config); + +private: + VKStagingTexture(StagingTextureType type, const TextureConfig& config, + std::unique_ptr buffer); + + std::unique_ptr m_staging_buffer; + VkFence m_flush_fence = VK_NULL_HANDLE; +}; + } // namespace Vulkan diff --git a/Source/Core/VideoCommon/AbstractStagingTexture.cpp b/Source/Core/VideoCommon/AbstractStagingTexture.cpp new file mode 100644 index 0000000000..6d4e973761 --- /dev/null +++ b/Source/Core/VideoCommon/AbstractStagingTexture.cpp @@ -0,0 +1,133 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/Assert.h" +#include "Common/MsgHandler.h" +#include "VideoCommon/AbstractStagingTexture.h" +#include "VideoCommon/AbstractTexture.h" + +AbstractStagingTexture::AbstractStagingTexture(StagingTextureType type, const TextureConfig& c) + : m_type(type), m_config(c), m_texel_size(AbstractTexture::GetTexelSizeForFormat(c.format)) +{ +} + +AbstractStagingTexture::~AbstractStagingTexture() = default; + +void AbstractStagingTexture::CopyFromTexture(const AbstractTexture* src, u32 src_layer, + u32 src_level) +{ + MathUtil::Rectangle src_rect = src->GetConfig().GetMipRect(src_level); + MathUtil::Rectangle dst_rect = m_config.GetRect(); + CopyFromTexture(src, src_rect, src_layer, src_level, dst_rect); +} + +void AbstractStagingTexture::CopyToTexture(AbstractTexture* dst, u32 dst_layer, u32 dst_level) +{ + MathUtil::Rectangle src_rect = m_config.GetRect(); + MathUtil::Rectangle dst_rect = dst->GetConfig().GetMipRect(dst_level); + CopyToTexture(src_rect, dst, dst_rect, dst_layer, dst_level); +} + +void AbstractStagingTexture::ReadTexels(const MathUtil::Rectangle& rect, void* out_ptr, + u32 out_stride) +{ + _assert_(m_type != StagingTextureType::Upload); + if (!PrepareForAccess()) + return; + + _assert_(rect.left >= 0 && static_cast(rect.right) <= m_config.width && rect.top >= 0 && + static_cast(rect.bottom) <= m_config.height); + + // Offset pointer to point to start of region being copied out. + const char* current_ptr = m_map_pointer; + current_ptr += rect.top * m_map_stride; + current_ptr += rect.left * m_texel_size; + + // Optimal path: same dimensions, same stride. + if (rect.left == 0 && static_cast(rect.right) == m_config.width && + m_map_stride == out_stride) + { + std::memcpy(out_ptr, current_ptr, m_map_stride * rect.GetHeight()); + return; + } + + size_t copy_size = std::min(static_cast(rect.GetWidth() * m_texel_size), m_map_stride); + int copy_height = rect.GetHeight(); + char* dst_ptr = reinterpret_cast(out_ptr); + for (int row = 0; row < copy_height; row++) + { + std::memcpy(dst_ptr, current_ptr, copy_size); + current_ptr += m_map_stride; + dst_ptr += out_stride; + } +} + +void AbstractStagingTexture::ReadTexel(u32 x, u32 y, void* out_ptr) +{ + _assert_(m_type != StagingTextureType::Upload); + if (!PrepareForAccess()) + return; + + _assert_(x < m_config.width && y < m_config.height); + const char* src_ptr = m_map_pointer + y * m_map_stride + x * m_texel_size; + std::memcpy(out_ptr, src_ptr, m_texel_size); +} + +void AbstractStagingTexture::WriteTexels(const MathUtil::Rectangle& rect, const void* in_ptr, + u32 in_stride) +{ + _assert_(m_type != StagingTextureType::Readback); + if (!PrepareForAccess()) + return; + + _assert_(rect.left >= 0 && static_cast(rect.right) <= m_config.width && rect.top >= 0 && + static_cast(rect.bottom) <= m_config.height); + + // Offset pointer to point to start of region being copied to. + char* current_ptr = m_map_pointer; + current_ptr += rect.top * m_map_stride; + current_ptr += rect.left * m_texel_size; + + // Optimal path: same dimensions, same stride. + if (rect.left == 0 && static_cast(rect.right) == m_config.width && m_map_stride == in_stride) + { + std::memcpy(current_ptr, in_ptr, m_map_stride * rect.GetHeight()); + return; + } + + size_t copy_size = std::min(static_cast(rect.GetWidth() * m_texel_size), m_map_stride); + int copy_height = rect.GetHeight(); + const char* src_ptr = reinterpret_cast(in_ptr); + for (int row = 0; row < copy_height; row++) + { + std::memcpy(current_ptr, src_ptr, copy_size); + current_ptr += m_map_stride; + src_ptr += in_stride; + } +} + +void AbstractStagingTexture::WriteTexel(u32 x, u32 y, const void* in_ptr) +{ + _assert_(m_type != StagingTextureType::Readback); + if (!PrepareForAccess()) + return; + + _assert_(x < m_config.width && y < m_config.height); + char* dest_ptr = m_map_pointer + y * m_map_stride + x * m_texel_size; + std::memcpy(dest_ptr, in_ptr, m_texel_size); +} + +bool AbstractStagingTexture::PrepareForAccess() +{ + if (m_needs_flush) + { + if (IsMapped()) + Unmap(); + Flush(); + } + return IsMapped() || Map(); +} diff --git a/Source/Core/VideoCommon/AbstractStagingTexture.h b/Source/Core/VideoCommon/AbstractStagingTexture.h new file mode 100644 index 0000000000..c87dfd70b0 --- /dev/null +++ b/Source/Core/VideoCommon/AbstractStagingTexture.h @@ -0,0 +1,86 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/MathUtil.h" +#include "VideoCommon/TextureConfig.h" + +class AbstractTexture; + +class AbstractStagingTexture +{ +public: + explicit AbstractStagingTexture(StagingTextureType type, const TextureConfig& c); + virtual ~AbstractStagingTexture(); + + const TextureConfig& GetConfig() const { return m_config; } + StagingTextureType GetType() const { return m_type; } + size_t GetTexelSize() const { return m_texel_size; } + bool IsMapped() const { return m_map_pointer != nullptr; } + char* GetMappedPointer() const { return m_map_pointer; } + size_t GetMappedStride() const { return m_map_stride; } + // Copies from the GPU texture object to the staging texture, which can be mapped/read by the CPU. + // Both src_rect and dst_rect must be with within the bounds of the the specified textures. + virtual void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, + u32 src_layer, u32 src_level, + const MathUtil::Rectangle& dst_rect) = 0; + + // Wrapper for copying a whole layer of a texture to a readback texture. + // Assumes that the level of src texture and this texture have the same dimensions. + void CopyFromTexture(const AbstractTexture* src, u32 src_layer = 0, u32 src_level = 0); + + // Copies from this staging texture to a GPU texture. + // Both src_rect and dst_rect must be with within the bounds of the the specified textures. + virtual void CopyToTexture(const MathUtil::Rectangle& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) = 0; + + // Wrapper for copying a whole layer of a texture to a readback texture. + // Assumes that the level of src texture and this texture have the same dimensions. + void CopyToTexture(AbstractTexture* dst, u32 dst_layer = 0, u32 dst_level = 0); + + // Maps the texture into the CPU address space, enabling it to read the contents. + // The Map call may not perform synchronization. If the contents of the staging texture + // has been updated by a CopyFromTexture call, you must call Flush() first. + // If persistent mapping is supported in the backend, this may be a no-op. + virtual bool Map() = 0; + + // Unmaps the CPU-readable copy of the texture. May be a no-op on backends which + // support persistent-mapped buffers. + virtual void Unmap() = 0; + + // Flushes pending writes from the CPU to the GPU, and reads from the GPU to the CPU. + // This may cause a command buffer flush depending on if one has occurred between the last + // call to CopyFromTexture()/CopyToTexture() and the Flush() call. + virtual void Flush() = 0; + + // Reads the specified rectangle from the staging texture to out_ptr, with the specified stride + // (length in bytes of each row). CopyFromTexture must be called first. The contents of any + // texels outside of the rectangle used for CopyFromTexture is undefined. + void ReadTexels(const MathUtil::Rectangle& rect, void* out_ptr, u32 out_stride); + void ReadTexel(u32 x, u32 y, void* out_ptr); + + // Copies the texels from in_ptr to the staging texture, which can be read by the GPU, with the + // specified stride (length in bytes of each row). After updating the staging texture with all + // changes, call CopyToTexture() to update the GPU copy. + void WriteTexels(const MathUtil::Rectangle& rect, const void* in_ptr, u32 in_stride); + void WriteTexel(u32 x, u32 y, const void* in_ptr); + +protected: + bool PrepareForAccess(); + + const StagingTextureType m_type; + const TextureConfig m_config; + const size_t m_texel_size; + + char* m_map_pointer = nullptr; + size_t m_map_stride = 0; + + bool m_needs_flush = false; +}; diff --git a/Source/Core/VideoCommon/AbstractTexture.cpp b/Source/Core/VideoCommon/AbstractTexture.cpp index 9fa099e602..7e09be5344 100644 --- a/Source/Core/VideoCommon/AbstractTexture.cpp +++ b/Source/Core/VideoCommon/AbstractTexture.cpp @@ -100,7 +100,7 @@ AbstractTexture::MapRegionImpl(u32 level, u32 x, u32 y, u32 width, u32 height) return {}; } -bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format) +bool AbstractTexture::IsCompressedFormat(AbstractTextureFormat format) { switch (format) { @@ -115,7 +115,7 @@ bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format } } -size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length) +size_t AbstractTexture::CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length) { switch (format) { @@ -134,6 +134,25 @@ size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat for } } +size_t AbstractTexture::GetTexelSizeForFormat(AbstractTextureFormat format) +{ + switch (format) + { + case AbstractTextureFormat::DXT1: + return 8; + case AbstractTextureFormat::DXT3: + case AbstractTextureFormat::DXT5: + case AbstractTextureFormat::BPTC: + return 16; + case AbstractTextureFormat::RGBA8: + case AbstractTextureFormat::BGRA8: + return 4; + default: + PanicAlert("Unhandled texture format."); + return 0; + } +} + const TextureConfig& AbstractTexture::GetConfig() const { return m_config; diff --git a/Source/Core/VideoCommon/AbstractTexture.h b/Source/Core/VideoCommon/AbstractTexture.h index 612ca34758..fd4b4bb621 100644 --- a/Source/Core/VideoCommon/AbstractTexture.h +++ b/Source/Core/VideoCommon/AbstractTexture.h @@ -39,8 +39,9 @@ public: virtual void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) = 0; - static bool IsCompressedHostTextureFormat(AbstractTextureFormat format); - static size_t CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length); + static bool IsCompressedFormat(AbstractTextureFormat format); + static size_t CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length); + static size_t GetTexelSizeForFormat(AbstractTextureFormat format); const TextureConfig& GetConfig() const; diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 4df4a01550..eff095d628 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -1,4 +1,5 @@ set(SRCS + AbstractStagingTexture.cpp AbstractTexture.cpp AsyncRequests.cpp AsyncShaderCompiler.cpp diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 7158f3f18e..b614c6a375 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -34,9 +34,11 @@ class AbstractRawTexture; class AbstractTexture; +class AbstractStagingTexture; class PostProcessingShaderImplementation; struct TextureConfig; enum class EFBAccessType; +enum class StagingTextureType; struct EfbPokeData { @@ -81,6 +83,8 @@ public: virtual void ResetAPIState() {} virtual void RestoreAPIState() {} virtual std::unique_ptr CreateTexture(const TextureConfig& config) = 0; + virtual std::unique_ptr + CreateStagingTexture(StagingTextureType type, const TextureConfig& config) = 0; // Ideal internal resolution - multiple of the native EFB resolution int GetTargetWidth() const { return m_target_width; } diff --git a/Source/Core/VideoCommon/TextureConfig.cpp b/Source/Core/VideoCommon/TextureConfig.cpp index 7a6930c798..d8155d59b1 100644 --- a/Source/Core/VideoCommon/TextureConfig.cpp +++ b/Source/Core/VideoCommon/TextureConfig.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "VideoCommon/TextureConfig.h" +#include "VideoCommon/AbstractTexture.h" #include @@ -12,7 +13,28 @@ bool TextureConfig::operator==(const TextureConfig& o) const std::tie(o.width, o.height, o.levels, o.layers, o.format, o.rendertarget); } +bool TextureConfig::operator!=(const TextureConfig& o) const +{ + return !operator==(o); +} + MathUtil::Rectangle TextureConfig::GetRect() const { return {0, 0, static_cast(width), static_cast(height)}; } + +MathUtil::Rectangle TextureConfig::GetMipRect(u32 level) const +{ + return {0, 0, static_cast(std::max(width >> level, 1u)), + static_cast(std::max(height >> level, 1u))}; +} + +size_t TextureConfig::GetStride() const +{ + return AbstractTexture::CalculateStrideForFormat(format, width); +} + +size_t TextureConfig::GetMipStride(u32 level) const +{ + return AbstractTexture::CalculateStrideForFormat(format, std::max(width >> level, 1u)); +} diff --git a/Source/Core/VideoCommon/TextureConfig.h b/Source/Core/VideoCommon/TextureConfig.h index ae6e54de5e..a212c7f86a 100644 --- a/Source/Core/VideoCommon/TextureConfig.h +++ b/Source/Core/VideoCommon/TextureConfig.h @@ -21,6 +21,13 @@ enum class AbstractTextureFormat : u32 Undefined }; +enum class StagingTextureType +{ + Readback, // Optimize for CPU reads, GPU writes, no CPU writes + Upload, // Optimize for CPU writes, GPU reads, no CPU reads + Mutable // Optimize for CPU reads, GPU writes, allow slow CPU reads +}; + struct TextureConfig { constexpr TextureConfig() = default; @@ -32,7 +39,11 @@ struct TextureConfig } bool operator==(const TextureConfig& o) const; + bool operator!=(const TextureConfig& o) const; MathUtil::Rectangle GetRect() const; + MathUtil::Rectangle GetMipRect(u32 level) const; + size_t GetStride() const; + size_t GetMipStride(u32 level) const; u32 width = 0; u32 height = 0; diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index 6bdd57a9fd..83be20714f 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -36,6 +36,7 @@ + @@ -96,6 +97,7 @@ + diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index 815fd86bbb..e77cceec23 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -191,6 +191,9 @@ Shader Generators + + Base + @@ -362,6 +365,9 @@ Shader Generators + + Base + From 56afebeb44f1ace990d9383e9b840fdc27610029 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 30 Oct 2017 21:51:42 +1000 Subject: [PATCH 08/16] AbstractTexture: Seperate CopyRectangleFromTexture to two methods ScaleRectangleFromTexture, which does a draw, and CopyRectangleFromTexture, which where possible, does a bit-for-bit copy. --- Source/Core/VideoBackends/D3D/DXTexture.cpp | 47 +++++++----- Source/Core/VideoBackends/D3D/DXTexture.h | 10 ++- .../Core/VideoBackends/Null/NullTexture.cpp | 12 ++- Source/Core/VideoBackends/Null/NullTexture.h | 10 ++- Source/Core/VideoBackends/OGL/OGLTexture.cpp | 74 ++++++++++++++++--- Source/Core/VideoBackends/OGL/OGLTexture.h | 10 ++- .../Core/VideoBackends/Software/SWTexture.cpp | 16 +++- .../Core/VideoBackends/Software/SWTexture.h | 10 ++- .../Core/VideoBackends/Vulkan/VKTexture.cpp | 58 ++++++--------- Source/Core/VideoBackends/Vulkan/VKTexture.h | 22 +++--- Source/Core/VideoCommon/AbstractTexture.h | 10 ++- Source/Core/VideoCommon/RenderBase.cpp | 4 +- Source/Core/VideoCommon/TextureCacheBase.cpp | 12 +-- 13 files changed, 188 insertions(+), 107 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/DXTexture.cpp b/Source/Core/VideoBackends/D3D/DXTexture.cpp index 92d3d112e9..722db73a5f 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.cpp +++ b/Source/Core/VideoBackends/D3D/DXTexture.cpp @@ -165,29 +165,36 @@ void DXTexture::Unmap() D3D::context->Unmap(m_staging_texture, 0); } -void DXTexture::CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) +void DXTexture::CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) +{ + const DXTexture* srcentry = static_cast(src); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); + + D3D11_BOX src_box; + src_box.left = src_rect.left; + src_box.top = src_rect.top; + src_box.right = src_rect.right; + src_box.bottom = src_rect.bottom; + src_box.front = 0; + src_box.back = 1; + + D3D::context->CopySubresourceRegion( + m_texture->GetTex(), D3D11CalcSubresource(dst_level, dst_layer, m_config.levels), + dst_rect.left, dst_rect.top, 0, srcentry->m_texture->GetTex(), + D3D11CalcSubresource(src_level, src_layer, srcentry->m_config.levels), &src_box); +} + +void DXTexture::ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& srcrect, + const MathUtil::Rectangle& dstrect) { const DXTexture* srcentry = static_cast(source); - if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight()) - { - D3D11_BOX srcbox; - srcbox.left = srcrect.left; - srcbox.top = srcrect.top; - srcbox.right = srcrect.right; - srcbox.bottom = srcrect.bottom; - srcbox.front = 0; - srcbox.back = srcentry->m_config.layers; + _assert_(m_config.rendertarget); - D3D::context->CopySubresourceRegion(m_texture->GetTex(), 0, dstrect.left, dstrect.top, 0, - srcentry->m_texture->GetTex(), 0, &srcbox); - return; - } - else if (!m_config.rendertarget) - { - return; - } g_renderer->ResetAPIState(); // reset any game specific settings const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(float(dstrect.left), float(dstrect.top), diff --git a/Source/Core/VideoBackends/D3D/DXTexture.h b/Source/Core/VideoBackends/D3D/DXTexture.h index 4403eb2d43..6723952740 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.h +++ b/Source/Core/VideoBackends/D3D/DXTexture.h @@ -22,9 +22,13 @@ public: void Bind(unsigned int stage) override; void Unmap() override; - void CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) override; + void CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) override; + void ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& srcrect, + const MathUtil::Rectangle& dstrect) override; void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) override; diff --git a/Source/Core/VideoBackends/Null/NullTexture.cpp b/Source/Core/VideoBackends/Null/NullTexture.cpp index b5363d3918..471ec6c01c 100644 --- a/Source/Core/VideoBackends/Null/NullTexture.cpp +++ b/Source/Core/VideoBackends/Null/NullTexture.cpp @@ -14,9 +14,15 @@ void NullTexture::Bind(unsigned int stage) { } -void NullTexture::CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) +void NullTexture::CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) +{ +} +void NullTexture::ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& srcrect, + const MathUtil::Rectangle& dstrect) { } diff --git a/Source/Core/VideoBackends/Null/NullTexture.h b/Source/Core/VideoBackends/Null/NullTexture.h index 81227e162f..8cfedc68ca 100644 --- a/Source/Core/VideoBackends/Null/NullTexture.h +++ b/Source/Core/VideoBackends/Null/NullTexture.h @@ -21,9 +21,13 @@ public: void Bind(unsigned int stage) override; - void CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) override; + void CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) override; + void ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& srcrect, + const MathUtil::Rectangle& dstrect) override; void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) override; }; diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.cpp b/Source/Core/VideoBackends/OGL/OGLTexture.cpp index f6390381c6..4891f87686 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.cpp +++ b/Source/Core/VideoBackends/OGL/OGLTexture.cpp @@ -236,20 +236,70 @@ void OGLTexture::MapRegionSlow(u32 level, u32 x, u32 y, u32 width, u32 height) m_staging_data.swap(partial_data); } -void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) +void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) +{ + const OGLTexture* srcentry = static_cast(src); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); + if (g_ogl_config.bSupportsCopySubImage) + { + glCopyImageSubData(srcentry->m_texId, GL_TEXTURE_2D_ARRAY, src_level, src_rect.left, + src_rect.top, src_layer, m_texId, GL_TEXTURE_2D_ARRAY, dst_level, + dst_rect.left, dst_rect.top, dst_layer, dst_rect.GetWidth(), + dst_rect.GetHeight(), 1); + } + else + { + // If it isn't a single leveled/layered texture, we need to update the framebuffer. + bool update_src_framebuffer = + srcentry->m_framebuffer == 0 || srcentry->m_config.layers != 0 || src_level != 0; + bool update_dst_framebuffer = m_framebuffer == 0 || m_config.layers != 0 || dst_level != 0; + if (!m_framebuffer) + glGenFramebuffers(1, &m_framebuffer); + if (!srcentry->m_framebuffer) + glGenFramebuffers(1, &const_cast(srcentry)->m_framebuffer); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, srcentry->m_framebuffer); + if (update_src_framebuffer) + { + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, srcentry->m_texId, + src_level, src_layer); + } + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_framebuffer); + if (update_dst_framebuffer) + { + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texId, dst_level, + dst_layer); + } + + glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left, + dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, + GL_NEAREST); + + if (update_src_framebuffer) + { + FramebufferManager::FramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D_ARRAY, srcentry->m_texId, 0); + } + if (update_dst_framebuffer) + { + FramebufferManager::FramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D_ARRAY, m_texId, 0); + } + + FramebufferManager::SetFramebuffer(0); + } +} +void OGLTexture::ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& srcrect, + const MathUtil::Rectangle& dstrect) { const OGLTexture* srcentry = static_cast(source); - if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight() && - g_ogl_config.bSupportsCopySubImage) - { - glCopyImageSubData(srcentry->m_texId, GL_TEXTURE_2D_ARRAY, 0, srcrect.left, srcrect.top, 0, - m_texId, GL_TEXTURE_2D_ARRAY, 0, dstrect.left, dstrect.top, 0, - dstrect.GetWidth(), dstrect.GetHeight(), srcentry->m_config.layers); - return; - } - else if (!m_framebuffer) + if (!m_framebuffer) { glGenFramebuffers(1, &m_framebuffer); FramebufferManager::SetFramebuffer(m_framebuffer); diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.h b/Source/Core/VideoBackends/OGL/OGLTexture.h index 064006fead..6db7b9387e 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.h +++ b/Source/Core/VideoBackends/OGL/OGLTexture.h @@ -21,9 +21,13 @@ public: void Bind(unsigned int stage) override; - void CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) override; + void CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) override; + void ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& srcrect, + const MathUtil::Rectangle& dstrect) override; void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) override; diff --git a/Source/Core/VideoBackends/Software/SWTexture.cpp b/Source/Core/VideoBackends/Software/SWTexture.cpp index 184a223392..db9b56020a 100644 --- a/Source/Core/VideoBackends/Software/SWTexture.cpp +++ b/Source/Core/VideoBackends/Software/SWTexture.cpp @@ -56,9 +56,19 @@ void SWTexture::Bind(unsigned int stage) { } -void SWTexture::CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) +void SWTexture::CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) +{ + _assert_(src_level == 0 && src_layer == 0 && dst_layer == 0 && dst_level == 0); + CopyTextureData(src->GetConfig(), static_cast(src)->m_data.data(), + src_rect.left, src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(), m_config, + m_data.data(), dst_rect.left, dst_rect.top); +} +void SWTexture::ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& srcrect, + const MathUtil::Rectangle& dstrect) { const SWTexture* software_source_texture = static_cast(source); diff --git a/Source/Core/VideoBackends/Software/SWTexture.h b/Source/Core/VideoBackends/Software/SWTexture.h index 2dbec7d0ab..403cf257b0 100644 --- a/Source/Core/VideoBackends/Software/SWTexture.h +++ b/Source/Core/VideoBackends/Software/SWTexture.h @@ -21,9 +21,13 @@ public: void Bind(unsigned int stage) override; - void CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) override; + void CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) override; + void ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& srcrect, + const MathUtil::Rectangle& dstrect) override; void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) override; diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index cfea34ff8e..f2db740551 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -163,10 +163,13 @@ void VKTexture::Unmap() m_staging_texture->Unmap(); } -void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle& src_rect) +void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) { + Texture2D* src_texture = static_cast(src)->GetRawTexIdentifier(); + _assert_msg_(VIDEO, static_cast(src_rect.GetWidth()) <= src_texture->GetWidth() && static_cast(src_rect.GetHeight()) <= src_texture->GetHeight(), "Source rect is too large for CopyRectangleFromTexture"); @@ -176,15 +179,11 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle& dst_rect, "Dest rect is too large for CopyRectangleFromTexture"); VkImageCopy image_copy = { - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, - src_texture->GetLayers()}, // VkImageSubresourceLayers srcSubresource - {src_rect.left, src_rect.top, 0}, // VkOffset3D srcOffset - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, // VkImageSubresourceLayers dstSubresource - m_config.layers}, - {dst_rect.left, dst_rect.top, 0}, // VkOffset3D dstOffset - {static_cast(src_rect.GetWidth()), static_cast(src_rect.GetHeight()), - 1} // VkExtent3D extent - }; + {VK_IMAGE_ASPECT_COLOR_BIT, src_level, src_layer, src_texture->GetLayers()}, + {src_rect.left, src_rect.top, 0}, + {VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, m_config.layers}, + {dst_rect.left, dst_rect.top, 0}, + {static_cast(src_rect.GetWidth()), static_cast(src_rect.GetHeight()), 1}}; // Must be called outside of a render pass. StateTracker::GetInstance()->EndRenderPass(); @@ -197,12 +196,20 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle& dst_rect, vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + + // Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound. + src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } -void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle& src_rect) +void VKTexture::ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& src_rect, + const MathUtil::Rectangle& dst_rect) { + Texture2D* src_texture = static_cast(source)->GetRawTexIdentifier(); + // Can't do this within a game render pass. StateTracker::GetInstance()->EndRenderPass(); StateTracker::GetInstance()->SetPendingRebind(); @@ -235,27 +242,10 @@ void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle& dst_rect, static_cast(src_texture->GetWidth()), static_cast(src_texture->GetHeight())); draw.EndRenderPass(); -} - -void VKTexture::CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) -{ - auto* raw_source_texture = static_cast(source)->GetRawTexIdentifier(); - CopyRectangleFromTexture(raw_source_texture, srcrect, dstrect); -} - -void VKTexture::CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) -{ - if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight()) - CopyTextureRectangle(dstrect, source, srcrect); - else - ScaleTextureRectangle(dstrect, source, srcrect); // Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound. - source->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.h b/Source/Core/VideoBackends/Vulkan/VKTexture.h index 5f2df0c26b..5c2116e4ee 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.h +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.h @@ -24,11 +24,15 @@ public: void Bind(unsigned int stage) override; void Unmap() override; - void CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) override; - void CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect); + void CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) override; + void ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& src_rect, + const MathUtil::Rectangle& dst_rect); + void ScaleRectangleFromTexture(Texture2D* src_texture, const MathUtil::Rectangle& src_rect, + const MathUtil::Rectangle& dst_rect); void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) override; @@ -41,14 +45,6 @@ private: VKTexture(const TextureConfig& tex_config, std::unique_ptr texture, VkFramebuffer framebuffer); - // Copies the contents of a texture using vkCmdCopyImage - void CopyTextureRectangle(const MathUtil::Rectangle& dst_rect, Texture2D* src_texture, - const MathUtil::Rectangle& src_rect); - - // Copies (and optionally scales) the contents of a texture using a framgent shader. - void ScaleTextureRectangle(const MathUtil::Rectangle& dst_rect, Texture2D* src_texture, - const MathUtil::Rectangle& src_rect); - std::optional MapFullImpl() override; std::optional MapRegionImpl(u32 level, u32 x, u32 y, u32 width, u32 height) override; diff --git a/Source/Core/VideoCommon/AbstractTexture.h b/Source/Core/VideoCommon/AbstractTexture.h index fd4b4bb621..d2ef2272fc 100644 --- a/Source/Core/VideoCommon/AbstractTexture.h +++ b/Source/Core/VideoCommon/AbstractTexture.h @@ -33,9 +33,13 @@ public: std::optional Map(u32 level); virtual void Unmap(); - virtual void CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) = 0; + virtual void CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) = 0; + virtual void ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& srcrect, + const MathUtil::Rectangle& dstrect) = 0; virtual void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) = 0; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 6f3e23923c..00e34cff71 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -733,8 +733,8 @@ void Renderer::UpdateFrameDumpTexture() config.rendertarget = true; m_dump_texture = CreateTexture(config); } - m_dump_texture->CopyRectangleFromTexture(m_last_xfb_texture, m_last_xfb_region, - EFBRectangle{0, 0, target_width, target_height}); + m_dump_texture->CopyRectangleFromTexture(m_last_xfb_texture, m_last_xfb_region, 0, 0, + EFBRectangle{0, 0, target_width, target_height}, 0, 0); } void Renderer::ShutdownFrameDumping() diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 521f5816f1..31701e6bc0 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -277,9 +277,9 @@ void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntry* e std::unique_ptr new_texture = AllocateTexture(newconfig); if (new_texture) { - new_texture->CopyRectangleFromTexture(entry->texture.get(), - entry->texture->GetConfig().GetRect(), - new_texture->GetConfig().GetRect()); + new_texture->ScaleRectangleFromTexture(entry->texture.get(), + entry->texture->GetConfig().GetRect(), + new_texture->GetConfig().GetRect()); entry->texture.swap(new_texture); auto config = new_texture->GetConfig(); @@ -406,7 +406,8 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* pale dstrect.top = dst_y; dstrect.right = (dst_x + copy_width); dstrect.bottom = (dst_y + copy_height); - entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, dstrect); + entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, 0, 0, + dstrect, 0, 0); if (isPaletteTexture) { @@ -1391,7 +1392,8 @@ bool TextureCacheBase::LoadTextureFromOverlappingTextures(TCacheEntry* entry_to_ dstrect.bottom -= 1; } - entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, dstrect); + entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, 0, 0, + dstrect, 0, 0); updated_entry = true; From c2cc128f1b6be695a4facd3c0fd697a24c3df17a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 30 Oct 2017 22:00:15 +1000 Subject: [PATCH 09/16] AbstractTexture: Implement Save using new common methods --- Source/Core/VideoBackends/OGL/TextureCache.h | 3 -- Source/Core/VideoCommon/AbstractTexture.cpp | 32 +++++++++++++++----- Source/Core/VideoCommon/AbstractTexture.h | 4 ++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/Source/Core/VideoBackends/OGL/TextureCache.h b/Source/Core/VideoBackends/OGL/TextureCache.h index ae848c8c9c..50366f7d1a 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.h +++ b/Source/Core/VideoBackends/OGL/TextureCache.h @@ -97,7 +97,4 @@ private: std::map, TextureDecodingProgramInfo> m_texture_decoding_program_info; std::array m_texture_decoding_buffer_views; }; - -bool SaveTexture(const std::string& filename, u32 textarget, u32 tex, int virtual_width, - int virtual_height, unsigned int level); } diff --git a/Source/Core/VideoCommon/AbstractTexture.cpp b/Source/Core/VideoCommon/AbstractTexture.cpp index 7e09be5344..109bb52897 100644 --- a/Source/Core/VideoCommon/AbstractTexture.cpp +++ b/Source/Core/VideoCommon/AbstractTexture.cpp @@ -6,8 +6,10 @@ #include "Common/Assert.h" #include "Common/MsgHandler.h" +#include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/ImageWrite.h" +#include "VideoCommon/RenderBase.h" AbstractTexture::AbstractTexture(const TextureConfig& c) : m_config(c) { @@ -20,17 +22,33 @@ bool AbstractTexture::Save(const std::string& filename, unsigned int level) // We can't dump compressed textures currently (it would mean drawing them to a RGBA8 // framebuffer, and saving that). TextureCache does not call Save for custom textures // anyway, so this is fine for now. - _assert_(m_config.format == AbstractTextureFormat::RGBA8); + _assert_(!IsCompressedFormat(m_config.format)); + _assert_(level < m_config.levels); - auto result = level == 0 ? Map() : Map(level); + // Determine dimensions of image we want to save. + u32 level_width = std::max(1u, m_config.width >> level); + u32 level_height = std::max(1u, m_config.height >> level); - if (!result.has_value()) - { + // Use a temporary staging texture for the download. Certainly not optimal, + // but this is not a frequently-executed code path.. + TextureConfig readback_texture_config(level_width, level_height, 1, 1, + AbstractTextureFormat::RGBA8, false); + auto readback_texture = + g_renderer->CreateStagingTexture(StagingTextureType::Readback, readback_texture_config); + if (!readback_texture) return false; - } - auto raw_data = result.value(); - return TextureToPng(raw_data.data, raw_data.stride, filename, raw_data.width, raw_data.height); + // Copy to the readback texture's buffer. + readback_texture->CopyFromTexture(this, 0, level); + readback_texture->Flush(); + + // Map it so we can encode it to the file. + if (!readback_texture->Map()) + return false; + + return TextureToPng(reinterpret_cast(readback_texture->GetMappedPointer()), + static_cast(readback_texture->GetMappedStride()), filename, level_width, + level_height); } std::optional AbstractTexture::Map() diff --git a/Source/Core/VideoCommon/AbstractTexture.h b/Source/Core/VideoCommon/AbstractTexture.h index d2ef2272fc..c18e1f17d3 100644 --- a/Source/Core/VideoCommon/AbstractTexture.h +++ b/Source/Core/VideoCommon/AbstractTexture.h @@ -17,8 +17,8 @@ class AbstractTexture public: explicit AbstractTexture(const TextureConfig& c); virtual ~AbstractTexture(); + virtual void Bind(unsigned int stage) = 0; - bool Save(const std::string& filename, unsigned int level); struct RawTextureInfo { @@ -43,6 +43,8 @@ public: virtual void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) = 0; + bool Save(const std::string& filename, unsigned int level); + static bool IsCompressedFormat(AbstractTextureFormat format); static size_t CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length); static size_t GetTexelSizeForFormat(AbstractTextureFormat format); From 59517318d6a237055bf244e05fed8ce059167e83 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 30 Oct 2017 22:58:43 +1000 Subject: [PATCH 10/16] Vulkan: Use new readback methods for texture encoding --- .../VideoBackends/Vulkan/TextureConverter.cpp | 107 +++++------------- .../VideoBackends/Vulkan/TextureConverter.h | 12 +- 2 files changed, 35 insertions(+), 84 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp index b8ac91d06b..e0dacded9e 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp @@ -67,9 +67,6 @@ TextureConverter::~TextureConverter() if (m_encoding_render_pass != VK_NULL_HANDLE) vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_encoding_render_pass, nullptr); - if (m_encoding_render_framebuffer != VK_NULL_HANDLE) - vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_encoding_render_framebuffer, nullptr); - for (auto& it : m_encoding_shaders) vkDestroyShaderModule(g_vulkan_context->GetDevice(), it.second, nullptr); @@ -111,12 +108,6 @@ bool TextureConverter::Initialize() return false; } - if (!CreateEncodingDownloadTexture()) - { - PanicAlert("Failed to create download texture"); - return false; - } - if (!CreateDecodingTexture()) { PanicAlert("Failed to create decoding texture"); @@ -245,8 +236,10 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p // Can't do our own draw within a render pass. StateTracker::GetInstance()->EndRenderPass(); - m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + static_cast(m_encoding_render_texture.get()) + ->GetRawTexIdentifier() + ->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), @@ -276,23 +269,15 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p render_height); VkRect2D render_region = {{0, 0}, {render_width, render_height}}; - draw.BeginRenderPass(m_encoding_render_framebuffer, render_region); + draw.BeginRenderPass(static_cast(m_encoding_render_texture.get())->GetFramebuffer(), + render_region); draw.DrawWithoutVertexBuffer(4); draw.EndRenderPass(); - // Transition the image before copying - m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - m_encoding_download_texture->CopyFromImage( - g_command_buffer_mgr->GetCurrentCommandBuffer(), m_encoding_render_texture->GetImage(), - VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, render_width, render_height, 0, 0); - - // Block until the GPU has finished copying to the staging texture. - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Copy from staging texture to the final destination, adjusting pitch if necessary. - m_encoding_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr, - memory_stride); + MathUtil::Rectangle copy_rect(0, 0, render_width, render_height); + m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0, + copy_rect); + m_encoding_readback_texture->ReadTexels(copy_rect, dest_ptr, memory_stride); } void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u32 dst_stride, @@ -304,8 +289,9 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u // Borrow framebuffer from EFB2RAM encoder. VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_encoding_render_texture->TransitionToLayout(command_buffer, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + static_cast(m_encoding_render_texture.get()) + ->GetRawTexIdentifier() + ->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // Use fragment shader to convert RGBA to YUYV. // Use linear sampler for downscaling. This texture is in BGRA order, so the data is already in @@ -317,7 +303,8 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u m_encoding_render_pass, g_shader_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE, m_rgb_to_yuyv_shader); VkRect2D region = {{0, 0}, {output_width, dst_height}}; - draw.BeginRenderPass(m_encoding_render_framebuffer, region); + draw.BeginRenderPass(static_cast(m_encoding_render_texture.get())->GetFramebuffer(), + region); draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler()); draw.DrawQuad(0, 0, static_cast(output_width), static_cast(dst_height), src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(), @@ -325,18 +312,11 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u static_cast(src_texture->GetHeight())); draw.EndRenderPass(); - // Render pass transitions to TRANSFER_SRC. - m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - // Copy from encoding texture to download buffer. - m_encoding_download_texture->CopyFromImage(command_buffer, m_encoding_render_texture->GetImage(), - VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, output_width, - dst_height, 0, 0); - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Finally, copy to guest memory. This may have a different stride. - m_encoding_download_texture->ReadTexels(0, 0, output_width, dst_height, dst_ptr, dst_stride); + MathUtil::Rectangle copy_rect(0, 0, output_width, dst_height); + m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0, + copy_rect); + m_encoding_readback_texture->ReadTexels(copy_rect, dst_ptr, dst_stride); } void TextureConverter::DecodeYUYVTextureFromMemory(VKTexture* dst_texture, const void* src_ptr, @@ -734,10 +714,10 @@ VkShaderModule TextureConverter::GetEncodingShader(const EFBCopyParams& params) bool TextureConverter::CreateEncodingRenderPass() { VkAttachmentDescription attachments[] = { - {0, ENCODING_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, - VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, - VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; + {0, Util::GetVkFormatForHostTextureFormat(ENCODING_TEXTURE_FORMAT), VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; VkAttachmentReference color_attachment_references[] = { {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; @@ -769,43 +749,14 @@ bool TextureConverter::CreateEncodingRenderPass() bool TextureConverter::CreateEncodingTexture() { - m_encoding_render_texture = Texture2D::Create( - ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, ENCODING_TEXTURE_FORMAT, - VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); - if (!m_encoding_render_texture) - return false; + TextureConfig config(ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, + ENCODING_TEXTURE_FORMAT, true); - VkImageView framebuffer_attachments[] = {m_encoding_render_texture->GetView()}; - VkFramebufferCreateInfo framebuffer_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - nullptr, - 0, - m_encoding_render_pass, - static_cast(ArraySize(framebuffer_attachments)), - framebuffer_attachments, - m_encoding_render_texture->GetWidth(), - m_encoding_render_texture->GetHeight(), - m_encoding_render_texture->GetLayers()}; + m_encoding_render_texture = g_renderer->CreateTexture(config); + m_encoding_readback_texture = + g_renderer->CreateStagingTexture(StagingTextureType::Readback, config); - VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, - &m_encoding_render_framebuffer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); - return false; - } - - return true; -} - -bool TextureConverter::CreateEncodingDownloadTexture() -{ - m_encoding_download_texture = - StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, ENCODING_TEXTURE_WIDTH, - ENCODING_TEXTURE_HEIGHT, ENCODING_TEXTURE_FORMAT); - - return m_encoding_download_texture && m_encoding_download_texture->Map(); + return m_encoding_render_texture && m_encoding_readback_texture; } bool TextureConverter::CreateDecodingTexture() diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.h b/Source/Core/VideoBackends/Vulkan/TextureConverter.h index 1a62590019..3adb26c5b7 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.h +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.h @@ -16,6 +16,9 @@ #include "VideoCommon/TextureDecoder.h" #include "VideoCommon/VideoCommon.h" +class AbstractTexture; +class AbstractStagingTexture; + namespace Vulkan { class StagingTexture2D; @@ -58,7 +61,7 @@ public: private: static const u32 ENCODING_TEXTURE_WIDTH = EFB_WIDTH * 4; static const u32 ENCODING_TEXTURE_HEIGHT = 1024; - static const VkFormat ENCODING_TEXTURE_FORMAT = VK_FORMAT_B8G8R8A8_UNORM; + static const AbstractTextureFormat ENCODING_TEXTURE_FORMAT = AbstractTextureFormat::BGRA8; static const size_t NUM_PALETTE_CONVERSION_SHADERS = 3; // Maximum size of a texture based on BP registers. @@ -75,8 +78,6 @@ private: bool CreateEncodingRenderPass(); bool CreateEncodingTexture(); - bool CreateEncodingDownloadTexture(); - bool CreateDecodingTexture(); bool CompileYUYVConversionShaders(); @@ -106,10 +107,9 @@ private: // Texture encoding - RGBA8->GX format in memory std::map m_encoding_shaders; + std::unique_ptr m_encoding_render_texture; + std::unique_ptr m_encoding_readback_texture; VkRenderPass m_encoding_render_pass = VK_NULL_HANDLE; - std::unique_ptr m_encoding_render_texture; - VkFramebuffer m_encoding_render_framebuffer = VK_NULL_HANDLE; - std::unique_ptr m_encoding_download_texture; // Texture decoding - GX format in memory->RGBA8 struct TextureDecodingPipeline From 9da9f26b9049ab590217750d8d582590a3409754 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 30 Oct 2017 23:06:46 +1000 Subject: [PATCH 11/16] OGL: Use new readback methods for EFB2RAM --- .../VideoBackends/OGL/TextureConverter.cpp | 79 ++++--------------- 1 file changed, 17 insertions(+), 62 deletions(-) diff --git a/Source/Core/VideoBackends/OGL/TextureConverter.cpp b/Source/Core/VideoBackends/OGL/TextureConverter.cpp index a2460bf935..1f368ee4e4 100644 --- a/Source/Core/VideoBackends/OGL/TextureConverter.cpp +++ b/Source/Core/VideoBackends/OGL/TextureConverter.cpp @@ -34,9 +34,8 @@ namespace TextureConverter { using OGL::TextureCache; -static GLuint s_texConvFrameBuffer[2] = {0, 0}; -static GLuint s_srcTexture = 0; // for decoding from RAM -static GLuint s_dstTexture = 0; // for encoding to RAM +std::unique_ptr s_encoding_render_texture; +std::unique_ptr s_encoding_readback_texture; const int renderBufferWidth = EFB_WIDTH * 4; const int renderBufferHeight = 1024; @@ -49,8 +48,6 @@ struct EncodingProgram }; static std::map s_encoding_programs; -static GLuint s_PBO = 0; // for readback with different strides - static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params) { auto iter = s_encoding_programs.find(params); @@ -87,42 +84,21 @@ static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params) void Init() { - glGenFramebuffers(2, s_texConvFrameBuffer); - - glActiveTexture(GL_TEXTURE9); - glGenTextures(1, &s_srcTexture); - glBindTexture(GL_TEXTURE_2D, s_srcTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - - glGenTextures(1, &s_dstTexture); - glBindTexture(GL_TEXTURE_2D, s_dstTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, renderBufferWidth, renderBufferHeight, 0, GL_RGBA, - GL_UNSIGNED_BYTE, nullptr); - - FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[0]); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s_dstTexture, 0); - FramebufferManager::SetFramebuffer(0); - - glGenBuffers(1, &s_PBO); + TextureConfig config(renderBufferWidth, renderBufferHeight, 1, 1, AbstractTextureFormat::BGRA8, + true); + s_encoding_render_texture = g_renderer->CreateTexture(config); + s_encoding_readback_texture = + g_renderer->CreateStagingTexture(StagingTextureType::Readback, config); } void Shutdown() { - glDeleteTextures(1, &s_srcTexture); - glDeleteTextures(1, &s_dstTexture); - glDeleteBuffers(1, &s_PBO); - glDeleteFramebuffers(2, s_texConvFrameBuffer); + s_encoding_readback_texture.reset(); + s_encoding_render_texture.reset(); for (auto& program : s_encoding_programs) program.second.program.Destroy(); s_encoding_programs.clear(); - - s_srcTexture = 0; - s_dstTexture = 0; - s_PBO = 0; - s_texConvFrameBuffer[0] = 0; - s_texConvFrameBuffer[1] = 0; } // dst_line_size, writeStride in bytes @@ -130,9 +106,8 @@ void Shutdown() static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line_size, u32 dstHeight, u32 writeStride, bool linearFilter, float y_scale) { - // switch to texture converter frame buffer - // attach render buffer as color destination - FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[0]); + FramebufferManager::SetFramebuffer( + static_cast(s_encoding_render_texture.get())->GetFramebuffer()); OpenGL_BindAttributelessVAO(); @@ -153,33 +128,13 @@ static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - int dstSize = dst_line_size * dstHeight; + MathUtil::Rectangle copy_rect(0, 0, dst_line_size / 4, dstHeight); + s_encoding_readback_texture->CopyFromTexture(s_encoding_render_texture.get(), copy_rect, 0, 0, + copy_rect); + s_encoding_readback_texture->ReadTexels(copy_rect, destAddr, writeStride); - // When the dst_line_size and writeStride are the same, we could use glReadPixels directly to RAM. - // But instead we always copy the data via a PBO, because macOS inexplicably prefers this (most - // noticeably in the Super Mario Sunshine transition). - glBindBuffer(GL_PIXEL_PACK_BUFFER, s_PBO); - glBufferData(GL_PIXEL_PACK_BUFFER, dstSize, nullptr, GL_STREAM_READ); - glReadPixels(0, 0, (GLsizei)(dst_line_size / 4), (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE, - nullptr); - u8* pbo = (u8*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, dstSize, GL_MAP_READ_BIT); - - if (dst_line_size == writeStride) - { - memcpy(destAddr, pbo, dst_line_size * dstHeight); - } - else - { - for (size_t i = 0; i < dstHeight; ++i) - { - memcpy(destAddr, pbo, dst_line_size); - pbo += dst_line_size; - destAddr += writeStride; - } - } - - glUnmapBuffer(GL_PIXEL_PACK_BUFFER); - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + FramebufferManager::SetFramebuffer(0); + OGLTexture::SetStage(); } void EncodeToRamFromTexture(u8* dest_ptr, const EFBCopyParams& params, u32 native_width, From 752dd4761d292c94623b166403733847c198162a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 30 Oct 2017 23:11:46 +1000 Subject: [PATCH 12/16] D3D: Use new readback methods for EFB2RAM --- .../VideoBackends/D3D/PSTextureEncoder.cpp | 95 ++++++------------- .../Core/VideoBackends/D3D/PSTextureEncoder.h | 14 +-- 2 files changed, 37 insertions(+), 72 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp b/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp index 4fcce4ef03..d97cd2bc35 100644 --- a/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp +++ b/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp @@ -4,17 +4,21 @@ #include "VideoBackends/D3D/PSTextureEncoder.h" +#include "Common/Assert.h" #include "Common/Logging/Log.h" #include "Core/HW/Memmap.h" #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DShader.h" #include "VideoBackends/D3D/D3DState.h" #include "VideoBackends/D3D/D3DUtil.h" +#include "VideoBackends/D3D/DXTexture.h" #include "VideoBackends/D3D/FramebufferManager.h" #include "VideoBackends/D3D/Render.h" #include "VideoBackends/D3D/TextureCache.h" #include "VideoBackends/D3D/VertexShaderCache.h" +#include "VideoCommon/AbstractStagingTexture.h" +#include "VideoCommon/AbstractTexture.h" #include "VideoCommon/TextureConversionShader.h" #include "VideoCommon/VideoCommon.h" @@ -31,76 +35,41 @@ struct EFBEncodeParams }; PSTextureEncoder::PSTextureEncoder() - : m_ready(false), m_out(nullptr), m_outRTV(nullptr), m_outStage(nullptr), - m_encodeParams(nullptr) { } +PSTextureEncoder::~PSTextureEncoder() = default; + void PSTextureEncoder::Init() { - m_ready = false; - - HRESULT hr; - - // Create output texture RGBA format - // TODO: This Texture is overly large and parts of it are unused - // EFB2RAM copies use max (EFB_WIDTH * 4) by (EFB_HEIGHT / 4) - // XFB2RAM copies use max (EFB_WIDTH / 2) by (EFB_HEIGHT) - D3D11_TEXTURE2D_DESC t2dd = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM, EFB_WIDTH * 4, 1024, - 1, 1, D3D11_BIND_RENDER_TARGET); - hr = D3D::device->CreateTexture2D(&t2dd, nullptr, &m_out); - CHECK(SUCCEEDED(hr), "create efb encode output texture"); - D3D::SetDebugObjectName(m_out, "efb encoder output texture"); - - // Create output render target view - D3D11_RENDER_TARGET_VIEW_DESC rtvd = CD3D11_RENDER_TARGET_VIEW_DESC( - m_out, D3D11_RTV_DIMENSION_TEXTURE2D, DXGI_FORMAT_B8G8R8A8_UNORM); - hr = D3D::device->CreateRenderTargetView(m_out, &rtvd, &m_outRTV); - CHECK(SUCCEEDED(hr), "create efb encode output render target view"); - D3D::SetDebugObjectName(m_outRTV, "efb encoder output rtv"); - - // Create output staging buffer - t2dd.Usage = D3D11_USAGE_STAGING; - t2dd.BindFlags = 0; - t2dd.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - hr = D3D::device->CreateTexture2D(&t2dd, nullptr, &m_outStage); - CHECK(SUCCEEDED(hr), "create efb encode output staging buffer"); - D3D::SetDebugObjectName(m_outStage, "efb encoder output staging buffer"); + // TODO: Move this to a constant somewhere in common. + TextureConfig encoding_texture_config(EFB_WIDTH * 4, 1024, 1, 1, AbstractTextureFormat::BGRA8, + true); + m_encoding_render_texture = g_renderer->CreateTexture(encoding_texture_config); + m_encoding_readback_texture = + g_renderer->CreateStagingTexture(StagingTextureType::Readback, encoding_texture_config); + _assert_(m_encoding_render_texture && m_encoding_readback_texture); // Create constant buffer for uploading data to shaders D3D11_BUFFER_DESC bd = CD3D11_BUFFER_DESC(sizeof(EFBEncodeParams), D3D11_BIND_CONSTANT_BUFFER); - hr = D3D::device->CreateBuffer(&bd, nullptr, &m_encodeParams); + HRESULT hr = D3D::device->CreateBuffer(&bd, nullptr, &m_encode_params); CHECK(SUCCEEDED(hr), "create efb encode params buffer"); - D3D::SetDebugObjectName(m_encodeParams, "efb encoder params buffer"); - - m_ready = true; + D3D::SetDebugObjectName(m_encode_params, "efb encoder params buffer"); } void PSTextureEncoder::Shutdown() { - m_ready = false; - for (auto& it : m_encoding_shaders) - { SAFE_RELEASE(it.second); - } m_encoding_shaders.clear(); - SAFE_RELEASE(m_encodeParams); - SAFE_RELEASE(m_outStage); - SAFE_RELEASE(m_outRTV); - SAFE_RELEASE(m_out); + SAFE_RELEASE(m_encode_params); } void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect, bool scale_by_half) { - if (!m_ready) // Make sure we initialized OK - return; - - HRESULT hr; - // Resolve MSAA targets before copying. // FIXME: Instead of resolving EFB, it would be better to pick out a // single sample from each pixel. The game may break if it isn't @@ -122,7 +91,10 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w constexpr EFBRectangle fullSrcRect(0, 0, EFB_WIDTH, EFB_HEIGHT); TargetRectangle targetRect = g_renderer->ConvertEFBRectangle(fullSrcRect); - D3D::context->OMSetRenderTargets(1, &m_outRTV, nullptr); + D3D::context->OMSetRenderTargets( + 1, + &static_cast(m_encoding_render_texture.get())->GetRawTexIdentifier()->GetRTV(), + nullptr); EFBEncodeParams encode_params; encode_params.SrcLeft = src_rect.left; @@ -130,8 +102,8 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w encode_params.DestWidth = native_width; encode_params.ScaleFactor = scale_by_half ? 2 : 1; encode_params.y_scale = params.y_scale; - D3D::context->UpdateSubresource(m_encodeParams, 0, nullptr, &encode_params, 0, 0); - D3D::stateman->SetPixelConstants(m_encodeParams); + D3D::context->UpdateSubresource(m_encode_params, 0, nullptr, &encode_params, 0, 0); + D3D::stateman->SetPixelConstants(m_encode_params); // We also linear filtering for both box filtering and downsampling higher resolutions to 1x // TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more @@ -148,24 +120,15 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w VertexShaderCache::GetSimpleInputLayout()); // Copy to staging buffer - D3D11_BOX srcBox = CD3D11_BOX(0, 0, 0, words_per_row, num_blocks_y, 1); - D3D::context->CopySubresourceRegion(m_outStage, 0, 0, 0, 0, m_out, 0, &srcBox); - - // Transfer staging buffer to GameCube/Wii RAM - D3D11_MAPPED_SUBRESOURCE map = {0}; - hr = D3D::context->Map(m_outStage, 0, D3D11_MAP_READ, 0, &map); - CHECK(SUCCEEDED(hr), "map staging buffer (0x%x)", hr); - - u8* src = (u8*)map.pData; - u32 readStride = std::min(bytes_per_row, map.RowPitch); - for (unsigned int y = 0; y < num_blocks_y; ++y) + MathUtil::Rectangle copy_rect(0, 0, words_per_row, num_blocks_y); + m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0, + copy_rect); + m_encoding_readback_texture->Flush(); + if (m_encoding_readback_texture->Map()) { - memcpy(dst, src, readStride); - dst += memory_stride; - src += map.RowPitch; + m_encoding_readback_texture->ReadTexels(copy_rect, dst, memory_stride); + m_encoding_readback_texture->Unmap(); } - - D3D::context->Unmap(m_outStage, 0); } // Restore API diff --git a/Source/Core/VideoBackends/D3D/PSTextureEncoder.h b/Source/Core/VideoBackends/D3D/PSTextureEncoder.h index 738f1b2d3e..7c45970d19 100644 --- a/Source/Core/VideoBackends/D3D/PSTextureEncoder.h +++ b/Source/Core/VideoBackends/D3D/PSTextureEncoder.h @@ -5,11 +5,15 @@ #pragma once #include +#include #include "Common/CommonTypes.h" #include "VideoCommon/TextureConversionShader.h" #include "VideoCommon/VideoCommon.h" +class AbstractTexture; +class AbstractStagingTexture; + struct ID3D11Texture2D; struct ID3D11RenderTargetView; struct ID3D11Buffer; @@ -29,6 +33,7 @@ class PSTextureEncoder final { public: PSTextureEncoder(); + ~PSTextureEncoder(); void Init(); void Shutdown(); @@ -39,12 +44,9 @@ public: private: ID3D11PixelShader* GetEncodingPixelShader(const EFBCopyParams& params); - bool m_ready; - - ID3D11Texture2D* m_out; - ID3D11RenderTargetView* m_outRTV; - ID3D11Texture2D* m_outStage; - ID3D11Buffer* m_encodeParams; + ID3D11Buffer* m_encode_params = nullptr; + std::unique_ptr m_encoding_render_texture; + std::unique_ptr m_encoding_readback_texture; std::map m_encoding_shaders; }; } From 6577365851967d03caf1b70f440e1eee76198ebb Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 19 Nov 2017 17:33:53 +1000 Subject: [PATCH 13/16] VideoCommon: Re-implement asynchronous frame dumping This was lost as a result of hybrid XFB, now it is back, and ~10% faster in very brief testing. --- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 1 - Source/Core/VideoCommon/MainBase.cpp | 2 +- Source/Core/VideoCommon/RenderBase.cpp | 133 ++++++++++++------ Source/Core/VideoCommon/RenderBase.h | 31 +++- 4 files changed, 116 insertions(+), 51 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index af19ad4043..c9e762eac6 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -611,7 +611,6 @@ void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region VK_SUBPASS_CONTENTS_INLINE); // Draw - TargetRectangle source_rc = xfb_texture->GetConfig().GetRect(); BlitScreen(m_swap_chain->GetRenderPass(), GetTargetRectangle(), xfb_region, xfb_texture->GetRawTexIdentifier()); diff --git a/Source/Core/VideoCommon/MainBase.cpp b/Source/Core/VideoCommon/MainBase.cpp index 280fed683d..4dfb97f84a 100644 --- a/Source/Core/VideoCommon/MainBase.cpp +++ b/Source/Core/VideoCommon/MainBase.cpp @@ -49,7 +49,7 @@ void VideoBackendBase::Video_CleanupShared() { // First stop any framedumping, which might need to dump the last xfb frame. This process // can require additional graphics sub-systems so it needs to be done first - g_renderer->ExitFramedumping(); + g_renderer->ShutdownFrameDumping(); Video_Cleanup(); } diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 00e34cff71..d20e23ac46 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -43,6 +43,7 @@ #include "Core/Movie.h" #include "VideoCommon/AVIDump.h" +#include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/CPMemory.h" @@ -99,15 +100,6 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height) Renderer::~Renderer() = default; -void Renderer::ExitFramedumping() -{ - ShutdownFrameDumping(); - if (m_frame_dump_thread.joinable()) - m_frame_dump_thread.join(); - - m_dump_texture.reset(); -} - void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight, float Gamma) { @@ -629,14 +621,10 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const m_aspect_wide = flush_count_anamorphic > 0.75 * flush_total; } - if (IsFrameDumping() && m_last_xfb_texture) - { - FinishFrameData(); - } - else - { - ShutdownFrameDumping(); - } + // Ensure the last frame was written to the dump. + // This is required even if frame dumping has stopped, since the frame dump is one frame + // behind the renderer. + FlushFrameDump(); bool update_frame_count = false; if (xfbAddr && fbWidth && fbStride && fbHeight) @@ -662,10 +650,9 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const m_fps_counter.Update(); update_frame_count = true; + if (IsFrameDumping()) - { - DoDumpFrame(); - } + DumpCurrentFrame(); } // Update our last xfb values @@ -695,20 +682,16 @@ bool Renderer::IsFrameDumping() return false; } -void Renderer::DoDumpFrame() +void Renderer::DumpCurrentFrame() { - UpdateFrameDumpTexture(); + // Scale/render to frame dump texture. + RenderFrameDump(); - auto result = m_dump_texture->Map(); - if (result.has_value()) - { - auto raw_data = result.value(); - DumpFrameData(raw_data.data, raw_data.width, raw_data.height, raw_data.stride, - AVIDump::FetchState(m_last_xfb_ticks)); - } + // Queue a readback for the next frame. + QueueFrameDumpReadback(); } -void Renderer::UpdateFrameDumpTexture() +void Renderer::RenderFrameDump() { int target_width, target_height; if (!g_ActiveConfig.bInternalResolutionFrameDumps) @@ -723,33 +706,99 @@ void Renderer::UpdateFrameDumpTexture() m_last_xfb_texture->GetConfig().width, m_last_xfb_texture->GetConfig().height); } - if (m_dump_texture == nullptr || - m_dump_texture->GetConfig().width != static_cast(target_width) || - m_dump_texture->GetConfig().height != static_cast(target_height)) + // Ensure framebuffer exists (we lazily allocate it in case frame dumping isn't used). + // Or, resize texture if it isn't large enough to accommodate the current frame. + if (!m_frame_dump_render_texture || + m_frame_dump_render_texture->GetConfig().width != static_cast(target_width) || + m_frame_dump_render_texture->GetConfig().height == static_cast(target_height)) { - TextureConfig config; - config.width = target_width; - config.height = target_height; - config.rendertarget = true; - m_dump_texture = CreateTexture(config); + // Recreate texture objects. Release before creating so we don't temporarily use twice the RAM. + TextureConfig config(target_width, target_height, 1, 1, AbstractTextureFormat::RGBA8, true); + m_frame_dump_render_texture.reset(); + m_frame_dump_render_texture = CreateTexture(config); + _assert_(m_frame_dump_render_texture); } - m_dump_texture->CopyRectangleFromTexture(m_last_xfb_texture, m_last_xfb_region, 0, 0, - EFBRectangle{0, 0, target_width, target_height}, 0, 0); + + // Scaling is likely to occur here, but if possible, do a bit-for-bit copy. + if (m_last_xfb_region.GetWidth() != target_width || + m_last_xfb_region.GetHeight() != target_height) + { + m_frame_dump_render_texture->ScaleRectangleFromTexture( + m_last_xfb_texture, m_last_xfb_region, EFBRectangle{0, 0, target_width, target_height}); + } + else + { + m_frame_dump_render_texture->CopyRectangleFromTexture( + m_last_xfb_texture, m_last_xfb_region, 0, 0, + EFBRectangle{0, 0, target_width, target_height}, 0, 0); + } +} + +void Renderer::QueueFrameDumpReadback() +{ + // Index 0 was just sent to AVI dump. Swap with the second texture. + if (m_frame_dump_readback_textures[0]) + std::swap(m_frame_dump_readback_textures[0], m_frame_dump_readback_textures[1]); + + std::unique_ptr& rbtex = m_frame_dump_readback_textures[0]; + if (!rbtex || rbtex->GetConfig() != m_frame_dump_render_texture->GetConfig()) + { + rbtex = CreateStagingTexture(StagingTextureType::Readback, + m_frame_dump_render_texture->GetConfig()); + } + + m_last_frame_state = AVIDump::FetchState(m_last_xfb_ticks); + m_last_frame_exported = true; + rbtex->CopyFromTexture(m_frame_dump_render_texture.get(), 0, 0); +} + +void Renderer::FlushFrameDump() +{ + if (!m_last_frame_exported) + return; + + // Ensure the previously-queued frame was encoded. + FinishFrameData(); + + // Queue encoding of the last frame dumped. + std::unique_ptr& rbtex = m_frame_dump_readback_textures[0]; + rbtex->Flush(); + if (rbtex->Map()) + { + DumpFrameData(reinterpret_cast(rbtex->GetMappedPointer()), rbtex->GetConfig().width, + rbtex->GetConfig().height, static_cast(rbtex->GetMappedStride()), + m_last_frame_state); + rbtex->Unmap(); + } + + m_last_frame_exported = false; + + // Shutdown frame dumping if it is no longer active. + if (!IsFrameDumping()) + ShutdownFrameDumping(); } void Renderer::ShutdownFrameDumping() { + // Ensure the last queued readback has been sent to the encoder. + FlushFrameDump(); + if (!m_frame_dump_thread_running.IsSet()) return; + // Ensure previous frame has been encoded. FinishFrameData(); + + // Wake thread up, and wait for it to exit. m_frame_dump_thread_running.Clear(); m_frame_dump_start.Set(); + if (m_frame_dump_thread.joinable()) + m_frame_dump_thread.join(); } void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state) { - m_frame_dump_config = FrameDumpConfig{m_last_xfb_texture, data, w, h, stride, state}; + m_frame_dump_config = FrameDumpConfig{data, w, h, stride, state}; if (!m_frame_dump_thread_running.IsSet()) { @@ -759,6 +808,7 @@ void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVI m_frame_dump_thread = std::thread(&Renderer::RunFrameDumps, this); } + // Wake worker thread up. m_frame_dump_start.Set(); m_frame_dump_frame_running = true; } @@ -770,7 +820,6 @@ void Renderer::FinishFrameData() m_frame_dump_done.Wait(); m_frame_dump_frame_running = false; - m_frame_dump_config.texture->Unmap(); } void Renderer::RunFrameDumps() diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index b614c6a375..551fc36871 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -151,7 +151,7 @@ public: virtual void ChangeSurface(void* new_surface_handle) {} bool UseVertexDepthRange() const; - void ExitFramedumping(); + void ShutdownFrameDumping(); protected: std::tuple CalculateTargetScale(int x, int y) const; @@ -190,11 +190,8 @@ protected: u32 m_last_host_config_bits = 0; private: - void DoDumpFrame(); void RunFrameDumps(); - void ShutdownFrameDumping(); std::tuple CalculateOutputDimensions(int width, int height); - void UpdateFrameDumpTexture(); PEControl::PixelFormat m_prev_efb_format = PEControl::INVALID_FMT; unsigned int m_efb_scale = 1; @@ -212,7 +209,6 @@ private: bool m_frame_dump_frame_running = false; struct FrameDumpConfig { - AbstractTexture* texture; const u8* data; int width; int height; @@ -220,13 +216,18 @@ private: AVIDump::Frame state; } m_frame_dump_config; + // Texture used for screenshot/frame dumping + std::unique_ptr m_frame_dump_render_texture; + std::array, 2> m_frame_dump_readback_textures; + AVIDump::Frame m_last_frame_state; + bool m_last_frame_exported = false; + + // Tracking of XFB textures so we don't render duplicate frames. AbstractTexture* m_last_xfb_texture = nullptr; u64 m_last_xfb_id = std::numeric_limits::max(); u64 m_last_xfb_ticks = 0; EFBRectangle m_last_xfb_region; - std::unique_ptr m_dump_texture; - // Note: Only used for auto-ir u32 m_last_xfb_width = MAX_XFB_WIDTH; u32 m_last_xfb_height = MAX_XFB_HEIGHT; @@ -240,7 +241,23 @@ private: void DumpFrameToImage(const FrameDumpConfig& config); bool IsFrameDumping(); + + // Asynchronously encodes the current staging texture to the frame dump. + void DumpCurrentFrame(); + + // Fills the frame dump render texture with the current XFB texture. + void RenderFrameDump(); + + // Queues the current frame for readback, which will be written to AVI next frame. + void QueueFrameDumpReadback(); + + // Asynchronously encodes the specified pointer of frame data to the frame dump. void DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state); + + // Ensures all rendered frames are queued for encoding. + void FlushFrameDump(); + + // Ensures all encoded frames have been written to the output file. void FinishFrameData(); }; From 193763ca3ab62086c5a2f09870ffdc84cbd8ee00 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 19 Nov 2017 17:35:07 +1000 Subject: [PATCH 14/16] TextureCacheBase: Don't crop last row/column of XFB copies Unsure why this was happening in the first place. --- Source/Core/VideoCommon/TextureCacheBase.cpp | 31 +++++--------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 31701e6bc0..d6290001a9 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -406,8 +406,11 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* pale dstrect.top = dst_y; dstrect.right = (dst_x + copy_width); dstrect.bottom = (dst_y + copy_height); - entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, 0, 0, - dstrect, 0, 0); + for (u32 layer = 0; layer < entry->texture->GetConfig().layers; layer++) + { + entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, layer, + 0, dstrect, layer, 0); + } if (isPaletteTexture) { @@ -1367,34 +1370,16 @@ bool TextureCacheBase::LoadTextureFromOverlappingTextures(TCacheEntry* entry_to_ srcrect.right = (src_x + copy_width); srcrect.bottom = (src_y + copy_height); - if (static_cast(entry->GetWidth()) == srcrect.GetWidth()) - { - srcrect.right -= 1; - } - - if (static_cast(entry->GetHeight()) == srcrect.GetHeight()) - { - srcrect.bottom -= 1; - } - dstrect.left = dst_x; dstrect.top = dst_y; dstrect.right = (dst_x + copy_width); dstrect.bottom = (dst_y + copy_height); - if (static_cast(entry_to_update->GetWidth()) == dstrect.GetWidth()) + for (u32 layer = 0; layer < entry->texture->GetConfig().layers; layer++) { - dstrect.right -= 1; + entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, layer, + 0, dstrect, layer, 0); } - - if (static_cast(entry_to_update->GetHeight()) == dstrect.GetHeight()) - { - dstrect.bottom -= 1; - } - - entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, 0, 0, - dstrect, 0, 0); - updated_entry = true; if (tex_info.is_palette_texture) From db1d9de933a914ff0c35a179d3ba1112fb0bcab1 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 19 Nov 2017 17:46:00 +1000 Subject: [PATCH 15/16] AbstractTexture: Drop slow map readback path --- Source/Core/VideoBackends/D3D/DXTexture.cpp | 69 ------------------- Source/Core/VideoBackends/D3D/DXTexture.h | 6 -- Source/Core/VideoBackends/OGL/OGLTexture.cpp | 69 ------------------- Source/Core/VideoBackends/OGL/OGLTexture.h | 6 -- .../Core/VideoBackends/Software/SWTexture.cpp | 6 -- .../Core/VideoBackends/Software/SWTexture.h | 2 - .../Core/VideoBackends/Vulkan/VKTexture.cpp | 50 -------------- Source/Core/VideoBackends/Vulkan/VKTexture.h | 6 -- Source/Core/VideoCommon/AbstractTexture.cpp | 67 ------------------ Source/Core/VideoCommon/AbstractTexture.h | 19 ----- 10 files changed, 300 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/DXTexture.cpp b/Source/Core/VideoBackends/D3D/DXTexture.cpp index 722db73a5f..2c37c61ffe 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.cpp +++ b/Source/Core/VideoBackends/D3D/DXTexture.cpp @@ -84,7 +84,6 @@ DXTexture::DXTexture(const TextureConfig& tex_config) : AbstractTexture(tex_conf DXTexture::~DXTexture() { m_texture->Release(); - SAFE_RELEASE(m_staging_texture); } D3DTexture2D* DXTexture::GetRawTexIdentifier() const @@ -97,74 +96,6 @@ void DXTexture::Bind(unsigned int stage) D3D::stateman->SetTexture(stage, m_texture->GetSRV()); } -std::optional DXTexture::MapFullImpl() -{ - CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, m_config.width, - m_config.height, 1, 1, 0, D3D11_USAGE_STAGING, - D3D11_CPU_ACCESS_READ); - - HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &m_staging_texture); - if (FAILED(hr)) - { - WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast(hr)); - return {}; - } - - // Copy the selected data to the staging texture - D3D::context->CopyResource(m_staging_texture, m_texture->GetTex()); - - // Map the staging texture to client memory, and encode it as a .png image. - D3D11_MAPPED_SUBRESOURCE map; - hr = D3D::context->Map(m_staging_texture, 0, D3D11_MAP_READ, 0, &map); - if (FAILED(hr)) - { - WARN_LOG(VIDEO, "Failed to map texture dumping readback texture: %X", static_cast(hr)); - return {}; - } - - return AbstractTexture::RawTextureInfo{reinterpret_cast(map.pData), map.RowPitch, - m_config.width, m_config.height}; -} - -std::optional DXTexture::MapRegionImpl(u32 level, u32 x, u32 y, - u32 width, u32 height) -{ - CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1, 1, 0, - D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ); - - HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &m_staging_texture); - if (FAILED(hr)) - { - WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast(hr)); - return {}; - } - - // Copy the selected data to the staging texture - CD3D11_BOX src_box(x, y, 0, width, height, 1); - D3D::context->CopySubresourceRegion(m_staging_texture, 0, 0, 0, 0, m_texture->GetTex(), - D3D11CalcSubresource(level, 0, m_config.levels), &src_box); - - // Map the staging texture to client memory, and encode it as a .png image. - D3D11_MAPPED_SUBRESOURCE map; - hr = D3D::context->Map(m_staging_texture, 0, D3D11_MAP_READ, 0, &map); - if (FAILED(hr)) - { - WARN_LOG(VIDEO, "Failed to map texture dumping readback texture: %X", static_cast(hr)); - return {}; - } - - return AbstractTexture::RawTextureInfo{reinterpret_cast(map.pData), map.RowPitch, - m_config.width, m_config.height}; -} - -void DXTexture::Unmap() -{ - if (!m_staging_texture) - return; - - D3D::context->Unmap(m_staging_texture, 0); -} - void DXTexture::CopyRectangleFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, u32 src_layer, u32 src_level, const MathUtil::Rectangle& dst_rect, diff --git a/Source/Core/VideoBackends/D3D/DXTexture.h b/Source/Core/VideoBackends/D3D/DXTexture.h index 6723952740..9644dbfc71 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.h +++ b/Source/Core/VideoBackends/D3D/DXTexture.h @@ -20,7 +20,6 @@ public: ~DXTexture(); void Bind(unsigned int stage) override; - void Unmap() override; void CopyRectangleFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, u32 src_layer, @@ -35,12 +34,7 @@ public: D3DTexture2D* GetRawTexIdentifier() const; private: - std::optional MapFullImpl() override; - std::optional MapRegionImpl(u32 level, u32 x, u32 y, u32 width, - u32 height) override; - D3DTexture2D* m_texture; - ID3D11Texture2D* m_staging_texture = nullptr; }; class DXStagingTexture final : public AbstractStagingTexture diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.cpp b/Source/Core/VideoBackends/OGL/OGLTexture.cpp index 4891f87686..bf1ec266d3 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.cpp +++ b/Source/Core/VideoBackends/OGL/OGLTexture.cpp @@ -167,75 +167,6 @@ void OGLTexture::Bind(unsigned int stage) } } -std::optional OGLTexture::MapFullImpl() -{ - if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL) - return {}; - - m_staging_data.reserve(m_config.width * m_config.height * 4); - glActiveTexture(GL_TEXTURE9); - - glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId); - glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_staging_data.data()); - OGLTexture::SetStage(); - return AbstractTexture::RawTextureInfo{reinterpret_cast(m_staging_data.data()), - m_config.width * 4, m_config.width, m_config.height}; -} - -std::optional OGLTexture::MapRegionImpl(u32 level, u32 x, u32 y, - u32 width, u32 height) -{ - if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL) - return {}; - m_staging_data.reserve(m_config.width * m_config.height * 4); - glActiveTexture(GL_TEXTURE9); - glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId); - if (g_ogl_config.bSupportTextureSubImage) - { - glGetTextureSubImage(GL_TEXTURE_2D_ARRAY, level, GLint(x), GLint(y), 0, GLsizei(width), - GLsizei(height), 0, GL_RGBA, GL_UNSIGNED_BYTE, GLsizei(width * height * 4), - m_staging_data.data()); - } - else - { - MapRegionSlow(level, x, y, width, height); - } - OGLTexture::SetStage(); - return AbstractTexture::RawTextureInfo{m_staging_data.data(), width * 4, width, height}; -} - -void OGLTexture::MapRegionSlow(u32 level, u32 x, u32 y, u32 width, u32 height) -{ - glActiveTexture(GL_TEXTURE9); - - glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId); - glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_staging_data.data()); - - // Now copy the region out of the staging data - - const u32 partial_stride = width * 4; - - std::vector partial_data; - partial_data.resize(partial_stride * height); - - const u32 staging_stride = m_config.width * 4; - const u32 x_offset = x * 4; - - auto staging_location = m_staging_data.begin() + staging_stride * y; - auto partial_location = partial_data.begin(); - - for (size_t i = 0; i < height; ++i) - { - auto starting_location = staging_location + x_offset; - std::copy(starting_location, starting_location + partial_stride, partial_location); - staging_location += staging_stride; - partial_location += partial_stride; - } - - // Now swap the region back in for the staging data - m_staging_data.swap(partial_data); -} - void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, u32 src_layer, u32 src_level, const MathUtil::Rectangle& dst_rect, diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.h b/Source/Core/VideoBackends/OGL/OGLTexture.h index 6db7b9387e..ab2f0cbc4f 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.h +++ b/Source/Core/VideoBackends/OGL/OGLTexture.h @@ -38,14 +38,8 @@ public: static void SetStage(); private: - std::optional MapFullImpl() override; - std::optional MapRegionImpl(u32 level, u32 x, u32 y, u32 width, - u32 height) override; - void MapRegionSlow(u32 level, u32 x, u32 y, u32 width, u32 height); - GLuint m_texId; GLuint m_framebuffer = 0; - std::vector m_staging_data; }; class OGLStagingTexture final : public AbstractStagingTexture diff --git a/Source/Core/VideoBackends/Software/SWTexture.cpp b/Source/Core/VideoBackends/Software/SWTexture.cpp index db9b56020a..13d2828fe9 100644 --- a/Source/Core/VideoBackends/Software/SWTexture.cpp +++ b/Source/Core/VideoBackends/Software/SWTexture.cpp @@ -107,12 +107,6 @@ u8* SWTexture::GetData() return m_data.data(); } -std::optional SWTexture::MapFullImpl() -{ - return AbstractTexture::RawTextureInfo{GetData(), m_config.width * 4, m_config.width, - m_config.height}; -} - SWStagingTexture::SWStagingTexture(StagingTextureType type, const TextureConfig& config) : AbstractStagingTexture(type, config) { diff --git a/Source/Core/VideoBackends/Software/SWTexture.h b/Source/Core/VideoBackends/Software/SWTexture.h index 403cf257b0..0aaf12c2ad 100644 --- a/Source/Core/VideoBackends/Software/SWTexture.h +++ b/Source/Core/VideoBackends/Software/SWTexture.h @@ -35,8 +35,6 @@ public: u8* GetData(); private: - std::optional MapFullImpl() override; - std::vector m_data; }; diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index f2db740551..d3ab16e0d9 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -113,56 +113,6 @@ void VKTexture::Bind(unsigned int stage) StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView()); } -std::optional VKTexture::MapFullImpl() -{ - // No support for optimization of full copy - return MapRegionImpl(0, 0, 0, m_config.width, m_config.height); -} - -std::optional VKTexture::MapRegionImpl(u32 level, u32 x, u32 y, - u32 width, u32 height) -{ - m_staging_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, width, height, - TEXTURECACHE_TEXTURE_FORMAT); - - // Transition image to transfer source, and invalidate the current state, - // since we'll be executing the command buffer. - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - StateTracker::GetInstance()->EndRenderPass(); - - // Copy to download buffer. - m_staging_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), - m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, x, y, width, - height, level, 0); - - // Restore original state of texture. - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - - // Block until the GPU has finished copying to the staging texture. - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Map the staging texture so we can copy the contents out. - if (!m_staging_texture->Map()) - { - PanicAlert("Failed to map staging texture"); - return {}; - } - - return AbstractTexture::RawTextureInfo{reinterpret_cast(m_staging_texture->GetMapPointer()), - static_cast(m_staging_texture->GetRowStride()), width, - height}; -} - -void VKTexture::Unmap() -{ - if (!m_staging_texture) - return; - - m_staging_texture->Unmap(); -} - void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, u32 src_layer, u32 src_level, const MathUtil::Rectangle& dst_rect, diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.h b/Source/Core/VideoBackends/Vulkan/VKTexture.h index 5c2116e4ee..426f29973d 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.h +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.h @@ -22,7 +22,6 @@ public: ~VKTexture(); void Bind(unsigned int stage) override; - void Unmap() override; void CopyRectangleFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, u32 src_layer, @@ -45,12 +44,7 @@ private: VKTexture(const TextureConfig& tex_config, std::unique_ptr texture, VkFramebuffer framebuffer); - std::optional MapFullImpl() override; - std::optional MapRegionImpl(u32 level, u32 x, u32 y, u32 width, - u32 height) override; - std::unique_ptr m_texture; - std::unique_ptr m_staging_texture; VkFramebuffer m_framebuffer; }; diff --git a/Source/Core/VideoCommon/AbstractTexture.cpp b/Source/Core/VideoCommon/AbstractTexture.cpp index 109bb52897..896d75feb8 100644 --- a/Source/Core/VideoCommon/AbstractTexture.cpp +++ b/Source/Core/VideoCommon/AbstractTexture.cpp @@ -51,73 +51,6 @@ bool AbstractTexture::Save(const std::string& filename, unsigned int level) level_height); } -std::optional AbstractTexture::Map() -{ - if (m_currently_mapped) - { - Unmap(); - m_currently_mapped = false; - } - auto result = MapFullImpl(); - - if (!result.has_value()) - { - m_currently_mapped = false; - return {}; - } - - m_currently_mapped = true; - return result; -} - -std::optional AbstractTexture::Map(u32 level, u32 x, u32 y, - u32 width, u32 height) -{ - _assert_(level < m_config.levels); - - u32 max_level_width = std::max(m_config.width >> level, 1u); - u32 max_level_height = std::max(m_config.height >> level, 1u); - - _assert_(width < max_level_width); - _assert_(height < max_level_height); - - auto result = MapRegionImpl(level, x, y, width, height); - - if (!result.has_value()) - { - m_currently_mapped = false; - return {}; - } - - m_currently_mapped = true; - return result; -} - -std::optional AbstractTexture::Map(u32 level) -{ - _assert_(level < m_config.levels); - - u32 level_width = std::max(m_config.width >> level, 1u); - u32 level_height = std::max(m_config.height >> level, 1u); - - return Map(level, 0, 0, level_width, level_height); -} - -void AbstractTexture::Unmap() -{ -} - -std::optional AbstractTexture::MapFullImpl() -{ - return {}; -} - -std::optional -AbstractTexture::MapRegionImpl(u32 level, u32 x, u32 y, u32 width, u32 height) -{ - return {}; -} - bool AbstractTexture::IsCompressedFormat(AbstractTextureFormat format) { switch (format) diff --git a/Source/Core/VideoCommon/AbstractTexture.h b/Source/Core/VideoCommon/AbstractTexture.h index c18e1f17d3..3e6937b3df 100644 --- a/Source/Core/VideoCommon/AbstractTexture.h +++ b/Source/Core/VideoCommon/AbstractTexture.h @@ -5,7 +5,6 @@ #pragma once #include -#include #include #include "Common/CommonTypes.h" @@ -20,19 +19,6 @@ public: virtual void Bind(unsigned int stage) = 0; - struct RawTextureInfo - { - const u8* data; - u32 stride; - u32 width; - u32 height; - }; - - std::optional Map(); - std::optional Map(u32 level, u32 x, u32 y, u32 width, u32 height); - std::optional Map(u32 level); - virtual void Unmap(); - virtual void CopyRectangleFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, u32 src_layer, u32 src_level, const MathUtil::Rectangle& dst_rect, @@ -52,10 +38,5 @@ public: const TextureConfig& GetConfig() const; protected: - virtual std::optional MapFullImpl(); - virtual std::optional MapRegionImpl(u32 level, u32 x, u32 y, u32 width, - u32 height); - bool m_currently_mapped = false; - const TextureConfig m_config; }; From 7f217a8bb24da323042081a02b65f84f9ce9761f Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 14 Nov 2017 00:21:48 +1000 Subject: [PATCH 16/16] Vulkan: Drop StagingTexture2D class as it is now duplicated functionality --- .../Core/VideoBackends/Vulkan/CMakeLists.txt | 1 - .../Vulkan/FramebufferManager.cpp | 64 +++---- .../VideoBackends/Vulkan/FramebufferManager.h | 7 +- .../VideoBackends/Vulkan/StagingTexture2D.cpp | 164 ------------------ .../VideoBackends/Vulkan/StagingTexture2D.h | 58 ------- .../VideoBackends/Vulkan/TextureConverter.cpp | 1 - .../Core/VideoBackends/Vulkan/VKTexture.cpp | 2 +- .../Core/VideoBackends/Vulkan/Vulkan.vcxproj | 2 - 8 files changed, 27 insertions(+), 272 deletions(-) delete mode 100644 Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp delete mode 100644 Source/Core/VideoBackends/Vulkan/StagingTexture2D.h diff --git a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt index f3d602afb6..25c12fb12c 100644 --- a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt +++ b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt @@ -11,7 +11,6 @@ set(SRCS ShaderCompiler.cpp StateTracker.cpp StagingBuffer.cpp - StagingTexture2D.cpp StreamBuffer.cpp SwapChain.cpp Texture2D.cpp diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp index 78689b9638..94f773fb94 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp @@ -16,7 +16,6 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/ObjectCache.h" -#include "VideoBackends/Vulkan/StagingTexture2D.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/Texture2D.h" @@ -698,7 +697,7 @@ u32 FramebufferManager::PeekEFBColor(u32 x, u32 y) return 0; u32 value; - m_color_readback_texture->ReadTexel(x, y, &value, sizeof(value)); + m_color_readback_texture->ReadTexel(x, y, &value); return value; } @@ -711,7 +710,6 @@ bool FramebufferManager::PopulateColorReadbackTexture() // Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on. VkRect2D src_region = {{0, 0}, {GetEFBWidth(), GetEFBHeight()}}; Texture2D* src_texture = m_efb_color_texture.get(); - VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_COLOR_BIT; if (GetEFBSamples() > 1) src_texture = ResolveEFBColorTexture(src_region); @@ -750,9 +748,9 @@ bool FramebufferManager::PopulateColorReadbackTexture() // Copy from EFB or copy texture to staging texture. src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - m_color_readback_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), - src_texture->GetImage(), src_aspect, 0, 0, EFB_WIDTH, - EFB_HEIGHT, 0, 0); + static_cast(m_color_readback_texture.get()) + ->CopyFromTexture(src_texture, m_color_readback_texture->GetConfig().GetRect(), 0, 0, + m_color_readback_texture->GetConfig().GetRect()); // Restore original layout if we used the EFB as a source. if (src_texture == m_efb_color_texture.get()) @@ -762,12 +760,7 @@ bool FramebufferManager::PopulateColorReadbackTexture() } // Wait until the copy is complete. - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Map to host memory. - if (!m_color_readback_texture->IsMapped() && !m_color_readback_texture->Map()) - return false; - + m_color_readback_texture->Flush(); m_color_readback_texture_valid = true; return true; } @@ -778,7 +771,7 @@ float FramebufferManager::PeekEFBDepth(u32 x, u32 y) return 0.0f; float value; - m_depth_readback_texture->ReadTexel(x, y, &value, sizeof(value)); + m_depth_readback_texture->ReadTexel(x, y, &value); return value; } @@ -791,12 +784,10 @@ bool FramebufferManager::PopulateDepthReadbackTexture() // Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on. VkRect2D src_region = {{0, 0}, {GetEFBWidth(), GetEFBHeight()}}; Texture2D* src_texture = m_efb_depth_texture.get(); - VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_DEPTH_BIT; if (GetEFBSamples() > 1) { // EFB depth resolves are written out as color textures src_texture = ResolveEFBDepthTexture(src_region); - src_aspect = VK_IMAGE_ASPECT_COLOR_BIT; } if (GetEFBWidth() != EFB_WIDTH || GetEFBHeight() != EFB_HEIGHT) { @@ -828,15 +819,14 @@ bool FramebufferManager::PopulateDepthReadbackTexture() // Use this as a source texture now. src_texture = m_depth_copy_texture.get(); - src_aspect = VK_IMAGE_ASPECT_COLOR_BIT; } // Copy from EFB or copy texture to staging texture. src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - m_depth_readback_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), - src_texture->GetImage(), src_aspect, 0, 0, EFB_WIDTH, - EFB_HEIGHT, 0, 0); + static_cast(m_depth_readback_texture.get()) + ->CopyFromTexture(src_texture, m_depth_readback_texture->GetConfig().GetRect(), 0, 0, + m_depth_readback_texture->GetConfig().GetRect()); // Restore original layout if we used the EFB as a source. if (src_texture == m_efb_depth_texture.get()) @@ -846,12 +836,7 @@ bool FramebufferManager::PopulateDepthReadbackTexture() } // Wait until the copy is complete. - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Map to host memory. - if (!m_depth_readback_texture->IsMapped() && !m_depth_readback_texture->Map()) - return false; - + m_depth_readback_texture->Flush(); m_depth_readback_texture_valid = true; return true; } @@ -1011,32 +996,27 @@ bool FramebufferManager::CreateReadbackTextures() VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); - m_color_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH, - EFB_HEIGHT, EFB_COLOR_TEXTURE_FORMAT); - if (!m_color_copy_texture || !m_color_readback_texture) - { - ERROR_LOG(VIDEO, "Failed to create EFB color readback texture"); - return false; - } - m_depth_copy_texture = Texture2D::Create(EFB_WIDTH, EFB_HEIGHT, 1, 1, EFB_DEPTH_AS_COLOR_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); - m_depth_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH, - EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT); - if (!m_depth_copy_texture || !m_depth_readback_texture) + if (!m_color_copy_texture || !m_depth_copy_texture) { - ERROR_LOG(VIDEO, "Failed to create EFB depth readback texture"); + ERROR_LOG(VIDEO, "Failed to create EFB copy textures"); return false; } - // With Vulkan, we can leave these textures mapped and use invalidate/flush calls instead. - if (!m_color_readback_texture->Map() || !m_depth_readback_texture->Map()) + TextureConfig readback_texture_config(EFB_WIDTH, EFB_HEIGHT, 1, 1, AbstractTextureFormat::RGBA8, + false); + m_color_readback_texture = + g_renderer->CreateStagingTexture(StagingTextureType::Mutable, readback_texture_config); + m_depth_readback_texture = + g_renderer->CreateStagingTexture(StagingTextureType::Mutable, readback_texture_config); + if (!m_color_readback_texture || !m_depth_readback_texture) { - ERROR_LOG(VIDEO, "Failed to map EFB readback textures"); + ERROR_LOG(VIDEO, "Failed to create EFB readback textures"); return false; } @@ -1113,7 +1093,7 @@ void FramebufferManager::PokeEFBColor(u32 x, u32 y, u32 color) // Update the peek cache if it's valid, since we know the color of the pixel now. if (m_color_readback_texture_valid) - m_color_readback_texture->WriteTexel(x, y, &color, sizeof(color)); + m_color_readback_texture->WriteTexel(x, y, &color); } void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth) @@ -1126,7 +1106,7 @@ void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth) // Update the peek cache if it's valid, since we know the color of the pixel now. if (m_depth_readback_texture_valid) - m_depth_readback_texture->WriteTexel(x, y, &depth, sizeof(depth)); + m_depth_readback_texture->WriteTexel(x, y, &depth); } void FramebufferManager::CreatePokeVertices(std::vector* destination_list, u32 x, diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.h b/Source/Core/VideoBackends/Vulkan/FramebufferManager.h index 0c1a160728..e765abc907 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.h +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.h @@ -13,9 +13,10 @@ #include "VideoCommon/FramebufferManagerBase.h" #include "VideoCommon/RenderState.h" +class AbstractStagingTexture; + namespace Vulkan { -class StagingTexture2D; class StateTracker; class StreamBuffer; class Texture2D; @@ -138,8 +139,8 @@ private: VkFramebuffer m_depth_copy_framebuffer = VK_NULL_HANDLE; // CPU-side EFB readback texture - std::unique_ptr m_color_readback_texture; - std::unique_ptr m_depth_readback_texture; + std::unique_ptr m_color_readback_texture; + std::unique_ptr m_depth_readback_texture; bool m_color_readback_texture_valid = false; bool m_depth_readback_texture_valid = false; diff --git a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp deleted file mode 100644 index ee726fc8f2..0000000000 --- a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include -#include - -#include "Common/Assert.h" - -#include "VideoBackends/Vulkan/CommandBufferManager.h" -#include "VideoBackends/Vulkan/StagingTexture2D.h" -#include "VideoBackends/Vulkan/Util.h" -#include "VideoBackends/Vulkan/VulkanContext.h" - -namespace Vulkan -{ -StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, - VkDeviceSize size, bool coherent, u32 width, u32 height, - VkFormat format, u32 stride) - : StagingBuffer(type, buffer, memory, size, coherent), m_width(width), m_height(height), - m_format(format), m_texel_size(Util::GetTexelSize(format)), m_row_stride(stride) -{ -} - -StagingTexture2D::~StagingTexture2D() -{ -} - -void StagingTexture2D::ReadTexel(u32 x, u32 y, void* data, size_t data_size) const -{ - _assert_(data_size >= m_texel_size); - - VkDeviceSize offset = y * m_row_stride + x * m_texel_size; - VkDeviceSize map_offset = offset - m_map_offset; - _assert_(offset >= m_map_offset && (map_offset + m_texel_size) <= (m_map_offset + m_map_size)); - - const char* ptr = m_map_pointer + map_offset; - memcpy(data, ptr, data_size); -} - -void StagingTexture2D::WriteTexel(u32 x, u32 y, const void* data, size_t data_size) -{ - _assert_(data_size >= m_texel_size); - - VkDeviceSize offset = y * m_row_stride + x * m_texel_size; - VkDeviceSize map_offset = offset - m_map_offset; - _assert_(offset >= m_map_offset && (map_offset + m_texel_size) <= (m_map_offset + m_map_size)); - - char* ptr = m_map_pointer + map_offset; - memcpy(ptr, data, data_size); -} - -void StagingTexture2D::ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data, - u32 data_stride) const -{ - const char* src_ptr = GetRowPointer(y); - - // Optimal path: same dimensions, same stride. - _assert_((x + width) <= m_width && (y + height) <= m_height); - if (x == 0 && width == m_width && m_row_stride == data_stride) - { - memcpy(data, src_ptr, m_row_stride * height); - return; - } - - u32 copy_size = std::min(width * m_texel_size, data_stride); - char* dst_ptr = reinterpret_cast(data); - for (u32 row = 0; row < height; row++) - { - memcpy(dst_ptr, src_ptr + (x * m_texel_size), copy_size); - src_ptr += m_row_stride; - dst_ptr += data_stride; - } -} - -void StagingTexture2D::WriteTexels(u32 x, u32 y, u32 width, u32 height, const void* data, - u32 data_stride) -{ - char* dst_ptr = GetRowPointer(y); - - // Optimal path: same dimensions, same stride. - _assert_((x + width) <= m_width && (y + height) <= m_height); - if (x == 0 && width == m_width && m_row_stride == data_stride) - { - memcpy(dst_ptr, data, m_row_stride * height); - return; - } - - u32 copy_size = std::min(width * m_texel_size, data_stride); - const char* src_ptr = reinterpret_cast(data); - for (u32 row = 0; row < height; row++) - { - memcpy(dst_ptr + (x * m_texel_size), src_ptr, copy_size); - dst_ptr += m_row_stride; - src_ptr += data_stride; - } -} - -void StagingTexture2D::CopyFromImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) -{ - // Issue the image->buffer copy. - VkBufferImageCopy image_copy = { - y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset - m_width, // uint32_t bufferRowLength - 0, // uint32_t bufferImageHeight - {src_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource - {static_cast(x), static_cast(y), 0}, // VkOffset3D imageOffset - {width, height, 1} // VkExtent3D imageExtent - }; - vkCmdCopyImageToBuffer(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1, - &image_copy); - - // Flush CPU and GPU caches if not coherent mapping. - VkDeviceSize buffer_flush_offset = y * m_row_stride; - VkDeviceSize buffer_flush_size = height * m_row_stride; - FlushGPUCache(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - buffer_flush_offset, buffer_flush_size); - InvalidateCPUCache(buffer_flush_offset, buffer_flush_size); -} - -void StagingTexture2D::CopyToImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) -{ - // Flush CPU and GPU caches if not coherent mapping. - VkDeviceSize buffer_flush_offset = y * m_row_stride; - VkDeviceSize buffer_flush_size = height * m_row_stride; - FlushCPUCache(buffer_flush_offset, buffer_flush_size); - InvalidateGPUCache(command_buffer, VK_ACCESS_HOST_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - buffer_flush_offset, buffer_flush_size); - - // Issue the buffer->image copy. - VkBufferImageCopy image_copy = { - y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset - m_width, // uint32_t bufferRowLength - 0, // uint32_t bufferImageHeight - {dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource - {static_cast(x), static_cast(y), 0}, // VkOffset3D imageOffset - {width, height, 1} // VkExtent3D imageExtent - }; - vkCmdCopyBufferToImage(command_buffer, m_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, - &image_copy); -} - -std::unique_ptr StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width, - u32 height, VkFormat format) -{ - // Assume tight packing. - u32 stride = Util::GetTexelSize(format) * width; - u32 size = stride * height; - VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - - VkBuffer buffer; - VkDeviceMemory memory; - bool coherent; - if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent)) - return nullptr; - - return std::make_unique(type, buffer, memory, size, coherent, width, height, - format, stride); -} -} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h deleted file mode 100644 index 74dbb8cf07..0000000000 --- a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include - -#include "Common/CommonTypes.h" -#include "VideoBackends/Vulkan/Constants.h" -#include "VideoBackends/Vulkan/StagingBuffer.h" - -namespace Vulkan -{ -class StagingTexture2D final : public StagingBuffer -{ -public: - StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, - VkDeviceSize size, bool coherent, u32 width, u32 height, VkFormat format, - u32 stride); - ~StagingTexture2D(); - - u32 GetWidth() const { return m_width; } - u32 GetHeight() const { return m_height; } - VkFormat GetFormat() const { return m_format; } - u32 GetRowStride() const { return m_row_stride; } - u32 GetTexelSize() const { return m_texel_size; } - // Requires Map() to be called first. - const char* GetRowPointer(u32 row) const { return m_map_pointer + row * m_row_stride; } - char* GetRowPointer(u32 row) { return m_map_pointer + row * m_row_stride; } - void ReadTexel(u32 x, u32 y, void* data, size_t data_size) const; - void WriteTexel(u32 x, u32 y, const void* data, size_t data_size); - void ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data, u32 data_stride) const; - void WriteTexels(u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride); - - // Assumes that image is in TRANSFER_SRC layout. - // Results are not ready until command_buffer has been executed. - void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer); - - // Assumes that image is in TRANSFER_DST layout. - // Buffer is not safe for re-use until after command_buffer has been executed. - void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer); - - // Creates the optimal format of image copy. - static std::unique_ptr Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, - VkFormat format); - -protected: - u32 m_width; - u32 m_height; - VkFormat m_format; - u32 m_texel_size; - u32 m_row_stride; -}; -} diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp index e0dacded9e..72995c7a4b 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp @@ -19,7 +19,6 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/FramebufferManager.h" #include "VideoBackends/Vulkan/ObjectCache.h" -#include "VideoBackends/Vulkan/StagingTexture2D.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/Texture2D.h" diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index d3ab16e0d9..59be6a0eba 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -14,7 +14,7 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/FramebufferManager.h" -#include "VideoBackends/Vulkan/StagingTexture2D.h" +#include "VideoBackends/Vulkan/StagingBuffer.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/Texture2D.h" #include "VideoBackends/Vulkan/Util.h" diff --git a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj index 3492f7b0f2..1a24ff053b 100644 --- a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj +++ b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj @@ -46,7 +46,6 @@ - @@ -72,7 +71,6 @@ -