GS/HW: Handle barrier hazards in RendererHW instead of Device.

Cleaner, easier to track.
This commit is contained in:
lightningterror
2026-05-23 11:14:46 +02:00
parent 054e0509c4
commit f08f679ba7
3 changed files with 41 additions and 8 deletions
+1 -1
View File
@@ -1469,7 +1469,7 @@ static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelect
out.WriteLn("no_color: {}", ps.no_color);
out.WriteLn("no_color1: {}", ps.no_color1);
out.WriteLn("channel: {} ({})", GetPSChannelName(static_cast<ChannelFetch>(ps.channel)), ps.channel);
out.WriteLn("channel_fb: {}", ps.channel_fb);
out.WriteLn("tex_hazard: {}", ps.tex_hazard);
out.WriteLn("dither: {} ({})", GetPSDitherName(ps.dither), ps.dither);
out.WriteLn("dither_adjust: {}", ps.dither_adjust);
out.WriteLn("zclamp: {}", ps.zclamp);
+3 -3
View File
@@ -397,7 +397,7 @@ struct alignas(16) GSHWDrawConfig
// Others ways to fetch the texture
u32 channel : 3;
u32 channel_fb : 1;
u32 tex_hazard : 2; // 1 rt, 2 ds
// Dithering
u32 dither : 2;
@@ -445,7 +445,7 @@ struct alignas(16) GSHWDrawConfig
const u32 sw_blend_bits = blend_a | blend_b | blend_d;
const bool sw_blend_needs_rt = (sw_blend_bits != 0 && ((sw_blend_bits | blend_c) & 1u)) || ((a_masked & blend_c) != 0);
const bool afail_needs_rt = afail == PS_AFAIL::ZB_ONLY || afail == PS_AFAIL::RGB_ONLY || afail == PS_AFAIL::RGB_ONLY_SW_Z;
return channel_fb || tex_is_fb || fbmask || (date >= 5) || sw_blend_needs_rt || afail_needs_rt;
return tex_hazard == 1 || tex_is_fb || fbmask || (date >= 5) || sw_blend_needs_rt || afail_needs_rt;
}
__fi bool IsFeedbackLoopDepth() const
@@ -453,7 +453,7 @@ struct alignas(16) GSHWDrawConfig
const bool afail_needs_depth = afail == PS_AFAIL::FB_ONLY || afail == PS_AFAIL::RGB_ONLY_SW_Z;
const bool ztst_needs_depth = ztst == ZTST_GEQUAL || ztst == ZTST_GREATER;
const bool aa1_needs_depth = aa1 == PS_AA1::TRIANGLE_SW_Z;
return afail_needs_depth || ztst_needs_depth || aa1_needs_depth;
return tex_hazard == 2 || afail_needs_depth || ztst_needs_depth || aa1_needs_depth;
}
/// Disables color output from the pixel shader, this is done when all channels are masked.
+37 -4
View File
@@ -7880,6 +7880,39 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
const int tex_diff = tex->m_from_target ? static_cast<int>(m_cached_ctx.TEX0.TBP0 - tex->m_from_target->m_TEX0.TBP0) : static_cast<int>(m_cached_ctx.TEX0.TBP0 - tex->m_TEX0.TBP0);
const int frame_diff = rt ? static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) : 0;
// Needs to be called everywhere we return early except tex is fb, or read only depth.
auto HandleBarrierHazard = [&](bool src_empty) {
if (rt && m_conf.tex == m_conf.rt)
{
m_conf.ps.tex_hazard = 1;
if (m_prim_overlap == PRIM_OVERLAP_NO || src_empty || m_channel_shuffle || !g_gs_device->Features().feedback_loops())
m_conf.require_one_barrier = true;
else
m_conf.require_full_barrier = true;
}
else if (ds && m_conf.tex == m_conf.ds)
{
// This might be hit again if it's called when there's no match.
if (g_gs_device->Features().test_and_sample_depth && (m_cached_ctx.ZBUF.ZMSK || m_cached_ctx.TEST.ZTST == ZTST_NEVER))
{
// No barrier, depth can be read safely.
}
// TODO: Add gpu copies for depth on VK/DX12 that doesn't rely on feedback loops.
else if (g_gs_device->Features().feedback_loops())
{
m_conf.ps.tex_hazard = 2;
if (m_prim_overlap == PRIM_OVERLAP_NO || src_empty || m_channel_shuffle)
m_conf.require_one_barrier = true;
else
m_conf.require_full_barrier = true;
}
else
{
m_conf.tex = nullptr;
}
}
};
// Detect framebuffer read that will need special handling
const GSTextureCache::Target* src_target = nullptr;
if (!m_downscale_source || !tex->m_from_target)
@@ -7909,6 +7942,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
// If the two don't overlap, there's no need to copy.
if (m_r.rintersect(src_rect).rempty())
{
HandleBarrierHazard(true);
unscaled_size = rt->GetUnscaledSize();
scale = rt->GetScale();
return;
@@ -7919,9 +7953,6 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
}
else if (ds && m_conf.tex == m_conf.ds)
{
// GL, Vulkan (in General layout), DirectX11 (binding dsv as read only) no support for DirectX12 yet!
const bool can_read_current_depth_buffer = g_gs_device->Features().test_and_sample_depth;
// If this is our current Z buffer, we might not be able to read it directly if it's being written to.
// Rather than leaving the backend to do it, we'll check it here.
if ((!m_channel_shuffle || tex_diff == frame_diff) && g_gs_device->Features().test_and_sample_depth && (m_cached_ctx.ZBUF.ZMSK || m_cached_ctx.TEST.ZTST == ZTST_NEVER))
@@ -7941,6 +7972,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
// If the two don't overlap, there's no need to copy.
if (m_r.rintersect(src_rect).rempty())
{
HandleBarrierHazard(true);
unscaled_size = ds->GetUnscaledSize();
scale = ds->GetScale();
return;
@@ -7958,6 +7990,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
else
{
// No match.
HandleBarrierHazard(false);
return;
}
}
@@ -8013,7 +8046,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
}
m_conf.cb_ps.ChannelShuffleOffset = GSVector2((horizontal_offset - m_r.x) * tex->GetScale(), (vertical_offset - m_r.y) * tex->GetScale());
m_conf.ps.channel_fb = 1;
m_conf.ps.tex_hazard = 1;
target_region = false;
source_region.bits = 0;