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:
// 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.
a.y = (tex_alpha_max < 500) ? min : (env.TEXA.AEM ? 0 : env.TEXA.TA0);
a.w = (tex_alpha_max < 500) ? max : env.TEXA.TA0;
a.y = (tex_alpha_max < INVALID_ALPHA_MINMAX) ? min : (env.TEXA.AEM ? 0 : env.TEXA.TA0);
a.w = (tex_alpha_max < INVALID_ALPHA_MINMAX) ? max : env.TEXA.TA0;
break;
case 2:
// 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.
a.y = (tex_alpha_max < 500) ? 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.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 < INVALID_ALPHA_MINMAX) ? max : std::max(env.TEXA.TA0, env.TEXA.TA1);
break;
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;
default:
ASSUME(0);

View File

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

View File

@ -993,7 +993,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const bool is_depth, c
if (palette)
{
AttachPaletteToSource(src, psm_s.pal, true);
AttachPaletteToSource(src, psm_s.pal, true, true);
}
}
else
@ -1645,7 +1645,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
if (TEX0.PSM == PSMT8H)
{
// Attach palette for GPU texture conversion
AttachPaletteToSource(src, psm_s.pal, true);
AttachPaletteToSource(src, psm_s.pal, true, true);
}
return src;
@ -1666,7 +1666,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
if (TEX0.PSM == PSMT8H)
{
// Attach palette for GPU texture conversion
AttachPaletteToSource(src, psm_s.pal, true);
AttachPaletteToSource(src, psm_s.pal, true, true);
}
return src;
@ -1715,6 +1715,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
// Guard against merged targets which don't actually link.
if (src->m_target && src->m_from_target)
{
src->m_valid_alpha_minmax = true;
if ((src->m_TEX0.PSM & 0xf) == PSMCT24)
{
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)
AttachPaletteToSource(src, gpu_clut);
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);
@ -4375,6 +4377,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
src->m_from_target = dst;
src->m_from_target_TEX0 = dst->m_TEX0;
src->m_valid_alpha_minmax = true;
if ((src->m_TEX0.PSM & 0xf) == PSMCT24)
{
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)
{
// Attach palette for GPU texture conversion
AttachPaletteToSource(src, psm.pal, true);
AttachPaletteToSource(src, psm.pal, true, true);
}
#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.
dst->Update();
src->m_valid_alpha_minmax = true;
if ((src->m_TEX0.PSM & 0xf) == PSMCT24)
{
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.
if (psm.pal > 0)
{
AttachPaletteToSource(src, psm.pal, true);
AttachPaletteToSource(src, psm.pal, true, true);
}
// 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_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)
AttachPaletteToSource(src, gpu_clut);
else if (psm.pal > 0)
AttachPaletteToSource(src, psm.pal, paltex);
AttachPaletteToSource(src, psm.pal, paltex, false);
}
else if (paltex)
{
@ -4762,7 +4767,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
if (gpu_clut)
AttachPaletteToSource(src, gpu_clut);
else
AttachPaletteToSource(src, psm.pal, true);
AttachPaletteToSource(src, psm.pal, true, true);
}
else
{
@ -4778,7 +4783,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
if (gpu_clut)
AttachPaletteToSource(src, gpu_clut);
else if (psm.pal > 0)
AttachPaletteToSource(src, psm.pal, false);
AttachPaletteToSource(src, psm.pal, false, true);
}
#ifdef PCSX2_DEVBUILD
@ -5231,10 +5236,12 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
return nullptr;
}
// upload base level
const bool is_direct = (GSLocalMemory::m_psm[TEX0.PSM].pal == 0);
// compute alpha minmax on all textures, unless paltex is on, because not all CLUT colors are used.
const bool compute_alpha_minmax = !paltex;
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
if (lod)
@ -5246,8 +5253,8 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
const GIFRegTEX0 MIP_TEX0{g_gs_renderer->GetTex0Layer(basemip + mip)};
std::pair<u8, u8> mip_alpha_minmax;
PreloadTexture(MIP_TEX0, TEXA, region.AdjustForMipmap(mip), g_gs_renderer->m_mem, paltex, tex, mip,
is_direct ? &mip_alpha_minmax : nullptr);
if (!is_direct)
compute_alpha_minmax ? &mip_alpha_minmax : nullptr);
if (compute_alpha_minmax)
{
alpha_minmax.first = std::min(alpha_minmax.first, mip_alpha_minmax.first);
alpha_minmax.second = std::max(alpha_minmax.second, mip_alpha_minmax.second);
@ -5260,7 +5267,7 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
key.RemoveCLUTHash();
// 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();
return &m_hash_cache.emplace(key, entry).first->second;
}
@ -5916,7 +5923,7 @@ void GSTextureCache::Source::PreloadLevel(int level)
m_layer_hash[level] = hash;
// 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,
m_texture, level, nullptr);
@ -6435,11 +6442,15 @@ void GSTextureCache::SourceMap::RemoveAt(Source* 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 = 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)
@ -6448,6 +6459,7 @@ void GSTextureCache::AttachPaletteToSource(Source* s, GSTexture* gpu_clut)
s->m_palette = gpu_clut;
// Unknown.
s->m_valid_alpha_minmax = false;
s->m_alpha_minmax.first = 0;
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;
// 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);
return;
}
@ -6665,6 +6677,7 @@ void GSTextureCache::InjectHashCacheTexture(const HashCacheKey& key, GSTexture*
// Reset age so we don't get thrown out too early.
it->second.age = 0;
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.
if (!it->second.is_replacement)

View File

@ -116,6 +116,7 @@ public:
u32 refcount;
u16 age;
std::pair<u8, u8> alpha_minmax;
bool valid_alpha_minmax;
bool is_replacement;
};
@ -286,6 +287,7 @@ public:
bool m_target = false;
bool m_target_direct = false;
bool m_repeating = false;
bool m_valid_alpha_minmax = false;
std::pair<u8, u8> m_alpha_minmax = {0u, 255u};
std::vector<GSVector2i>* m_p2t = nullptr;
// Keep a trace of the target origin. There is no guarantee that pointer will
@ -537,7 +539,7 @@ public:
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);
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);