GS/HW: Compute source alpha min/max based on texture instead of CLUT

Stops alpha from unused CLUT colours from leaking into the alpha range,
saves a good number of draw calls on many games, and some RTA
conversions.
This commit is contained in:
Stenzek 2024-04-01 01:12:24 +10:00 committed by Connor McLaughlin
parent 64a471b5a2
commit fce8317da7
5 changed files with 52 additions and 27 deletions

View File

@ -3916,17 +3916,25 @@ void GSState::CalcAlphaMinMax(const int tex_alpha_min, const int tex_alpha_max)
case 1: case 1:
// If we're using the alpha from the texture, not the whole range, we can just use tex_alpha_min/max. // If we're using the alpha from the texture, not the whole range, we can just use tex_alpha_min/max.
// AEM and TA0 re precomputed with GSBlock::ReadAndExpandBlock24, so already worked out for tex_alpha. // AEM and TA0 re precomputed with GSBlock::ReadAndExpandBlock24, so already worked out for tex_alpha.
a.y = (tex_alpha_max < 500) ? min : (env.TEXA.AEM ? 0 : env.TEXA.TA0); a.y = (tex_alpha_max < INVALID_ALPHA_MINMAX) ? min : (env.TEXA.AEM ? 0 : env.TEXA.TA0);
a.w = (tex_alpha_max < 500) ? max : env.TEXA.TA0; a.w = (tex_alpha_max < INVALID_ALPHA_MINMAX) ? max : env.TEXA.TA0;
break; break;
case 2: case 2:
// If we're using the alpha from the texture, not the whole range, we can just use tex_alpha_min/max. // If we're using the alpha from the texture, not the whole range, we can just use tex_alpha_min/max.
// AEM, TA0 and TA1 are precomputed with GSBlock::ReadAndExpandBlock16, so already worked out for tex_alpha. // AEM, TA0 and TA1 are precomputed with GSBlock::ReadAndExpandBlock16, so already worked out for tex_alpha.
a.y = (tex_alpha_max < 500) ? min : (env.TEXA.AEM ? 0 : std::min(env.TEXA.TA0, env.TEXA.TA1)); a.y = (tex_alpha_max < INVALID_ALPHA_MINMAX) ? min : (env.TEXA.AEM ? 0 : std::min(env.TEXA.TA0, env.TEXA.TA1));
a.w = (tex_alpha_max < 500) ? max : std::max(env.TEXA.TA0, env.TEXA.TA1); a.w = (tex_alpha_max < INVALID_ALPHA_MINMAX) ? max : std::max(env.TEXA.TA0, env.TEXA.TA1);
break; break;
case 3: case 3:
m_mem.m_clut.GetAlphaMinMax32(a.y, a.w); if (tex_alpha_max < INVALID_ALPHA_MINMAX)
{
a.y = min;
a.w = max;
}
else
{
m_mem.m_clut.GetAlphaMinMax32(a.y, a.w);
}
break; break;
default: default:
ASSUME(0); ASSUME(0);

View File

@ -122,6 +122,8 @@ private:
} m_tr; } m_tr;
protected: protected:
static constexpr int INVALID_ALPHA_MINMAX = 500;
GSVertex m_v = {}; GSVertex m_v = {};
float m_q = 1.0f; float m_q = 1.0f;
GSVector4i m_scissor_cull_min = {}; GSVector4i m_scissor_cull_min = {};
@ -163,7 +165,7 @@ protected:
GSVertexTrace::VertexAlpha& GetAlphaMinMax() GSVertexTrace::VertexAlpha& GetAlphaMinMax()
{ {
if (!m_vt.m_alpha.valid) if (!m_vt.m_alpha.valid)
CalcAlphaMinMax(0, 500); CalcAlphaMinMax(0, INVALID_ALPHA_MINMAX);
return m_vt.m_alpha; return m_vt.m_alpha;
} }
struct TextureMinMaxResult struct TextureMinMaxResult

View File

