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/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/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/DXTexture.cpp b/Source/Core/VideoBackends/D3D/DXTexture.cpp index 5f7a4e39eb..2c37c61ffe 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; } } @@ -80,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 @@ -93,97 +96,36 @@ void DXTexture::Bind(unsigned int stage) D3D::stateman->SetTexture(stage, m_texture->GetSRV()); } -std::optional DXTexture::MapFullImpl() +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) { - 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); + const DXTexture* srcentry = static_cast(src); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); - 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 {}; - } + 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; - // 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}; + 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); } -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* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) +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), @@ -213,8 +155,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..9644dbfc71 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; @@ -19,23 +20,47 @@ public: ~DXTexture(); 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; 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 +{ +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/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; }; } diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 4fc2b1f084..a7518cdf64 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -53,31 +53,42 @@ typedef struct _Nv_Stereo_Image_Header #define NVSTEREO_IMAGE_SIGNATURE 0x4433564e -struct GXPipelineState +Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferHeight()) { - std::array samplers; - BlendingState blend; - DepthState zmode; - RasterizationState raster; -}; + m_last_multisamples = g_ActiveConfig.iMultisamples; + m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; + m_last_fullscreen_mode = D3D::GetFullscreenState(); -static u32 s_last_multisamples = 1; -static bool s_last_stereo_mode = false; -static bool s_last_fullscreen_mode = false; + g_framebuffer_manager = std::make_unique(m_target_width, m_target_height); + SetupDeviceObjects(); -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; + // Setup GX pipeline state + for (auto& sampler : m_gx_state.samplers) + sampler.hex = RenderState::GetPointSamplerState().hex; -static ID3D11Texture2D* s_screenshot_texture = nullptr; -static D3DTexture2D* s_3d_vision_texture = nullptr; + 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; -static GXPipelineState s_gx_state; -static StateCache s_gx_state_cache; + // Clear EFB textures + constexpr std::array clear_color{{0.f, 0.f, 0.f, 1.f}}; + D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(), + clear_color.data()); + D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(), + D3D11_CLEAR_DEPTH, 0.f, 0); -static void SetupDeviceObjects() + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)m_target_width, (float)m_target_height); + D3D::context->RSSetViewports(1, &vp); + FramebufferManager::BindEFBRenderTarget(); +} + +Renderer::~Renderer() +{ + TeardownDeviceObjects(); +} + +void Renderer::SetupDeviceObjects() { HRESULT hr; @@ -88,22 +99,22 @@ static void SetupDeviceObjects() 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]); + 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, &s_clear_depth_states[1]); + 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, &s_clear_depth_states[2]); + hr = D3D::device->CreateDepthStencilState(&ddesc, &m_clear_depth_states[2]); CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen"); - D3D::SetDebugObjectName(s_clear_depth_states[0], + D3D::SetDebugObjectName(m_clear_depth_states[0], "depth state for Renderer::ClearScreen (depth buffer disabled)"); D3D::SetDebugObjectName( - s_clear_depth_states[1], + m_clear_depth_states[1], "depth state for Renderer::ClearScreen (depth buffer enabled, writing enabled)"); D3D::SetDebugObjectName( - s_clear_depth_states[2], + m_clear_depth_states[2], "depth state for Renderer::ClearScreen (depth buffer enabled, writing disabled)"); D3D11_BLEND_DESC blenddesc; @@ -117,24 +128,24 @@ static void SetupDeviceObjects() 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); + hr = D3D::device->CreateBlendState(&blenddesc, &m_reset_blend_state); CHECK(hr == S_OK, "Create blend state for Renderer::ResetAPIState"); - D3D::SetDebugObjectName(s_reset_blend_state, "blend state for Renderer::ResetAPIState"); + D3D::SetDebugObjectName(m_reset_blend_state, "blend state for Renderer::ResetAPIState"); - s_clear_blend_states[0] = s_reset_blend_state; - s_reset_blend_state->AddRef(); + 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, &s_clear_blend_states[1]); + 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, &s_clear_blend_states[2]); + 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, &s_clear_blend_states[3]); + hr = D3D::device->CreateBlendState(&blenddesc, &m_clear_blend_states[3]); CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen"); ddesc.DepthEnable = FALSE; @@ -143,41 +154,39 @@ static void SetupDeviceObjects() 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); + hr = D3D::device->CreateDepthStencilState(&ddesc, &m_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"); + 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, &s_reset_rast_state); + hr = D3D::device->CreateRasterizerState(&rastdesc, &m_reset_rast_state); CHECK(hr == S_OK, "Create rasterizer state for Renderer::ResetAPIState"); - D3D::SetDebugObjectName(s_reset_rast_state, "rasterizer state for Renderer::ResetAPIState"); + D3D::SetDebugObjectName(m_reset_rast_state, "rasterizer state for Renderer::ResetAPIState"); - s_screenshot_texture = nullptr; + m_screenshot_texture = nullptr; } // Kill off all device objects -static void TeardownDeviceObjects() +void Renderer::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); - - s_gx_state_cache.Clear(); + 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); } -static void Create3DVisionTexture(int width, int height) +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. @@ -197,48 +206,20 @@ static void Create3DVisionTexture(int width, int height) sys_data.SysMemPitch = pitch; sys_data.pSysMem = memory.get(); - s_3d_vision_texture = + 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); } -Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferHeight()) +std::unique_ptr Renderer::CreateTexture(const TextureConfig& config) { - s_last_multisamples = g_ActiveConfig.iMultisamples; - s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; - s_last_fullscreen_mode = D3D::GetFullscreenState(); - - g_framebuffer_manager = std::make_unique(m_target_width, m_target_height); - SetupDeviceObjects(); - - // Setup GX pipeline state - for (auto& sampler : s_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; - - // Clear EFB textures - constexpr std::array clear_color{{0.f, 0.f, 0.f, 1.f}}; - D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(), - clear_color.data()); - D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(), - D3D11_CLEAR_DEPTH, 0.f, 0); - - 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(); + return std::make_unique(config); } -Renderer::~Renderer() +std::unique_ptr Renderer::CreateStagingTexture(StagingTextureType type, + const TextureConfig& config) { - TeardownDeviceObjects(); - D3D::EndFrame(); - D3D::Present(); - D3D::Close(); + return DXStagingTexture::Create(type, config); } void Renderer::RenderText(const std::string& text, int left, int top, u32 color) @@ -443,8 +424,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()); @@ -526,21 +507,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); @@ -600,7 +581,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. @@ -633,7 +614,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region Renderer::DrawDebugText(); OSD::DrawMessages(); - D3D::EndFrame(); g_texture_cache->Cleanup(frameCount); @@ -646,33 +626,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); @@ -694,7 +674,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // begin next frame RestoreAPIState(); - D3D::BeginFrame(); FramebufferManager::BindEFBRenderTarget(); SetViewport(); } @@ -702,9 +681,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() @@ -719,15 +698,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 +725,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() @@ -831,7 +810,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(), @@ -840,7 +819,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, @@ -858,7 +837,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 b35e0f9d20..4668d54a92 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -4,7 +4,9 @@ #pragma once +#include #include +#include "VideoBackends/D3D/D3DState.h" #include "VideoCommon/RenderBase.h" enum class EFBAccessType; @@ -19,6 +21,11 @@ public: Renderer(); ~Renderer() override; + 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; void SetRasterizationState(const RasterizationState& state) override; @@ -56,7 +63,35 @@ public: bool CheckForResize(); private: + struct GXPipelineState + { + std::array samplers; + BlendingState blend; + DepthState zmode; + 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; }; } 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/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(); } diff --git a/Source/Core/VideoBackends/Null/NullTexture.cpp b/Source/Core/VideoBackends/Null/NullTexture.cpp index 7d852f7ddf..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) { } @@ -25,4 +31,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..8cfedc68ca 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 @@ -18,11 +21,36 @@ 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; }; +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 b1a88f7a80..c589c8278f 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,17 @@ Renderer::~Renderer() UpdateActiveConfig(); } +std::unique_ptr Renderer::CreateTexture(const TextureConfig& config) +{ + 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 2a21918f81..32b1d560e5 100644 --- a/Source/Core/VideoBackends/Null/Render.h +++ b/Source/Core/VideoBackends/Null/Render.h @@ -14,6 +14,10 @@ public: Renderer(); ~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; } 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..bf1ec266d3 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" @@ -36,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 storage ? GL_RGBA8 : GL_BGRA; default: + PanicAlert("Unhandled texture format."); return storage ? GL_RGBA8 : GL_RGBA; } } @@ -47,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; @@ -58,12 +63,22 @@ 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: 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) @@ -85,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) { @@ -100,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(); @@ -148,89 +167,70 @@ void OGLTexture::Bind(unsigned int stage) } } -std::optional OGLTexture::MapFullImpl() +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) { - 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) + const OGLTexture* srcentry = static_cast(src); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); + if (g_ogl_config.bSupportsCopySubImage) { - 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()); + 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 { - MapRegionSlow(level, x, y, width, height); + // 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); } - 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* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) +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); @@ -269,7 +269,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) { @@ -315,4 +315,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..ab2f0cbc4f 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 @@ -20,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; @@ -33,14 +38,39 @@ 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 +{ +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 84c1ce0dfc..428492c72a 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 { @@ -814,6 +816,17 @@ void Renderer::Init() OpenGL_CreateAttributelessVAO(); } +std::unique_ptr Renderer::CreateTexture(const TextureConfig& config) +{ + 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 725cc7a639..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; @@ -77,6 +77,10 @@ public: void Init(); 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; 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..50366f7d1a 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; @@ -98,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/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, diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index 4c298e927e..4b84655002 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,17 @@ void SWRenderer::Shutdown() UpdateActiveConfig(); } +std::unique_ptr SWRenderer::CreateTexture(const TextureConfig& config) +{ + 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 a87531fcab..312acd1991 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -16,6 +16,10 @@ public: static void Init(); 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; void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {} diff --git a/Source/Core/VideoBackends/Software/SWTexture.cpp b/Source/Core/VideoBackends/Software/SWTexture.cpp index aa279b520b..13d2828fe9 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); @@ -31,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); @@ -72,10 +107,49 @@ u8* SWTexture::GetData() return m_data.data(); } -std::optional SWTexture::MapFullImpl() +SWStagingTexture::SWStagingTexture(StagingTextureType type, const TextureConfig& config) + : AbstractStagingTexture(type, config) { - return AbstractTexture::RawTextureInfo{GetData(), m_config.width * 4, m_config.width, - m_config.height}; + 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..0aaf12c2ad 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 @@ -20,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; @@ -30,8 +35,27 @@ public: u8* GetData(); private: - std::optional MapFullImpl() override; + 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; }; 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/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/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/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/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 084e7a2880..742a525b98 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -159,6 +159,17 @@ void Renderer::DestroySemaphores() } } +std::unique_ptr Renderer::CreateTexture(const TextureConfig& config) +{ + 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 e8b7952e7d..b3f8168347 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -32,6 +32,10 @@ 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(); } bool Initialize(); 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/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/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/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp index b8ac91d06b..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" @@ -67,9 +66,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 +107,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 +235,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 +268,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 +288,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 +302,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 +311,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 +713,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 +748,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 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/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index cbb6e0c063..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" @@ -113,60 +113,13 @@ void VKTexture::Bind(unsigned int stage) StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView()); } -std::optional VKTexture::MapFullImpl() +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) { - // No support for optimization of full copy - return MapRegionImpl(0, 0, 0, m_config.width, m_config.height); -} + Texture2D* src_texture = static_cast(src)->GetRawTexIdentifier(); -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::CopyTextureRectangle(const MathUtil::Rectangle& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle& src_rect) -{ _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 +129,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 +146,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 +192,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); } @@ -291,7 +231,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 +296,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..426f29973d 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 @@ -20,13 +22,16 @@ public: ~VKTexture(); 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; @@ -39,21 +44,41 @@ 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; - std::unique_ptr m_texture; - std::unique_ptr m_staging_texture; 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/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 @@ - 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 7e4f493601..896d75feb8 100644 --- a/Source/Core/VideoCommon/AbstractTexture.cpp +++ b/Source/Core/VideoCommon/AbstractTexture.cpp @@ -5,9 +5,11 @@ #include #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,93 +22,51 @@ 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; + + // 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); +} + +bool AbstractTexture::IsCompressedFormat(AbstractTextureFormat format) +{ + switch (format) { + case AbstractTextureFormat::DXT1: + case AbstractTextureFormat::DXT3: + case AbstractTextureFormat::DXT5: + case AbstractTextureFormat::BPTC: + return true; + + default: return false; } - - auto raw_data = result.value(); - return TextureToPng(raw_data.data, raw_data.stride, filename, raw_data.width, raw_data.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::IsCompressedHostTextureFormat(AbstractTextureFormat format) -{ - // This will need to be changed if we add any other uncompressed formats. - return format != AbstractTextureFormat::RGBA8; -} - -size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length) +size_t AbstractTexture::CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length) { switch (format) { @@ -117,8 +77,30 @@ 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; + } +} + +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; } } diff --git a/Source/Core/VideoCommon/AbstractTexture.h b/Source/Core/VideoCommon/AbstractTexture.h index 612ca34758..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" @@ -17,38 +16,27 @@ 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 - { - 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* 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; - static bool IsCompressedHostTextureFormat(AbstractTextureFormat format); - static size_t CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length); + 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); 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; }; 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/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 2edd6dcfd5..2145c57eeb 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" @@ -100,15 +101,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) { @@ -635,14 +627,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) @@ -668,10 +656,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 @@ -701,20 +688,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 && !IsHeadless()) @@ -729,33 +712,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 = g_texture_cache->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, - EFBRectangle{0, 0, target_width, target_height}); + + // 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()) { @@ -765,6 +814,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; } @@ -776,7 +826,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 1edb5c75d4..90fe3ca3c9 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -34,8 +34,11 @@ class AbstractRawTexture; class AbstractTexture; +class AbstractStagingTexture; class PostProcessingShaderImplementation; +struct TextureConfig; enum class EFBAccessType; +enum class StagingTextureType; struct EfbPokeData { @@ -79,6 +82,10 @@ public: virtual void RestoreState() {} 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; } int GetTargetHeight() const { return m_target_height; } @@ -145,7 +152,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; @@ -185,11 +192,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; @@ -207,7 +211,6 @@ private: bool m_frame_dump_frame_running = false; struct FrameDumpConfig { - AbstractTexture* texture; const u8* data; int width; int height; @@ -215,13 +218,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; @@ -235,7 +243,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(); }; diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 7e3f7b25c0..d6290001a9 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,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, dstrect); + 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) { @@ -1366,33 +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, dstrect); - updated_entry = true; if (tex_info.is_palette_texture) @@ -2090,7 +2077,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.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 86ea88f651..a212c7f86a 100644 --- a/Source/Core/VideoCommon/TextureConfig.h +++ b/Source/Core/VideoCommon/TextureConfig.h @@ -13,17 +13,37 @@ enum class AbstractTextureFormat : u32 { RGBA8, + BGRA8, DXT1, DXT3, DXT5, - BPTC + BPTC, + 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; + 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; + 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 +