GS/HW: Calculate target alpha min/max

This commit is contained in:
refractionpcsx2 2023-07-08 14:26:40 +01:00
parent 472f4922bd
commit 189f6fa2a3
6 changed files with 75 additions and 10 deletions

View File

@ -3669,12 +3669,13 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
return { vr, uses_border };
}
void GSState::CalcAlphaMinMax()
void GSState::CalcAlphaMinMax(const int tex_alpha_min, const int tex_alpha_max)
{
if (m_vt.m_alpha.valid)
if (m_vt.m_alpha.valid && tex_alpha_min == 0 && tex_alpha_max == 255)
return;
int min = 0, max = 0;
// We wanted to force an update as we now know the alpha of the non-indexed texture.
int min = tex_alpha_min, max = tex_alpha_max;
if (IsCoverageAlpha())
{
@ -3692,8 +3693,8 @@ void GSState::CalcAlphaMinMax()
switch (GSLocalMemory::m_psm[context->TEX0.PSM].fmt)
{
case 0:
a.y = 0;
a.w = 0xff;
a.y = min;
a.w = max;
break;
case 1:
a.y = env.TEXA.AEM ? 0 : env.TEXA.TA0;

View File

@ -134,9 +134,6 @@ private:
} m_tr;
private:
void CalcAlphaMinMax();
protected:
GSVertex m_v = {};
float m_q = 1.0f;
@ -179,7 +176,7 @@ protected:
GSVertexTrace::VertexAlpha& GetAlphaMinMax()
{
if (!m_vt.m_alpha.valid)
CalcAlphaMinMax();
CalcAlphaMinMax(0, 255);
return m_vt.m_alpha;
}
struct TextureMinMaxResult
@ -202,6 +199,7 @@ protected:
bool IsMipMapDraw();
bool IsMipMapActive();
bool IsCoverageAlpha();
void CalcAlphaMinMax(const int tex_min, const int tex_max);
public:
struct GSUploadQueue

View File

@ -846,6 +846,8 @@ bool GSHwHack::OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTextu
{
GL_INS("OI_RozenMaidenGebetGarden FB clear");
g_gs_device->ClearRenderTarget(tmp_rt->m_texture, 0);
tmp_rt->m_alpha_max = 0;
tmp_rt->m_alpha_min = 0;
}
return false;

View File

@ -2116,6 +2116,9 @@ void GSRendererHW::Draw()
cleanup_cancelled_draw();
return;
}
if (src->m_target)
CalcAlphaMinMax(src->m_from_target->m_alpha_min, src->m_from_target->m_alpha_max);
}
// Estimate size based on the scissor rectangle and height cache.
@ -4736,6 +4739,52 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
}
// Blend
if (rt)
{
if (!m_channel_shuffle && !m_texture_shuffle)
{
const int fba_value = m_prev_env.CTXT[m_prev_env.PRIM.CTXT].FBA.FBA * 128;
if ((m_cached_ctx.FRAME.FBMSK & 0xff000000) == 0)
{
if (rt->m_valid.rintersect(m_r).eq(rt->m_valid) && PrimitiveCoversWithoutGaps() && !(m_cached_ctx.TEST.DATE || m_cached_ctx.TEST.ATE || m_cached_ctx.TEST.ZTST != ZTST_ALWAYS))
{
rt->m_alpha_max = GetAlphaMinMax().max | fba_value;
rt->m_alpha_min = GetAlphaMinMax().min | fba_value;
}
else
{
rt->m_alpha_max = std::max(GetAlphaMinMax().max | fba_value, rt->m_alpha_max);
rt->m_alpha_min = std::min(GetAlphaMinMax().min | fba_value, rt->m_alpha_min);
}
}
else if ((m_cached_ctx.FRAME.FBMSK & 0xff000000) != 0xff000000) // We can't be sure of the alpha if it's partially masked.
{
rt->m_alpha_max |= std::max(GetAlphaMinMax().max | fba_value, rt->m_alpha_max);
rt->m_alpha_min = std::min(GetAlphaMinMax().min | fba_value, rt->m_alpha_min);
}
else
{
// If both are zero then we probably don't know what the alpha is.
if (rt->m_alpha_max == 0 && rt->m_alpha_min == 0)
{
rt->m_alpha_max = 255;
rt->m_alpha_min = 0;
}
}
}
else if ((m_texture_shuffle && m_conf.ps.write_rg == false) || m_channel_shuffle)
{
rt->m_alpha_max = 255;
rt->m_alpha_min = 0;
}
}
// Not gonna spend too much time with this, it's not likely to be used much, can't be less accurate than it was.
if (ds)
{
ds->m_alpha_max = std::max(ds->m_alpha_max, static_cast<int>(m_vt.m_max.p.z) >> 24);
ds->m_alpha_min = std::min(ds->m_alpha_min, static_cast<int>(m_vt.m_min.p.z) >> 24);
}
bool blending_alpha_pass = false;
if ((!IsOpaque() || m_context->ALPHA.IsBlack()) && rt && (m_conf.colormask.wrgba & 0x7))
@ -5511,6 +5560,8 @@ bool GSRendererHW::TryTargetClear(GSTextureCache::Target* rt, GSTextureCache::Ta
const u32 c = GetConstantDirectWriteMemClearColor();
GL_INS("TryTargetClear(): RT at %x <= %08X", rt->m_TEX0.TBP0, c);
g_gs_device->ClearRenderTarget(rt->m_texture, c);
rt->m_alpha_max = c >> 24;
rt->m_alpha_min = c >> 24;
}
else
{
@ -5527,6 +5578,8 @@ bool GSRendererHW::TryTargetClear(GSTextureCache::Target* rt, GSTextureCache::Ta
const float d = static_cast<float>(z) * (g_gs_device->Features().clip_control ? 0x1p-32f : 0x1p-24f);
GL_INS("TryTargetClear(): DS at %x <= %f", ds->m_TEX0.TBP0, d);
g_gs_device->ClearDepth(ds->m_texture, d);
ds->m_alpha_max = z >> 24;
ds->m_alpha_min = z >> 24;
}
else
{

View File

@ -1403,6 +1403,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
dst->m_32_bits_fmt = dst_match->m_32_bits_fmt;
dst->OffsetHack_modxy = dst_match->OffsetHack_modxy;
dst->m_end_block = dst_match->m_end_block; // If we're copying the size, we need to keep the end block.
ShaderConvert shader;
// m_32_bits_fmt gets set on a shuffle or if the format isn't 16bit.
@ -1432,6 +1433,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
// so just move the dirty rects across.
dst->m_dirty = std::move(dst_match->m_dirty);
dst_match->m_dirty = {};
dst->m_alpha_max = dst_match->m_alpha_max;
dst->m_alpha_min = dst_match->m_alpha_min;
// Don't bother copying the old target in if the whole thing is dirty.
if (dst->m_dirty.empty() || (~dst->m_dirty.GetDirtyChannels() & GSUtil::GetChannelMask(TEX0.PSM)) != 0 ||
@ -2876,6 +2879,8 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
dst->UpdateValidBits(GSLocalMemory::m_psm[DPSM].fmsk);
dst->UpdateValidity(GSVector4i(dx, dy, dx + w, dy + h));
dst->UpdateDrawn(GSVector4i(dx, dy, dx + w, dy + h));
dst->m_alpha_max = src->m_alpha_max;
dst->m_alpha_min = src->m_alpha_min;
// Invalidate any sources that overlap with the target (since they're now stale).
InvalidateVideoMem(g_gs_renderer->m_mem.GetOffset(DBP, DBW, DPSM), GSVector4i(dx, dy, dx + w, dy + h), false);
return true;
@ -4736,6 +4741,8 @@ GSTextureCache::Target::Target(GIFRegTEX0 TEX0, int type, const GSVector2i& unsc
m_unscaled_size = unscaled_size;
m_scale = scale;
m_texture = texture;
m_alpha_min = 0;
m_alpha_max = 0;
m_32_bits_fmt |= (GSLocalMemory::m_psm[TEX0.PSM].trbpp != 16);
}
@ -4872,7 +4879,9 @@ void GSTextureCache::Target::Update()
}
UpdateValidity(total_rect);
// We don't know what the dirty alpha is gonna be, so assume max.
m_alpha_min = 0;
m_alpha_max = 255;
g_gs_device->Recycle(t);
m_dirty.clear();
}

View File

@ -212,6 +212,8 @@ public:
{
public:
const int m_type = 0;
int m_alpha_max = 0;
int m_alpha_min = 0;
// Valid alpha means "we have rendered to the alpha channel of this target".
// A false value means that the alpha in local memory is still valid/up-to-date.