Skip to content

Commit

Permalink
Decode EFB copies used as paletted textures.
Browse files Browse the repository at this point in the history
A number of games make an EFB copy in I4/I8 format, then use it as a
texture in C4/C8 format.  Detect when this happens, and decode the copy on
the GPU using the specified palette.

This has a few advantages: it allows using EFB2Tex for a few more games,
it, it preserves the resolution of scaled EFB copies, and it's probably a
bit faster.

D3D only at the moment, but porting to OpenGL should be straightforward..
  • Loading branch information
magumagu committed Feb 15, 2015
1 parent 16a6392 commit f116343
Show file tree
Hide file tree
Showing 7 changed files with 371 additions and 101 deletions.
158 changes: 157 additions & 1 deletion Source/Core/VideoBackends/D3D/TextureCache.cpp
Expand Up @@ -4,6 +4,7 @@

#include "Core/HW/Memmap.h"
#include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/D3DShader.h"
#include "VideoBackends/D3D/D3DState.h"
#include "VideoBackends/D3D/D3DUtil.h"
#include "VideoBackends/D3D/FramebufferManager.h"
Expand All @@ -14,6 +15,7 @@
#include "VideoBackends/D3D/TextureEncoder.h"
#include "VideoBackends/D3D/VertexShaderCache.h"
#include "VideoCommon/ImageWrite.h"
#include "VideoCommon/LookUpTables.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/VideoConfig.h"

Expand Down Expand Up @@ -179,17 +181,165 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo

size_in_bytes = (u32)encoded_size;

TextureCache::MakeRangeDynamic(addr, (u32)encoded_size);
TextureCache::MakeRangeDynamic(dstAddr, (u32)encoded_size);

this->hash = hash;
}
}

const char palette_shader[] =
R"HLSL(
sampler samp0 : register(s0);
Texture2DArray Tex0 : register(t0);
Buffer<uint> Tex1 : register(t1);
uniform float Multiply;
uint Convert3To8(uint v)
{
// Swizzle bits: 00000123 -> 12312312
return (v << 5) | (v << 2) | (v >> 1);
}
uint Convert4To8(uint v)
{
// Swizzle bits: 00001234 -> 12341234
return (v << 4) | v;
}
uint Convert5To8(uint v)
{
// Swizzle bits: 00012345 -> 12345123
return (v << 3) | (v >> 2);
}
uint Convert6To8(uint v)
{
// Swizzle bits: 00123456 -> 12345612
return (v << 2) | (v >> 4);
}
float4 DecodePixel_RGB5A3(uint val)
{
int r,g,b,a;
if ((val&0x8000))
{
r=Convert5To8((val>>10) & 0x1f);
g=Convert5To8((val>>5 ) & 0x1f);
b=Convert5To8((val ) & 0x1f);
a=0xFF;
}
else
{
a=Convert3To8((val>>12) & 0x7);
r=Convert4To8((val>>8 ) & 0xf);
g=Convert4To8((val>>4 ) & 0xf);
b=Convert4To8((val ) & 0xf);
}
return float4(r, g, b, a) / 255;
}
float4 DecodePixel_RGB565(uint val)
{
int r, g, b, a;
r = Convert5To8((val >> 11) & 0x1f);
g = Convert6To8((val >> 5) & 0x3f);
b = Convert5To8((val)& 0x1f);
a = 0xFF;
return float4(r, g, b, a) / 255;
}
float4 DecodePixel_IA8(uint val)
{
int i = val & 0xFF;
int a = val >> 8;
return float4(i, i, i, a) / 255;
}
void main(
out float4 ocol0 : SV_Target,
in float4 pos : SV_Position,
in float3 uv0 : TEXCOORD0)
{
uint src = round(Tex0.Sample(samp0,uv0) * Multiply).r;
src = Tex1.Load(src);
src = ((src << 8) & 0xFF00) | (src >> 8);
ocol0 = DECODE(src);
}
)HLSL";

void TextureCache::ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, TlutFormat format)
{
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);
D3D::context->RSSetViewports(1, &vp);

D3D11_BOX box{ 0, 0, 0, 512, 1, 1 };
D3D::context->UpdateSubresource(palette_buf, 0, &box, palette, 0, 0);

D3D::stateman->SetTexture(1, palette_buf_srv);

float params[4] = { unconverted->format == 0 ? 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);

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::context->OMSetRenderTargets(1, &static_cast<TCacheEntry*>(entry)->texture->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::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), FramebufferManager::GetEFBDepthTexture()->GetDSV());

g_renderer->RestoreAPIState();
}

