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
+