Split out the CLUT texture cache from "TextureShaderCache"

This commit is contained in:
Henrik Rydgård committed 2026-06-11 10:57:30 +02:00
1 parent f034cb45bb
commit d33425d01d
9 files changed
+168 -144

No files matched your search

+22 -14
View File
@@ -92,7 +92,7 @@ static bool GetBestFramebufferCandidate(FramebufferManagerCommon *fbManager, con
// These are Data::Format:: B4G4R4A4_PACK16, B5G6R6_PACK16, B5G5R5A1_PACK16, R8G8B8A8
TextureCacheCommon::TextureCacheCommon(Draw::DrawContext *draw, Draw2D *draw2D)
: draw_(draw), draw2D_(draw2D), replacer_(draw) {
: draw_(draw), draw2D_(draw2D), replacer_(draw), textureShaderCache_(draw, draw2D_) {
decimationCounter_ = TEXCACHE_DECIMATION_INTERVAL;
// It's only possible to have 1KB of palette entries, although we allow 2KB in a hack.
@@ -111,13 +111,9 @@ TextureCacheCommon::TextureCacheCommon(Draw::DrawContext *draw, Draw2D *draw2D)
// These buffers will grow if necessary, but most won't need more than this.
tmpTexBuf32_.resize(512 * 512); // 1MB
tmpTexBufRearrange_.resize(512 * 512); // 1MB
textureShaderCache_ = new TextureShaderCache(draw, draw2D_);
}
TextureCacheCommon::~TextureCacheCommon() {
delete textureShaderCache_;
FreeAlignedMemory(clutBufConverted_);
FreeAlignedMemory(clutBufRaw_);
FreeAlignedMemory(expandClut_);
@@ -125,7 +121,7 @@ TextureCacheCommon::~TextureCacheCommon() {
void TextureCacheCommon::StartFrame() {
ForgetLastTexture();
textureShaderCache_->Decimate();
clutTextureCache_.Decimate();
replacementTimeThisFrame_ = 0.0;
float fps;
@@ -2323,7 +2319,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
if (need_depalettize) {
if (clutRenderAddress_ == 0xFFFFFFFF) {
clutTexture = textureShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBufRaw_);
clutTexture = clutTextureCache_.GetClutTexture(clutFormat, clutHash_, clutBufRaw_);
smoothedDepal = CanUseSmoothDepal(gstate, fbFormat, clutTexture);
} else {
// The CLUT texture is dynamic, it's the framebuffer pointed to by clutRenderAddress.
@@ -2376,7 +2372,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
depthUpperBits = (depth && framebuffer->fb_format == GE_FORMAT_8888) ? ((gstate.getTextureAddress(0) & 0x600000) >> 20) : 0;
textureShader = textureShaderCache_->GetDepalettizeShader(clutMode, texFormat, fbFormat, smoothedDepal, depthUpperBits);
textureShader = textureShaderCache_.GetDepalettizeShader(clutMode, texFormat, fbFormat, smoothedDepal, depthUpperBits);
gstate_c.SetUseShaderDepal(ShaderDepalMode::OFF);
}
@@ -2421,8 +2417,8 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
} else {
draw_->BindFramebufferAsTexture(dynamicClutFbo_, 1, Draw::Aspect::COLOR_BIT, 0);
}
Draw::SamplerState *nearest = textureShaderCache_->GetSampler(false);
Draw::SamplerState *clutSampler = textureShaderCache_->GetSampler(smoothedDepal);
Draw::SamplerState *nearest = textureShaderCache_.GetSampler(false);
Draw::SamplerState *clutSampler = textureShaderCache_.GetSampler(smoothedDepal);
draw_->BindSamplerStates(0, 1, &nearest);
draw_->BindSamplerStates(1, 1, &clutSampler);
@@ -2490,7 +2486,7 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) {
framebufferManager_->BlitUsingRaster(
dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, dynamicClutFbo_, 0.0f, 0.0f, scaleFactorX * 512.0f, 1.0f, false, 1.0f, reinterpret, "reinterpret_clut");
Draw2DPipeline *textureShader = textureShaderCache_->GetDepalettizeShader(clutMode, GE_TFMT_CLUT8, GE_FORMAT_CLUT8, false, 0);
Draw2DPipeline *textureShader = textureShaderCache_.GetDepalettizeShader(clutMode, GE_TFMT_CLUT8, GE_FORMAT_CLUT8, false, 0);
gstate_c.SetUseShaderDepal(ShaderDepalMode::OFF);
int texWidth = gstate.getTextureWidth(0);
@@ -2523,8 +2519,8 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) {
draw_->BindNativeTexture(0, GetNativeTextureView(entry, false));
draw_->BindFramebufferAsTexture(dynamicClutFbo_, 1, Draw::Aspect::COLOR_BIT, 0);
Draw::SamplerState *nearest = textureShaderCache_->GetSampler(false);
Draw::SamplerState *clutSampler = textureShaderCache_->GetSampler(false);
Draw::SamplerState *nearest = textureShaderCache_.GetSampler(false);
Draw::SamplerState *clutSampler = textureShaderCache_.GetSampler(false);
draw_->BindSamplerStates(0, 1, &nearest);
draw_->BindSamplerStates(1, 1, &clutSampler);
@@ -2557,8 +2553,20 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) {
gstate_c.Dirty(DIRTY_ALL_RENDER_STATE);
}
void TextureCacheCommon::DeviceLost() {
textureShaderCache_.DeviceLost();
clutTextureCache_.DeviceLost();
Clear(false);
}
void TextureCacheCommon::DeviceRestore(Draw::DrawContext *draw) {
draw_ = draw;
textureShaderCache_.DeviceRestore(draw);
clutTextureCache_.DeviceRestore(draw);
}
void TextureCacheCommon::Clear(bool delete_them) {
textureShaderCache_->Clear();
textureShaderCache_.Clear();
for (TexCache::iterator iter = cache_.begin(); iter != cache_.end(); ++iter) {
ReleaseTexture(iter->second.get(), delete_them);
+5 -4
View File
@@ -338,7 +338,7 @@ public:
void InvalidateAll(GPUInvalidationType type);
void ClearNextFrame();
TextureShaderCache *GetTextureShaderCache() { return textureShaderCache_; }
TextureShaderCache &GetTextureShaderCache() { return textureShaderCache_; }
virtual void ForgetLastTexture() = 0;
virtual void Clear(bool delete_them);
@@ -364,8 +364,8 @@ public:
virtual void StartFrame();
virtual void DeviceLost() = 0;
virtual void DeviceRestore(Draw::DrawContext *draw) = 0;
virtual void DeviceLost();
virtual void DeviceRestore(Draw::DrawContext *draw);
// Accessors for the debugger.
virtual void *GetNativeTextureView(const TexCacheEntry *entry, bool flat) const = 0;
@@ -480,7 +480,8 @@ protected:
TextureReplacer replacer_;
TextureScalerCommon scaler_;
FramebufferManagerCommon *framebufferManager_;
TextureShaderCache *textureShaderCache_;
TextureShaderCache textureShaderCache_;
ClutTextureCache clutTextureCache_;
ShaderManagerCommon *shaderManager_;
bool clearCacheNextFrame_ = false;
+116 -103
View File
@@ -53,7 +53,120 @@ void TextureShaderCache::DeviceLost() {
draw_ = nullptr;
}
ClutTexture TextureShaderCache::GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, const u32 *rawClut) {
Draw::SamplerState *TextureShaderCache::GetSampler(bool linearFilter) {
if (linearFilter) {
if (!linearSampler_) {
Draw::SamplerStateDesc desc{};
desc.magFilter = Draw::TextureFilter::LINEAR;
desc.minFilter = Draw::TextureFilter::LINEAR;
desc.wrapU = Draw::TextureAddressMode::CLAMP_TO_EDGE;
desc.wrapV = Draw::TextureAddressMode::CLAMP_TO_EDGE;
desc.wrapW = Draw::TextureAddressMode::CLAMP_TO_EDGE;
linearSampler_ = draw_->CreateSamplerState(desc);
}
return linearSampler_;
} else {
if (!nearestSampler_) {
Draw::SamplerStateDesc desc{};
desc.wrapU = Draw::TextureAddressMode::CLAMP_TO_EDGE;
desc.wrapV = Draw::TextureAddressMode::CLAMP_TO_EDGE;
desc.wrapW = Draw::TextureAddressMode::CLAMP_TO_EDGE;
nearestSampler_ = draw_->CreateSamplerState(desc);
}
return nearestSampler_;
}
}
void TextureShaderCache::Clear() {
for (auto shader = pipelineCache_.begin(); shader != pipelineCache_.end(); ++shader) {
if (shader->second->pipeline) {
shader->second->pipeline->Release();
}
delete shader->second;
}
pipelineCache_.clear();
if (nearestSampler_) {
nearestSampler_->Release();
nearestSampler_ = nullptr;
}
if (linearSampler_) {
linearSampler_->Release();
linearSampler_ = nullptr;
}
}
Draw2DPipeline *TextureShaderCache::GetDepalettizeShader(uint32_t clutMode, GETextureFormat textureFormat, GEBufferFormat bufferFormat, bool smoothedDepal, u32 depthUpperBits) {
using namespace Draw;
// Generate an ID for depal shaders.
u64 id = ((u64)depthUpperBits << 32) | (clutMode & 0xFFFFFF) | (textureFormat << 24) | (bufferFormat << 28);
auto shader = pipelineCache_.find(id);
if (shader != pipelineCache_.end()) {
return shader->second;
}
// TODO: Parse these out of clutMode some nice way, to become a bit more stateless.
DepalConfig config;
config.clutFormat = gstate.getClutPaletteFormat();
config.startPos = gstate.getClutIndexStartPos();
config.shift = gstate.getClutIndexShift();
config.mask = gstate.getClutIndexMask();
config.bufferFormat = bufferFormat;
config.textureFormat = textureFormat;
config.smoothedDepal = smoothedDepal;
config.depthUpperBits = depthUpperBits;
Draw2DPipeline *ts = draw2D_->Create2DPipeline([config](ShaderWriter &writer) -> Draw2DPipelineInfo {
GenerateDepalFs(writer, config);
return Draw2DPipelineInfo{
"depal",
config.bufferFormat == GE_FORMAT_DEPTH16 ? RASTER_DEPTH : RASTER_COLOR,
RASTER_COLOR,
samplers
};
});
pipelineCache_[id] = ts;
return ts->pipeline ? ts : nullptr;
}
std::vector<std::string> TextureShaderCache::DebugGetShaderIDs(DebugShaderType type) {
std::vector<std::string> ids;
for (auto &entry : pipelineCache_) {
ids.push_back(StringFromFormat("%08x", entry.first));
}
return ids;
}
std::string TextureShaderCache::DebugGetShaderString(const std::string &idstr, DebugShaderType type, DebugShaderStringType stringType) {
uint32_t id = 0;
sscanf(idstr.c_str(), "%08x", &id);
auto iter = pipelineCache_.find(id);
if (iter == pipelineCache_.end())
return "";
switch (stringType) {
case SHADER_STRING_SHORT_DESC:
return idstr;
case SHADER_STRING_SOURCE_CODE:
return iter->second->code;
default:
return "";
}
}
void ClutTextureCache::DeviceRestore(Draw::DrawContext *draw) {
draw_ = draw;
}
void ClutTextureCache::DeviceLost() {
Clear();
draw_ = nullptr;
}
// TODO: We could cache many CLUTs in one big texture, might be more efficient.
ClutTexture ClutTextureCache::GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, const u32 *rawClut) {
// Simplistic, but works well enough.
u32 clutId = clutHash ^ (uint32_t)clutFormat;
@@ -138,54 +251,15 @@ ClutTexture TextureShaderCache::GetClutTexture(GEPaletteFormat clutFormat, const
return *tex;
}
void TextureShaderCache::Clear() {
for (auto shader = depalCache_.begin(); shader != depalCache_.end(); ++shader) {
if (shader->second->pipeline) {
shader->second->pipeline->Release();
}
delete shader->second;
}
depalCache_.clear();
void ClutTextureCache::Clear() {
for (auto tex = texCache_.begin(); tex != texCache_.end(); ++tex) {
tex->second->texture->Release();
delete tex->second;
}
texCache_.clear();
if (nearestSampler_) {
nearestSampler_->Release();
nearestSampler_ = nullptr;
}
if (linearSampler_) {
linearSampler_->Release();
linearSampler_ = nullptr;
}
}
Draw::SamplerState *TextureShaderCache::GetSampler(bool linearFilter) {
if (linearFilter) {
if (!linearSampler_) {
Draw::SamplerStateDesc desc{};
desc.magFilter = Draw::TextureFilter::LINEAR;
desc.minFilter = Draw::TextureFilter::LINEAR;
desc.wrapU = Draw::TextureAddressMode::CLAMP_TO_EDGE;
desc.wrapV = Draw::TextureAddressMode::CLAMP_TO_EDGE;
desc.wrapW = Draw::TextureAddressMode::CLAMP_TO_EDGE;
linearSampler_ = draw_->CreateSamplerState(desc);
}
return linearSampler_;
} else {
if (!nearestSampler_) {
Draw::SamplerStateDesc desc{};
desc.wrapU = Draw::TextureAddressMode::CLAMP_TO_EDGE;
desc.wrapV = Draw::TextureAddressMode::CLAMP_TO_EDGE;
desc.wrapW = Draw::TextureAddressMode::CLAMP_TO_EDGE;
nearestSampler_ = draw_->CreateSamplerState(desc);
}
return nearestSampler_;
}
}
void TextureShaderCache::Decimate() {
void ClutTextureCache::Decimate() {
for (auto tex = texCache_.begin(); tex != texCache_.end(); ) {
if (tex->second->lastFrame + DEPAL_TEXTURE_OLD_AGE < gpuStats.totals.numFlips) {
tex->second->texture->Release();
@@ -197,64 +271,3 @@ void TextureShaderCache::Decimate() {
}
gpuStats.perFrame.numClutTextures = (int)texCache_.size();
}
Draw2DPipeline *TextureShaderCache::GetDepalettizeShader(uint32_t clutMode, GETextureFormat textureFormat, GEBufferFormat bufferFormat, bool smoothedDepal, u32 depthUpperBits) {
using namespace Draw;
// Generate an ID for depal shaders.
u64 id = ((u64)depthUpperBits << 32) | (clutMode & 0xFFFFFF) | (textureFormat << 24) | (bufferFormat << 28);
auto shader = depalCache_.find(id);
if (shader != depalCache_.end()) {
return shader->second;
}
// TODO: Parse these out of clutMode some nice way, to become a bit more stateless.
DepalConfig config;
config.clutFormat = gstate.getClutPaletteFormat();
config.startPos = gstate.getClutIndexStartPos();
config.shift = gstate.getClutIndexShift();
config.mask = gstate.getClutIndexMask();
config.bufferFormat = bufferFormat;
config.textureFormat = textureFormat;
config.smoothedDepal = smoothedDepal;
config.depthUpperBits = depthUpperBits;
Draw2DPipeline *ts = draw2D_->Create2DPipeline([config](ShaderWriter &writer) -> Draw2DPipelineInfo {
GenerateDepalFs(writer, config);
return Draw2DPipelineInfo{
"depal",
config.bufferFormat == GE_FORMAT_DEPTH16 ? RASTER_DEPTH : RASTER_COLOR,
RASTER_COLOR,
samplers
};
});
depalCache_[id] = ts;
return ts->pipeline ? ts : nullptr;
}
std::vector<std::string> TextureShaderCache::DebugGetShaderIDs(DebugShaderType type) {
std::vector<std::string> ids;
for (auto &entry : depalCache_) {
ids.push_back(StringFromFormat("%08x", entry.first));
}
return ids;
}
std::string TextureShaderCache::DebugGetShaderString(const std::string &idstr, DebugShaderType type, DebugShaderStringType stringType) {
uint32_t id = 0;
sscanf(idstr.c_str(), "%08x", &id);
auto iter = depalCache_.find(id);
if (iter == depalCache_.end())
return "";
switch (stringType) {
case SHADER_STRING_SHORT_DESC:
return idstr;
case SHADER_STRING_SOURCE_CODE:
return iter->second->code;
default:
return "";
}
}
+17 -6
View File
@@ -17,7 +17,7 @@
#pragma once
#include <map>
#include <unordered_map>
#include <vector>
#include <string>
@@ -36,6 +36,21 @@ public:
int rampStarts[MAX_RAMPS];
};
class ClutTextureCache {
public:
ClutTexture GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, const u32 *rawClut);
void Clear();
void Decimate();
void DeviceLost();
void DeviceRestore(Draw::DrawContext *draw);
private:
Draw::DrawContext *draw_;
std::unordered_map<u32, ClutTexture *> texCache_;
};
// For CLUT depal shaders, and other pre-bind texture shaders.
// Caches both shaders and palette textures.
class TextureShaderCache {
@@ -44,12 +59,10 @@ public:
~TextureShaderCache();
Draw2DPipeline *GetDepalettizeShader(uint32_t clutMode, GETextureFormat texFormat, GEBufferFormat pixelFormat, bool smoothedDepal, u32 depthUpperBits);
ClutTexture GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, const u32 *rawClut);
Draw::SamplerState *GetSampler(bool linearFilter);
void Clear();
void Decimate();
std::vector<std::string> DebugGetShaderIDs(DebugShaderType type);
std::string DebugGetShaderString(const std::string &id, DebugShaderType type, DebugShaderStringType stringType);
@@ -61,7 +74,5 @@ private:
Draw::SamplerState *nearestSampler_ = nullptr;
Draw::SamplerState *linearSampler_ = nullptr;
Draw2D *draw2D_;
std::map<u64, Draw2DPipeline *> depalCache_;
std::map<u32, ClutTexture *> texCache_;
std::unordered_map<u64, Draw2DPipeline *> pipelineCache_;
};
+2 -2
View File
@@ -175,13 +175,13 @@ void TextureCacheD3D11::DestroyDeviceObjects() {
}
void TextureCacheD3D11::DeviceLost() {
Clear(false);
TextureCacheCommon::DeviceLost();
DestroyDeviceObjects();
draw_ = nullptr;
}
void TextureCacheD3D11::DeviceRestore(Draw::DrawContext *draw) {
draw_ = draw;
TextureCacheCommon::DeviceRestore(draw);
InitDeviceObjects();
}
+2 -4
View File
@@ -413,16 +413,14 @@ bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level,
}
void TextureCacheGLES::DeviceLost() {
textureShaderCache_->DeviceLost();
Clear(false);
TextureCacheCommon::DeviceLost();
draw_ = nullptr;
render_ = nullptr;
}
void TextureCacheGLES::DeviceRestore(Draw::DrawContext *draw) {
draw_ = draw;
TextureCacheCommon::DeviceRestore(draw);
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
textureShaderCache_->DeviceRestore(draw);
}
void *TextureCacheGLES::GetNativeTextureView(const TexCacheEntry *entry, bool flat) const {
-3
View File
@@ -40,9 +40,6 @@ public:
void StartFrame() override;
void SetFramebufferManager(FramebufferManagerGLES *fbManager);
void SetDepalShaderCache(TextureShaderCache *dpCache) {
textureShaderCache_ = dpCache;
}
void SetDrawEngine(DrawEngineGLES *td) {
drawEngine_ = td;
}
+2 -2
View File
@@ -651,7 +651,7 @@ std::vector<std::string> GPUCommonHW::DebugGetShaderIDs(DebugShaderType type) {
case SHADER_TYPE_VERTEXLOADER:
return drawEngineCommon_->DebugGetVertexLoaderIDs();
case SHADER_TYPE_TEXTURE:
return textureCache_->GetTextureShaderCache()->DebugGetShaderIDs(type);
return textureCache_->GetTextureShaderCache().DebugGetShaderIDs(type);
default:
return shaderManager_->DebugGetShaderIDs(type);
}
@@ -662,7 +662,7 @@ std::string GPUCommonHW::DebugGetShaderString(std::string id, DebugShaderType ty
case SHADER_TYPE_VERTEXLOADER:
return drawEngineCommon_->DebugGetVertexLoaderString(id, stringType);
case SHADER_TYPE_TEXTURE:
return textureCache_->GetTextureShaderCache()->DebugGetShaderString(id, type, stringType);
return textureCache_->GetTextureShaderCache().DebugGetShaderString(id, type, stringType);
default:
return shaderManager_->DebugGetShaderString(id, type, stringType);
}
+2 -6
View File
@@ -216,12 +216,10 @@ void TextureCacheVulkan::SetFramebufferManager(FramebufferManagerVulkan *fbManag
}
void TextureCacheVulkan::DeviceLost() {
textureShaderCache_->DeviceLost();
TextureCacheCommon::DeviceLost();
VulkanContext *vulkan = draw_ ? (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT) : nullptr;
Clear(true);
samplerCache_.DeviceLost();
if (samplerNearest_)
vulkan->Delete().QueueDeleteSampler(samplerNearest_);
@@ -236,13 +234,11 @@ void TextureCacheVulkan::DeviceLost() {
}
void TextureCacheVulkan::DeviceRestore(Draw::DrawContext *draw) {
draw_ = draw;
TextureCacheCommon::DeviceRestore(draw);
VulkanContext *vulkan = (VulkanContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT);
_assert_(vulkan);
samplerCache_.DeviceRestore(vulkan);
textureShaderCache_->DeviceRestore(draw);
VkSamplerCreateInfo samp{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;