ID3D11PixelShader *GetConvertShader(const char* Type)
{
std::string shader = "#define DECODE DecodePixel_";
shader.append(Type);
shader.append("\n");
shader.append(palette_shader);
return D3D::CompileAndCreatePixelShader(shader);
}

TextureCache::TextureCache()
{
// FIXME: Is it safe here?
g_encoder = new PSTextureEncoder;
g_encoder->Init();

palette_buf = nullptr;
palette_buf_srv = nullptr;
palette_uniform = nullptr;
palette_pixel_shader[GX_TL_IA8] = GetConvertShader("IA8");
palette_pixel_shader[GX_TL_RGB565] = GetConvertShader("RGB565");
palette_pixel_shader[GX_TL_RGB5A3] = GetConvertShader("RGB5A3");
auto lutBd = CD3D11_BUFFER_DESC(sizeof(u16) * 256, D3D11_BIND_SHADER_RESOURCE);
HRESULT hr = D3D::device->CreateBuffer(&lutBd, nullptr, &palette_buf);
CHECK(SUCCEEDED(hr), "create palette decoder lut buffer");
D3D::SetDebugObjectName(palette_buf, "texture decoder lut buffer");
auto outlutUavDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(palette_buf, DXGI_FORMAT_R16_UINT, 0, 256, 0);
hr = D3D::device->CreateShaderResourceView(palette_buf, &outlutUavDesc, &palette_buf_srv);
CHECK(SUCCEEDED(hr), "create palette decoder lut srv");
D3D::SetDebugObjectName(palette_buf_srv, "texture decoder lut srv");
const D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(16, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT);
hr = D3D::device->CreateBuffer(&cbdesc, nullptr, &palette_uniform);
CHECK(SUCCEEDED(hr), "Create palette decoder constant buffer");
D3D::SetDebugObjectName((ID3D11DeviceChild*)palette_uniform, "a constant buffer used in TextureCache::CopyRenderTargetToTexture");
}

TextureCache::~TextureCache()
Expand All @@ -200,6 +350,12 @@ TextureCache::~TextureCache()
g_encoder->Shutdown();
delete g_encoder;
g_encoder = nullptr;

SAFE_RELEASE(palette_buf);
SAFE_RELEASE(palette_buf_srv);
SAFE_RELEASE(palette_uniform);
for (ID3D11PixelShader*& shader : palette_pixel_shader)
SAFE_RELEASE(shader);
}

}
7 changes: 7 additions & 0 deletions Source/Core/VideoBackends/D3D/TextureCache.h
Expand Up @@ -42,8 +42,15 @@ class TextureCache : public ::TextureCache

u64 EncodeToRamFromTexture(u32 address, void* source_texture, u32 SourceW, u32 SourceH, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) {return 0;};

virtual void ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, TlutFormat format) override;

void CompileShaders() override { }
void DeleteShaders() override { }

ID3D11Buffer *palette_buf;
ID3D11ShaderResourceView *palette_buf_srv;
ID3D11Buffer *palette_uniform;
ID3D11PixelShader* palette_pixel_shader[3];
};

}
12 changes: 9 additions & 3 deletions Source/Core/VideoBackends/OGL/TextureCache.cpp
Expand Up @@ -204,20 +204,20 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo
if (false == g_ActiveConfig.bCopyEFBToTexture)
{
int encoded_size = TextureConverter::EncodeToRamFromTexture(
addr,
dstAddr,
read_texture,
srcFormat == PEControl::Z24,
isIntensity,
dstFormat,
scaleByHalf,
srcRect);

u8* dst = Memory::GetPointer(addr);
u8* dst = Memory::GetPointer(dstAddr);
u64 const new_hash = GetHash64(dst,encoded_size,g_ActiveConfig.iSafeTextureCache_ColorSamples);

size_in_bytes = (u32)encoded_size;

TextureCache::MakeRangeDynamic(addr,encoded_size);
TextureCache::MakeRangeDynamic(dstAddr, encoded_size);

hash = new_hash;
}
Expand Down Expand Up @@ -359,4 +359,10 @@ void TextureCache::DeleteShaders()
s_DepthMatrixProgram.Destroy();
}

void TextureCache::ConvertTexture(TextureCache::TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, TlutFormat format)
{
// TODO: Implement.
return;
}

}
1 change: 1 addition & 0 deletions Source/Core/VideoBackends/OGL/TextureCache.h
Expand Up @@ -48,6 +48,7 @@ class TextureCache : public ::TextureCache
~TextureCache();

TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) override;
void ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, TlutFormat format) override;

void CompileShaders() override;
void DeleteShaders() override;
Expand Down

0 comments on commit f116343

Please sign in to comment.