diff --git a/Source/Core/VideoBackends/D3D/CMakeLists.txt b/Source/Core/VideoBackends/D3D/CMakeLists.txt index 9a95efe46d..953f8737e5 100644 --- a/Source/Core/VideoBackends/D3D/CMakeLists.txt +++ b/Source/Core/VideoBackends/D3D/CMakeLists.txt @@ -13,6 +13,8 @@ set(SRCS D3DTexture.h D3DUtil.cpp D3DUtil.h + DXTexture.cpp + DXTexture.h FramebufferManager.cpp FramebufferManager.h GeometryShaderCache.cpp diff --git a/Source/Core/VideoBackends/D3D/D3D.vcxproj b/Source/Core/VideoBackends/D3D/D3D.vcxproj index 141d479b78..e766387de8 100644 --- a/Source/Core/VideoBackends/D3D/D3D.vcxproj +++ b/Source/Core/VideoBackends/D3D/D3D.vcxproj @@ -43,6 +43,7 @@ + @@ -65,6 +66,7 @@ + diff --git a/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters b/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters index 04eb8fb19f..cc05377bda 100644 --- a/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters +++ b/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters @@ -67,6 +67,9 @@ Render + + Render + @@ -124,5 +127,8 @@ Render + + Render + \ No newline at end of file diff --git a/Source/Core/VideoBackends/D3D/DXTexture.cpp b/Source/Core/VideoBackends/D3D/DXTexture.cpp new file mode 100644 index 0000000000..765438d679 --- /dev/null +++ b/Source/Core/VideoBackends/D3D/DXTexture.cpp @@ -0,0 +1,196 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/Assert.h" +#include "Common/CommonTypes.h" +#include "Common/Logging/Log.h" + +#include "VideoBackends/D3D/D3DBase.h" +#include "VideoBackends/D3D/D3DState.h" +#include "VideoBackends/D3D/D3DTexture.h" +#include "VideoBackends/D3D/D3DUtil.h" +#include "VideoBackends/D3D/DXTexture.h" +#include "VideoBackends/D3D/FramebufferManager.h" +#include "VideoBackends/D3D/GeometryShaderCache.h" +#include "VideoBackends/D3D/PixelShaderCache.h" +#include "VideoBackends/D3D/TextureCache.h" +#include "VideoBackends/D3D/VertexShaderCache.h" + +#include "VideoCommon/ImageWrite.h" +#include "VideoCommon/TextureConfig.h" + +namespace DX11 +{ +namespace +{ +DXGI_FORMAT GetDXGIFormatForHostFormat(AbstractTextureFormat format) +{ + switch (format) + { + case AbstractTextureFormat::DXT1: + return DXGI_FORMAT_BC1_UNORM; + case AbstractTextureFormat::DXT3: + return DXGI_FORMAT_BC2_UNORM; + case AbstractTextureFormat::DXT5: + return DXGI_FORMAT_BC3_UNORM; + case AbstractTextureFormat::RGBA8: + default: + return DXGI_FORMAT_R8G8B8A8_UNORM; + } +} +} // Anonymous namespace + +DXTexture::DXTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config) +{ + DXGI_FORMAT dxgi_format = GetDXGIFormatForHostFormat(m_config.format); + if (m_config.rendertarget) + { + m_texture = D3DTexture2D::Create( + m_config.width, m_config.height, + (D3D11_BIND_FLAG)((int)D3D11_BIND_RENDER_TARGET | (int)D3D11_BIND_SHADER_RESOURCE), + D3D11_USAGE_DEFAULT, dxgi_format, 1, m_config.layers); + } + else + { + const D3D11_TEXTURE2D_DESC texdesc = + CD3D11_TEXTURE2D_DESC(dxgi_format, m_config.width, m_config.height, 1, m_config.levels, + D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DEFAULT, 0); + + ID3D11Texture2D* pTexture; + const HRESULT hr = D3D::device->CreateTexture2D(&texdesc, nullptr, &pTexture); + CHECK(SUCCEEDED(hr), "Create texture of the TextureCache"); + + m_texture = new D3DTexture2D(pTexture, D3D11_BIND_SHADER_RESOURCE); + + // TODO: better debug names + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_texture->GetTex(), + "a texture of the TextureCache"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_texture->GetSRV(), + "shader resource view of a texture of the TextureCache"); + + SAFE_RELEASE(pTexture); + } +} + +DXTexture::~DXTexture() +{ + m_texture->Release(); +} + +D3DTexture2D* DXTexture::GetRawTexIdentifier() const +{ + return m_texture; +} + +void DXTexture::Bind(unsigned int stage) +{ + D3D::stateman->SetTexture(stage, m_texture->GetSRV()); +} + +bool DXTexture::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); + + // Create a staging/readback texture with the dimensions of the specified mip level. + u32 mip_width = std::max(m_config.width >> level, 1u); + u32 mip_height = std::max(m_config.height >> level, 1u); + CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, mip_width, mip_height, 1, + 1, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ); + + ID3D11Texture2D* staging_texture; + HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &staging_texture); + if (FAILED(hr)) + { + WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast(hr)); + return false; + } + + // Copy the selected mip level to the staging texture. + CD3D11_BOX src_box(0, 0, 0, mip_width, mip_height, 1); + D3D::context->CopySubresourceRegion(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(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)); + staging_texture->Release(); + return false; + } + + bool encode_result = + TextureToPng(reinterpret_cast(map.pData), map.RowPitch, filename, mip_width, mip_height); + D3D::context->Unmap(staging_texture, 0); + staging_texture->Release(); + + return encode_result; +} + +void DXTexture::CopyRectangleFromTexture(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; + + 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), + float(dstrect.GetWidth()), float(dstrect.GetHeight())); + + D3D::stateman->UnsetTexture(m_texture->GetSRV()); + D3D::stateman->Apply(); + + D3D::context->OMSetRenderTargets(1, &m_texture->GetRTV(), nullptr); + D3D::context->RSSetViewports(1, &vp); + D3D::SetLinearCopySampler(); + D3D11_RECT srcRC; + srcRC.left = srcrect.left; + srcRC.right = srcrect.right; + srcRC.top = srcrect.top; + srcRC.bottom = srcrect.bottom; + D3D::drawShadedTexQuad(srcentry->m_texture->GetSRV(), &srcRC, srcentry->m_config.width, + srcentry->m_config.height, PixelShaderCache::GetColorCopyProgram(false), + VertexShaderCache::GetSimpleVertexShader(), + VertexShaderCache::GetSimpleInputLayout(), + GeometryShaderCache::GetCopyGeometryShader(), 1.0, 0); + + D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), + FramebufferManager::GetEFBDepthTexture()->GetDSV()); + + g_renderer->RestoreAPIState(); +} + +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); + D3D::context->UpdateSubresource(m_texture->GetTex(), level, nullptr, buffer, + static_cast(src_pitch), 0); +} +} // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/DXTexture.h b/Source/Core/VideoBackends/D3D/DXTexture.h new file mode 100644 index 0000000000..73055343f8 --- /dev/null +++ b/Source/Core/VideoBackends/D3D/DXTexture.h @@ -0,0 +1,37 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/CommonTypes.h" + +#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/VideoCommon.h" + +class D3DTexture2D; + +namespace DX11 +{ +class DXTexture final : public AbstractTexture +{ +public: + explicit DXTexture(const TextureConfig& tex_config); + ~DXTexture(); + + void Bind(unsigned int stage) override; + bool Save(const std::string& filename, unsigned int level) override; + + void CopyRectangleFromTexture(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: + D3DTexture2D* m_texture; +}; + +} // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/TextureCache.cpp b/Source/Core/VideoBackends/D3D/TextureCache.cpp index cd737e7201..a696dba452 100644 --- a/Source/Core/VideoBackends/D3D/TextureCache.cpp +++ b/Source/Core/VideoBackends/D3D/TextureCache.cpp @@ -7,265 +7,35 @@ #include #include -#include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DShader.h" #include "VideoBackends/D3D/D3DState.h" +#include "VideoBackends/D3D/D3DTexture.h" #include "VideoBackends/D3D/D3DUtil.h" +#include "VideoBackends/D3D/DXTexture.h" #include "VideoBackends/D3D/FramebufferManager.h" #include "VideoBackends/D3D/GeometryShaderCache.h" #include "VideoBackends/D3D/PSTextureEncoder.h" #include "VideoBackends/D3D/PixelShaderCache.h" #include "VideoBackends/D3D/VertexShaderCache.h" + #include "VideoCommon/ImageWrite.h" #include "VideoCommon/RenderBase.h" +#include "VideoCommon/TextureConfig.h" #include "VideoCommon/VideoConfig.h" namespace DX11 { +static const size_t MAX_COPY_BUFFERS = 32; +static ID3D11Buffer* s_efbcopycbuf[MAX_COPY_BUFFERS] = {0}; static std::unique_ptr g_encoder; -const size_t MAX_COPY_BUFFERS = 32; -ID3D11Buffer* efbcopycbuf[MAX_COPY_BUFFERS] = {0}; -static DXGI_FORMAT GetDXGIFormatForHostFormat(HostTextureFormat format) +std::unique_ptr TextureCache::CreateTexture(const TextureConfig& config) { - switch (format) - { - case HostTextureFormat::DXT1: - return DXGI_FORMAT_BC1_UNORM; - - case HostTextureFormat::DXT3: - return DXGI_FORMAT_BC2_UNORM; - - case HostTextureFormat::DXT5: - return DXGI_FORMAT_BC3_UNORM; - - case HostTextureFormat::RGBA8: - default: - return DXGI_FORMAT_R8G8B8A8_UNORM; - } -} - -TextureCache::TCacheEntry::~TCacheEntry() -{ - texture->Release(); -} - -void TextureCache::TCacheEntry::Bind(unsigned int stage) -{ - D3D::stateman->SetTexture(stage, texture->GetSRV()); -} - -bool TextureCache::TCacheEntry::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_(config.format == HostTextureFormat::RGBA8); - - // Create a staging/readback texture with the dimensions of the specified mip level. - u32 mip_width = std::max(config.width >> level, 1u); - u32 mip_height = std::max(config.height >> level, 1u); - CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, mip_width, mip_height, 1, - 1, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ); - - ID3D11Texture2D* staging_texture; - HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &staging_texture); - if (FAILED(hr)) - { - WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast(hr)); - return false; - } - - // Copy the selected mip level to the staging texture. - CD3D11_BOX src_box(0, 0, 0, mip_width, mip_height, 1); - D3D::context->CopySubresourceRegion(staging_texture, 0, 0, 0, 0, texture->GetTex(), - D3D11CalcSubresource(level, 0, 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(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)); - staging_texture->Release(); - return false; - } - - bool encode_result = - TextureToPng(reinterpret_cast(map.pData), map.RowPitch, filename, mip_width, mip_height); - D3D::context->Unmap(staging_texture, 0); - staging_texture->Release(); - - return encode_result; -} - -void TextureCache::TCacheEntry::CopyRectangleFromTexture(const TCacheEntryBase* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) -{ - TCacheEntry* srcentry = (TCacheEntry*)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->config.layers; - - D3D::context->CopySubresourceRegion(texture->GetTex(), 0, dstrect.left, dstrect.top, 0, - srcentry->texture->GetTex(), 0, &srcbox); - return; - } - else if (!config.rendertarget) - { - return; - } - g_renderer->ResetAPIState(); // reset any game specific settings - - const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(float(dstrect.left), float(dstrect.top), - float(dstrect.GetWidth()), float(dstrect.GetHeight())); - - D3D::stateman->UnsetTexture(texture->GetSRV()); - D3D::stateman->Apply(); - - D3D::context->OMSetRenderTargets(1, &texture->GetRTV(), nullptr); - D3D::context->RSSetViewports(1, &vp); - D3D::SetLinearCopySampler(); - D3D11_RECT srcRC; - srcRC.left = srcrect.left; - srcRC.right = srcrect.right; - srcRC.top = srcrect.top; - srcRC.bottom = srcrect.bottom; - D3D::drawShadedTexQuad(srcentry->texture->GetSRV(), &srcRC, srcentry->config.width, - srcentry->config.height, PixelShaderCache::GetColorCopyProgram(false), - VertexShaderCache::GetSimpleVertexShader(), - VertexShaderCache::GetSimpleInputLayout(), - GeometryShaderCache::GetCopyGeometryShader(), 1.0, 0); - - D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), - FramebufferManager::GetEFBDepthTexture()->GetDSV()); - - g_renderer->RestoreAPIState(); -} - -void TextureCache::TCacheEntry::Load(u32 level, u32 width, u32 height, u32 row_length, - const u8* buffer, size_t buffer_size) -{ - size_t src_pitch = CalculateHostTextureLevelPitch(config.format, row_length); - D3D::context->UpdateSubresource(texture->GetTex(), level, nullptr, buffer, - static_cast(src_pitch), 0); -} - -TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config) -{ - DXGI_FORMAT dxgi_format = GetDXGIFormatForHostFormat(config.format); - if (config.rendertarget) - { - return new TCacheEntry( - config, D3DTexture2D::Create(config.width, config.height, - (D3D11_BIND_FLAG)((int)D3D11_BIND_RENDER_TARGET | - (int)D3D11_BIND_SHADER_RESOURCE), - D3D11_USAGE_DEFAULT, dxgi_format, 1, config.layers)); - } - else - { - const D3D11_TEXTURE2D_DESC texdesc = - CD3D11_TEXTURE2D_DESC(dxgi_format, config.width, config.height, 1, config.levels, - D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DEFAULT, 0); - - ID3D11Texture2D* pTexture; - const HRESULT hr = D3D::device->CreateTexture2D(&texdesc, nullptr, &pTexture); - CHECK(SUCCEEDED(hr), "Create texture of the TextureCache"); - - TCacheEntry* const entry = - new TCacheEntry(config, new D3DTexture2D(pTexture, D3D11_BIND_SHADER_RESOURCE)); - - // TODO: better debug names - D3D::SetDebugObjectName((ID3D11DeviceChild*)entry->texture->GetTex(), - "a texture of the TextureCache"); - D3D::SetDebugObjectName((ID3D11DeviceChild*)entry->texture->GetSRV(), - "shader resource view of a texture of the TextureCache"); - - SAFE_RELEASE(pTexture); - - return entry; - } -} - -void TextureCache::TCacheEntry::FromRenderTarget(bool is_depth_copy, const EFBRectangle& srcRect, - bool scaleByHalf, unsigned int cbufid, - const float* colmat) -{ - // When copying at half size, in multisampled mode, resolve the color/depth buffer first. - // This is because multisampled texture reads go through Load, not Sample, and the linear - // filter is ignored. - bool multisampled = (g_ActiveConfig.iMultisamples > 1); - ID3D11ShaderResourceView* efbTexSRV = is_depth_copy ? - FramebufferManager::GetEFBDepthTexture()->GetSRV() : - FramebufferManager::GetEFBColorTexture()->GetSRV(); - if (multisampled && scaleByHalf) - { - multisampled = false; - efbTexSRV = is_depth_copy ? FramebufferManager::GetResolvedEFBDepthTexture()->GetSRV() : - FramebufferManager::GetResolvedEFBColorTexture()->GetSRV(); - } - - g_renderer->ResetAPIState(); - - // stretch picture with increased internal resolution - const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)config.width, (float)config.height); - D3D::context->RSSetViewports(1, &vp); - - // set transformation - if (nullptr == efbcopycbuf[cbufid]) - { - const D3D11_BUFFER_DESC cbdesc = - CD3D11_BUFFER_DESC(28 * sizeof(float), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT); - D3D11_SUBRESOURCE_DATA data; - data.pSysMem = colmat; - HRESULT hr = D3D::device->CreateBuffer(&cbdesc, &data, &efbcopycbuf[cbufid]); - CHECK(SUCCEEDED(hr), "Create efb copy constant buffer %d", cbufid); - D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopycbuf[cbufid], - "a constant buffer used in TextureCache::CopyRenderTargetToTexture"); - } - D3D::stateman->SetPixelConstants(efbcopycbuf[cbufid]); - - const TargetRectangle targetSource = g_renderer->ConvertEFBRectangle(srcRect); - // TODO: try targetSource.asRECT(); - const D3D11_RECT sourcerect = - CD3D11_RECT(targetSource.left, targetSource.top, targetSource.right, targetSource.bottom); - - // Use linear filtering if (bScaleByHalf), use point filtering otherwise - if (scaleByHalf) - D3D::SetLinearCopySampler(); - else - D3D::SetPointCopySampler(); - - // Make sure we don't draw with the texture set as both a source and target. - // (This can happen because we don't unbind textures when we free them.) - D3D::stateman->UnsetTexture(texture->GetSRV()); - D3D::stateman->Apply(); - - D3D::context->OMSetRenderTargets(1, &texture->GetRTV(), nullptr); - - // Create texture copy - D3D::drawShadedTexQuad( - efbTexSRV, &sourcerect, g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight(), - is_depth_copy ? PixelShaderCache::GetDepthMatrixProgram(multisampled) : - PixelShaderCache::GetColorMatrixProgram(multisampled), - VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), - GeometryShaderCache::GetCopyGeometryShader()); - - D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), - FramebufferManager::GetEFBDepthTexture()->GetDSV()); - - g_renderer->RestoreAPIState(); + return std::make_unique(config); } void TextureCache::CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_width, @@ -356,14 +126,16 @@ void main( } )HLSL"; -void TextureCache::ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, - void* palette, TlutFormat format) +void TextureCache::ConvertTexture(TCacheEntry* destination, TCacheEntry* source, void* palette, + TlutFormat format) { + DXTexture* source_texture = static_cast(source->texture.get()); + DXTexture* destination_texture = static_cast(destination->texture.get()); g_renderer->ResetAPIState(); // stretch picture with increased internal resolution - const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)unconverted->config.width, - (float)unconverted->config.height); + const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, static_cast(source->GetWidth()), + static_cast(source->GetHeight())); D3D::context->RSSetViewports(1, &vp); D3D11_BOX box{0, 0, 0, 512, 1, 1}; @@ -372,29 +144,27 @@ void TextureCache::ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* uncon D3D::stateman->SetTexture(1, palette_buf_srv); // TODO: Add support for C14X2 format. (Different multiplier, more palette entries.) - float params[4] = {(unconverted->format & 0xf) == GX_TF_I4 ? 15.f : 255.f}; + float params[4] = {(source->format & 0xf) == GX_TF_I4 ? 15.f : 255.f}; D3D::context->UpdateSubresource(palette_uniform, 0, nullptr, ¶ms, 0, 0); D3D::stateman->SetPixelConstants(palette_uniform); - const D3D11_RECT sourcerect = - CD3D11_RECT(0, 0, unconverted->config.width, unconverted->config.height); + const D3D11_RECT sourcerect = CD3D11_RECT(0, 0, source->GetWidth(), source->GetHeight()); D3D::SetPointCopySampler(); // Make sure we don't draw with the texture set as both a source and target. // (This can happen because we don't unbind textures when we free them.) - D3D::stateman->UnsetTexture(static_cast(entry)->texture->GetSRV()); + D3D::stateman->UnsetTexture(destination_texture->GetRawTexIdentifier()->GetSRV()); D3D::stateman->Apply(); - D3D::context->OMSetRenderTargets(1, &static_cast(entry)->texture->GetRTV(), + D3D::context->OMSetRenderTargets(1, &destination_texture->GetRawTexIdentifier()->GetRTV(), nullptr); // Create texture copy - D3D::drawShadedTexQuad(static_cast(unconverted)->texture->GetSRV(), &sourcerect, - unconverted->config.width, unconverted->config.height, - palette_pixel_shader[format], VertexShaderCache::GetSimpleVertexShader(), - VertexShaderCache::GetSimpleInputLayout(), - GeometryShaderCache::GetCopyGeometryShader()); + D3D::drawShadedTexQuad( + source_texture->GetRawTexIdentifier()->GetSRV(), &sourcerect, source->GetWidth(), + source->GetHeight(), palette_pixel_shader[format], VertexShaderCache::GetSimpleVertexShader(), + VertexShaderCache::GetSimpleInputLayout(), GeometryShaderCache::GetCopyGeometryShader()); D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), FramebufferManager::GetEFBDepthTexture()->GetDSV()); @@ -444,7 +214,7 @@ TextureCache::TextureCache() TextureCache::~TextureCache() { for (unsigned int k = 0; k < MAX_COPY_BUFFERS; ++k) - SAFE_RELEASE(efbcopycbuf[k]); + SAFE_RELEASE(s_efbcopycbuf[k]); g_encoder->Shutdown(); g_encoder.reset(); @@ -455,4 +225,79 @@ TextureCache::~TextureCache() for (ID3D11PixelShader*& shader : palette_pixel_shader) SAFE_RELEASE(shader); } + +void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, + const EFBRectangle& src_rect, bool scale_by_half, + unsigned int cbuf_id, const float* colmat) +{ + auto* destination_texture = static_cast(entry->texture.get()); + + // When copying at half size, in multisampled mode, resolve the color/depth buffer first. + // This is because multisampled texture reads go through Load, not Sample, and the linear + // filter is ignored. + bool multisampled = (g_ActiveConfig.iMultisamples > 1); + ID3D11ShaderResourceView* efbTexSRV = is_depth_copy ? + FramebufferManager::GetEFBDepthTexture()->GetSRV() : + FramebufferManager::GetEFBColorTexture()->GetSRV(); + if (multisampled && scale_by_half) + { + multisampled = false; + efbTexSRV = is_depth_copy ? FramebufferManager::GetResolvedEFBDepthTexture()->GetSRV() : + FramebufferManager::GetResolvedEFBColorTexture()->GetSRV(); + } + + g_renderer->ResetAPIState(); + + // stretch picture with increased internal resolution + const D3D11_VIEWPORT vp = + CD3D11_VIEWPORT(0.f, 0.f, static_cast(destination_texture->GetConfig().width), + static_cast(destination_texture->GetConfig().height)); + D3D::context->RSSetViewports(1, &vp); + + // set transformation + if (nullptr == s_efbcopycbuf[cbuf_id]) + { + const D3D11_BUFFER_DESC cbdesc = + CD3D11_BUFFER_DESC(28 * sizeof(float), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT); + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = colmat; + HRESULT hr = D3D::device->CreateBuffer(&cbdesc, &data, &s_efbcopycbuf[cbuf_id]); + CHECK(SUCCEEDED(hr), "Create efb copy constant buffer %d", cbuf_id); + D3D::SetDebugObjectName((ID3D11DeviceChild*)s_efbcopycbuf[cbuf_id], + "a constant buffer used in TextureCache::CopyRenderTargetToTexture"); + } + D3D::stateman->SetPixelConstants(s_efbcopycbuf[cbuf_id]); + + const TargetRectangle targetSource = g_renderer->ConvertEFBRectangle(src_rect); + // TODO: try targetSource.asRECT(); + const D3D11_RECT sourcerect = + CD3D11_RECT(targetSource.left, targetSource.top, targetSource.right, targetSource.bottom); + + // Use linear filtering if (bScaleByHalf), use point filtering otherwise + if (scale_by_half) + D3D::SetLinearCopySampler(); + else + D3D::SetPointCopySampler(); + + // Make sure we don't draw with the texture set as both a source and target. + // (This can happen because we don't unbind textures when we free them.) + D3D::stateman->UnsetTexture(destination_texture->GetRawTexIdentifier()->GetSRV()); + D3D::stateman->Apply(); + + D3D::context->OMSetRenderTargets(1, &destination_texture->GetRawTexIdentifier()->GetRTV(), + nullptr); + + // Create texture copy + D3D::drawShadedTexQuad( + efbTexSRV, &sourcerect, g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight(), + is_depth_copy ? PixelShaderCache::GetDepthMatrixProgram(multisampled) : + PixelShaderCache::GetColorMatrixProgram(multisampled), + VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), + GeometryShaderCache::GetCopyGeometryShader()); + + D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), + FramebufferManager::GetEFBDepthTexture()->GetDSV()); + + g_renderer->RestoreAPIState(); +} } diff --git a/Source/Core/VideoBackends/D3D/TextureCache.h b/Source/Core/VideoBackends/D3D/TextureCache.h index 78450eefdd..87586ca7f3 100644 --- a/Source/Core/VideoBackends/D3D/TextureCache.h +++ b/Source/Core/VideoBackends/D3D/TextureCache.h @@ -7,6 +7,9 @@ #include "VideoBackends/D3D/D3DTexture.h" #include "VideoCommon/TextureCacheBase.h" +class AbstractTexture; +struct TextureConfig; + namespace DX11 { class TextureCache : public TextureCacheBase @@ -16,31 +19,7 @@ public: ~TextureCache(); private: - struct TCacheEntry : TCacheEntryBase - { - D3DTexture2D* const texture; - - TCacheEntry(const TCacheEntryConfig& config, D3DTexture2D* _tex) - : TCacheEntryBase(config), texture(_tex) - { - } - ~TCacheEntry(); - - void CopyRectangleFromTexture(const TCacheEntryBase* 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; - - void FromRenderTarget(bool is_depth_copy, const EFBRectangle& srcRect, bool scaleByHalf, - unsigned int cbufid, const float* colmat) override; - - void Bind(unsigned int stage) override; - bool Save(const std::string& filename, unsigned int level) override; - }; - - TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) override; + 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, @@ -49,13 +28,16 @@ private: return 0; }; - void ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, + void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, void* palette, TlutFormat format) override; void CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, bool is_depth_copy, const EFBRectangle& src_rect, bool scale_by_half) override; + void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect, + bool scale_by_half, unsigned int cbuf_id, const float* colmat) override; + bool CompileShaders() override { return true; } void DeleteShaders() override {} ID3D11Buffer* palette_buf; diff --git a/Source/Core/VideoBackends/Null/CMakeLists.txt b/Source/Core/VideoBackends/Null/CMakeLists.txt index 5182a86c4e..82efe38241 100644 --- a/Source/Core/VideoBackends/Null/CMakeLists.txt +++ b/Source/Core/VideoBackends/Null/CMakeLists.txt @@ -1,5 +1,6 @@ set(SRCS NullBackend.cpp + NullTexture.cpp Render.cpp VertexManager.cpp ShaderCache.cpp diff --git a/Source/Core/VideoBackends/Null/Null.vcxproj b/Source/Core/VideoBackends/Null/Null.vcxproj index 13d92758e0..178575ef81 100644 --- a/Source/Core/VideoBackends/Null/Null.vcxproj +++ b/Source/Core/VideoBackends/Null/Null.vcxproj @@ -37,12 +37,14 @@ + + diff --git a/Source/Core/VideoBackends/Null/NullTexture.cpp b/Source/Core/VideoBackends/Null/NullTexture.cpp new file mode 100644 index 0000000000..7d852f7ddf --- /dev/null +++ b/Source/Core/VideoBackends/Null/NullTexture.cpp @@ -0,0 +1,28 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoBackends/Null/NullTexture.h" + +namespace Null +{ +NullTexture::NullTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config) +{ +} + +void NullTexture::Bind(unsigned int stage) +{ +} + +void NullTexture::CopyRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& srcrect, + const MathUtil::Rectangle& dstrect) +{ +} + +void NullTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, + size_t buffer_size) +{ +} + +} // namespace Null diff --git a/Source/Core/VideoBackends/Null/NullTexture.h b/Source/Core/VideoBackends/Null/NullTexture.h new file mode 100644 index 0000000000..c8f8e77004 --- /dev/null +++ b/Source/Core/VideoBackends/Null/NullTexture.h @@ -0,0 +1,29 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/CommonTypes.h" + +#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/VideoCommon.h" + +namespace Null +{ +class NullTexture final : public AbstractTexture +{ +public: + explicit NullTexture(const TextureConfig& config); + ~NullTexture() = default; + + void Bind(unsigned int stage) override; + + void CopyRectangleFromTexture(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; +}; + +} // namespace Null diff --git a/Source/Core/VideoBackends/Null/TextureCache.h b/Source/Core/VideoBackends/Null/TextureCache.h index 399af04764..e4d115d49d 100644 --- a/Source/Core/VideoBackends/Null/TextureCache.h +++ b/Source/Core/VideoBackends/Null/TextureCache.h @@ -4,7 +4,12 @@ #pragma once +#include + +#include "VideoBackends/Null/NullTexture.h" + #include "VideoCommon/TextureCacheBase.h" +#include "VideoCommon/TextureConfig.h" namespace Null { @@ -15,7 +20,7 @@ public: ~TextureCache() {} bool CompileShaders() override { return true; } void DeleteShaders() override {} - void ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, + void ConvertTexture(TCacheEntry* entry, TCacheEntry* unconverted, void* palette, TlutFormat format) override { } @@ -26,33 +31,15 @@ public: { } + void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect, + bool scale_by_half, unsigned int cbuf_id, const float* colmat) override + { + } + private: - struct TCacheEntry : TCacheEntryBase + std::unique_ptr CreateTexture(const TextureConfig& config) override { - TCacheEntry(const TCacheEntryConfig& _config) : TCacheEntryBase(_config) {} - ~TCacheEntry() {} - void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, - size_t buffer_size) override - { - } - void FromRenderTarget(bool is_depth_copy, const EFBRectangle& src_rect, bool scale_by_half, - unsigned int cbufid, const float* colmat) override - { - } - - void CopyRectangleFromTexture(const TCacheEntryBase* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) override - { - } - - void Bind(unsigned int stage) override {} - bool Save(const std::string& filename, unsigned int level) override { return false; } - }; - - TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) override - { - return new TCacheEntry(config); + return std::make_unique(config); } }; diff --git a/Source/Core/VideoBackends/OGL/CMakeLists.txt b/Source/Core/VideoBackends/OGL/CMakeLists.txt index 486d6a017f..9be7537704 100644 --- a/Source/Core/VideoBackends/OGL/CMakeLists.txt +++ b/Source/Core/VideoBackends/OGL/CMakeLists.txt @@ -3,6 +3,7 @@ set(SRCS FramebufferManager.cpp main.cpp NativeVertexFormat.cpp + OGLTexture.cpp PerfQuery.cpp PostProcessing.cpp ProgramShaderCache.cpp diff --git a/Source/Core/VideoBackends/OGL/OGL.vcxproj b/Source/Core/VideoBackends/OGL/OGL.vcxproj index 75d72c07b2..c8ee325158 100644 --- a/Source/Core/VideoBackends/OGL/OGL.vcxproj +++ b/Source/Core/VideoBackends/OGL/OGL.vcxproj @@ -36,6 +36,7 @@ + @@ -52,6 +53,7 @@ + diff --git a/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters b/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters index aac1b1fe9e..63e3c6918e 100644 --- a/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters +++ b/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters @@ -53,6 +53,9 @@ + + Render + @@ -93,6 +96,9 @@ GLUtil + + Render + diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.cpp b/Source/Core/VideoBackends/OGL/OGLTexture.cpp new file mode 100644 index 0000000000..242e19888e --- /dev/null +++ b/Source/Core/VideoBackends/OGL/OGLTexture.cpp @@ -0,0 +1,274 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/Assert.h" +#include "Common/CommonTypes.h" +#include "Common/GL/GLInterfaceBase.h" +#include "Common/MsgHandler.h" + +#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" + +#include "VideoCommon/ImageWrite.h" +#include "VideoCommon/TextureConfig.h" + +namespace OGL +{ +namespace +{ +std::array s_Textures; +u32 s_ActiveTexture; + +GLenum GetGLInternalFormatForTextureFormat(AbstractTextureFormat format, bool storage) +{ + switch (format) + { + case AbstractTextureFormat::DXT1: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case AbstractTextureFormat::DXT3: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case AbstractTextureFormat::DXT5: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case AbstractTextureFormat::RGBA8: + default: + return storage ? GL_RGBA8 : GL_RGBA; + } +} + +GLenum GetGLFormatForTextureFormat(AbstractTextureFormat format) +{ + switch (format) + { + case AbstractTextureFormat::RGBA8: + return GL_RGBA; + // Compressed texture formats don't use this parameter. + default: + return GL_UNSIGNED_BYTE; + } +} + +GLenum GetGLTypeForTextureFormat(AbstractTextureFormat format) +{ + switch (format) + { + case AbstractTextureFormat::RGBA8: + return GL_UNSIGNED_BYTE; + // Compressed texture formats don't use this parameter. + default: + return GL_UNSIGNED_BYTE; + } +} +} // Anonymous namespace + +bool SaveTexture(const std::string& filename, u32 textarget, u32 tex, int virtual_width, + int virtual_height, unsigned int level) +{ + if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL) + return false; + int width = std::max(virtual_width >> level, 1); + int height = std::max(virtual_height >> level, 1); + std::vector data(width * height * 4); + glActiveTexture(GL_TEXTURE9); + glBindTexture(textarget, tex); + glGetTexImage(textarget, level, GL_RGBA, GL_UNSIGNED_BYTE, data.data()); + OGLTexture::SetStage(); + + return TextureToPng(data.data(), width * 4, filename, width, height, true); +} + +OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config) +{ + glGenTextures(1, &m_texId); + + glActiveTexture(GL_TEXTURE9); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, m_config.levels - 1); + + if (g_ogl_config.bSupportsTextureStorage) + { + GLenum gl_internal_format = GetGLInternalFormatForTextureFormat(m_config.format, true); + glTexStorage3D(GL_TEXTURE_2D_ARRAY, m_config.levels, gl_internal_format, m_config.width, + m_config.height, m_config.layers); + } + + if (m_config.rendertarget) + { + // We can't render to compressed formats. + _assert_(!IsCompressedHostTextureFormat(m_config.format)); + + if (!g_ogl_config.bSupportsTextureStorage) + { + for (u32 level = 0; level < m_config.levels; level++) + { + glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA, std::max(m_config.width >> level, 1u), + std::max(m_config.height >> level, 1u), m_config.layers, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); + } + } + glGenFramebuffers(1, &m_framebuffer); + FramebufferManager::SetFramebuffer(m_framebuffer); + FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D_ARRAY, m_texId, 0); + } + + SetStage(); +} + +OGLTexture::~OGLTexture() +{ + if (m_texId) + { + for (auto& gtex : s_Textures) + if (gtex == m_texId) + gtex = 0; + glDeleteTextures(1, &m_texId); + m_texId = 0; + } + + if (m_framebuffer) + { + glDeleteFramebuffers(1, &m_framebuffer); + m_framebuffer = 0; + } +} + +GLuint OGLTexture::GetRawTexIdentifier() const +{ + return m_texId; +} + +GLuint OGLTexture::GetFramebuffer() const +{ + return m_framebuffer; +} + +void OGLTexture::Bind(unsigned int stage) +{ + if (s_Textures[stage] != m_texId) + { + if (s_ActiveTexture != stage) + { + glActiveTexture(GL_TEXTURE0 + stage); + s_ActiveTexture = stage; + } + + glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId); + s_Textures[stage] = m_texId; + } +} + +bool OGLTexture::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); + + return SaveTexture(filename, GL_TEXTURE_2D_ARRAY, m_texId, m_config.width, m_config.height, + level); +} + +void OGLTexture::CopyRectangleFromTexture(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) + { + glGenFramebuffers(1, &m_framebuffer); + FramebufferManager::SetFramebuffer(m_framebuffer); + FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D_ARRAY, m_texId, 0); + } + g_renderer->ResetAPIState(); + FramebufferManager::SetFramebuffer(m_framebuffer); + glActiveTexture(GL_TEXTURE9); + glBindTexture(GL_TEXTURE_2D_ARRAY, srcentry->m_texId); + g_sampler_cache->BindLinearSampler(9); + glViewport(dstrect.left, dstrect.top, dstrect.GetWidth(), dstrect.GetHeight()); + TextureCache::GetInstance()->GetColorCopyProgram().Bind(); + glUniform4f(TextureCache::GetInstance()->GetColorCopyPositionUniform(), float(srcrect.left), + float(srcrect.top), float(srcrect.GetWidth()), float(srcrect.GetHeight())); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + FramebufferManager::SetFramebuffer(0); + g_renderer->RestoreAPIState(); +} + +void OGLTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, + size_t buffer_size) +{ + if (level >= m_config.levels) + PanicAlert("Texture only has %d levels, can't update level %d", m_config.levels, level); + if (width != std::max(1u, m_config.width >> level) || + height != std::max(1u, m_config.height >> level)) + PanicAlert("size of level %d must be %dx%d, but %dx%d requested", level, + std::max(1u, m_config.width >> level), std::max(1u, m_config.height >> level), width, + height); + + glActiveTexture(GL_TEXTURE9); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId); + + if (row_length != width) + glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length); + + GLenum gl_internal_format = GetGLInternalFormatForTextureFormat(m_config.format, false); + if (IsCompressedHostTextureFormat(m_config.format)) + { + if (g_ogl_config.bSupportsTextureStorage) + { + glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, 0, width, height, 1, + gl_internal_format, static_cast(buffer_size), buffer); + } + else + { + glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, level, gl_internal_format, width, height, 1, 0, + static_cast(buffer_size), buffer); + } + } + else + { + GLenum gl_format = GetGLFormatForTextureFormat(m_config.format); + GLenum gl_type = GetGLTypeForTextureFormat(m_config.format); + if (g_ogl_config.bSupportsTextureStorage) + { + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, 0, width, height, 1, gl_format, gl_type, + buffer); + } + else + { + glTexImage3D(GL_TEXTURE_2D_ARRAY, level, gl_internal_format, width, height, 1, 0, gl_format, + gl_type, buffer); + } + } + + if (row_length != width) + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + SetStage(); +} + +void OGLTexture::DisableStage(unsigned int stage) +{ +} + +void OGLTexture::SetStage() +{ + // -1 is the initial value as we don't know which texture should be bound + if (s_ActiveTexture != (u32)-1) + glActiveTexture(GL_TEXTURE0 + s_ActiveTexture); +} + +} // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.h b/Source/Core/VideoBackends/OGL/OGLTexture.h new file mode 100644 index 0000000000..34322bf120 --- /dev/null +++ b/Source/Core/VideoBackends/OGL/OGLTexture.h @@ -0,0 +1,40 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/GL/GLUtil.h" + +#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/VideoCommon.h" + +namespace OGL +{ +class OGLTexture final : public AbstractTexture +{ +public: + explicit OGLTexture(const TextureConfig& tex_config); + ~OGLTexture(); + + void Bind(unsigned int stage) override; + bool Save(const std::string& filename, unsigned int level) override; + + void CopyRectangleFromTexture(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; + + GLuint GetRawTexIdentifier() const; + GLuint GetFramebuffer() const; + + static void DisableStage(unsigned int stage); + static void SetStage(); + +private: + GLuint m_texId; + GLuint m_framebuffer = 0; +}; + +} // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp index 44027314b7..4f1db6e0c2 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp @@ -138,7 +138,7 @@ void SHADER::SetProgramBindings(bool is_compute) } } -void SHADER::Bind() +void SHADER::Bind() const { if (CurrentProgram != glprogid) { diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h index a8b2bfcbc1..7af4a2c38e 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h @@ -47,7 +47,7 @@ struct SHADER void SetProgramVariables(); void SetProgramBindings(bool is_compute); - void Bind(); + void Bind() const; }; class ProgramShaderCache diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index a0cb982ea3..494a478501 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -27,6 +27,7 @@ #include "VideoBackends/OGL/BoundingBox.h" #include "VideoBackends/OGL/FramebufferManager.h" +#include "VideoBackends/OGL/OGLTexture.h" #include "VideoBackends/OGL/PostProcessing.h" #include "VideoBackends/OGL/ProgramShaderCache.h" #include "VideoBackends/OGL/RasterFont.h" @@ -1691,7 +1692,7 @@ void Renderer::PrepareFrameDumpRenderTexture(u32 width, u32 height) m_frame_dump_render_texture_width = width; m_frame_dump_render_texture_height = height; - TextureCache::SetStage(); + OGLTexture::SetStage(); } void Renderer::DestroyFrameDumpResources() @@ -1744,7 +1745,7 @@ void Renderer::RestoreAPIState() if (vm->m_last_vao) glBindVertexArray(vm->m_last_vao); - TextureCache::SetStage(); + OGLTexture::SetStage(); } void Renderer::SetGenerationMode() diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp index 169dec0bc5..b4f16266ee 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.cpp +++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "VideoBackends/OGL/TextureCache.h" - #include #include #include @@ -11,17 +9,18 @@ #include #include -#include "Common/Assert.h" #include "Common/GL/GLInterfaceBase.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" #include "VideoBackends/OGL/FramebufferManager.h" #include "VideoBackends/OGL/GPUTimer.h" +#include "VideoBackends/OGL/OGLTexture.h" #include "VideoBackends/OGL/ProgramShaderCache.h" #include "VideoBackends/OGL/Render.h" #include "VideoBackends/OGL/SamplerCache.h" #include "VideoBackends/OGL/StreamBuffer.h" +#include "VideoBackends/OGL/TextureCache.h" #include "VideoBackends/OGL/TextureConverter.h" #include "VideoCommon/ImageWrite.h" @@ -31,20 +30,8 @@ namespace OGL { -static SHADER s_ColorCopyProgram; -static SHADER s_ColorMatrixProgram; -static SHADER s_DepthMatrixProgram; -static GLuint s_ColorMatrixUniform; -static GLuint s_DepthMatrixUniform; -static GLuint s_ColorCopyPositionUniform; -static GLuint s_ColorMatrixPositionUniform; -static GLuint s_DepthCopyPositionUniform; static u32 s_ColorCbufid; static u32 s_DepthCbufid; - -static u32 s_Textures[8]; -static u32 s_ActiveTexture; - static SHADER s_palette_pixel_shader[3]; static std::unique_ptr s_palette_stream_buffer; static GLuint s_palette_resolv_texture; @@ -72,290 +59,9 @@ static std::array static void CreateTextureDecodingResources(); static void DestroyTextureDecodingResources(); -bool SaveTexture(const std::string& filename, u32 textarget, u32 tex, int virtual_width, - int virtual_height, unsigned int level) +std::unique_ptr TextureCache::CreateTexture(const TextureConfig& config) { - if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL) - return false; - int width = std::max(virtual_width >> level, 1); - int height = std::max(virtual_height >> level, 1); - std::vector data(width * height * 4); - glActiveTexture(GL_TEXTURE9); - glBindTexture(textarget, tex); - glGetTexImage(textarget, level, GL_RGBA, GL_UNSIGNED_BYTE, data.data()); - TextureCache::SetStage(); - - return TextureToPng(data.data(), width * 4, filename, width, height, true); -} - -static GLenum GetGLInternalFormatForTextureFormat(HostTextureFormat format, bool storage) -{ - switch (format) - { - case HostTextureFormat::DXT1: - return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - case HostTextureFormat::DXT3: - return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - case HostTextureFormat::DXT5: - return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - case HostTextureFormat::RGBA8: - default: - return storage ? GL_RGBA8 : GL_RGBA; - } -} - -static GLenum GetGLFormatForTextureFormat(HostTextureFormat format) -{ - switch (format) - { - case HostTextureFormat::RGBA8: - return GL_RGBA; - - // Compressed texture formats don't use this parameter. - default: - return GL_UNSIGNED_BYTE; - } -} - -static GLenum GetGLTypeForTextureFormat(HostTextureFormat format) -{ - switch (format) - { - case HostTextureFormat::RGBA8: - return GL_UNSIGNED_BYTE; - - // Compressed texture formats don't use this parameter. - default: - return GL_UNSIGNED_BYTE; - } -} - -TextureCache::TCacheEntry::~TCacheEntry() -{ - if (texture) - { - for (auto& gtex : s_Textures) - if (gtex == texture) - gtex = 0; - glDeleteTextures(1, &texture); - texture = 0; - } - - if (framebuffer) - { - glDeleteFramebuffers(1, &framebuffer); - framebuffer = 0; - } -} - -TextureCache::TCacheEntry::TCacheEntry(const TCacheEntryConfig& _config) : TCacheEntryBase(_config) -{ - glGenTextures(1, &texture); - - framebuffer = 0; -} - -void TextureCache::TCacheEntry::Bind(unsigned int stage) -{ - if (s_Textures[stage] != texture) - { - if (s_ActiveTexture != stage) - { - glActiveTexture(GL_TEXTURE0 + stage); - s_ActiveTexture = stage; - } - - glBindTexture(GL_TEXTURE_2D_ARRAY, texture); - s_Textures[stage] = texture; - } -} - -bool TextureCache::TCacheEntry::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_(config.format == HostTextureFormat::RGBA8); - - return SaveTexture(filename, GL_TEXTURE_2D_ARRAY, texture, config.width, config.height, level); -} - -TextureCache::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config) -{ - TCacheEntry* entry = new TCacheEntry(config); - - glActiveTexture(GL_TEXTURE9); - glBindTexture(GL_TEXTURE_2D_ARRAY, entry->texture); - - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, config.levels - 1); - - if (g_ogl_config.bSupportsTextureStorage) - { - GLenum gl_internal_format = GetGLInternalFormatForTextureFormat(config.format, true); - glTexStorage3D(GL_TEXTURE_2D_ARRAY, config.levels, gl_internal_format, config.width, - config.height, config.layers); - } - - if (config.rendertarget) - { - // We can't render to compressed formats. - _assert_(!IsCompressedHostTextureFormat(config.format)); - if (!g_ogl_config.bSupportsTextureStorage) - { - for (u32 level = 0; level < config.levels; level++) - { - glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA, std::max(config.width >> level, 1u), - std::max(config.height >> level, 1u), config.layers, 0, GL_RGBA, - GL_UNSIGNED_BYTE, nullptr); - } - } - glGenFramebuffers(1, &entry->framebuffer); - FramebufferManager::SetFramebuffer(entry->framebuffer); - FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D_ARRAY, entry->texture, 0); - } - - TextureCache::SetStage(); - return entry; -} - -void TextureCache::TCacheEntry::CopyRectangleFromTexture(const TCacheEntryBase* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) -{ - TCacheEntry* srcentry = (TCacheEntry*)source; - if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight() && - g_ogl_config.bSupportsCopySubImage) - { - glCopyImageSubData(srcentry->texture, GL_TEXTURE_2D_ARRAY, 0, srcrect.left, srcrect.top, 0, - texture, GL_TEXTURE_2D_ARRAY, 0, dstrect.left, dstrect.top, 0, - dstrect.GetWidth(), dstrect.GetHeight(), srcentry->config.layers); - return; - } - else if (!framebuffer) - { - glGenFramebuffers(1, &framebuffer); - FramebufferManager::SetFramebuffer(framebuffer); - FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D_ARRAY, texture, 0); - } - g_renderer->ResetAPIState(); - FramebufferManager::SetFramebuffer(framebuffer); - glActiveTexture(GL_TEXTURE9); - glBindTexture(GL_TEXTURE_2D_ARRAY, srcentry->texture); - g_sampler_cache->BindLinearSampler(9); - glViewport(dstrect.left, dstrect.top, dstrect.GetWidth(), dstrect.GetHeight()); - s_ColorCopyProgram.Bind(); - glUniform4f(s_ColorCopyPositionUniform, float(srcrect.left), float(srcrect.top), - float(srcrect.GetWidth()), float(srcrect.GetHeight())); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - FramebufferManager::SetFramebuffer(0); - g_renderer->RestoreAPIState(); -} - -void TextureCache::TCacheEntry::Load(u32 level, u32 width, u32 height, u32 row_length, - const u8* buffer, size_t buffer_size) -{ - if (level >= config.levels) - PanicAlert("Texture only has %d levels, can't update level %d", config.levels, level); - if (width != std::max(1u, config.width >> level) || - height != std::max(1u, config.height >> level)) - PanicAlert("size of level %d must be %dx%d, but %dx%d requested", level, - std::max(1u, config.width >> level), std::max(1u, config.height >> level), width, - height); - - glActiveTexture(GL_TEXTURE9); - glBindTexture(GL_TEXTURE_2D_ARRAY, texture); - - if (row_length != width) - glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length); - - GLenum gl_internal_format = GetGLInternalFormatForTextureFormat(config.format, false); - if (IsCompressedHostTextureFormat(config.format)) - { - if (g_ogl_config.bSupportsTextureStorage) - { - glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, 0, width, height, 1, - gl_internal_format, static_cast(buffer_size), buffer); - } - else - { - glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, level, gl_internal_format, width, height, 1, 0, - static_cast(buffer_size), buffer); - } - } - else - { - GLenum gl_format = GetGLFormatForTextureFormat(config.format); - GLenum gl_type = GetGLTypeForTextureFormat(config.format); - if (g_ogl_config.bSupportsTextureStorage) - { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, 0, width, height, 1, gl_format, gl_type, - buffer); - } - else - { - glTexImage3D(GL_TEXTURE_2D_ARRAY, level, gl_internal_format, width, height, 1, 0, gl_format, - gl_type, buffer); - } - } - - if (row_length != width) - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - TextureCache::SetStage(); -} - -void TextureCache::TCacheEntry::FromRenderTarget(bool is_depth_copy, const EFBRectangle& srcRect, - bool scaleByHalf, unsigned int cbufid, - const float* colmat) -{ - g_renderer->ResetAPIState(); // reset any game specific settings - - // Make sure to resolve anything we need to read from. - const GLuint read_texture = is_depth_copy ? - FramebufferManager::ResolveAndGetDepthTarget(srcRect) : - FramebufferManager::ResolveAndGetRenderTarget(srcRect); - - FramebufferManager::SetFramebuffer(framebuffer); - - OpenGL_BindAttributelessVAO(); - - glActiveTexture(GL_TEXTURE9); - glBindTexture(GL_TEXTURE_2D_ARRAY, read_texture); - if (scaleByHalf) - g_sampler_cache->BindLinearSampler(9); - else - g_sampler_cache->BindNearestSampler(9); - - glViewport(0, 0, config.width, config.height); - - GLuint uniform_location; - if (is_depth_copy) - { - s_DepthMatrixProgram.Bind(); - if (s_DepthCbufid != cbufid) - glUniform4fv(s_DepthMatrixUniform, 5, colmat); - s_DepthCbufid = cbufid; - uniform_location = s_DepthCopyPositionUniform; - } - else - { - s_ColorMatrixProgram.Bind(); - if (s_ColorCbufid != cbufid) - glUniform4fv(s_ColorMatrixUniform, 7, colmat); - s_ColorCbufid = cbufid; - uniform_location = s_ColorMatrixPositionUniform; - } - - TargetRectangle R = g_renderer->ConvertEFBRectangle(srcRect); - glUniform4f(uniform_location, static_cast(R.left), static_cast(R.top), - static_cast(R.right), static_cast(R.bottom)); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - FramebufferManager::SetFramebuffer(0); - g_renderer->RestoreAPIState(); + return std::make_unique(config); } void TextureCache::CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_width, @@ -370,10 +76,6 @@ TextureCache::TextureCache() { CompileShaders(); - s_ActiveTexture = UINT32_MAX; - for (auto& gtex : s_Textures) - gtex = UINT32_MAX; - if (g_ActiveConfig.backend_info.bSupportsPaletteConversion) { s32 buffer_size_mb = (g_ActiveConfig.backend_info.bSupportsGPUTextureDecoding ? 32 : 1); @@ -409,15 +111,19 @@ TextureCache::~TextureCache() } } -void TextureCache::DisableStage(unsigned int stage) +TextureCache* TextureCache::GetInstance() { + return static_cast(g_texture_cache.get()); } -void TextureCache::SetStage() +const SHADER& TextureCache::GetColorCopyProgram() const { - // -1 is the initial value as we don't know which texture should be bound - if (s_ActiveTexture != (u32)-1) - glActiveTexture(GL_TEXTURE0 + s_ActiveTexture); + return m_colorCopyProgram; +} + +GLuint TextureCache::GetColorCopyPositionUniform() const +{ + return m_colorCopyPositionUniform; } bool TextureCache::CompileShaders() @@ -504,28 +210,28 @@ bool TextureCache::CompileShaders() const char* prefix = geo_program.empty() ? "f" : "v"; const char* depth_layer = g_ActiveConfig.bStereoEFBMonoDepth ? "0.0" : "f_uv0.z"; - if (!ProgramShaderCache::CompileShader(s_ColorCopyProgram, + if (!ProgramShaderCache::CompileShader(m_colorCopyProgram, StringFromFormat(vertex_program, prefix, prefix), color_copy_program, geo_program) || - !ProgramShaderCache::CompileShader(s_ColorMatrixProgram, + !ProgramShaderCache::CompileShader(m_colorMatrixProgram, StringFromFormat(vertex_program, prefix, prefix), color_matrix_program, geo_program) || !ProgramShaderCache::CompileShader( - s_DepthMatrixProgram, StringFromFormat(vertex_program, prefix, prefix), + m_depthMatrixProgram, StringFromFormat(vertex_program, prefix, prefix), StringFromFormat(depth_matrix_program, depth_layer), geo_program)) { return false; } - s_ColorMatrixUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "colmat"); - s_DepthMatrixUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "colmat"); - s_ColorCbufid = UINT32_MAX; - s_DepthCbufid = UINT32_MAX; + m_colorMatrixUniform = glGetUniformLocation(m_colorMatrixProgram.glprogid, "colmat"); + m_depthMatrixUniform = glGetUniformLocation(m_depthMatrixProgram.glprogid, "colmat"); + s_ColorCbufid = UINT_MAX; + s_DepthCbufid = UINT_MAX; - s_ColorCopyPositionUniform = glGetUniformLocation(s_ColorCopyProgram.glprogid, "copy_position"); - s_ColorMatrixPositionUniform = - glGetUniformLocation(s_ColorMatrixProgram.glprogid, "copy_position"); - s_DepthCopyPositionUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "copy_position"); + m_colorCopyPositionUniform = glGetUniformLocation(m_colorCopyProgram.glprogid, "copy_position"); + m_colorMatrixPositionUniform = + glGetUniformLocation(m_colorMatrixProgram.glprogid, "copy_position"); + m_depthCopyPositionUniform = glGetUniformLocation(m_depthMatrixProgram.glprogid, "copy_position"); std::string palette_shader = R"GLSL( @@ -654,43 +360,42 @@ bool TextureCache::CompileShaders() void TextureCache::DeleteShaders() { - s_ColorMatrixProgram.Destroy(); - s_DepthMatrixProgram.Destroy(); + m_colorMatrixProgram.Destroy(); + m_depthMatrixProgram.Destroy(); if (g_ActiveConfig.backend_info.bSupportsPaletteConversion) for (auto& shader : s_palette_pixel_shader) shader.Destroy(); } -void TextureCache::ConvertTexture(TCacheEntryBase* _entry, TCacheEntryBase* _unconverted, - void* palette, TlutFormat format) +void TextureCache::ConvertTexture(TCacheEntry* destination, TCacheEntry* source, void* palette, + TlutFormat format) { if (!g_ActiveConfig.backend_info.bSupportsPaletteConversion) return; g_renderer->ResetAPIState(); - TCacheEntry* entry = (TCacheEntry*)_entry; - TCacheEntry* unconverted = (TCacheEntry*)_unconverted; + OGLTexture* source_texture = static_cast(source->texture.get()); + OGLTexture* destination_texture = static_cast(destination->texture.get()); glActiveTexture(GL_TEXTURE9); - glBindTexture(GL_TEXTURE_2D_ARRAY, unconverted->texture); + glBindTexture(GL_TEXTURE_2D_ARRAY, source_texture->GetRawTexIdentifier()); g_sampler_cache->BindNearestSampler(9); - FramebufferManager::SetFramebuffer(entry->framebuffer); - glViewport(0, 0, entry->config.width, entry->config.height); + FramebufferManager::SetFramebuffer(destination_texture->GetFramebuffer()); + glViewport(0, 0, destination->GetWidth(), destination->GetHeight()); s_palette_pixel_shader[format].Bind(); // C14 textures are currently unsupported - int size = (unconverted->format & 0xf) == GX_TF_I4 ? 32 : 512; + int size = (source->format & 0xf) == GX_TF_I4 ? 32 : 512; auto buffer = s_palette_stream_buffer->Map(size); memcpy(buffer.first, palette, size); s_palette_stream_buffer->Unmap(size); glUniform1i(s_palette_buffer_offset_uniform[format], buffer.second / 2); - glUniform1f(s_palette_multiplier_uniform[format], - (unconverted->format & 0xf) == 0 ? 15.0f : 255.0f); - glUniform4f(s_palette_copy_position_uniform[format], 0.0f, 0.0f, (float)unconverted->config.width, - (float)unconverted->config.height); + glUniform1f(s_palette_multiplier_uniform[format], (source->format & 0xf) == 0 ? 15.0f : 255.0f); + glUniform4f(s_palette_copy_position_uniform[format], 0.0f, 0.0f, + static_cast(source->GetWidth()), static_cast(source->GetHeight())); glActiveTexture(GL_TEXTURE10); glBindTexture(GL_TEXTURE_BUFFER, s_palette_resolv_texture); @@ -775,7 +480,7 @@ bool TextureCache::SupportsGPUTextureDecode(TextureFormat format, TlutFormat pal return true; } -void TextureCache::DecodeTextureOnGPU(TCacheEntryBase* entry, u32 dst_level, const u8* data, +void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data, size_t data_size, TextureFormat format, u32 width, u32 height, u32 aligned_width, u32 aligned_height, u32 row_stride, const u8* palette, TlutFormat palette_format) @@ -846,16 +551,69 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntryBase* entry, u32 dst_level, con auto dispatch_groups = TextureConversionShader::GetDispatchCount(info.base_info, aligned_width, aligned_height); - glBindImageTexture(0, static_cast(entry)->texture, dst_level, GL_TRUE, 0, - GL_WRITE_ONLY, GL_RGBA8); + glBindImageTexture(0, static_cast(entry->texture.get())->GetRawTexIdentifier(), + dst_level, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA8); glDispatchCompute(dispatch_groups.first, dispatch_groups.second, 1); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); - TextureCache::SetStage(); + OGLTexture::SetStage(); #ifdef TIME_TEXTURE_DECODING WARN_LOG(VIDEO, "Decode texture format %u size %ux%u took %.4fms", static_cast(format), width, height, timer.GetTimeMilliseconds()); #endif } + +void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, + const EFBRectangle& src_rect, bool scale_by_half, + unsigned int cbuf_id, const float* colmat) +{ + auto* destination_texture = static_cast(entry->texture.get()); + g_renderer->ResetAPIState(); // reset any game specific settings + + // Make sure to resolve anything we need to read from. + const GLuint read_texture = is_depth_copy ? + FramebufferManager::ResolveAndGetDepthTarget(src_rect) : + FramebufferManager::ResolveAndGetRenderTarget(src_rect); + + FramebufferManager::SetFramebuffer(destination_texture->GetFramebuffer()); + + OpenGL_BindAttributelessVAO(); + + glActiveTexture(GL_TEXTURE9); + glBindTexture(GL_TEXTURE_2D_ARRAY, read_texture); + if (scale_by_half) + g_sampler_cache->BindLinearSampler(9); + else + g_sampler_cache->BindNearestSampler(9); + + glViewport(0, 0, destination_texture->GetConfig().width, destination_texture->GetConfig().height); + + GLuint uniform_location; + if (is_depth_copy) + { + m_depthMatrixProgram.Bind(); + if (s_DepthCbufid != cbuf_id) + glUniform4fv(m_depthMatrixUniform, 5, colmat); + s_DepthCbufid = cbuf_id; + uniform_location = m_depthCopyPositionUniform; + } + else + { + m_colorMatrixProgram.Bind(); + if (s_ColorCbufid != cbuf_id) + glUniform4fv(m_colorMatrixUniform, 7, colmat); + s_ColorCbufid = cbuf_id; + uniform_location = m_colorMatrixPositionUniform; + } + + TargetRectangle R = g_renderer->ConvertEFBRectangle(src_rect); + glUniform4f(uniform_location, static_cast(R.left), static_cast(R.top), + static_cast(R.right), static_cast(R.bottom)); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + FramebufferManager::SetFramebuffer(0); + g_renderer->RestoreAPIState(); +} } diff --git a/Source/Core/VideoBackends/OGL/TextureCache.h b/Source/Core/VideoBackends/OGL/TextureCache.h index de0de485f5..93805de0d1 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.h +++ b/Source/Core/VideoBackends/OGL/TextureCache.h @@ -8,10 +8,14 @@ #include "Common/CommonTypes.h" #include "Common/GL/GLUtil.h" +#include "VideoBackends/OGL/ProgramShaderCache.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/VideoCommon.h" +class AbstractTexture; +struct TextureConfig; + namespace OGL { class TextureCache : public TextureCacheBase @@ -20,51 +24,40 @@ public: TextureCache(); ~TextureCache(); - static void DisableStage(unsigned int stage); - static void SetStage(); + static TextureCache* GetInstance(); bool SupportsGPUTextureDecode(TextureFormat format, TlutFormat palette_format) override; - void DecodeTextureOnGPU(TCacheEntryBase* entry, u32 dst_level, const u8* data, size_t data_size, + void DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data, size_t data_size, TextureFormat format, u32 width, u32 height, u32 aligned_width, u32 aligned_height, u32 row_stride, const u8* palette, TlutFormat palette_format) override; + const SHADER& GetColorCopyProgram() const; + GLuint GetColorCopyPositionUniform() const; + private: - struct TCacheEntry : TCacheEntryBase - { - GLuint texture; - GLuint framebuffer; - - // TexMode0 mode; // current filter and clamp modes that texture is set to - // TexMode1 mode1; // current filter and clamp modes that texture is set to - - TCacheEntry(const TCacheEntryConfig& config); - ~TCacheEntry(); - - void CopyRectangleFromTexture(const TCacheEntryBase* 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; - - void FromRenderTarget(bool is_depth_copy, const EFBRectangle& srcRect, bool scaleByHalf, - unsigned int cbufid, const float* colmat) override; - - void Bind(unsigned int stage) override; - bool Save(const std::string& filename, unsigned int level) override; - }; - - TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) override; - void ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, + std::unique_ptr CreateTexture(const TextureConfig& config) override; + void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, void* palette, TlutFormat format) override; void CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, bool is_depth_copy, const EFBRectangle& src_rect, bool scale_by_half) override; + void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect, + bool scale_by_half, unsigned int cbuf_id, const float* colmat) override; + bool CompileShaders() override; void DeleteShaders() override; + + SHADER m_colorCopyProgram; + SHADER m_colorMatrixProgram; + SHADER m_depthMatrixProgram; + GLuint m_colorMatrixUniform; + GLuint m_depthMatrixUniform; + GLuint m_colorCopyPositionUniform; + GLuint m_colorMatrixPositionUniform; + GLuint m_depthCopyPositionUniform; }; bool SaveTexture(const std::string& filename, u32 textarget, u32 tex, int virtual_width, diff --git a/Source/Core/VideoBackends/OGL/TextureConverter.cpp b/Source/Core/VideoBackends/OGL/TextureConverter.cpp index 1a51d8fb09..d352ea6701 100644 --- a/Source/Core/VideoBackends/OGL/TextureConverter.cpp +++ b/Source/Core/VideoBackends/OGL/TextureConverter.cpp @@ -17,6 +17,7 @@ #include "Core/HW/Memmap.h" #include "VideoBackends/OGL/FramebufferManager.h" +#include "VideoBackends/OGL/OGLTexture.h" #include "VideoBackends/OGL/ProgramShaderCache.h" #include "VideoBackends/OGL/Render.h" #include "VideoBackends/OGL/SamplerCache.h" @@ -309,7 +310,7 @@ void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc, u8* des // Otherwise we get jaggies when a game uses yscaling (most PAL games) EncodeToRamUsingShader(srcTexture, destAddr, dstWidth * 2, dstHeight, dstStride, true); FramebufferManager::SetFramebuffer(0); - TextureCache::DisableStage(0); + OGLTexture::DisableStage(0); g_renderer->RestoreAPIState(); } diff --git a/Source/Core/VideoBackends/Software/CMakeLists.txt b/Source/Core/VideoBackends/Software/CMakeLists.txt index d2b34188ad..effa29cec8 100644 --- a/Source/Core/VideoBackends/Software/CMakeLists.txt +++ b/Source/Core/VideoBackends/Software/CMakeLists.txt @@ -6,6 +6,7 @@ set(SRCS Rasterizer.cpp SWOGLWindow.cpp SWRenderer.cpp + SWTexture.cpp SWVertexLoader.cpp SWmain.cpp SetupUnit.cpp diff --git a/Source/Core/VideoBackends/Software/SWTexture.cpp b/Source/Core/VideoBackends/Software/SWTexture.cpp new file mode 100644 index 0000000000..164c0d5314 --- /dev/null +++ b/Source/Core/VideoBackends/Software/SWTexture.cpp @@ -0,0 +1,28 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoBackends/Software/SWTexture.h" + +namespace SW +{ +SWTexture::SWTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config) +{ +} + +void SWTexture::Bind(unsigned int stage) +{ +} + +void SWTexture::CopyRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& srcrect, + const MathUtil::Rectangle& dstrect) +{ +} + +void SWTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, + size_t buffer_size) +{ +} + +} // namespace SW diff --git a/Source/Core/VideoBackends/Software/SWTexture.h b/Source/Core/VideoBackends/Software/SWTexture.h new file mode 100644 index 0000000000..8737792cc0 --- /dev/null +++ b/Source/Core/VideoBackends/Software/SWTexture.h @@ -0,0 +1,29 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/CommonTypes.h" + +#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/VideoCommon.h" + +namespace SW +{ +class SWTexture final : public AbstractTexture +{ +public: + explicit SWTexture(const TextureConfig& tex_config); + ~SWTexture() = default; + + void Bind(unsigned int stage) override; + + void CopyRectangleFromTexture(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; +}; + +} // namespace SW diff --git a/Source/Core/VideoBackends/Software/SWmain.cpp b/Source/Core/VideoBackends/Software/SWmain.cpp index e004a0cac3..5319ccecd6 100644 --- a/Source/Core/VideoBackends/Software/SWmain.cpp +++ b/Source/Core/VideoBackends/Software/SWmain.cpp @@ -15,6 +15,7 @@ #include "VideoBackends/Software/Rasterizer.h" #include "VideoBackends/Software/SWOGLWindow.h" #include "VideoBackends/Software/SWRenderer.h" +#include "VideoBackends/Software/SWTexture.h" #include "VideoBackends/Software/SWVertexLoader.h" #include "VideoBackends/Software/VideoBackend.h" @@ -49,7 +50,7 @@ class TextureCache : public TextureCacheBase public: bool CompileShaders() override { return true; } void DeleteShaders() override {} - void ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, + void ConvertTexture(TCacheEntry* entry, TCacheEntry* unconverted, void* palette, TlutFormat format) override { } @@ -61,33 +62,15 @@ public: } private: - struct TCacheEntry : TCacheEntryBase + std::unique_ptr CreateTexture(const TextureConfig& config) override { - TCacheEntry(const TCacheEntryConfig& _config) : TCacheEntryBase(_config) {} - ~TCacheEntry() {} - void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, - size_t buffer_size) override - { - } - void FromRenderTarget(bool is_depth_copy, const EFBRectangle& srcRect, bool scaleByHalf, - unsigned int cbufid, const float* colmat) override - { - EfbCopy::CopyEfb(); - } + return std::make_unique(config); + } - void CopyRectangleFromTexture(const TCacheEntryBase* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) override - { - } - - void Bind(unsigned int stage) override {} - bool Save(const std::string& filename, unsigned int level) override { return false; } - }; - - TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) override + void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect, + bool scale_by_half, unsigned int cbuf_id, const float* colmat) override { - return new TCacheEntry(config); + EfbCopy::CopyEfb(); } }; diff --git a/Source/Core/VideoBackends/Software/Software.vcxproj b/Source/Core/VideoBackends/Software/Software.vcxproj index 716a85e05c..bf13233d67 100644 --- a/Source/Core/VideoBackends/Software/Software.vcxproj +++ b/Source/Core/VideoBackends/Software/Software.vcxproj @@ -45,6 +45,7 @@ + @@ -61,6 +62,7 @@ + diff --git a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt index 1b5ed96f9e..744b471d73 100644 --- a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt +++ b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt @@ -19,6 +19,7 @@ set(SRCS Util.cpp VertexFormat.cpp VertexManager.cpp + VKTexture.cpp VulkanContext.cpp VulkanLoader.cpp main.cpp diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp index a17135fa35..3f76d180b0 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp @@ -22,10 +22,12 @@ #include "VideoBackends/Vulkan/Texture2D.h" #include "VideoBackends/Vulkan/TextureConverter.h" #include "VideoBackends/Vulkan/Util.h" +#include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VertexFormat.h" #include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoCommon/RenderBase.h" +#include "VideoCommon/TextureConfig.h" #include "VideoCommon/VideoConfig.h" namespace Vulkan @@ -1354,20 +1356,19 @@ std::unique_ptr FramebufferManager::CreateXFBSource(unsigned int unsigned int target_height, unsigned int layers) { - TextureCacheBase::TCacheEntryConfig config; + TextureConfig config; config.width = target_width; config.height = target_height; config.layers = layers; config.rendertarget = true; - auto* base_texture = TextureCache::GetInstance()->CreateTexture(config); - auto* texture = static_cast(base_texture); + auto texture = TextureCache::GetInstance()->CreateTexture(config); if (!texture) { PanicAlert("Failed to create texture for XFB source"); return nullptr; } - return std::make_unique(std::unique_ptr(texture)); + return std::make_unique(std::move(texture)); } void FramebufferManager::CopyToRealXFB(u32 xfb_addr, u32 fb_stride, u32 fb_height, @@ -1405,7 +1406,7 @@ void FramebufferManager::CopyToRealXFB(u32 xfb_addr, u32 fb_stride, u32 fb_heigh } } -XFBSource::XFBSource(std::unique_ptr texture) +XFBSource::XFBSource(std::unique_ptr texture) : XFBSourceBase(), m_texture(std::move(texture)) { } @@ -1414,13 +1415,18 @@ XFBSource::~XFBSource() { } +VKTexture* XFBSource::GetTexture() const +{ + return static_cast(m_texture.get()); +} + void XFBSource::DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height) { // Guest memory -> GPU EFB Textures const u8* src_ptr = Memory::GetPointer(xfb_addr); _assert_(src_ptr); TextureCache::GetInstance()->GetTextureConverter()->DecodeYUYVTextureFromMemory( - m_texture.get(), src_ptr, fb_width, fb_width * 2, fb_height); + static_cast(m_texture.get()), src_ptr, fb_width, fb_width * 2, fb_height); } void XFBSource::CopyEFB(float gamma) @@ -1434,7 +1440,7 @@ void XFBSource::CopyEFB(float gamma) {static_cast(rect.GetWidth()), static_cast(rect.GetHeight())}}; Texture2D* src_texture = FramebufferManager::GetInstance()->ResolveEFBColorTexture(vk_rect); - TextureCache::GetInstance()->CopyRectangleFromTexture(m_texture.get(), rect, src_texture, rect); + static_cast(m_texture.get())->CopyRectangleFromTexture(src_texture, rect, rect); // If we sourced directly from the EFB framebuffer, restore it to a color attachment. if (src_texture == FramebufferManager::GetInstance()->GetEFBColorTexture()) diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.h b/Source/Core/VideoBackends/Vulkan/FramebufferManager.h index 2cc7a59a9f..db4455756d 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.h +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.h @@ -19,6 +19,7 @@ class StateTracker; class StreamBuffer; class Texture2D; class VertexFormat; +class VKTexture; class XFBSource; class FramebufferManager : public FramebufferManagerBase @@ -176,10 +177,10 @@ private: class XFBSource final : public XFBSourceBase { public: - explicit XFBSource(std::unique_ptr texture); + explicit XFBSource(std::unique_ptr texture); ~XFBSource(); - TextureCache::TCacheEntry* GetTexture() const { return m_texture.get(); } + VKTexture* GetTexture() const; // Guest -> GPU EFB Textures void DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height) override; @@ -187,7 +188,7 @@ public: void CopyEFB(float gamma) override; private: - std::unique_ptr m_texture; + std::unique_ptr m_texture; }; } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 61764e8c00..4800d99424 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "VideoBackends/Vulkan/Renderer.h" - #include #include #include @@ -23,11 +21,13 @@ #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/PostProcessing.h" #include "VideoBackends/Vulkan/RasterFont.h" +#include "VideoBackends/Vulkan/Renderer.h" #include "VideoBackends/Vulkan/StagingTexture2D.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/SwapChain.h" #include "VideoBackends/Vulkan/TextureCache.h" #include "VideoBackends/Vulkan/Util.h" +#include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoCommon/AVIDump.h" @@ -626,7 +626,7 @@ void Renderer::TransitionBuffersForSwap(const TargetRectangle& scaled_rect, for (u32 i = 0; i < xfb_count; i++) { const XFBSource* xfb_source = static_cast(xfb_sources[i]); - xfb_source->GetTexture()->GetTexture()->TransitionToLayout( + xfb_source->GetTexture()->GetRawTexIdentifier()->TransitionToLayout( command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } } @@ -687,7 +687,8 @@ void Renderer::DrawVirtualXFB(VkRenderPass render_pass, const TargetRectangle& t 2; source_rect.right -= Renderer::EFBToScaledX(fb_stride - fb_width); - BlitScreen(render_pass, draw_rect, source_rect, xfb_source->GetTexture()->GetTexture()); + BlitScreen(render_pass, draw_rect, source_rect, + xfb_source->GetTexture()->GetRawTexIdentifier()); } } @@ -701,7 +702,8 @@ void Renderer::DrawRealXFB(VkRenderPass render_pass, const TargetRectangle& targ TargetRectangle source_rect = xfb_source->sourceRc; TargetRectangle draw_rect = target_rect; source_rect.right -= fb_stride - fb_width; - BlitScreen(render_pass, draw_rect, source_rect, xfb_source->GetTexture()->GetTexture()); + BlitScreen(render_pass, draw_rect, source_rect, + xfb_source->GetTexture()->GetRawTexIdentifier()); } } diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index 737a05c280..30d251d343 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -9,7 +9,6 @@ #include #include -#include "Common/Align.h" #include "Common/Assert.h" #include "Common/CommonFuncs.h" #include "Common/Logging/Log.h" @@ -19,15 +18,16 @@ #include "VideoBackends/Vulkan/FramebufferManager.h" #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/Renderer.h" -#include "VideoBackends/Vulkan/StagingTexture2D.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/Texture2D.h" #include "VideoBackends/Vulkan/TextureConverter.h" #include "VideoBackends/Vulkan/Util.h" +#include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoCommon/ImageWrite.h" +#include "VideoCommon/TextureConfig.h" namespace Vulkan { @@ -42,6 +42,21 @@ TextureCache::~TextureCache() TextureCache::DeleteShaders(); } +VkShaderModule TextureCache::GetCopyShader() const +{ + return m_copy_shader; +} + +VkRenderPass TextureCache::GetTextureCopyRenderPass() const +{ + return m_render_pass; +} + +StreamBuffer* TextureCache::GetTextureUploadBuffer() const +{ + return m_texture_upload_buffer.get(); +} + TextureCache* TextureCache::GetInstance() { return static_cast(g_texture_cache.get()); @@ -80,19 +95,20 @@ bool TextureCache::Initialize() return true; } -void TextureCache::ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase* base_unconverted, - void* palette, TlutFormat format) +void TextureCache::ConvertTexture(TCacheEntry* destination, TCacheEntry* source, void* palette, + TlutFormat format) { - TCacheEntry* entry = static_cast(base_entry); - TCacheEntry* unconverted = static_cast(base_unconverted); - - m_texture_converter->ConvertTexture(entry, unconverted, m_render_pass, palette, format); + m_texture_converter->ConvertTexture(destination, source, m_render_pass, palette, format); // Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound. - unconverted->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - entry->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + static_cast(source->texture.get()) + ->GetRawTexIdentifier() + ->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + static_cast(destination->texture.get()) + ->GetRawTexIdentifier() + ->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } void TextureCache::CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_width, @@ -136,30 +152,12 @@ void TextureCache::CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_widt src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), original_layout); } -void TextureCache::CopyRectangleFromTexture(TCacheEntry* dst_texture, - const MathUtil::Rectangle& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle& src_rect) -{ - // Fast path when not scaling the image. - if (src_rect.GetWidth() == dst_rect.GetWidth() && src_rect.GetHeight() == dst_rect.GetHeight()) - CopyTextureRectangle(dst_texture, dst_rect, src_texture, src_rect); - else - ScaleTextureRectangle(dst_texture, dst_rect, src_texture, src_rect); - - // 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); - dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); -} - bool TextureCache::SupportsGPUTextureDecode(TextureFormat format, TlutFormat palette_format) { return m_texture_converter->SupportsTextureDecoding(format, palette_format); } -void TextureCache::DecodeTextureOnGPU(TCacheEntryBase* entry, u32 dst_level, const u8* data, +void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data, size_t data_size, TextureFormat format, u32 width, u32 height, u32 aligned_width, u32 aligned_height, u32 row_stride, const u8* palette, TlutFormat palette_format) @@ -167,144 +165,22 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntryBase* entry, u32 dst_level, con // Group compute shader dispatches together in the init command buffer. That way we don't have to // pay a penalty for switching from graphics->compute, or end/restart our render pass. VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentInitCommandBuffer(); - m_texture_converter->DecodeTexture(command_buffer, static_cast(entry), dst_level, - data, data_size, format, width, height, aligned_width, - aligned_height, row_stride, palette, palette_format); + m_texture_converter->DecodeTexture(command_buffer, entry, dst_level, data, data_size, format, + width, height, aligned_width, aligned_height, row_stride, + palette, palette_format); // Last mip level? Ensure the texture is ready for use. - if (dst_level == (entry->config.levels - 1)) + if (dst_level == (entry->GetNumLevels() - 1)) { - static_cast(entry)->GetTexture()->TransitionToLayout( - command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + static_cast(entry->texture.get()) + ->GetRawTexIdentifier() + ->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } } -void TextureCache::CopyTextureRectangle(TCacheEntry* dst_texture, - const MathUtil::Rectangle& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle& src_rect) +std::unique_ptr TextureCache::CreateTexture(const TextureConfig& config) { - _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"); - - _assert_msg_(VIDEO, static_cast(dst_rect.GetWidth()) <= dst_texture->config.width && - static_cast(dst_rect.GetHeight()) <= dst_texture->config.height, - "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 - dst_texture->config.layers}, - {dst_rect.left, dst_rect.top, 0}, // VkOffset3D dstOffset - {static_cast(src_rect.GetWidth()), static_cast(src_rect.GetHeight()), - 1} // VkExtent3D extent - }; - - // Must be called outside of a render pass. - StateTracker::GetInstance()->EndRenderPass(); - - src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->GetImage(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_texture->GetTexture()->GetImage(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); -} - -void TextureCache::ScaleTextureRectangle(TCacheEntry* dst_texture, - const MathUtil::Rectangle& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle& src_rect) -{ - // Can't do this within a game render pass. - StateTracker::GetInstance()->EndRenderPass(); - StateTracker::GetInstance()->SetPendingRebind(); - - // Can't render to a non-rendertarget (no framebuffer). - _assert_msg_(VIDEO, dst_texture->config.rendertarget, - "Destination texture for partial copy is not a rendertarget"); - - // Render pass expects dst_texture to be in COLOR_ATTACHMENT_OPTIMAL state. - // src_texture should already be in SHADER_READ_ONLY state, but transition in case (XFB). - src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - dst_texture->GetTexture()->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_STANDARD), m_render_pass, - g_object_cache->GetPassthroughVertexShader(), - g_object_cache->GetPassthroughGeometryShader(), m_copy_shader); - - VkRect2D region = { - {dst_rect.left, dst_rect.top}, - {static_cast(dst_rect.GetWidth()), static_cast(dst_rect.GetHeight())}}; - draw.BeginRenderPass(dst_texture->GetFramebuffer(), region); - draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler()); - draw.DrawQuad(dst_rect.left, dst_rect.top, dst_rect.GetWidth(), dst_rect.GetHeight(), - src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(), - static_cast(src_texture->GetWidth()), - static_cast(src_texture->GetHeight())); - draw.EndRenderPass(); -} - -TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config) -{ - // Determine image usage, we need to flag as an attachment if it can be used as a rendertarget. - VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT; - if (config.rendertarget) - usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - - // Allocate texture object - VkFormat vk_format = Util::GetVkFormatForHostTextureFormat(config.format); - std::unique_ptr texture = Texture2D::Create( - config.width, config.height, config.levels, config.layers, vk_format, VK_SAMPLE_COUNT_1_BIT, - VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, usage); - - if (!texture) - return nullptr; - - // If this is a render target (for efb copies), allocate a framebuffer - VkFramebuffer framebuffer = VK_NULL_HANDLE; - if (config.rendertarget) - { - VkImageView framebuffer_attachments[] = {texture->GetView()}; - VkFramebufferCreateInfo framebuffer_info = { - VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - nullptr, - 0, - m_render_pass, - static_cast(ArraySize(framebuffer_attachments)), - framebuffer_attachments, - texture->GetWidth(), - texture->GetHeight(), - texture->GetLayers()}; - - VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, - &framebuffer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); - return nullptr; - } - - // Clear render targets before use to prevent reading uninitialized memory. - VkClearColorValue clear_value = {{0.0f, 0.0f, 0.0f, 1.0f}}; - VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, config.levels, 0, - config.layers}; - texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - vkCmdClearColorImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), texture->GetImage(), - texture->GetLayout(), &clear_value, 1, &clear_range); - } - - return new TCacheEntry(config, std::move(texture), framebuffer); + return VKTexture::Create(config); } bool TextureCache::CreateRenderPasses() @@ -351,264 +227,6 @@ bool TextureCache::CreateRenderPasses() return true; } -TextureCache::TCacheEntry::TCacheEntry(const TCacheEntryConfig& config_, - std::unique_ptr texture, - VkFramebuffer framebuffer) - : TCacheEntryBase(config_), m_texture(std::move(texture)), m_framebuffer(framebuffer) -{ -} - -TextureCache::TCacheEntry::~TCacheEntry() -{ - // Texture is automatically cleaned up, however, we don't want to leave it bound. - StateTracker::GetInstance()->UnbindTexture(m_texture->GetView()); - if (m_framebuffer != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferFramebufferDestruction(m_framebuffer); -} - -void TextureCache::TCacheEntry::Load(u32 level, u32 width, u32 height, u32 row_length, - const u8* buffer, size_t buffer_size) -{ - // Can't copy data larger than the texture extents. - width = std::max(1u, std::min(width, m_texture->GetWidth() >> level)); - height = std::max(1u, std::min(height, m_texture->GetHeight() >> level)); - - // We don't care about the existing contents of the texture, so we could the image layout to - // VK_IMAGE_LAYOUT_UNDEFINED here. However, under section 2.2.1, Queue Operation of the Vulkan - // specification, it states: - // - // Command buffer submissions to a single queue must always adhere to command order and - // API order, but otherwise may overlap or execute out of order. - // - // Therefore, if a previous frame's command buffer is still sampling from this texture, and we - // overwrite it without a pipeline barrier, a texture sample could occur in parallel with the - // texture upload/copy. I'm not sure if any drivers currently take advantage of this, but we - // should insert an explicit pipeline barrier just in case (done by TransitionToLayout). - // - // We transition to TRANSFER_DST, ready for the image copy, and leave the texture in this state. - // When the last mip level is uploaded, we transition to SHADER_READ_ONLY, ready for use. This is - // because we can't transition in a render pass, and we don't necessarily know when this texture - // is going to be used. - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - // For unaligned textures, we can save some memory in the transfer buffer by skipping the rows - // that lie outside of the texture's dimensions. - 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(config.format, row_length); - size_t upload_size = source_pitch * num_rows; - std::unique_ptr temp_buffer; - VkBuffer upload_buffer; - VkDeviceSize upload_buffer_offset; - - // Does this texture data fit within the streaming buffer? - if (upload_size <= STAGING_TEXTURE_UPLOAD_THRESHOLD && - upload_size <= MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE) - { - StreamBuffer* stream_buffer = TextureCache::GetInstance()->m_texture_upload_buffer.get(); - if (!stream_buffer->ReserveMemory(upload_size, upload_alignment)) - { - // Execute the command buffer first. - WARN_LOG(VIDEO, "Executing command list while waiting for space in texture upload buffer"); - Util::ExecuteCurrentCommandsAndRestoreState(false); - - // Try allocating again. This may cause a fence wait. - if (!stream_buffer->ReserveMemory(upload_size, upload_alignment)) - PanicAlert("Failed to allocate space in texture upload buffer"); - } - - // Copy to the streaming buffer. - upload_buffer = stream_buffer->GetBuffer(); - upload_buffer_offset = stream_buffer->GetCurrentOffset(); - std::memcpy(stream_buffer->GetCurrentHostPointer(), buffer, upload_size); - stream_buffer->CommitMemory(upload_size); - } - else - { - // Create a temporary staging buffer that is destroyed after the image is copied. - temp_buffer = StagingBuffer::Create(STAGING_BUFFER_TYPE_UPLOAD, upload_size, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT); - if (!temp_buffer || !temp_buffer->Map()) - { - PanicAlert("Failed to allocate staging texture for large texture upload."); - return; - } - - upload_buffer = temp_buffer->GetBuffer(); - upload_buffer_offset = 0; - temp_buffer->Write(0, buffer, upload_size, true); - temp_buffer->Unmap(); - } - - // Copy from the streaming buffer to the actual image. - VkBufferImageCopy image_copy = { - upload_buffer_offset, // VkDeviceSize bufferOffset - row_length, // uint32_t bufferRowLength - 0, // uint32_t bufferImageHeight - {VK_IMAGE_ASPECT_COLOR_BIT, level, 0, 1}, // VkImageSubresourceLayers imageSubresource - {0, 0, 0}, // VkOffset3D imageOffset - {width, height, 1} // VkExtent3D imageExtent - }; - vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), upload_buffer, - m_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, - &image_copy); - - // Last mip level? We shouldn't be doing any further uploads now, so transition for rendering. - if (level == (config.levels - 1)) - { - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - } -} - -void TextureCache::TCacheEntry::FromRenderTarget(bool is_depth_copy, const EFBRectangle& src_rect, - bool scale_by_half, unsigned int cbufid, - const float* colmat) -{ - // A better way of doing this would be nice. - FramebufferManager* framebuffer_mgr = - static_cast(g_framebuffer_manager.get()); - TargetRectangle scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect); - - // Flush EFB pokes first, as they're expected to be included. - framebuffer_mgr->FlushEFBPokes(); - - // Has to be flagged as a render target. - _assert_(m_framebuffer != VK_NULL_HANDLE); - - // Can't be done in a render pass, since we're doing our own render pass! - VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); - StateTracker::GetInstance()->EndRenderPass(); - - // Transition EFB to shader resource before binding. - // An out-of-bounds source region is valid here, and fine for the draw (since it is converted - // to texture coordinates), but it's not valid to resolve an out-of-range rectangle. - VkRect2D region = {{scaled_src_rect.left, scaled_src_rect.top}, - {static_cast(scaled_src_rect.GetWidth()), - static_cast(scaled_src_rect.GetHeight())}}; - region = Util::ClampRect2D(region, FramebufferManager::GetInstance()->GetEFBWidth(), - FramebufferManager::GetInstance()->GetEFBHeight()); - Texture2D* src_texture; - if (is_depth_copy) - src_texture = FramebufferManager::GetInstance()->ResolveEFBDepthTexture(region); - else - src_texture = FramebufferManager::GetInstance()->ResolveEFBColorTexture(region); - - VkSampler src_sampler = - scale_by_half ? g_object_cache->GetLinearSampler() : g_object_cache->GetPointSampler(); - VkImageLayout original_layout = src_texture->GetLayout(); - src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - UtilityShaderDraw draw( - command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), - TextureCache::GetInstance()->m_render_pass, g_object_cache->GetPassthroughVertexShader(), - g_object_cache->GetPassthroughGeometryShader(), - is_depth_copy ? TextureCache::GetInstance()->m_efb_depth_to_tex_shader : - TextureCache::GetInstance()->m_efb_color_to_tex_shader); - - draw.SetPushConstants(colmat, (is_depth_copy ? sizeof(float) * 20 : sizeof(float) * 28)); - draw.SetPSSampler(0, src_texture->GetView(), src_sampler); - - VkRect2D dest_region = {{0, 0}, {m_texture->GetWidth(), m_texture->GetHeight()}}; - - draw.BeginRenderPass(m_framebuffer, dest_region); - - draw.DrawQuad(0, 0, config.width, config.height, scaled_src_rect.left, scaled_src_rect.top, 0, - scaled_src_rect.GetWidth(), scaled_src_rect.GetHeight(), - framebuffer_mgr->GetEFBWidth(), framebuffer_mgr->GetEFBHeight()); - - draw.EndRenderPass(); - - // We touched everything, so put it back. - StateTracker::GetInstance()->SetPendingRebind(); - - // Transition the EFB back to its original layout. - src_texture->TransitionToLayout(command_buffer, original_layout); - - // Ensure texture is in SHADER_READ_ONLY layout, ready for usage. - m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); -} - -void TextureCache::TCacheEntry::CopyRectangleFromTexture(const TCacheEntryBase* source, - const MathUtil::Rectangle& src_rect, - const MathUtil::Rectangle& dst_rect) -{ - const TCacheEntry* source_vk = static_cast(source); - TextureCache::GetInstance()->CopyRectangleFromTexture(this, dst_rect, source_vk->GetTexture(), - src_rect); - - // Ensure textures are ready for use again. - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - source_vk->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); -} - -void TextureCache::TCacheEntry::Bind(unsigned int stage) -{ - // Texture should always be in SHADER_READ_ONLY layout prior to use. - // This is so we don't need to transition during render passes. - _assert_(m_texture->GetLayout() == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView()); -} - -bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int level) -{ - _assert_(level < config.levels); - - // 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_(config.format == HostTextureFormat::RGBA8); - - // Determine dimensions of image we want to save. - u32 level_width = std::max(1u, config.width >> level); - u32 level_height = std::max(1u, config.height >> level); - - // Use a temporary staging texture for the download. Certainly not optimal, - // but since we have to idle the GPU anyway it doesn't really matter. - std::unique_ptr staging_texture = StagingTexture2D::Create( - STAGING_BUFFER_TYPE_READBACK, level_width, level_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. - staging_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), - m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, - level_width, level_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 (!staging_texture->Map()) - { - PanicAlert("Failed to map staging texture"); - return false; - } - - // Write texture out to file. - // It's okay to throw this texture away immediately, since we're done with it, and - // we blocked until the copy completed on the GPU anyway. - bool result = TextureToPng(reinterpret_cast(staging_texture->GetMapPointer()), - static_cast(staging_texture->GetRowStride()), filename, - level_width, level_height); - - staging_texture->Unmap(); - return result; -} - bool TextureCache::CompileShaders() { static const char COPY_SHADER_SOURCE[] = R"( @@ -723,4 +341,76 @@ void TextureCache::DeleteShaders() } } +void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, + const EFBRectangle& src_rect, bool scale_by_half, + unsigned int cbuf_id, const float* colmat) +{ + VKTexture* texture = static_cast(entry->texture.get()); + + // A better way of doing this would be nice. + FramebufferManager* framebuffer_mgr = + static_cast(g_framebuffer_manager.get()); + TargetRectangle scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect); + + // Flush EFB pokes first, as they're expected to be included. + framebuffer_mgr->FlushEFBPokes(); + + // Has to be flagged as a render target. + _assert_(texture->GetFramebuffer() != VK_NULL_HANDLE); + + // Can't be done in a render pass, since we're doing our own render pass! + VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); + StateTracker::GetInstance()->EndRenderPass(); + + // Transition EFB to shader resource before binding. + // An out-of-bounds source region is valid here, and fine for the draw (since it is converted + // to texture coordinates), but it's not valid to resolve an out-of-range rectangle. + VkRect2D region = {{scaled_src_rect.left, scaled_src_rect.top}, + {static_cast(scaled_src_rect.GetWidth()), + static_cast(scaled_src_rect.GetHeight())}}; + region = Util::ClampRect2D(region, FramebufferManager::GetInstance()->GetEFBWidth(), + FramebufferManager::GetInstance()->GetEFBHeight()); + Texture2D* src_texture; + if (is_depth_copy) + src_texture = FramebufferManager::GetInstance()->ResolveEFBDepthTexture(region); + else + src_texture = FramebufferManager::GetInstance()->ResolveEFBColorTexture(region); + + VkSampler src_sampler = + scale_by_half ? g_object_cache->GetLinearSampler() : g_object_cache->GetPointSampler(); + VkImageLayout original_layout = src_texture->GetLayout(); + src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + texture->GetRawTexIdentifier()->TransitionToLayout(command_buffer, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + UtilityShaderDraw draw(command_buffer, + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), + m_render_pass, g_object_cache->GetPassthroughVertexShader(), + g_object_cache->GetPassthroughGeometryShader(), + is_depth_copy ? m_efb_depth_to_tex_shader : m_efb_color_to_tex_shader); + + draw.SetPushConstants(colmat, (is_depth_copy ? sizeof(float) * 20 : sizeof(float) * 28)); + draw.SetPSSampler(0, src_texture->GetView(), src_sampler); + + VkRect2D dest_region = {{0, 0}, {texture->GetConfig().width, texture->GetConfig().height}}; + + draw.BeginRenderPass(texture->GetFramebuffer(), dest_region); + + draw.DrawQuad(0, 0, texture->GetConfig().width, texture->GetConfig().height, scaled_src_rect.left, + scaled_src_rect.top, 0, scaled_src_rect.GetWidth(), scaled_src_rect.GetHeight(), + framebuffer_mgr->GetEFBWidth(), framebuffer_mgr->GetEFBHeight()); + + draw.EndRenderPass(); + + // We touched everything, so put it back. + StateTracker::GetInstance()->SetPendingRebind(); + + // Transition the EFB back to its original layout. + src_texture->TransitionToLayout(command_buffer, original_layout); + + // Ensure texture is in SHADER_READ_ONLY layout, ready for usage. + texture->GetRawTexIdentifier()->TransitionToLayout(command_buffer, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +} + } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.h b/Source/Core/VideoBackends/Vulkan/TextureCache.h index 884d82ddf4..d17dd3d1e7 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.h +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.h @@ -15,34 +15,11 @@ namespace Vulkan class TextureConverter; class StateTracker; class Texture2D; +class VKTexture; class TextureCache : public TextureCacheBase { public: - struct TCacheEntry : TCacheEntryBase - { - TCacheEntry(const TCacheEntryConfig& config_, std::unique_ptr texture, - VkFramebuffer framebuffer); - ~TCacheEntry(); - - Texture2D* GetTexture() const { return m_texture.get(); } - VkFramebuffer GetFramebuffer() const { return m_framebuffer; } - void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, - size_t buffer_size) override; - void FromRenderTarget(bool is_depth_copy, const EFBRectangle& src_rect, bool scale_by_half, - unsigned int cbufid, const float* colmat) override; - void CopyRectangleFromTexture(const TCacheEntryBase* source, - const MathUtil::Rectangle& src_rect, - const MathUtil::Rectangle& dst_rect) override; - - void Bind(unsigned int stage) override; - bool Save(const std::string& filename, unsigned int level) override; - - private: - std::unique_ptr m_texture; - VkFramebuffer m_framebuffer; - }; - TextureCache(); ~TextureCache(); @@ -54,35 +31,31 @@ public: bool CompileShaders() override; void DeleteShaders() override; - TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) override; + std::unique_ptr CreateTexture(const TextureConfig& config) override; - void ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase* base_unconverted, void* palette, + void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, void* palette, TlutFormat format) override; void CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, bool is_depth_copy, const EFBRectangle& src_rect, bool scale_by_half) override; - void CopyRectangleFromTexture(TCacheEntry* dst_texture, const MathUtil::Rectangle& dst_rect, - Texture2D* src_texture, const MathUtil::Rectangle& src_rect); - bool SupportsGPUTextureDecode(TextureFormat format, TlutFormat palette_format) override; - void DecodeTextureOnGPU(TCacheEntryBase* entry, u32 dst_level, const u8* data, size_t data_size, + void DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data, size_t data_size, TextureFormat format, u32 width, u32 height, u32 aligned_width, u32 aligned_height, u32 row_stride, const u8* palette, TlutFormat palette_format) override; + VkShaderModule GetCopyShader() const; + VkRenderPass GetTextureCopyRenderPass() const; + StreamBuffer* GetTextureUploadBuffer() const; + private: bool CreateRenderPasses(); - // Copies the contents of a texture using vkCmdCopyImage - void CopyTextureRectangle(TCacheEntry* dst_texture, 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(TCacheEntry* dst_texture, const MathUtil::Rectangle& dst_rect, - Texture2D* src_texture, const MathUtil::Rectangle& src_rect); + void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect, + bool scale_by_half, unsigned int cbuf_id, const float* colmat) override; VkRenderPass m_render_pass = VK_NULL_HANDLE; diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp index 28918e85dd..d5dd9b7fd4 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp @@ -23,6 +23,7 @@ #include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/Texture2D.h" #include "VideoBackends/Vulkan/Util.h" +#include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoCommon/TextureConversionShader.h" @@ -162,8 +163,8 @@ TextureConverter::GetCommandBufferForTextureConversion(const TextureCache::TCach } } -void TextureConverter::ConvertTexture(TextureCache::TCacheEntry* dst_entry, - TextureCache::TCacheEntry* src_entry, +void TextureConverter::ConvertTexture(TextureCacheBase::TCacheEntry* dst_entry, + TextureCacheBase::TCacheEntry* src_entry, VkRenderPass render_pass, const void* palette, TlutFormat palette_format) { @@ -174,8 +175,11 @@ void TextureConverter::ConvertTexture(TextureCache::TCacheEntry* dst_entry, int pad[2]; }; + VKTexture* source_texture = static_cast(src_entry->texture.get()); + VKTexture* destination_texture = static_cast(dst_entry->texture.get()); + _assert_(static_cast(palette_format) < NUM_PALETTE_CONVERSION_SHADERS); - _assert_(dst_entry->config.rendertarget); + _assert_(destination_texture->GetConfig().rendertarget); // We want to align to 2 bytes (R16) or the device's texel buffer alignment, whichever is greater. size_t palette_size = (src_entry->format & 0xF) == GX_TF_I4 ? 32 : 512; @@ -188,10 +192,10 @@ void TextureConverter::ConvertTexture(TextureCache::TCacheEntry* dst_entry, m_texel_buffer->CommitMemory(palette_size); VkCommandBuffer command_buffer = GetCommandBufferForTextureConversion(src_entry); - src_entry->GetTexture()->TransitionToLayout(command_buffer, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - dst_entry->GetTexture()->TransitionToLayout(command_buffer, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + source_texture->GetRawTexIdentifier()->TransitionToLayout( + command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + destination_texture->GetRawTexIdentifier()->TransitionToLayout( + command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // Bind and draw to the destination. UtilityShaderDraw draw(command_buffer, @@ -199,16 +203,17 @@ void TextureConverter::ConvertTexture(TextureCache::TCacheEntry* dst_entry, render_pass, g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE, m_palette_conversion_shaders[palette_format]); - VkRect2D region = {{0, 0}, {dst_entry->config.width, dst_entry->config.height}}; - draw.BeginRenderPass(dst_entry->GetFramebuffer(), region); + VkRect2D region = {{0, 0}, {dst_entry->GetWidth(), dst_entry->GetHeight()}}; + draw.BeginRenderPass(destination_texture->GetFramebuffer(), region); PSUniformBlock uniforms = {}; uniforms.multiplier = (src_entry->format & 0xF) == GX_TF_I4 ? 15.0f : 255.0f; uniforms.texel_buffer_offset = static_cast(palette_offset / sizeof(u16)); draw.SetPushConstants(&uniforms, sizeof(uniforms)); - draw.SetPSSampler(0, src_entry->GetTexture()->GetView(), g_object_cache->GetPointSampler()); + draw.SetPSSampler(0, source_texture->GetRawTexIdentifier()->GetView(), + g_object_cache->GetPointSampler()); draw.SetPSTexelBuffer(m_texel_buffer_view_r16_uint); - draw.SetViewportAndScissor(0, 0, dst_entry->config.width, dst_entry->config.height); + draw.SetViewportAndScissor(0, 0, dst_entry->GetWidth(), dst_entry->GetHeight()); draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4); draw.EndRenderPass(); } @@ -319,9 +324,8 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u m_encoding_download_texture->ReadTexels(0, 0, output_width, dst_height, dst_ptr, dst_stride); } -void TextureConverter::DecodeYUYVTextureFromMemory(TextureCache::TCacheEntry* dst_texture, - const void* src_ptr, u32 src_width, - u32 src_stride, u32 src_height) +void TextureConverter::DecodeYUYVTextureFromMemory(VKTexture* dst_texture, const void* src_ptr, + u32 src_width, u32 src_stride, u32 src_height) { // Copies (and our decoding step) cannot be done inside a render pass. StateTracker::GetInstance()->EndRenderPass(); @@ -356,8 +360,8 @@ void TextureConverter::DecodeYUYVTextureFromMemory(TextureCache::TCacheEntry* ds VkDeviceSize texel_buffer_offset = m_texel_buffer->GetCurrentOffset(); m_texel_buffer->CommitMemory(upload_size); - dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + dst_texture->GetRawTexIdentifier()->TransitionToLayout( + g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // We divide the offset by 4 here because we're fetching RGBA8 elements. // The stride is in RGBA8 elements, so we divide by two because our data is two bytes per pixel. @@ -422,6 +426,7 @@ void TextureConverter::DecodeTexture(VkCommandBuffer command_buffer, u32 width, u32 height, u32 aligned_width, u32 aligned_height, u32 row_stride, const u8* palette, TlutFormat palette_format) { + VKTexture* destination_texture = static_cast(entry->texture.get()); auto key = std::make_pair(format, palette_format); auto iter = m_decoding_pipelines.find(key); if (iter == m_decoding_pipelines.end()) @@ -514,15 +519,16 @@ void TextureConverter::DecodeTexture(VkCommandBuffer command_buffer, dispatcher.Dispatch(groups.first, groups.second, 1); // Copy from temporary texture to final destination. + Texture2D* vulkan_tex_identifier = destination_texture->GetRawTexIdentifier(); m_decoding_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - entry->GetTexture()->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + vulkan_tex_identifier->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkImageCopy image_copy = {{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, {0, 0, 0}, {VK_IMAGE_ASPECT_COLOR_BIT, dst_level, 0, 1}, {0, 0, 0}, {width, height, 1}}; vkCmdCopyImage(command_buffer, m_decoding_texture->GetImage(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, entry->GetTexture()->GetImage(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vulkan_tex_identifier->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); } diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.h b/Source/Core/VideoBackends/Vulkan/TextureConverter.h index 135c2c217f..3b9afee9ea 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.h +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.h @@ -20,6 +20,7 @@ namespace Vulkan { class StagingTexture2D; class Texture2D; +class VKTexture; class TextureConverter { @@ -30,8 +31,9 @@ public: bool Initialize(); // Applies palette to dst_entry, using indices from src_entry. - void ConvertTexture(TextureCache::TCacheEntry* dst_entry, TextureCache::TCacheEntry* src_entry, - VkRenderPass render_pass, const void* palette, TlutFormat palette_format); + void ConvertTexture(TextureCacheBase::TCacheEntry* dst_entry, + TextureCache::TCacheEntry* src_entry, VkRenderPass render_pass, + const void* palette, TlutFormat palette_format); // Uses an encoding shader to copy src_texture to dest_ptr. // NOTE: Executes the current command buffer. @@ -45,8 +47,8 @@ public: Texture2D* src_texture, const MathUtil::Rectangle& src_rect); // Decodes data from guest memory in XFB (YUYV) format to a RGBA format texture on the GPU. - void DecodeYUYVTextureFromMemory(TextureCache::TCacheEntry* dst_texture, const void* src_ptr, - u32 src_width, u32 src_stride, u32 src_height); + void DecodeYUYVTextureFromMemory(VKTexture* dst_texture, const void* src_ptr, u32 src_width, + u32 src_stride, u32 src_height); bool SupportsTextureDecoding(TextureFormat format, TlutFormat palette_format); void DecodeTexture(VkCommandBuffer command_buffer, TextureCache::TCacheEntry* entry, diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp index 7ce433a79e..dd676d0b6c 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.cpp +++ b/Source/Core/VideoBackends/Vulkan/Util.cpp @@ -88,20 +88,20 @@ VkFormat GetLinearFormat(VkFormat format) } } -VkFormat GetVkFormatForHostTextureFormat(HostTextureFormat format) +VkFormat GetVkFormatForHostTextureFormat(AbstractTextureFormat format) { switch (format) { - case HostTextureFormat::DXT1: + case AbstractTextureFormat::DXT1: return VK_FORMAT_BC1_RGBA_UNORM_BLOCK; - case HostTextureFormat::DXT3: + case AbstractTextureFormat::DXT3: return VK_FORMAT_BC2_UNORM_BLOCK; - case HostTextureFormat::DXT5: + case AbstractTextureFormat::DXT5: return VK_FORMAT_BC3_UNORM_BLOCK; - case HostTextureFormat::RGBA8: + case AbstractTextureFormat::RGBA8: default: return VK_FORMAT_R8G8B8A8_UNORM; } diff --git a/Source/Core/VideoBackends/Vulkan/Util.h b/Source/Core/VideoBackends/Vulkan/Util.h index 85d447cf75..79b070cbb8 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.h +++ b/Source/Core/VideoBackends/Vulkan/Util.h @@ -11,6 +11,7 @@ #include "VideoBackends/Vulkan/Constants.h" #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoCommon/RenderState.h" +#include "VideoCommon/TextureConfig.h" namespace Vulkan { @@ -27,7 +28,7 @@ u32 MakeRGBA8Color(float r, float g, float b, float a); bool IsDepthFormat(VkFormat format); bool IsCompressedFormat(VkFormat format); VkFormat GetLinearFormat(VkFormat format); -VkFormat GetVkFormatForHostTextureFormat(HostTextureFormat format); +VkFormat GetVkFormatForHostTextureFormat(AbstractTextureFormat format); u32 GetTexelSize(VkFormat format); u32 GetBlockSize(VkFormat format); diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp new file mode 100644 index 0000000000..e937f06f33 --- /dev/null +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -0,0 +1,363 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include "Common/Align.h" +#include "Common/Assert.h" +#include "Common/CommonTypes.h" +#include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" + +#include "VideoBackends/Vulkan/CommandBufferManager.h" +#include "VideoBackends/Vulkan/FramebufferManager.h" +#include "VideoBackends/Vulkan/StagingTexture2D.h" +#include "VideoBackends/Vulkan/StateTracker.h" +#include "VideoBackends/Vulkan/Texture2D.h" +#include "VideoBackends/Vulkan/Util.h" +#include "VideoBackends/Vulkan/VKTexture.h" +#include "VideoBackends/Vulkan/VulkanContext.h" + +#include "VideoCommon/ImageWrite.h" +#include "VideoCommon/TextureConfig.h" + +namespace Vulkan +{ +VKTexture::VKTexture(const TextureConfig& tex_config, std::unique_ptr texture, + VkFramebuffer framebuffer) + : AbstractTexture(tex_config), m_texture(std::move(texture)), m_framebuffer(framebuffer) +{ +} + +std::unique_ptr VKTexture::Create(const TextureConfig& tex_config) +{ + // Determine image usage, we need to flag as an attachment if it can be used as a rendertarget. + VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT; + if (tex_config.rendertarget) + usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + // Allocate texture object + VkFormat vk_format = Util::GetVkFormatForHostTextureFormat(tex_config.format); + auto texture = Texture2D::Create(tex_config.width, tex_config.height, tex_config.levels, + tex_config.layers, vk_format, VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, usage); + + if (!texture) + { + return nullptr; + } + + // If this is a render target (for efb copies), allocate a framebuffer + VkFramebuffer framebuffer = VK_NULL_HANDLE; + if (tex_config.rendertarget) + { + VkImageView framebuffer_attachments[] = {texture->GetView()}; + VkFramebufferCreateInfo framebuffer_info = { + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + nullptr, + 0, + TextureCache::GetInstance()->GetTextureCopyRenderPass(), + static_cast(ArraySize(framebuffer_attachments)), + framebuffer_attachments, + texture->GetWidth(), + texture->GetHeight(), + texture->GetLayers()}; + + VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, + &framebuffer); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); + return nullptr; + } + + // Clear render targets before use to prevent reading uninitialized memory. + VkClearColorValue clear_value = {{0.0f, 0.0f, 0.0f, 1.0f}}; + VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, tex_config.levels, 0, + tex_config.layers}; + texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + vkCmdClearColorImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), texture->GetImage(), + texture->GetLayout(), &clear_value, 1, &clear_range); + } + + return std::unique_ptr(new VKTexture(tex_config, std::move(texture), framebuffer)); +} + +VKTexture::~VKTexture() +{ + // Texture is automatically cleaned up, however, we don't want to leave it bound. + StateTracker::GetInstance()->UnbindTexture(m_texture->GetView()); + if (m_framebuffer != VK_NULL_HANDLE) + g_command_buffer_mgr->DeferFramebufferDestruction(m_framebuffer); +} + +Texture2D* VKTexture::GetRawTexIdentifier() const +{ + return m_texture.get(); +} +VkFramebuffer VKTexture::GetFramebuffer() const +{ + return m_framebuffer; +} + +void VKTexture::Bind(unsigned int stage) +{ + // Texture should always be in SHADER_READ_ONLY layout prior to use. + // This is so we don't need to transition during render passes. + _assert_(m_texture->GetLayout() == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView()); +} + +bool VKTexture::Save(const std::string& filename, unsigned int level) +{ + _assert_(level < m_config.levels); + + // 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); + + // 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); + + // Use a temporary staging texture for the download. Certainly not optimal, + // but since we have to idle the GPU anyway it doesn't really matter. + std::unique_ptr staging_texture = StagingTexture2D::Create( + STAGING_BUFFER_TYPE_READBACK, level_width, level_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. + staging_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), + m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, + level_width, level_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 (!staging_texture->Map()) + { + PanicAlert("Failed to map staging texture"); + return false; + } + + // Write texture out to file. + // It's okay to throw this texture away immediately, since we're done with it, and + // we blocked until the copy completed on the GPU anyway. + bool result = TextureToPng(reinterpret_cast(staging_texture->GetMapPointer()), + static_cast(staging_texture->GetRowStride()), filename, + level_width, level_height); + + staging_texture->Unmap(); + return result; +} + +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"); + + _assert_msg_(VIDEO, static_cast(dst_rect.GetWidth()) <= m_config.width && + static_cast(dst_rect.GetHeight()) <= m_config.height, + "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 + }; + + // Must be called outside of a render pass. + StateTracker::GetInstance()->EndRenderPass(); + + src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + 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); +} + +void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle& dst_rect, + Texture2D* src_texture, + const MathUtil::Rectangle& src_rect) +{ + // Can't do this within a game render pass. + StateTracker::GetInstance()->EndRenderPass(); + StateTracker::GetInstance()->SetPendingRebind(); + + // Can't render to a non-rendertarget (no framebuffer). + _assert_msg_(VIDEO, m_config.rendertarget, + "Destination texture for partial copy is not a rendertarget"); + + // Render pass expects dst_texture to be in COLOR_ATTACHMENT_OPTIMAL state. + // src_texture should already be in SHADER_READ_ONLY state, but transition in case (XFB). + 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_COLOR_ATTACHMENT_OPTIMAL); + + UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), + TextureCache::GetInstance()->GetTextureCopyRenderPass(), + g_object_cache->GetPassthroughVertexShader(), + g_object_cache->GetPassthroughGeometryShader(), + TextureCache::GetInstance()->GetCopyShader()); + + VkRect2D region = { + {dst_rect.left, dst_rect.top}, + {static_cast(dst_rect.GetWidth()), static_cast(dst_rect.GetHeight())}}; + draw.BeginRenderPass(m_framebuffer, region); + draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler()); + draw.DrawQuad(dst_rect.left, dst_rect.top, dst_rect.GetWidth(), dst_rect.GetHeight(), + src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(), + 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); + m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +} + +void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, + size_t buffer_size) +{ + // Can't copy data larger than the texture extents. + width = std::max(1u, std::min(width, m_texture->GetWidth() >> level)); + height = std::max(1u, std::min(height, m_texture->GetHeight() >> level)); + + // We don't care about the existing contents of the texture, so we could the image layout to + // VK_IMAGE_LAYOUT_UNDEFINED here. However, under section 2.2.1, Queue Operation of the Vulkan + // specification, it states: + // + // Command buffer submissions to a single queue must always adhere to command order and + // API order, but otherwise may overlap or execute out of order. + // + // Therefore, if a previous frame's command buffer is still sampling from this texture, and we + // overwrite it without a pipeline barrier, a texture sample could occur in parallel with the + // texture upload/copy. I'm not sure if any drivers currently take advantage of this, but we + // should insert an explicit pipeline barrier just in case (done by TransitionToLayout). + // + // We transition to TRANSFER_DST, ready for the image copy, and leave the texture in this state. + // When the last mip level is uploaded, we transition to SHADER_READ_ONLY, ready for use. This is + // because we can't transition in a render pass, and we don't necessarily know when this texture + // is going to be used. + m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // For unaligned textures, we can save some memory in the transfer buffer by skipping the rows + // that lie outside of the texture's dimensions. + 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 upload_size = source_pitch * num_rows; + std::unique_ptr temp_buffer; + VkBuffer upload_buffer; + VkDeviceSize upload_buffer_offset; + + // Does this texture data fit within the streaming buffer? + if (upload_size <= STAGING_TEXTURE_UPLOAD_THRESHOLD && + upload_size <= MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE) + { + StreamBuffer* stream_buffer = TextureCache::GetInstance()->GetTextureUploadBuffer(); + if (!stream_buffer->ReserveMemory(upload_size, upload_alignment)) + { + // Execute the command buffer first. + WARN_LOG(VIDEO, "Executing command list while waiting for space in texture upload buffer"); + Util::ExecuteCurrentCommandsAndRestoreState(false); + + // Try allocating again. This may cause a fence wait. + if (!stream_buffer->ReserveMemory(upload_size, upload_alignment)) + PanicAlert("Failed to allocate space in texture upload buffer"); + } + // Copy to the streaming buffer. + upload_buffer = stream_buffer->GetBuffer(); + upload_buffer_offset = stream_buffer->GetCurrentOffset(); + std::memcpy(stream_buffer->GetCurrentHostPointer(), buffer, upload_size); + stream_buffer->CommitMemory(upload_size); + } + else + { + // Create a temporary staging buffer that is destroyed after the image is copied. + temp_buffer = StagingBuffer::Create(STAGING_BUFFER_TYPE_UPLOAD, upload_size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT); + if (!temp_buffer || !temp_buffer->Map()) + { + PanicAlert("Failed to allocate staging texture for large texture upload."); + return; + } + + upload_buffer = temp_buffer->GetBuffer(); + upload_buffer_offset = 0; + temp_buffer->Write(0, buffer, upload_size, true); + temp_buffer->Unmap(); + } + + // Copy from the streaming buffer to the actual image. + VkBufferImageCopy image_copy = { + upload_buffer_offset, // VkDeviceSize bufferOffset + row_length, // uint32_t bufferRowLength + 0, // uint32_t bufferImageHeight + {VK_IMAGE_ASPECT_COLOR_BIT, level, 0, 1}, // VkImageSubresourceLayers imageSubresource + {0, 0, 0}, // VkOffset3D imageOffset + {width, height, 1} // VkExtent3D imageExtent + }; + vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), upload_buffer, + m_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, + &image_copy); + + // Last mip level? We shouldn't be doing any further uploads now, so transition for rendering. + if (level == (m_config.levels - 1)) + { + m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } +} + +} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.h b/Source/Core/VideoBackends/Vulkan/VKTexture.h new file mode 100644 index 0000000000..0983703db5 --- /dev/null +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.h @@ -0,0 +1,55 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/VideoCommon.h" + +namespace Vulkan +{ +class Texture2D; + +class VKTexture final : public AbstractTexture +{ +public: + VKTexture() = delete; + ~VKTexture(); + + void Bind(unsigned int stage) override; + bool Save(const std::string& filename, unsigned int level) 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 Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, + size_t buffer_size) override; + + Texture2D* GetRawTexIdentifier() const; + VkFramebuffer GetFramebuffer() const; + + static std::unique_ptr Create(const TextureConfig& tex_config); + +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::unique_ptr m_texture; + VkFramebuffer m_framebuffer; +}; + +} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj index 531d2da843..f6ec82bb66 100644 --- a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj +++ b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj @@ -57,6 +57,7 @@ + @@ -83,6 +84,7 @@ + diff --git a/Source/Core/VideoCommon/AbstractTexture.cpp b/Source/Core/VideoCommon/AbstractTexture.cpp new file mode 100644 index 0000000000..1b6fac8bff --- /dev/null +++ b/Source/Core/VideoCommon/AbstractTexture.cpp @@ -0,0 +1,44 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "VideoCommon/AbstractTexture.h" + +AbstractTexture::AbstractTexture(const TextureConfig& c) : m_config(c) +{ +} + +AbstractTexture::~AbstractTexture() = default; + +bool AbstractTexture::Save(const std::string& filename, unsigned int level) +{ + return false; +} + +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) +{ + switch (format) + { + case AbstractTextureFormat::DXT1: + return static_cast(std::max(1u, row_length / 4)) * 8; + case AbstractTextureFormat::DXT3: + case AbstractTextureFormat::DXT5: + return static_cast(std::max(1u, row_length / 4)) * 16; + case AbstractTextureFormat::RGBA8: + default: + return static_cast(row_length) * 4; + } +} + +const TextureConfig AbstractTexture::GetConfig() const +{ + return m_config; +} diff --git a/Source/Core/VideoCommon/AbstractTexture.h b/Source/Core/VideoCommon/AbstractTexture.h new file mode 100644 index 0000000000..2b06d9c401 --- /dev/null +++ b/Source/Core/VideoCommon/AbstractTexture.h @@ -0,0 +1,33 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/CommonTypes.h" +#include "Common/MathUtil.h" +#include "VideoCommon/TextureConfig.h" +#include "VideoCommon/VideoCommon.h" + +class AbstractTexture +{ +public: + explicit AbstractTexture(const TextureConfig& c); + virtual ~AbstractTexture(); + virtual void Bind(unsigned int stage) = 0; + virtual bool Save(const std::string& filename, unsigned int level); + + virtual void CopyRectangleFromTexture(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); + + const TextureConfig GetConfig() const; + +protected: + const TextureConfig m_config; +}; diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index a50cd2774a..9eeb9f93bb 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -1,4 +1,5 @@ set(SRCS + AbstractTexture.cpp AsyncRequests.cpp BoundingBox.cpp BPFunctions.cpp @@ -30,6 +31,7 @@ set(SRCS RenderState.cpp Statistics.cpp TextureCacheBase.cpp + TextureConfig.cpp TextureConversionShader.cpp TextureDecoder_Common.cpp VertexLoader.cpp diff --git a/Source/Core/VideoCommon/HiresTextures.cpp b/Source/Core/VideoCommon/HiresTextures.cpp index 95a70efc59..7b4a1d387c 100644 --- a/Source/Core/VideoCommon/HiresTextures.cpp +++ b/Source/Core/VideoCommon/HiresTextures.cpp @@ -533,7 +533,7 @@ bool HiresTexture::LoadTexture(Level& level, const std::vector& buffer) // Images loaded by SOIL are converted to RGBA. level.width = static_cast(width); level.height = static_cast(height); - level.format = HostTextureFormat::RGBA8; + level.format = AbstractTextureFormat::RGBA8; level.data = ImageDataPointer(data, SOIL_free_image_data); level.row_length = level.width; level.data_size = static_cast(level.row_length) * 4 * level.height; @@ -555,7 +555,7 @@ HiresTexture::~HiresTexture() { } -HostTextureFormat HiresTexture::GetFormat() const +AbstractTextureFormat HiresTexture::GetFormat() const { return m_levels.at(0).format; } diff --git a/Source/Core/VideoCommon/HiresTextures.h b/Source/Core/VideoCommon/HiresTextures.h index 30bb96d40c..3e7625c993 100644 --- a/Source/Core/VideoCommon/HiresTextures.h +++ b/Source/Core/VideoCommon/HiresTextures.h @@ -9,7 +9,7 @@ #include #include "Common/CommonTypes.h" -#include "VideoCommon/VideoCommon.h" +#include "VideoCommon/TextureConfig.h" class HiresTexture { @@ -32,13 +32,13 @@ public: ~HiresTexture(); - HostTextureFormat GetFormat() const; + AbstractTextureFormat GetFormat() const; struct Level { Level(); ImageDataPointer data; - HostTextureFormat format = HostTextureFormat::RGBA8; + AbstractTextureFormat format = AbstractTextureFormat::RGBA8; u32 width = 0; u32 height = 0; u32 row_length = 0; diff --git a/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp b/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp index 27af3b6828..5b9bbeced9 100644 --- a/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp +++ b/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp @@ -142,7 +142,7 @@ struct DDSLoadInfo u32 width = 0; u32 height = 0; u32 mip_count = 0; - HostTextureFormat format = HostTextureFormat::RGBA8; + AbstractTextureFormat format = AbstractTextureFormat::RGBA8; size_t first_mip_offset = 0; size_t first_mip_size = 0; u32 first_mip_row_length = 0; @@ -300,21 +300,21 @@ bool ParseDDSHeader(File::IOFile& file, DDSLoadInfo* info) // In the future, this could be extended, but these isn't much benefit in doing so currently. if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '1') || dxt10_format == 71) { - info->format = HostTextureFormat::DXT1; + info->format = AbstractTextureFormat::DXT1; info->block_size = 4; info->bytes_per_block = 8; needs_s3tc = true; } else if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '3') || dxt10_format == 74) { - info->format = HostTextureFormat::DXT3; + info->format = AbstractTextureFormat::DXT3; info->block_size = 4; info->bytes_per_block = 16; needs_s3tc = true; } else if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '5') || dxt10_format == 77) { - info->format = HostTextureFormat::DXT5; + info->format = AbstractTextureFormat::DXT5; info->block_size = 4; info->bytes_per_block = 16; needs_s3tc = true; @@ -353,7 +353,7 @@ bool ParseDDSHeader(File::IOFile& file, DDSLoadInfo* info) } // All these formats are RGBA, just with byte swapping. - info->format = HostTextureFormat::RGBA8; + info->format = AbstractTextureFormat::RGBA8; info->block_size = 1; info->bytes_per_block = header.ddspf.dwRGBBitCount / 8; } diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 72978f6752..c61fa1fe5e 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -39,35 +39,18 @@ static const u64 TEXHASH_INVALID = 0; // Sonic the Fighters (inside Sonic Gems Collection) loops a 64 frames animation static const int TEXTURE_KILL_THRESHOLD = 64; static const int TEXTURE_POOL_KILL_THRESHOLD = 3; -static const int FRAMECOUNT_INVALID = 0; std::unique_ptr g_texture_cache; -TextureCacheBase::TCacheEntryBase::~TCacheEntryBase() +TextureCacheBase::TCacheEntry::TCacheEntry(std::unique_ptr tex) + : texture(std::move(tex)) { } -bool TextureCacheBase::IsCompressedHostTextureFormat(HostTextureFormat format) +TextureCacheBase::TCacheEntry::~TCacheEntry() { - // This will need to be changed if we add any other uncompressed formats. - return format != HostTextureFormat::RGBA8; -} - -size_t TextureCacheBase::CalculateHostTextureLevelPitch(HostTextureFormat format, u32 row_length) -{ - switch (format) - { - case HostTextureFormat::DXT1: - return static_cast(std::max(1u, row_length / 4)) * 8; - - case HostTextureFormat::DXT3: - case HostTextureFormat::DXT5: - return static_cast(std::max(1u, row_length / 4)) * 16; - - case HostTextureFormat::RGBA8: - default: - return static_cast(row_length) * 4; - } + for (auto& reference : references) + reference->references.erase(this); } void TextureCacheBase::CheckTempSize(size_t required_size) @@ -106,10 +89,6 @@ void TextureCacheBase::Invalidate() textures_by_address.clear(); textures_by_hash.clear(); - for (auto& rt : texture_pool) - { - delete rt.second; - } texture_pool.clear(); } @@ -197,13 +176,12 @@ void TextureCacheBase::Cleanup(int _frameCount) TexPool::iterator tcend2 = texture_pool.end(); while (iter2 != tcend2) { - if (iter2->second->frameCount == FRAMECOUNT_INVALID) + if (iter2->second.frameCount == FRAMECOUNT_INVALID) { - iter2->second->frameCount = _frameCount; + iter2->second.frameCount = _frameCount; } - if (_frameCount > TEXTURE_POOL_KILL_THRESHOLD + iter2->second->frameCount) + if (_frameCount > TEXTURE_POOL_KILL_THRESHOLD + iter2->second.frameCount) { - delete iter2->second; iter2 = texture_pool.erase(iter2); } else @@ -213,7 +191,7 @@ void TextureCacheBase::Cleanup(int _frameCount) } } -bool TextureCacheBase::TCacheEntryBase::OverlapsMemoryRange(u32 range_address, u32 range_size) const +bool TextureCacheBase::TCacheEntry::OverlapsMemoryRange(u32 range_address, u32 range_size) const { if (addr + size_in_bytes <= range_address) return false; @@ -236,14 +214,14 @@ void TextureCacheBase::SetBackupConfig(const VideoConfig& config) backup_config.gpu_texture_decoding = config.bEnableGPUTextureDecoding; } -TextureCacheBase::TCacheEntryBase* TextureCacheBase::ApplyPaletteToEntry(TCacheEntryBase* entry, - u8* palette, u32 tlutfmt) +TextureCacheBase::TCacheEntry* TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, + u8* palette, u32 tlutfmt) { - TCacheEntryConfig new_config = entry->config; + TextureConfig new_config = entry->texture->GetConfig(); new_config.levels = 1; new_config.rendertarget = true; - TCacheEntryBase* decoded_entry = AllocateTexture(new_config); + TCacheEntry* decoded_entry = AllocateCacheEntry(new_config); if (!decoded_entry) return nullptr; @@ -259,10 +237,10 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::ApplyPaletteToEntry(TCacheE return decoded_entry; } -void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntryBase** entry, - u32 new_width, u32 new_height) +void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntry* entry, u32 new_width, + u32 new_height) { - if ((*entry)->config.width == new_width && (*entry)->config.height == new_height) + if (entry->GetWidth() == new_width && entry->GetHeight() == new_height) { return; } @@ -274,41 +252,24 @@ void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntryBas return; } - TextureCacheBase::TCacheEntryConfig newconfig; + TextureConfig newconfig; newconfig.width = new_width; newconfig.height = new_height; - newconfig.layers = (*entry)->config.layers; + newconfig.layers = entry->GetNumLayers(); newconfig.rendertarget = true; - TCacheEntryBase* newentry = AllocateTexture(newconfig); - if (newentry) + std::unique_ptr new_texture = AllocateTexture(newconfig); + if (new_texture) { - newentry->SetGeneralParameters((*entry)->addr, (*entry)->size_in_bytes, (*entry)->format); - newentry->SetDimensions((*entry)->native_width, (*entry)->native_height, 1); - newentry->SetHashes((*entry)->base_hash, (*entry)->hash); - newentry->frameCount = frameCount; - newentry->is_efb_copy = (*entry)->is_efb_copy; - MathUtil::Rectangle srcrect, dstrect; - srcrect.left = 0; - srcrect.top = 0; - srcrect.right = (*entry)->config.width; - srcrect.bottom = (*entry)->config.height; - dstrect.left = 0; - dstrect.top = 0; - dstrect.right = new_width; - dstrect.bottom = new_height; - newentry->CopyRectangleFromTexture(*entry, srcrect, dstrect); + new_texture->CopyRectangleFromTexture(entry->texture.get(), + entry->texture->GetConfig().GetRect(), + new_texture->GetConfig().GetRect()); + entry->texture.swap(new_texture); - // Keep track of the pointer for textures_by_hash - if ((*entry)->textures_by_hash_iter != textures_by_hash.end()) - { - newentry->textures_by_hash_iter = textures_by_hash.emplace((*entry)->hash, newentry); - } - - InvalidateTexture(GetTexCacheIter(*entry)); - - *entry = newentry; - textures_by_address.emplace((*entry)->addr, *entry); + auto config = new_texture->GetConfig(); + // At this point new_texture has the old texture in it, + // we can potentially reuse this, so let's move it back to the pool + texture_pool.emplace(config, TexPoolEntry(std::move(new_texture))); } else { @@ -316,9 +277,8 @@ void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntryBas } } -TextureCacheBase::TCacheEntryBase* -TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* palette, - u32 tlutfmt) +TextureCacheBase::TCacheEntry* +TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* palette, u32 tlutfmt) { // If the flag may_have_overlapping_textures is cleared, there are no overlapping EFB copies, // which aren't applied already. It is set for new textures, and for the affected range @@ -346,7 +306,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* auto iter = FindOverlappingTextures(entry_to_update->addr, entry_to_update->size_in_bytes); while (iter.first != iter.second) { - TCacheEntryBase* entry = iter.first->second; + TCacheEntry* entry = iter.first->second; if (entry != entry_to_update && entry->IsEfbCopy() && entry->references.count(entry_to_update) == 0 && entry->OverlapsMemoryRange(entry_to_update->addr, entry_to_update->size_in_bytes) && @@ -356,7 +316,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* { if (isPaletteTexture) { - TCacheEntryBase* decoded_entry = ApplyPaletteToEntry(entry, palette, tlutfmt); + TCacheEntry* decoded_entry = ApplyPaletteToEntry(entry, palette, tlutfmt); if (decoded_entry) { // Link the efb copy with the partially updated texture, so we won't apply this partial @@ -404,15 +364,14 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* std::min(entry->native_height - src_y, entry_to_update->native_height - dst_y); // If one of the textures is scaled, scale both with the current efb scaling factor - if (entry_to_update->native_width != entry_to_update->config.width || - entry_to_update->native_height != entry_to_update->config.height || - entry->native_width != entry->config.width || - entry->native_height != entry->config.height) + if (entry_to_update->native_width != entry_to_update->GetWidth() || + entry_to_update->native_height != entry_to_update->GetHeight() || + entry->native_width != entry->GetWidth() || entry->native_height != entry->GetHeight()) { - ScaleTextureCacheEntryTo(&entry_to_update, + ScaleTextureCacheEntryTo(entry_to_update, g_renderer->EFBToScaledX(entry_to_update->native_width), g_renderer->EFBToScaledY(entry_to_update->native_height)); - ScaleTextureCacheEntryTo(&entry, g_renderer->EFBToScaledX(entry->native_width), + ScaleTextureCacheEntryTo(entry, g_renderer->EFBToScaledX(entry->native_width), g_renderer->EFBToScaledY(entry->native_height)); src_x = g_renderer->EFBToScaledX(src_x); @@ -432,7 +391,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* dstrect.top = dst_y; dstrect.right = (dst_x + copy_width); dstrect.bottom = (dst_y + copy_height); - entry_to_update->CopyRectangleFromTexture(entry, srcrect, dstrect); + entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, dstrect); if (isPaletteTexture) { @@ -460,7 +419,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* return entry_to_update; } -void TextureCacheBase::DumpTexture(TCacheEntryBase* entry, std::string basename, unsigned int level) +void TextureCacheBase::DumpTexture(TCacheEntry* entry, std::string basename, unsigned int level) { std::string szDir = File::GetUserPath(D_DUMPTEXTURES_IDX) + SConfig::GetInstance().GetGameID(); @@ -475,7 +434,7 @@ void TextureCacheBase::DumpTexture(TCacheEntryBase* entry, std::string basename, std::string filename = szDir + "/" + basename + ".png"; if (!File::Exists(filename)) - entry->Save(filename, level); + entry->texture->Save(filename, level); } static u32 CalculateLevelSize(u32 level_0_size, u32 level) @@ -484,8 +443,7 @@ static u32 CalculateLevelSize(u32 level_0_size, u32 level) } // Used by TextureCacheBase::Load -TextureCacheBase::TCacheEntryBase* TextureCacheBase::ReturnEntry(unsigned int stage, - TCacheEntryBase* entry) +TextureCacheBase::TCacheEntry* TextureCacheBase::ReturnEntry(unsigned int stage, TCacheEntry* entry) { entry->frameCount = FRAMECOUNT_INVALID; bound_textures[stage] = entry; @@ -500,7 +458,7 @@ void TextureCacheBase::BindTextures() for (size_t i = 0; i < bound_textures.size(); ++i) { if (bound_textures[i]) - bound_textures[i]->Bind(static_cast(i)); + bound_textures[i]->texture->Bind(static_cast(i)); } } @@ -509,7 +467,7 @@ void TextureCacheBase::UnbindTextures() bound_textures.fill(nullptr); } -TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) +TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage) { const FourTexUnits& tex = bpmem.tex[stage >> 2]; const u32 id = stage & 3; @@ -651,7 +609,7 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) while (iter != iter_range.second) { - TCacheEntryBase* entry = iter->second; + TCacheEntry* entry = iter->second; // Do not load strided EFB copies, they are not meant to be used directly if (entry->IsEfbCopy() && entry->native_width == nativeW && entry->native_height == nativeH && entry->memory_stride == entry->BytesPerRow()) @@ -714,7 +672,7 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) if (unconverted_copy != textures_by_address.end()) { - TCacheEntryBase* decoded_entry = + TCacheEntry* decoded_entry = ApplyPaletteToEntry(unconverted_copy->second, &texMem[tlutaddr], tlutfmt); if (decoded_entry) @@ -737,7 +695,7 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) TexHashCache::iterator hash_iter = hash_range.first; while (hash_iter != hash_range.second) { - TCacheEntryBase* entry = hash_iter->second; + TCacheEntry* entry = hash_iter->second; // All parameters, except the address, need to match here if (entry->format == full_format && entry->native_levels >= tex_levels && entry->native_width == nativeW && entry->native_height == nativeH) @@ -791,13 +749,13 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) !(from_tmem && texformat == GX_TF_RGBA8); // create the entry/texture - TCacheEntryConfig config; + TextureConfig config; config.width = width; config.height = height; config.levels = texLevels; - config.format = hires_tex ? hires_tex->GetFormat() : HostTextureFormat::RGBA8; + config.format = hires_tex ? hires_tex->GetFormat() : AbstractTextureFormat::RGBA8; - TCacheEntryBase* entry = AllocateTexture(config); + TCacheEntry* entry = AllocateCacheEntry(config); GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); if (!entry) @@ -807,8 +765,10 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) if (hires_tex) { const auto& level = hires_tex->m_levels[0]; - entry->Load(0, level.width, level.height, level.row_length, level.data.get(), level.data_size); + entry->texture->Load(0, level.width, level.height, level.row_length, level.data.get(), + level.data_size); } + if (!hires_tex && decode_on_gpu) { u32 row_stride = bytes_per_block * (expandedWidth / bsw); @@ -832,7 +792,7 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) TexDecoder_DecodeRGBA8FromTmem(temp, src_data, src_data_gb, expandedWidth, expandedHeight); } - entry->Load(0, width, height, expandedWidth, temp, decoded_texture_size); + entry->texture->Load(0, width, height, expandedWidth, temp, decoded_texture_size); } iter = textures_by_address.emplace(address, entry); @@ -862,8 +822,8 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) for (u32 level_index = 1; level_index != texLevels; ++level_index) { const auto& level = hires_tex->m_levels[level_index]; - entry->Load(level_index, level.width, level.height, level.row_length, level.data.get(), - level.data_size); + entry->texture->Load(level_index, level.width, level.height, level.row_length, + level.data.get(), level.data_size); } } else @@ -905,7 +865,8 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) size_t decoded_mip_size = expanded_mip_width * sizeof(u32) * expanded_mip_height; TexDecoder_Decode(temp, mip_src_data, expanded_mip_width, expanded_mip_height, texformat, tlut, (TlutFormat)tlutfmt); - entry->Load(level, mip_width, mip_height, expanded_mip_width, temp, decoded_mip_size); + entry->texture->Load(level, mip_width, mip_height, expanded_mip_width, temp, + decoded_mip_size); } mip_src_data += mip_size; @@ -1357,7 +1318,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo auto iter = FindOverlappingTextures(dstAddr, covered_range); while (iter.first != iter.second) { - TCacheEntryBase* entry = iter.first->second; + TCacheEntry* entry = iter.first->second; if (entry->OverlapsMemoryRange(dstAddr, covered_range)) { if (invalidate_textures) @@ -1373,13 +1334,13 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo if (copy_to_vram) { // create the texture - TCacheEntryConfig config; + TextureConfig config; config.rendertarget = true; config.width = scaled_tex_w; config.height = scaled_tex_h; config.layers = FramebufferManagerBase::GetEFBLayers(); - TCacheEntryBase* entry = AllocateTexture(config); + TCacheEntry* entry = AllocateCacheEntry(config); if (entry) { @@ -1390,7 +1351,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo entry->SetEfbCopy(dstStride); entry->is_custom_tex = false; - entry->FromRenderTarget(is_depth_copy, srcRect, scaleByHalf, cbufid, colmat); + CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, cbufid, colmat); u64 hash = entry->CalculateHash(); entry->SetHashes(hash, hash); @@ -1398,9 +1359,10 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo if (g_ActiveConfig.bDumpEFBTarget) { static int count = 0; - entry->Save(StringFromFormat("%sefb_frame_%i.png", - File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(), count++), - 0); + entry->texture->Save(StringFromFormat("%sefb_frame_%i.png", + File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(), + count++), + 0); } textures_by_address.emplace(dstAddr, entry); @@ -1408,14 +1370,26 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo } } -TextureCacheBase::TCacheEntryBase* -TextureCacheBase::AllocateTexture(const TCacheEntryConfig& config) +TextureCacheBase::TCacheEntry* TextureCacheBase::AllocateCacheEntry(const TextureConfig& config) +{ + std::unique_ptr texture = AllocateTexture(config); + + if (!texture) + { + return nullptr; + } + TCacheEntry* cacheEntry = new TCacheEntry(std::move(texture)); + cacheEntry->textures_by_hash_iter = textures_by_hash.end(); + return cacheEntry; +} + +std::unique_ptr TextureCacheBase::AllocateTexture(const TextureConfig& config) { TexPool::iterator iter = FindMatchingTextureFromPool(config); - TextureCacheBase::TCacheEntryBase* entry; + std::unique_ptr entry; if (iter != texture_pool.end()) { - entry = iter->second; + entry = std::move(iter->second.texture); texture_pool.erase(iter); } else @@ -1427,13 +1401,11 @@ TextureCacheBase::AllocateTexture(const TCacheEntryConfig& config) INCSTAT(stats.numTexturesCreated); } - entry->textures_by_hash_iter = textures_by_hash.end(); - entry->may_have_overlapping_textures = true; return entry; } TextureCacheBase::TexPool::iterator -TextureCacheBase::FindMatchingTextureFromPool(const TCacheEntryConfig& config) +TextureCacheBase::FindMatchingTextureFromPool(const TextureConfig& config) { // Find a texture from the pool that does not have a frameCount of FRAMECOUNT_INVALID. // This prevents a texture from being used twice in a single frame with different data, @@ -1442,13 +1414,13 @@ TextureCacheBase::FindMatchingTextureFromPool(const TCacheEntryConfig& config) // As non-render-target textures are usually static, this should not matter much. auto range = texture_pool.equal_range(config); auto matching_iter = std::find_if(range.first, range.second, [](const auto& iter) { - return iter.first.rendertarget || iter.second->frameCount != FRAMECOUNT_INVALID; + return iter.first.rendertarget || iter.second.frameCount != FRAMECOUNT_INVALID; }); return matching_iter != range.second ? matching_iter : texture_pool.end(); } TextureCacheBase::TexAddrCache::iterator -TextureCacheBase::GetTexCacheIter(TextureCacheBase::TCacheEntryBase* entry) +TextureCacheBase::GetTexCacheIter(TextureCacheBase::TCacheEntry* entry) { auto iter_range = textures_by_address.equal_range(entry->addr); TexAddrCache::iterator iter = iter_range.first; @@ -1486,7 +1458,7 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter) if (iter == textures_by_address.end()) return textures_by_address.end(); - TCacheEntryBase* entry = iter->second; + TCacheEntry* entry = iter->second; if (entry->textures_by_hash_iter != textures_by_hash.end()) { @@ -1494,15 +1466,13 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter) entry->textures_by_hash_iter = textures_by_hash.end(); } - entry->DestroyAllReferences(); - - entry->frameCount = FRAMECOUNT_INVALID; - texture_pool.emplace(entry->config, entry); + auto config = entry->texture->GetConfig(); + texture_pool.emplace(config, TexPoolEntry(std::move(entry->texture))); return textures_by_address.erase(iter); } -u32 TextureCacheBase::TCacheEntryBase::BytesPerRow() const +u32 TextureCacheBase::TCacheEntry::BytesPerRow() const { const u32 blockW = TexDecoder_GetBlockWidthInTexels(format); @@ -1517,7 +1487,7 @@ u32 TextureCacheBase::TCacheEntryBase::BytesPerRow() const return numBlocksX * bytes_per_block; } -u32 TextureCacheBase::TCacheEntryBase::NumBlocksY() const +u32 TextureCacheBase::TCacheEntry::NumBlocksY() const { u32 blockH = TexDecoder_GetBlockHeightInTexels(format); // Round up source height to multiple of block size @@ -1526,7 +1496,7 @@ u32 TextureCacheBase::TCacheEntryBase::NumBlocksY() const return actualHeight / blockH; } -void TextureCacheBase::TCacheEntryBase::SetEfbCopy(u32 stride) +void TextureCacheBase::TCacheEntry::SetEfbCopy(u32 stride) { is_efb_copy = true; memory_stride = stride; @@ -1536,7 +1506,7 @@ void TextureCacheBase::TCacheEntryBase::SetEfbCopy(u32 stride) size_in_bytes = memory_stride * NumBlocksY(); } -u64 TextureCacheBase::TCacheEntryBase::CalculateHash() const +u64 TextureCacheBase::TCacheEntry::CalculateHash() const { u8* ptr = Memory::GetPointer(addr); if (memory_stride == BytesPerRow()) diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index 7f2491a5c7..23f25f48fc 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -12,7 +12,9 @@ #include #include "Common/CommonTypes.h" +#include "VideoCommon/AbstractTexture.h" #include "VideoCommon/BPMemory.h" +#include "VideoCommon/TextureConfig.h" #include "VideoCommon/TextureDecoder.h" #include "VideoCommon/VideoCommon.h" @@ -20,40 +22,14 @@ struct VideoConfig; class TextureCacheBase { +private: + static const int FRAMECOUNT_INVALID = 0; + public: - struct TCacheEntryConfig + struct TCacheEntry { - constexpr TCacheEntryConfig() = default; - - bool operator==(const TCacheEntryConfig& o) const - { - return std::tie(width, height, levels, layers, format, rendertarget) == - std::tie(o.width, o.height, o.levels, o.layers, o.format, o.rendertarget); - } - - struct Hasher : std::hash - { - size_t operator()(const TCacheEntryConfig& c) const - { - u64 id = (u64)c.rendertarget << 63 | (u64)c.format << 50 | (u64)c.layers << 48 | - (u64)c.levels << 32 | (u64)c.height << 16 | (u64)c.width; - return std::hash::operator()(id); - } - }; - - u32 width = 0; - u32 height = 0; - u32 levels = 1; - u32 layers = 1; - HostTextureFormat format = HostTextureFormat::RGBA8; - bool rendertarget = false; - }; - - struct TCacheEntryBase - { - const TCacheEntryConfig config; - // common members + std::unique_ptr texture; u32 addr; u32 size_in_bytes; u64 base_hash; @@ -62,23 +38,27 @@ public: u32 memory_stride; bool is_efb_copy; bool is_custom_tex; - bool may_have_overlapping_textures; + bool may_have_overlapping_textures = true; unsigned int native_width, native_height; // Texture dimensions from the GameCube's point of view unsigned int native_levels; // used to delete textures which haven't been used for TEXTURE_KILL_THRESHOLD frames - int frameCount; + int frameCount = FRAMECOUNT_INVALID; // Keep an iterator to the entry in textures_by_hash, so it does not need to be searched when // removing the cache entry - std::multimap::iterator textures_by_hash_iter; + std::multimap::iterator textures_by_hash_iter; // This is used to keep track of both: // * efb copies used by this partially updated texture // * partially updated textures which refer to this efb copy - std::unordered_set references; + std::unordered_set references; + + explicit TCacheEntry(std::unique_ptr tex); + + ~TCacheEntry(); void SetGeneralParameters(u32 _addr, u32 _size, u32 _format) { @@ -103,38 +83,15 @@ public: } // This texture entry is used by the other entry as a sub-texture - void CreateReference(TCacheEntryBase* other_entry) + void CreateReference(TCacheEntry* other_entry) { // References are two-way, so they can easily be destroyed later this->references.emplace(other_entry); other_entry->references.emplace(this); } - void DestroyAllReferences() - { - for (auto& reference : references) - reference->references.erase(this); - - references.clear(); - } - void SetEfbCopy(u32 stride); - TCacheEntryBase(const TCacheEntryConfig& c) : config(c) {} - virtual ~TCacheEntryBase(); - - virtual void Bind(unsigned int stage) = 0; - virtual bool Save(const std::string& filename, unsigned int level) = 0; - - virtual void CopyRectangleFromTexture(const TCacheEntryBase* 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; - virtual void FromRenderTarget(bool is_depth_copy, const EFBRectangle& srcRect, bool scaleByHalf, - unsigned int cbufid, const float* colmat) = 0; - bool OverlapsMemoryRange(u32 range_address, u32 range_size) const; bool IsEfbCopy() const { return is_efb_copy; } @@ -142,14 +99,16 @@ public: u32 BytesPerRow() const; u64 CalculateHash() const; + + u32 GetWidth() const { return texture->GetConfig().width; } + u32 GetHeight() const { return texture->GetConfig().height; } + u32 GetNumLevels() const { return texture->GetConfig().levels; } + u32 GetNumLayers() const { return texture->GetConfig().layers; } + AbstractTextureFormat GetFormat() const { return texture->GetConfig().format; } }; virtual ~TextureCacheBase(); // needs virtual for DX11 dtor - // TODO: Move these to AbstractTexture once it is finished. - static bool IsCompressedHostTextureFormat(HostTextureFormat format); - static size_t CalculateHostTextureLevelPitch(HostTextureFormat format, u32 row_length); - void OnConfigChanged(VideoConfig& config); // Removes textures which aren't used for more than TEXTURE_KILL_THRESHOLD frames, @@ -158,8 +117,6 @@ public: void Invalidate(); - virtual TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) = 0; - virtual void CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, bool is_depth_copy, const EFBRectangle& src_rect, bool scale_by_half) = 0; @@ -167,14 +124,14 @@ public: virtual bool CompileShaders() = 0; virtual void DeleteShaders() = 0; - TCacheEntryBase* Load(const u32 stage); + TCacheEntry* Load(const u32 stage); void UnbindTextures(); virtual void BindTextures(); void CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, u32 dstStride, bool is_depth_copy, const EFBRectangle& srcRect, bool isIntensity, bool scaleByHalf); - virtual void ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, + virtual void ConvertTexture(TCacheEntry* entry, TCacheEntry* unconverted, void* palette, TlutFormat format) = 0; // Returns true if the texture data and palette formats are supported by the GPU decoder. @@ -187,7 +144,7 @@ public: // width, height are the size of the image in pixels. // aligned_width, aligned_height are the size of the image in pixels, aligned to the block size. // row_stride is the number of bytes for a row of blocks, not pixels. - virtual void DecodeTextureOnGPU(TCacheEntryBase* entry, u32 dst_level, const u8* data, + virtual void DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data, size_t data_size, TextureFormat format, u32 width, u32 height, u32 aligned_width, u32 aligned_height, u32 row_stride, const u8* palette, TlutFormat palette_format) @@ -200,38 +157,50 @@ protected: alignas(16) u8* temp = nullptr; size_t temp_size = 0; - std::array bound_textures{}; + std::array bound_textures{}; private: - typedef std::multimap TexAddrCache; - typedef std::multimap TexHashCache; - typedef std::unordered_multimap - TexPool; + // Minimal version of TCacheEntry just for TexPool + struct TexPoolEntry + { + std::unique_ptr texture; + int frameCount = FRAMECOUNT_INVALID; + TexPoolEntry(std::unique_ptr tex) : texture(std::move(tex)) {} + }; + typedef std::multimap TexAddrCache; + typedef std::multimap TexHashCache; + typedef std::unordered_multimap TexPool; void SetBackupConfig(const VideoConfig& config); - TCacheEntryBase* ApplyPaletteToEntry(TCacheEntryBase* entry, u8* palette, u32 tlutfmt); + TCacheEntry* ApplyPaletteToEntry(TCacheEntry* entry, u8* palette, u32 tlutfmt); - void ScaleTextureCacheEntryTo(TCacheEntryBase** entry, u32 new_width, u32 new_height); - TCacheEntryBase* DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* palette, - u32 tlutfmt); + void ScaleTextureCacheEntryTo(TCacheEntry* entry, u32 new_width, u32 new_height); + TCacheEntry* DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* palette, u32 tlutfmt); - void DumpTexture(TCacheEntryBase* entry, std::string basename, unsigned int level); + void DumpTexture(TCacheEntry* entry, std::string basename, unsigned int level); void CheckTempSize(size_t required_size); - TCacheEntryBase* AllocateTexture(const TCacheEntryConfig& config); - TexPool::iterator FindMatchingTextureFromPool(const TCacheEntryConfig& config); - TexAddrCache::iterator GetTexCacheIter(TCacheEntryBase* entry); + TCacheEntry* AllocateCacheEntry(const TextureConfig& config); + std::unique_ptr AllocateTexture(const TextureConfig& config); + TexPool::iterator FindMatchingTextureFromPool(const TextureConfig& config); + TexAddrCache::iterator GetTexCacheIter(TCacheEntry* entry); // Return all possible overlapping textures. As addr+size of the textures is not // indexed, this may return false positives. std::pair FindOverlappingTextures(u32 addr, u32 size_in_bytes); + virtual std::unique_ptr CreateTexture(const TextureConfig& config) = 0; + + virtual void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, + const EFBRectangle& src_rect, bool scale_by_half, + unsigned int cbuf_id, const float* colmat) = 0; + // Removes and unlinks texture from texture cache and returns it to the pool TexAddrCache::iterator InvalidateTexture(TexAddrCache::iterator t_iter); - TCacheEntryBase* ReturnEntry(unsigned int stage, TCacheEntryBase* entry); + TCacheEntry* ReturnEntry(unsigned int stage, TCacheEntry* entry); TexAddrCache textures_by_address; TexHashCache textures_by_hash; diff --git a/Source/Core/VideoCommon/TextureConfig.cpp b/Source/Core/VideoCommon/TextureConfig.cpp new file mode 100644 index 0000000000..7a6930c798 --- /dev/null +++ b/Source/Core/VideoCommon/TextureConfig.cpp @@ -0,0 +1,18 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoCommon/TextureConfig.h" + +#include + +bool TextureConfig::operator==(const TextureConfig& o) const +{ + return std::tie(width, height, levels, layers, format, rendertarget) == + std::tie(o.width, o.height, o.levels, o.layers, o.format, o.rendertarget); +} + +MathUtil::Rectangle TextureConfig::GetRect() const +{ + return {0, 0, static_cast(width), static_cast(height)}; +} diff --git a/Source/Core/VideoCommon/TextureConfig.h b/Source/Core/VideoCommon/TextureConfig.h new file mode 100644 index 0000000000..27a0c935a1 --- /dev/null +++ b/Source/Core/VideoCommon/TextureConfig.h @@ -0,0 +1,43 @@ +// 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" + +enum class AbstractTextureFormat : u32 +{ + RGBA8, + DXT1, + DXT3, + DXT5 +}; + +struct TextureConfig +{ + constexpr TextureConfig() = default; + bool operator==(const TextureConfig& o) const; + MathUtil::Rectangle GetRect() const; + + u32 width = 0; + u32 height = 0; + u32 levels = 1; + u32 layers = 1; + AbstractTextureFormat format = AbstractTextureFormat::RGBA8; + bool rendertarget = false; + + struct Hasher : std::hash + { + size_t operator()(const TextureConfig& c) const + { + u64 id = (u64)c.rendertarget << 63 | (u64)c.format << 50 | (u64)c.layers << 48 | + (u64)c.levels << 32 | (u64)c.height << 16 | (u64)c.width; + return std::hash::operator()(id); + } + }; +}; diff --git a/Source/Core/VideoCommon/VideoCommon.h b/Source/Core/VideoCommon/VideoCommon.h index cff2883318..e25b2b66a5 100644 --- a/Source/Core/VideoCommon/VideoCommon.h +++ b/Source/Core/VideoCommon/VideoCommon.h @@ -77,15 +77,6 @@ enum class APIType Nothing }; -// Texture formats that videocommon can upload/use. -enum class HostTextureFormat : u32 -{ - RGBA8, - DXT1, - DXT3, - DXT5 -}; - inline u32 RGBA8ToRGBA6ToRGBA8(u32 src) { u32 color = src; diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index 88d3e4b759..97adb25ac6 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -36,6 +36,7 @@ + @@ -68,6 +69,7 @@ + @@ -89,6 +91,7 @@ + @@ -124,6 +127,7 @@ + diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index eb45d40ffc..be09444957 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -167,6 +167,12 @@ Util + + Base + + + Base + @@ -317,6 +323,12 @@ Util + + Base + + + Base +