@ -2489,7 +2489,7 @@ void GSRendererHW::Draw()
// We don't know the alpha range of direct sources when we first tried to optimize the alpha test. // We don't know the alpha range of direct sources when we first tried to optimize the alpha test.
// Moving the texture lookup before the ATST optimization complicates things a lot, so instead, // Moving the texture lookup before the ATST optimization complicates things a lot, so instead,
// recompute it, and everything derived from it again if it changes. // recompute it, and everything derived from it again if it changes.
if (GSLocalMemory::m_psm[src->m_TEX0.PSM].pal == 0) if (src->m_valid_alpha_minmax)
{ {
CalcAlphaMinMax(src->m_alpha_minmax.first, src->m_alpha_minmax.second); CalcAlphaMinMax(src->m_alpha_minmax.first, src->m_alpha_minmax.second);

View File

@ -993,7 +993,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const bool is_depth, c
if (palette) if (palette)
{ {
AttachPaletteToSource(src, psm_s.pal, true); AttachPaletteToSource(src, psm_s.pal, true, true);
} }
} }
else else
@ -1645,7 +1645,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
if (TEX0.PSM == PSMT8H) if (TEX0.PSM == PSMT8H)
{ {
// Attach palette for GPU texture conversion // Attach palette for GPU texture conversion
AttachPaletteToSource(src, psm_s.pal, true); AttachPaletteToSource(src, psm_s.pal, true, true);
} }
return src; return src;
@ -1666,7 +1666,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
if (TEX0.PSM == PSMT8H) if (TEX0.PSM == PSMT8H)
{ {
// Attach palette for GPU texture conversion // Attach palette for GPU texture conversion
AttachPaletteToSource(src, psm_s.pal, true); AttachPaletteToSource(src, psm_s.pal, true, true);
} }
return src; return src;
@ -1715,6 +1715,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
// Guard against merged targets which don't actually link. // Guard against merged targets which don't actually link.
if (src->m_target && src->m_from_target) if (src->m_target && src->m_from_target)
{ {
src->m_valid_alpha_minmax = true;
if ((src->m_TEX0.PSM & 0xf) == PSMCT24) if ((src->m_TEX0.PSM & 0xf) == PSMCT24)
{ {
src->m_alpha_minmax.first = TEXA.AEM ? 0 : TEXA.TA0; src->m_alpha_minmax.first = TEXA.AEM ? 0 : TEXA.TA0;
@ -1739,7 +1741,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
if (gpu_clut) if (gpu_clut)
AttachPaletteToSource(src, gpu_clut); AttachPaletteToSource(src, gpu_clut);
else if (src->m_palette && (!src->m_palette_obj || !src->ClutMatch({clut, psm_s.pal}))) else if (src->m_palette && (!src->m_palette_obj || !src->ClutMatch({clut, psm_s.pal})))
AttachPaletteToSource(src, psm_s.pal, true); AttachPaletteToSource(src, psm_s.pal, true, true);
} }
src->Update(r); src->Update(r);
@ -4375,6 +4377,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
src->m_from_target = dst; src->m_from_target = dst;
src->m_from_target_TEX0 = dst->m_TEX0; src->m_from_target_TEX0 = dst->m_TEX0;
src->m_valid_alpha_minmax = true;
if ((src->m_TEX0.PSM & 0xf) == PSMCT24) if ((src->m_TEX0.PSM & 0xf) == PSMCT24)
{ {
src->m_alpha_minmax.first = TEXA.AEM ? 0 : TEXA.TA0; src->m_alpha_minmax.first = TEXA.AEM ? 0 : TEXA.TA0;
@ -4399,7 +4402,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
if (psm.pal > 0) if (psm.pal > 0)
{ {
// Attach palette for GPU texture conversion // Attach palette for GPU texture conversion
AttachPaletteToSource(src, psm.pal, true); AttachPaletteToSource(src, psm.pal, true, true);
} }
#ifdef PCSX2_DEVBUILD #ifdef PCSX2_DEVBUILD
@ -4455,6 +4458,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
// Do this first as we could be adding in alpha from an upgraded 24bit target. // Do this first as we could be adding in alpha from an upgraded 24bit target.
dst->Update(); dst->Update();
src->m_valid_alpha_minmax = true;
if ((src->m_TEX0.PSM & 0xf) == PSMCT24) if ((src->m_TEX0.PSM & 0xf) == PSMCT24)
{ {
src->m_alpha_minmax.first = TEXA.AEM ? 0 : TEXA.TA0; src->m_alpha_minmax.first = TEXA.AEM ? 0 : TEXA.TA0;
@ -4710,7 +4714,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
// Because the texture is already on the GPU, CPU can't convert it. // Because the texture is already on the GPU, CPU can't convert it.
if (psm.pal > 0) if (psm.pal > 0)
{ {
AttachPaletteToSource(src, psm.pal, true); AttachPaletteToSource(src, psm.pal, true, true);
} }
// Offset hack. Can be enabled via GS options. // Offset hack. Can be enabled via GS options.
@ -4742,11 +4746,12 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
{ {
src->m_texture = src->m_from_hash_cache->texture; src->m_texture = src->m_from_hash_cache->texture;
src->m_alpha_minmax = src->m_from_hash_cache->alpha_minmax; src->m_alpha_minmax = src->m_from_hash_cache->alpha_minmax;
src->m_valid_alpha_minmax = src->m_from_hash_cache->valid_alpha_minmax;
if (gpu_clut) if (gpu_clut)
AttachPaletteToSource(src, gpu_clut); AttachPaletteToSource(src, gpu_clut);
else if (psm.pal > 0) else if (psm.pal > 0)
AttachPaletteToSource(src, psm.pal, paltex); AttachPaletteToSource(src, psm.pal, paltex, false);
} }
else if (paltex) else if (paltex)
{ {
@ -4762,7 +4767,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
if (gpu_clut) if (gpu_clut)
AttachPaletteToSource(src, gpu_clut); AttachPaletteToSource(src, gpu_clut);
else else
AttachPaletteToSource(src, psm.pal, true); AttachPaletteToSource(src, psm.pal, true, true);
} }
else else
{ {
@ -4778,7 +4783,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
if (gpu_clut) if (gpu_clut)
AttachPaletteToSource(src, gpu_clut); AttachPaletteToSource(src, gpu_clut);
else if (psm.pal > 0) else if (psm.pal > 0)
AttachPaletteToSource(src, psm.pal, false); AttachPaletteToSource(src, psm.pal, false, true);
} }
#ifdef PCSX2_DEVBUILD #ifdef PCSX2_DEVBUILD
@ -5231,10 +5236,12 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
return nullptr; return nullptr;
} }
// upload base level // compute alpha minmax on all textures, unless paltex is on, because not all CLUT colors are used.
const bool is_direct = (GSLocalMemory::m_psm[TEX0.PSM].pal == 0); const bool compute_alpha_minmax = !paltex;
std::pair<u8, u8> alpha_minmax = {0u, 255u}; std::pair<u8, u8> alpha_minmax = {0u, 255u};
PreloadTexture(TEX0, TEXA, region, g_gs_renderer->m_mem, paltex, tex, 0, is_direct ? &alpha_minmax : nullptr);
// upload base level
PreloadTexture(TEX0, TEXA, region, g_gs_renderer->m_mem, paltex, tex, 0, compute_alpha_minmax ? &alpha_minmax : nullptr);
// upload mips if present // upload mips if present
if (lod) if (lod)
@ -5246,8 +5253,8 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
const GIFRegTEX0 MIP_TEX0{g_gs_renderer->GetTex0Layer(basemip + mip)}; const GIFRegTEX0 MIP_TEX0{g_gs_renderer->GetTex0Layer(basemip + mip)};
std::pair<u8, u8> mip_alpha_minmax; std::pair<u8, u8> mip_alpha_minmax;
PreloadTexture(MIP_TEX0, TEXA, region.AdjustForMipmap(mip), g_gs_renderer->m_mem, paltex, tex, mip, PreloadTexture(MIP_TEX0, TEXA, region.AdjustForMipmap(mip), g_gs_renderer->m_mem, paltex, tex, mip,
is_direct ? &mip_alpha_minmax : nullptr); compute_alpha_minmax ? &mip_alpha_minmax : nullptr);
if (!is_direct) if (compute_alpha_minmax)
{ {
alpha_minmax.first = std::min(alpha_minmax.first, mip_alpha_minmax.first); alpha_minmax.first = std::min(alpha_minmax.first, mip_alpha_minmax.first);
alpha_minmax.second = std::max(alpha_minmax.second, mip_alpha_minmax.second); alpha_minmax.second = std::max(alpha_minmax.second, mip_alpha_minmax.second);
@ -5260,7 +5267,7 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
key.RemoveCLUTHash(); key.RemoveCLUTHash();
// insert into the cache cache, and we're done // insert into the cache cache, and we're done
const HashCacheEntry entry{tex, 1u, 0u, alpha_minmax, false}; const HashCacheEntry entry{tex, 1u, 0u, alpha_minmax, compute_alpha_minmax, false};
m_hash_cache_memory_usage += tex->GetMemUsage(); m_hash_cache_memory_usage += tex->GetMemUsage();
return &m_hash_cache.emplace(key, entry).first->second; return &m_hash_cache.emplace(key, entry).first->second;
} }
@ -5916,7 +5923,7 @@ void GSTextureCache::Source::PreloadLevel(int level)
m_layer_hash[level] = hash; m_layer_hash[level] = hash;
// And upload the texture. // And upload the texture.
if (IsPaletteFormat()) if (!m_valid_alpha_minmax)
{ {
PreloadTexture(m_TEX0, m_TEXA, m_region.AdjustForMipmap(level), g_gs_renderer->m_mem, m_palette != nullptr, PreloadTexture(m_TEX0, m_TEXA, m_region.AdjustForMipmap(level), g_gs_renderer->m_mem, m_palette != nullptr,
m_texture, level, nullptr); m_texture, level, nullptr);
@ -6435,11 +6442,15 @@ void GSTextureCache::SourceMap::RemoveAt(Source* s)
delete s; delete s;
} }
void GSTextureCache::AttachPaletteToSource(Source* s, u16 pal, bool need_gs_texture) void GSTextureCache::AttachPaletteToSource(Source* s, u16 pal, bool need_gs_texture, bool update_alpha_minmax)
{ {
s->m_palette_obj = m_palette_map.LookupPalette(pal, need_gs_texture); s->m_palette_obj = m_palette_map.LookupPalette(pal, need_gs_texture);
s->m_palette = need_gs_texture ? s->m_palette_obj->GetPaletteGSTexture() : nullptr; s->m_palette = need_gs_texture ? s->m_palette_obj->GetPaletteGSTexture() : nullptr;
s->m_alpha_minmax = s->m_palette_obj->GetAlphaMinMax(); if (update_alpha_minmax)
{
s->m_alpha_minmax = s->m_palette_obj->GetAlphaMinMax();
s->m_valid_alpha_minmax = true;
}
} }
void GSTextureCache::AttachPaletteToSource(Source* s, GSTexture* gpu_clut) void GSTextureCache::AttachPaletteToSource(Source* s, GSTexture* gpu_clut)
@ -6448,6 +6459,7 @@ void GSTextureCache::AttachPaletteToSource(Source* s, GSTexture* gpu_clut)
s->m_palette = gpu_clut; s->m_palette = gpu_clut;
// Unknown. // Unknown.
s->m_valid_alpha_minmax = false;
s->m_alpha_minmax.first = 0; s->m_alpha_minmax.first = 0;
s->m_alpha_minmax.second = 255; s->m_alpha_minmax.second = 255;
} }
@ -6657,7 +6669,7 @@ void GSTextureCache::InjectHashCacheTexture(const HashCacheKey& key, GSTexture*
{ {
// We must've got evicted before we finished loading. No matter, add it in there anyway; // We must've got evicted before we finished loading. No matter, add it in there anyway;
// if it's not used again, it'll get tossed out later. // if it's not used again, it'll get tossed out later.
const HashCacheEntry entry{tex, 1u, 0u, alpha_minmax, true}; const HashCacheEntry entry{tex, 1u, 0u, alpha_minmax, true, true};
m_hash_cache.emplace(key, entry); m_hash_cache.emplace(key, entry);
return; return;
} }
@ -6665,6 +6677,7 @@ void GSTextureCache::InjectHashCacheTexture(const HashCacheKey& key, GSTexture*
// Reset age so we don't get thrown out too early. // Reset age so we don't get thrown out too early.
it->second.age = 0; it->second.age = 0;
it->second.alpha_minmax = alpha_minmax; it->second.alpha_minmax = alpha_minmax;
it->second.valid_alpha_minmax = true;
// Update memory usage, swap the textures, and recycle the old one for reuse. // Update memory usage, swap the textures, and recycle the old one for reuse.
if (!it->second.is_replacement) if (!it->second.is_replacement)

View File

@ -116,6 +116,7 @@ public:
u32 refcount; u32 refcount;
u16 age; u16 age;
std::pair<u8, u8> alpha_minmax; std::pair<u8, u8> alpha_minmax;
bool valid_alpha_minmax;
bool is_replacement; bool is_replacement;
}; };
@ -286,6 +287,7 @@ public:
bool m_target = false; bool m_target = false;
bool m_target_direct = false; bool m_target_direct = false;
bool m_repeating = false; bool m_repeating = false;
bool m_valid_alpha_minmax = false;
std::pair<u8, u8> m_alpha_minmax = {0u, 255u}; std::pair<u8, u8> m_alpha_minmax = {0u, 255u};
std::vector<GSVector2i>* m_p2t = nullptr; std::vector<GSVector2i>* m_p2t = nullptr;
// Keep a trace of the target origin. There is no guarantee that pointer will // Keep a trace of the target origin. There is no guarantee that pointer will
@ -537,7 +539,7 @@ public:
return (type == DepthStencil) ? "Depth" : "Color"; return (type == DepthStencil) ? "Depth" : "Color";
} }
void AttachPaletteToSource(Source* s, u16 pal, bool need_gs_texture); void AttachPaletteToSource(Source* s, u16 pal, bool need_gs_texture, bool update_alpha_minmax);
void AttachPaletteToSource(Source* s, GSTexture* gpu_clut); void AttachPaletteToSource(Source* s, GSTexture* gpu_clut);
SurfaceOffset ComputeSurfaceOffset(const GSOffset& off, const GSVector4i& r, const Target* t); SurfaceOffset ComputeSurfaceOffset(const GSOffset& off, const GSVector4i& r, const Target* t);
SurfaceOffset ComputeSurfaceOffset(const uint32_t bp, const uint32_t bw, const uint32_t psm, const GSVector4i& r, const Target* t); SurfaceOffset ComputeSurfaceOffset(const uint32_t bp, const uint32_t bw, const uint32_t psm, const GSVector4i& r, const Target* t);