Merge pull request #5305 from iwubcode/abstract_texture

Abstract Texture
This commit is contained in:
shuffle2 2017-06-18 12:57:05 -07:00 committed by GitHub
commit e63c337830
54 changed files with 1847 additions and 1423 deletions

View File

@ -13,6 +13,8 @@ set(SRCS
D3DTexture.h
D3DUtil.cpp
D3DUtil.h
DXTexture.cpp
DXTexture.h
FramebufferManager.cpp
FramebufferManager.h
GeometryShaderCache.cpp

View File

@ -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" />

View File

@ -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>

View 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

View 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

View File

@ -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, &params, 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();
}
}

View File

@ -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;

View File

@ -1,5 +1,6 @@
set(SRCS
NullBackend.cpp
NullTexture.cpp
Render.cpp
VertexManager.cpp
ShaderCache.cpp

View File

@ -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" />

View 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

View 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

View File

@ -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);
}
};

View File

@ -3,6 +3,7 @@ set(SRCS
FramebufferManager.cpp
main.cpp
NativeVertexFormat.cpp
OGLTexture.cpp
PerfQuery.cpp
PostProcessing.cpp
ProgramShaderCache.cpp

View File

@ -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" />

View File

@ -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" />

View 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

View 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

View File

@ -138,7 +138,7 @@ void SHADER::SetProgramBindings(bool is_compute)
}
}
void SHADER::Bind()
void SHADER::Bind() const
{
if (CurrentProgram != glprogid)
{

View File

@ -47,7 +47,7 @@ struct SHADER
void SetProgramVariables();
void SetProgramBindings(bool is_compute);
void Bind();
void Bind() const;
};
class ProgramShaderCache

View File

@ -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()

View File

@ -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();
}
}

View File

@ -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,

View File

@ -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();
}

View File

@ -6,6 +6,7 @@ set(SRCS
Rasterizer.cpp
SWOGLWindow.cpp
SWRenderer.cpp
SWTexture.cpp
SWVertexLoader.cpp
SWmain.cpp
SetupUnit.cpp

View 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

View 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

View File

@ -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();
}
};

View File

@ -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" />

View File

@ -19,6 +19,7 @@ set(SRCS
Util.cpp
VertexFormat.cpp
VertexManager.cpp
VKTexture.cpp
VulkanContext.cpp
VulkanLoader.cpp
main.cpp

View File

@ -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())

View File

@ -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

View File

@ -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());
}
}

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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);

View 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

View 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

View File

@ -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>

View 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;
}

View 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;
};

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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())

View File

@ -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;

View 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)};
}

View 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);
}
};
};

View File

@ -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;

View File

@ -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" />

View File

@ -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" />