mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-10 16:19:28 +01:00
Merge pull request #5305 from iwubcode/abstract_texture
Abstract Texture
This commit is contained in:
commit
e63c337830
@ -13,6 +13,8 @@ set(SRCS
|
||||
D3DTexture.h
|
||||
D3DUtil.cpp
|
||||
D3DUtil.h
|
||||
DXTexture.cpp
|
||||
DXTexture.h
|
||||
FramebufferManager.cpp
|
||||
FramebufferManager.h
|
||||
GeometryShaderCache.cpp
|
||||
|
@ -43,6 +43,7 @@
|
||||
<ClCompile Include="D3DState.cpp" />
|
||||
<ClCompile Include="D3DTexture.cpp" />
|
||||
<ClCompile Include="D3DUtil.cpp" />
|
||||
<ClCompile Include="DXTexture.cpp" />
|
||||
<ClCompile Include="FramebufferManager.cpp" />
|
||||
<ClCompile Include="GeometryShaderCache.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
@ -65,6 +66,7 @@
|
||||
<ClInclude Include="D3DState.h" />
|
||||
<ClInclude Include="D3DTexture.h" />
|
||||
<ClInclude Include="D3DUtil.h" />
|
||||
<ClInclude Include="DXTexture.h" />
|
||||
<ClInclude Include="FramebufferManager.h" />
|
||||
<ClInclude Include="GeometryShaderCache.h" />
|
||||
<ClInclude Include="PerfQuery.h" />
|
||||
|
@ -67,6 +67,9 @@
|
||||
<ClCompile Include="BoundingBox.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DXTexture.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="D3DBase.h">
|
||||
@ -124,5 +127,8 @@
|
||||
<ClInclude Include="BoundingBox.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DXTexture.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
196
Source/Core/VideoBackends/D3D/DXTexture.cpp
Normal file
196
Source/Core/VideoBackends/D3D/DXTexture.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
#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<u32>(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<u32>(hr));
|
||||
staging_texture->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool encode_result =
|
||||
TextureToPng(reinterpret_cast<u8*>(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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
{
|
||||
const DXTexture* srcentry = static_cast<const DXTexture*>(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<UINT>(src_pitch), 0);
|
||||
}
|
||||
} // namespace DX11
|
37
Source/Core/VideoBackends/D3D/DXTexture.h
Normal file
37
Source/Core/VideoBackends/D3D/DXTexture.h
Normal file
@ -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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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
|
@ -7,265 +7,35 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#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<PSTextureEncoder> g_encoder;
|
||||
const size_t MAX_COPY_BUFFERS = 32;
|
||||
ID3D11Buffer* efbcopycbuf[MAX_COPY_BUFFERS] = {0};
|
||||
|
||||
static DXGI_FORMAT GetDXGIFormatForHostFormat(HostTextureFormat format)
|
||||
std::unique_ptr<AbstractTexture> 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<u32>(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<u32>(hr));
|
||||
staging_texture->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool encode_result =
|
||||
TextureToPng(reinterpret_cast<u8*>(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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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<UINT>(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<DXTexture>(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<DXTexture*>(source->texture.get());
|
||||
DXTexture* destination_texture = static_cast<DXTexture*>(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<float>(source->GetWidth()),
|
||||
static_cast<float>(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<TCacheEntry*>(entry)->texture->GetSRV());
|
||||
D3D::stateman->UnsetTexture(destination_texture->GetRawTexIdentifier()->GetSRV());
|
||||
D3D::stateman->Apply();
|
||||
|
||||
D3D::context->OMSetRenderTargets(1, &static_cast<TCacheEntry*>(entry)->texture->GetRTV(),
|
||||
D3D::context->OMSetRenderTargets(1, &destination_texture->GetRawTexIdentifier()->GetRTV(),
|
||||
nullptr);
|
||||
|
||||
// Create texture copy
|
||||
D3D::drawShadedTexQuad(static_cast<TCacheEntry*>(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<DXTexture*>(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<float>(destination_texture->GetConfig().width),
|
||||
static_cast<float>(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();
|
||||
}
|
||||
}
|
||||
|
@ -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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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<AbstractTexture> 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;
|
||||
|
@ -1,5 +1,6 @@
|
||||
set(SRCS
|
||||
NullBackend.cpp
|
||||
NullTexture.cpp
|
||||
Render.cpp
|
||||
VertexManager.cpp
|
||||
ShaderCache.cpp
|
||||
|
@ -37,12 +37,14 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="NullBackend.cpp" />
|
||||
<ClCompile Include="NullTexture.cpp" />
|
||||
<ClCompile Include="Render.cpp" />
|
||||
<ClCompile Include="ShaderCache.cpp" />
|
||||
<ClCompile Include="VertexManager.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="FramebufferManager.h" />
|
||||
<ClInclude Include="NullTexture.h" />
|
||||
<ClInclude Include="PerfQuery.h" />
|
||||
<ClInclude Include="Render.h" />
|
||||
<ClInclude Include="ShaderCache.h" />
|
||||
|
28
Source/Core/VideoBackends/Null/NullTexture.cpp
Normal file
28
Source/Core/VideoBackends/Null/NullTexture.cpp
Normal file
@ -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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
{
|
||||
}
|
||||
|
||||
void NullTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
|
||||
size_t buffer_size)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Null
|
29
Source/Core/VideoBackends/Null/NullTexture.h
Normal file
29
Source/Core/VideoBackends/Null/NullTexture.h
Normal file
@ -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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect) override;
|
||||
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
|
||||
size_t buffer_size) override;
|
||||
};
|
||||
|
||||
} // namespace Null
|
@ -4,7 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#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<AbstractTexture> 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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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<NullTexture>(config);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@ set(SRCS
|
||||
FramebufferManager.cpp
|
||||
main.cpp
|
||||
NativeVertexFormat.cpp
|
||||
OGLTexture.cpp
|
||||
PerfQuery.cpp
|
||||
PostProcessing.cpp
|
||||
ProgramShaderCache.cpp
|
||||
|
@ -36,6 +36,7 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="OGLTexture.cpp" />
|
||||
<ClCompile Include="BoundingBox.cpp" />
|
||||
<ClCompile Include="FramebufferManager.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
@ -52,6 +53,7 @@
|
||||
<ClCompile Include="VertexManager.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="OGLTexture.h" />
|
||||
<ClInclude Include="BoundingBox.h" />
|
||||
<ClInclude Include="FramebufferManager.h" />
|
||||
<ClInclude Include="GPUTimer.h" />
|
||||
|
@ -53,6 +53,9 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="SamplerCache.cpp" />
|
||||
<ClCompile Include="OGLTexture.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="VertexManager.h">
|
||||
@ -93,6 +96,9 @@
|
||||
<ClInclude Include="GPUTimer.h">
|
||||
<Filter>GLUtil</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="OGLTexture.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
274
Source/Core/VideoBackends/OGL/OGLTexture.cpp
Normal file
274
Source/Core/VideoBackends/OGL/OGLTexture.cpp
Normal file
@ -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<u32, 8> 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<u8> 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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
{
|
||||
const OGLTexture* srcentry = static_cast<const OGLTexture*>(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<GLsizei>(buffer_size), buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, level, gl_internal_format, width, height, 1, 0,
|
||||
static_cast<GLsizei>(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
|
40
Source/Core/VideoBackends/OGL/OGLTexture.h
Normal file
40
Source/Core/VideoBackends/OGL/OGLTexture.h
Normal file
@ -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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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
|
@ -138,7 +138,7 @@ void SHADER::SetProgramBindings(bool is_compute)
|
||||
}
|
||||
}
|
||||
|
||||
void SHADER::Bind()
|
||||
void SHADER::Bind() const
|
||||
{
|
||||
if (CurrentProgram != glprogid)
|
||||
{
|
||||
|
@ -47,7 +47,7 @@ struct SHADER
|
||||
|
||||
void SetProgramVariables();
|
||||
void SetProgramBindings(bool is_compute);
|
||||
void Bind();
|
||||
void Bind() const;
|
||||
};
|
||||
|
||||
class ProgramShaderCache
|
||||
|
@ -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()
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "VideoBackends/OGL/TextureCache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
@ -11,17 +9,18 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#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<StreamBuffer> s_palette_stream_buffer;
|
||||
static GLuint s_palette_resolv_texture;
|
||||
@ -72,290 +59,9 @@ static std::array<GLuint, TextureConversionShader::BUFFER_FORMAT_COUNT>
|
||||
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<AbstractTexture> 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<u8> 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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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<GLsizei>(buffer_size), buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, level, gl_internal_format, width, height, 1, 0,
|
||||
static_cast<GLsizei>(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<float>(R.left), static_cast<float>(R.top),
|
||||
static_cast<float>(R.right), static_cast<float>(R.bottom));
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
FramebufferManager::SetFramebuffer(0);
|
||||
g_renderer->RestoreAPIState();
|
||||
return std::make_unique<OGLTexture>(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<TextureCache*>(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<OGLTexture*>(source->texture.get());
|
||||
OGLTexture* destination_texture = static_cast<OGLTexture*>(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<float>(source->GetWidth()), static_cast<float>(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<TCacheEntry*>(entry)->texture, dst_level, GL_TRUE, 0,
|
||||
GL_WRITE_ONLY, GL_RGBA8);
|
||||
glBindImageTexture(0, static_cast<OGLTexture*>(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<u32>(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<OGLTexture*>(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<float>(R.left), static_cast<float>(R.top),
|
||||
static_cast<float>(R.right), static_cast<float>(R.bottom));
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
FramebufferManager::SetFramebuffer(0);
|
||||
g_renderer->RestoreAPIState();
|
||||
}
|
||||
}
|
||||
|
@ -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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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<AbstractTexture> 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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ set(SRCS
|
||||
Rasterizer.cpp
|
||||
SWOGLWindow.cpp
|
||||
SWRenderer.cpp
|
||||
SWTexture.cpp
|
||||
SWVertexLoader.cpp
|
||||
SWmain.cpp
|
||||
SetupUnit.cpp
|
||||
|
28
Source/Core/VideoBackends/Software/SWTexture.cpp
Normal file
28
Source/Core/VideoBackends/Software/SWTexture.cpp
Normal file
@ -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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
{
|
||||
}
|
||||
|
||||
void SWTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
|
||||
size_t buffer_size)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace SW
|
29
Source/Core/VideoBackends/Software/SWTexture.h
Normal file
29
Source/Core/VideoBackends/Software/SWTexture.h
Normal file
@ -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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect) override;
|
||||
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
|
||||
size_t buffer_size) override;
|
||||
};
|
||||
|
||||
} // namespace SW
|
@ -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<AbstractTexture> 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<SWTexture>(config);
|
||||
}
|
||||
|
||||
void CopyRectangleFromTexture(const TCacheEntryBase* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
||||
<ClCompile Include="SWmain.cpp" />
|
||||
<ClCompile Include="SWOGLWindow.cpp" />
|
||||
<ClCompile Include="SWRenderer.cpp" />
|
||||
<ClCompile Include="SWTexture.cpp" />
|
||||
<ClCompile Include="SWVertexLoader.cpp" />
|
||||
<ClCompile Include="Tev.cpp" />
|
||||
<ClCompile Include="TextureEncoder.cpp" />
|
||||
@ -61,6 +62,7 @@
|
||||
<ClInclude Include="SetupUnit.h" />
|
||||
<ClInclude Include="SWOGLWindow.h" />
|
||||
<ClInclude Include="SWRenderer.h" />
|
||||
<ClInclude Include="SWTexture.h" />
|
||||
<ClInclude Include="SWVertexLoader.h" />
|
||||
<ClInclude Include="Tev.h" />
|
||||
<ClInclude Include="TextureEncoder.h" />
|
||||
|
@ -19,6 +19,7 @@ set(SRCS
|
||||
Util.cpp
|
||||
VertexFormat.cpp
|
||||
VertexManager.cpp
|
||||
VKTexture.cpp
|
||||
VulkanContext.cpp
|
||||
VulkanLoader.cpp
|
||||
main.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<XFBSourceBase> 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<TextureCache::TCacheEntry*>(base_texture);
|
||||
auto texture = TextureCache::GetInstance()->CreateTexture(config);
|
||||
if (!texture)
|
||||
{
|
||||
PanicAlert("Failed to create texture for XFB source");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<XFBSource>(std::unique_ptr<TextureCache::TCacheEntry>(texture));
|
||||
return std::make_unique<XFBSource>(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<TextureCache::TCacheEntry> texture)
|
||||
XFBSource::XFBSource(std::unique_ptr<AbstractTexture> texture)
|
||||
: XFBSourceBase(), m_texture(std::move(texture))
|
||||
{
|
||||
}
|
||||
@ -1414,13 +1415,18 @@ XFBSource::~XFBSource()
|
||||
{
|
||||
}
|
||||
|
||||
VKTexture* XFBSource::GetTexture() const
|
||||
{
|
||||
return static_cast<VKTexture*>(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<VKTexture*>(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<u32>(rect.GetWidth()), static_cast<u32>(rect.GetHeight())}};
|
||||
|
||||
Texture2D* src_texture = FramebufferManager::GetInstance()->ResolveEFBColorTexture(vk_rect);
|
||||
TextureCache::GetInstance()->CopyRectangleFromTexture(m_texture.get(), rect, src_texture, rect);
|
||||
static_cast<VKTexture*>(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())
|
||||
|
@ -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<TextureCache::TCacheEntry> texture);
|
||||
explicit XFBSource(std::unique_ptr<AbstractTexture> 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<TextureCache::TCacheEntry> m_texture;
|
||||
std::unique_ptr<AbstractTexture> m_texture;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "VideoBackends/Vulkan/Renderer.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
@ -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<const XFBSource*>(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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<TextureCache*>(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<TCacheEntry*>(base_entry);
|
||||
TCacheEntry* unconverted = static_cast<TCacheEntry*>(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<VKTexture*>(source->texture.get())
|
||||
->GetRawTexIdentifier()
|
||||
->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
static_cast<VKTexture*>(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<int>& dst_rect,
|
||||
Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& 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<TCacheEntry*>(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<TCacheEntry*>(entry)->GetTexture()->TransitionToLayout(
|
||||
command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
static_cast<VKTexture*>(entry->texture.get())
|
||||
->GetRawTexIdentifier()
|
||||
->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCache::CopyTextureRectangle(TCacheEntry* dst_texture,
|
||||
const MathUtil::Rectangle<int>& dst_rect,
|
||||
Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect)
|
||||
std::unique_ptr<AbstractTexture> TextureCache::CreateTexture(const TextureConfig& config)
|
||||
{
|
||||
_assert_msg_(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= src_texture->GetWidth() &&
|
||||
static_cast<u32>(src_rect.GetHeight()) <= src_texture->GetHeight(),
|
||||
"Source rect is too large for CopyRectangleFromTexture");
|
||||
|
||||
_assert_msg_(VIDEO, static_cast<u32>(dst_rect.GetWidth()) <= dst_texture->config.width &&
|
||||
static_cast<u32>(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<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(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<int>& dst_rect,
|
||||
Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& 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<u32>(dst_rect.GetWidth()), static_cast<u32>(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<int>(src_texture->GetWidth()),
|
||||
static_cast<int>(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<Texture2D> 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<u32>(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<Texture2D> 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<u32>(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<StagingBuffer> 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<FramebufferManager*>(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<u32>(scaled_src_rect.GetWidth()),
|
||||
static_cast<u32>(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<int>& src_rect,
|
||||
const MathUtil::Rectangle<int>& dst_rect)
|
||||
{
|
||||
const TCacheEntry* source_vk = static_cast<const TCacheEntry*>(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<StagingTexture2D> 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<u8*>(staging_texture->GetMapPointer()),
|
||||
static_cast<u32>(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<VKTexture*>(entry->texture.get());
|
||||
|
||||
// A better way of doing this would be nice.
|
||||
FramebufferManager* framebuffer_mgr =
|
||||
static_cast<FramebufferManager*>(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<u32>(scaled_src_rect.GetWidth()),
|
||||
static_cast<u32>(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
|
||||
|
@ -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<Texture2D> 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<int>& src_rect,
|
||||
const MathUtil::Rectangle<int>& dst_rect) override;
|
||||
|
||||
void Bind(unsigned int stage) override;
|
||||
bool Save(const std::string& filename, unsigned int level) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Texture2D> 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<AbstractTexture> 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<int>& dst_rect,
|
||||
Texture2D* src_texture, const MathUtil::Rectangle<int>& 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<int>& dst_rect,
|
||||
Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect);
|
||||
|
||||
// Copies (and optionally scales) the contents of a texture using a framgent shader.
|
||||
void ScaleTextureRectangle(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect,
|
||||
Texture2D* src_texture, const MathUtil::Rectangle<int>& 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;
|
||||
|
||||
|
@ -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<VKTexture*>(src_entry->texture.get());
|
||||
VKTexture* destination_texture = static_cast<VKTexture*>(dst_entry->texture.get());
|
||||
|
||||
_assert_(static_cast<size_t>(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<int>(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<VKTexture*>(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);
|
||||
}
|
||||
|
||||
|
@ -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<int>& 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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
363
Source/Core/VideoBackends/Vulkan/VKTexture.cpp
Normal file
363
Source/Core/VideoBackends/Vulkan/VKTexture.cpp
Normal file
@ -0,0 +1,363 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
#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<Texture2D> texture,
|
||||
VkFramebuffer framebuffer)
|
||||
: AbstractTexture(tex_config), m_texture(std::move(texture)), m_framebuffer(framebuffer)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<VKTexture> 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<u32>(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<VKTexture>(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<StagingTexture2D> 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<u8*>(staging_texture->GetMapPointer()),
|
||||
static_cast<u32>(staging_texture->GetRowStride()), filename,
|
||||
level_width, level_height);
|
||||
|
||||
staging_texture->Unmap();
|
||||
return result;
|
||||
}
|
||||
|
||||
void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
||||
Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect)
|
||||
{
|
||||
_assert_msg_(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= src_texture->GetWidth() &&
|
||||
static_cast<u32>(src_rect.GetHeight()) <= src_texture->GetHeight(),
|
||||
"Source rect is too large for CopyRectangleFromTexture");
|
||||
|
||||
_assert_msg_(VIDEO, static_cast<u32>(dst_rect.GetWidth()) <= m_config.width &&
|
||||
static_cast<u32>(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<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(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<int>& dst_rect,
|
||||
Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& 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<u32>(dst_rect.GetWidth()), static_cast<u32>(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<int>(src_texture->GetWidth()),
|
||||
static_cast<int>(src_texture->GetHeight()));
|
||||
draw.EndRenderPass();
|
||||
}
|
||||
|
||||
void VKTexture::CopyRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
{
|
||||
auto* raw_source_texture = static_cast<const VKTexture*>(source)->GetRawTexIdentifier();
|
||||
CopyRectangleFromTexture(raw_source_texture, srcrect, dstrect);
|
||||
}
|
||||
|
||||
void VKTexture::CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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<u32>(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<StagingBuffer> 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
|
55
Source/Core/VideoBackends/Vulkan/VKTexture.h
Normal file
55
Source/Core/VideoBackends/Vulkan/VKTexture.h
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect) override;
|
||||
void CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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<VKTexture> Create(const TextureConfig& tex_config);
|
||||
|
||||
private:
|
||||
VKTexture(const TextureConfig& tex_config, std::unique_ptr<Texture2D> texture,
|
||||
VkFramebuffer framebuffer);
|
||||
|
||||
// Copies the contents of a texture using vkCmdCopyImage
|
||||
void CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect);
|
||||
|
||||
// Copies (and optionally scales) the contents of a texture using a framgent shader.
|
||||
void ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect);
|
||||
|
||||
std::unique_ptr<Texture2D> m_texture;
|
||||
VkFramebuffer m_framebuffer;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
@ -57,6 +57,7 @@
|
||||
<ClCompile Include="Texture2D.cpp" />
|
||||
<ClCompile Include="TextureCache.cpp" />
|
||||
<ClCompile Include="VertexManager.cpp" />
|
||||
<ClCompile Include="VKTexture.cpp" />
|
||||
<ClCompile Include="VulkanContext.cpp" />
|
||||
<ClCompile Include="VulkanLoader.cpp" />
|
||||
</ItemGroup>
|
||||
@ -83,6 +84,7 @@
|
||||
<ClInclude Include="TextureCache.h" />
|
||||
<ClInclude Include="VertexManager.h" />
|
||||
<ClInclude Include="VideoBackend.h" />
|
||||
<ClInclude Include="VKTexture.h" />
|
||||
<ClInclude Include="VulkanContext.h" />
|
||||
<ClInclude Include="VulkanLoader.h" />
|
||||
</ItemGroup>
|
||||
|
44
Source/Core/VideoCommon/AbstractTexture.cpp
Normal file
44
Source/Core/VideoCommon/AbstractTexture.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#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<size_t>(std::max(1u, row_length / 4)) * 8;
|
||||
case AbstractTextureFormat::DXT3:
|
||||
case AbstractTextureFormat::DXT5:
|
||||
return static_cast<size_t>(std::max(1u, row_length / 4)) * 16;
|
||||
case AbstractTextureFormat::RGBA8:
|
||||
default:
|
||||
return static_cast<size_t>(row_length) * 4;
|
||||
}
|
||||
}
|
||||
|
||||
const TextureConfig AbstractTexture::GetConfig() const
|
||||
{
|
||||
return m_config;
|
||||
}
|
33
Source/Core/VideoCommon/AbstractTexture.h
Normal file
33
Source/Core/VideoCommon/AbstractTexture.h
Normal file
@ -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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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;
|
||||
};
|
@ -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
|
||||
|
@ -533,7 +533,7 @@ bool HiresTexture::LoadTexture(Level& level, const std::vector<u8>& buffer)
|
||||
// Images loaded by SOIL are converted to RGBA.
|
||||
level.width = static_cast<u32>(width);
|
||||
level.height = static_cast<u32>(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<size_t>(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;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <vector>
|
||||
|
||||
#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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<TextureCacheBase> g_texture_cache;
|
||||
|
||||
TextureCacheBase::TCacheEntryBase::~TCacheEntryBase()
|
||||
TextureCacheBase::TCacheEntry::TCacheEntry(std::unique_ptr<AbstractTexture> 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<size_t>(std::max(1u, row_length / 4)) * 8;
|
||||
|
||||
case HostTextureFormat::DXT3:
|
||||
case HostTextureFormat::DXT5:
|
||||
return static_cast<size_t>(std::max(1u, row_length / 4)) * 16;
|
||||
|
||||
case HostTextureFormat::RGBA8:
|
||||
default:
|
||||
return static_cast<size_t>(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<AbstractTexture> 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<int> 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<u32>(i));
|
||||
bound_textures[i]->texture->Bind(static_cast<u32>(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<AbstractTexture> 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<AbstractTexture> TextureCacheBase::AllocateTexture(const TextureConfig& config)
|
||||
{
|
||||
TexPool::iterator iter = FindMatchingTextureFromPool(config);
|
||||
TextureCacheBase::TCacheEntryBase* entry;
|
||||
std::unique_ptr<AbstractTexture> 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())
|
||||
|
@ -12,7 +12,9 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#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<u64>
|
||||
{
|
||||
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<u64>::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<AbstractTexture> 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<u64, TCacheEntryBase*>::iterator textures_by_hash_iter;
|
||||
std::multimap<u64, TCacheEntry*>::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<TCacheEntryBase*> references;
|
||||
std::unordered_set<TCacheEntry*> references;
|
||||
|
||||
explicit TCacheEntry(std::unique_ptr<AbstractTexture> 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<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& 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<TCacheEntryBase*, 8> bound_textures{};
|
||||
std::array<TCacheEntry*, 8> bound_textures{};
|
||||
|
||||
private:
|
||||
typedef std::multimap<u32, TCacheEntryBase*> TexAddrCache;
|
||||
typedef std::multimap<u64, TCacheEntryBase*> TexHashCache;
|
||||
typedef std::unordered_multimap<TCacheEntryConfig, TCacheEntryBase*, TCacheEntryConfig::Hasher>
|
||||
TexPool;
|
||||
// Minimal version of TCacheEntry just for TexPool
|
||||
struct TexPoolEntry
|
||||
{
|
||||
std::unique_ptr<AbstractTexture> texture;
|
||||
int frameCount = FRAMECOUNT_INVALID;
|
||||
TexPoolEntry(std::unique_ptr<AbstractTexture> tex) : texture(std::move(tex)) {}
|
||||
};
|
||||
typedef std::multimap<u32, TCacheEntry*> TexAddrCache;
|
||||
typedef std::multimap<u64, TCacheEntry*> TexHashCache;
|
||||
typedef std::unordered_multimap<TextureConfig, TexPoolEntry, TextureConfig::Hasher> 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<AbstractTexture> 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<TexAddrCache::iterator, TexAddrCache::iterator>
|
||||
FindOverlappingTextures(u32 addr, u32 size_in_bytes);
|
||||
|
||||
virtual std::unique_ptr<AbstractTexture> 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;
|
||||
|
18
Source/Core/VideoCommon/TextureConfig.cpp
Normal file
18
Source/Core/VideoCommon/TextureConfig.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "VideoCommon/TextureConfig.h"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
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<int> TextureConfig::GetRect() const
|
||||
{
|
||||
return {0, 0, static_cast<int>(width), static_cast<int>(height)};
|
||||
}
|
43
Source/Core/VideoCommon/TextureConfig.h
Normal file
43
Source/Core/VideoCommon/TextureConfig.h
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
|
||||
#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<int> 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<u64>
|
||||
{
|
||||
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<u64>::operator()(id);
|
||||
}
|
||||
};
|
||||
};
|
@ -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;
|
||||
|
@ -36,6 +36,7 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AbstractTexture.cpp" />
|
||||
<ClCompile Include="AsyncRequests.cpp" />
|
||||
<ClCompile Include="AVIDump.cpp" />
|
||||
<ClCompile Include="BoundingBox.cpp" />
|
||||
@ -68,6 +69,7 @@
|
||||
<ClCompile Include="GeometryShaderGen.cpp" />
|
||||
<ClCompile Include="GeometryShaderManager.cpp" />
|
||||
<ClCompile Include="TextureCacheBase.cpp" />
|
||||
<ClCompile Include="TextureConfig.cpp" />
|
||||
<ClCompile Include="TextureConversionShader.cpp" />
|
||||
<ClCompile Include="VertexLoader.cpp" />
|
||||
<ClCompile Include="VertexLoaderBase.cpp" />
|
||||
@ -89,6 +91,7 @@
|
||||
<ClCompile Include="XFStructs.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AbstractTexture.h" />
|
||||
<ClInclude Include="AsyncRequests.h" />
|
||||
<ClInclude Include="AVIDump.h" />
|
||||
<ClInclude Include="BoundingBox.h" />
|
||||
@ -124,6 +127,7 @@
|
||||
<ClInclude Include="GeometryShaderGen.h" />
|
||||
<ClInclude Include="GeometryShaderManager.h" />
|
||||
<ClInclude Include="TextureCacheBase.h" />
|
||||
<ClInclude Include="TextureConfig.h" />
|
||||
<ClInclude Include="TextureConversionShader.h" />
|
||||
<ClInclude Include="TextureDecoder.h" />
|
||||
<ClInclude Include="VertexLoader.h" />
|
||||
|
@ -167,6 +167,12 @@
|
||||
<ClCompile Include="HiresTextures_DDSLoader.cpp">
|
||||
<Filter>Util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TextureConfig.cpp">
|
||||
<Filter>Base</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AbstractTexture.cpp">
|
||||
<Filter>Base</Filter>
|
||||
</C1Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="CommandProcessor.h" />
|
||||
@ -317,6 +323,12 @@
|
||||
<ClInclude Include="SamplerCommon.h">
|
||||
<Filter>Util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TextureConfig.h">
|
||||
<Filter>Base</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AbstractTexture.h">
|
||||
<Filter>Base</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user