GS/HW: Use sampled area for RT/DS sampling hazards.

This commit is contained in:
TJnotJT
2026-05-23 11:14:46 +02:00
committed by lightningterror
parent 0e178aecb8
commit 9cd0ec1634
8 changed files with 118 additions and 29 deletions
+12 -1
View File
@@ -1426,6 +1426,17 @@ static const char* GetPSAA1Name(u32 aa1)
return "Unknown";
}
static const char* GetTexHazardName(u32 tex_hazard)
{
switch (tex_hazard)
{
case GSHWDrawConfig::TEX_HAZARD_NONE: return "NONE";
case GSHWDrawConfig::TEX_HAZARD_RT: return "RT";
case GSHWDrawConfig::TEX_HAZARD_DEPTH: return "DEPTH";
}
return "Unknown";
}
static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelector& ps)
{
out.WriteLn("aem_fmt: {}", ps.aem_fmt);
@@ -1469,7 +1480,6 @@ 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("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);
@@ -1608,6 +1618,7 @@ static void DumpConfig(DrawConfigWriter& out, const GSHWDrawConfig& conf,
out.WriteLn("topology: {} ({})", GetTopologyName(conf.topology), static_cast<u32>(conf.topology));
out.WriteLn("require_one_barrier: {}", conf.require_one_barrier);
out.WriteLn("require_full_barrier: {}", conf.require_full_barrier);
out.WriteLn("tex_hazard: {}", GetTexHazardName(conf.tex_hazard));
out.WriteLn("destination_alpha: {} ({})", GetDestinationAlphaModeName(conf.destination_alpha), static_cast<u32>(conf.destination_alpha));
out.WriteLn("datm: {} ({})", GetSetDATMName(conf.datm), static_cast<u32>(conf.datm));
+23 -5
View File
@@ -397,7 +397,6 @@ struct alignas(16) GSHWDrawConfig
// Others ways to fetch the texture
u32 channel : 3;
u32 tex_hazard : 2; // 1 rt, 2 ds
// Dithering
u32 dither : 2;
@@ -445,7 +444,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 tex_hazard == 1 || tex_is_fb || fbmask || (date >= 5) || sw_blend_needs_rt || afail_needs_rt;
return tex_is_fb || fbmask || (date >= 5) || sw_blend_needs_rt || afail_needs_rt;
}
__fi bool IsFeedbackLoopDepth() const
@@ -453,7 +452,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 tex_hazard == 2 || afail_needs_depth || ztst_needs_depth || aa1_needs_depth;
return afail_needs_depth || ztst_needs_depth || aa1_needs_depth;
}
/// Disables color output from the pixel shader, this is done when all channels are masked.
@@ -820,10 +819,12 @@ struct alignas(16) GSHWDrawConfig
u32 nverts; ///< Number of vertices
u32 nindices; ///< Number of indices
u32 indices_per_prim; ///< Number of indices that make up one primitive
const std::vector<size_t>* drawlist; ///< For reducing barriers on sprites
const std::vector<GSVector4i>* drawlist_bbox; ///< For RT copy when barriers not available.
const std::vector<size_t>* drawlist; ///< For reducing barriers on sprites
const std::vector<GSVector4i>* drawlist_bbox; ///< For RT copy when barriers not available.
const std::vector<GSVector4i>* drawlist_bbox_tex; ///< Additionally if we need to sample not from the same pixel of the RT.
GSVector4i scissor; ///< Scissor rect
GSVector4i drawarea; ///< Area in the framebuffer which will be modified.
GSVector4i samplearea; ///< Area in the texture which will be sampled.
Topology topology; ///< Draw topology
alignas(8) PSSelector ps;
@@ -837,6 +838,13 @@ struct alignas(16) GSHWDrawConfig
bool require_one_barrier; ///< Require texture barrier before draw (also used to requst an rt copy if texture barrier isn't supported)
bool require_full_barrier; ///< Require texture barrier between all prims
enum : u32
{
TEX_HAZARD_NONE,
TEX_HAZARD_RT,
TEX_HAZARD_DEPTH,
} tex_hazard;
AlphaTestMode alpha_test;
DestinationAlphaMode destination_alpha;
@@ -877,6 +885,16 @@ struct alignas(16) GSHWDrawConfig
GIFRegFRAME colclip_frame;
GSVector4i colclip_update_area; ///< Area in the framebuffer which colclip will modify;
__fi bool IsFeedbackLoopRT(const PSSelector& ps) const
{
return ps.IsFeedbackLoopRT() || (tex_hazard == TEX_HAZARD_RT);
}
__fi bool IsFeedbackLoopDepth(const PSSelector& ps) const
{
return ps.IsFeedbackLoopDepth() || (tex_hazard == TEX_HAZARD_DEPTH);
}
// Dumping
static void DumpConfig(const std::string& path, const GSHWDrawConfig& conf,
bool ps = true, bool vs = true, bool bs = true, bool dss = true, bool ss = true, bool asp = true, bool bmp = true,
+27 -4
View File
@@ -2948,8 +2948,8 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
draw_ds = m_state.current_ds;
}
const bool rt_feedbackloop_pass1 = config.ps.IsFeedbackLoopRT();
const bool rt_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopRT();
const bool rt_feedbackloop_pass1 = config.IsFeedbackLoopRT(config.ps);
const bool rt_feedbackloop_pass2 = config.IsFeedbackLoopRT(config.alpha_second_pass.ps);
if (draw_rt && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(rt_feedbackloop_pass1 || rt_feedbackloop_pass2))))
{
@@ -3034,7 +3034,7 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
const bool one_barrier, const bool full_barrier)
{
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !((config.tex && config.tex == draw_rt) || config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]]
if ((one_barrier || full_barrier) && !((config.tex && config.tex == draw_rt) || config.IsFeedbackLoopRT(config.ps) || config.IsFeedbackLoopDepth(config.ps))) [[unlikely]]
Console.Warning("D3D11: Possible unnecessary copy detected.");
#endif
@@ -3080,7 +3080,30 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
}
if (one_barrier)
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
{
if (config.tex_hazard)
{
GSVector4i union_rect = config.drawarea.runion(config.samplearea);
u32 size_union = union_rect.width() * union_rect.height();
u32 size_indiv = config.drawarea.width() * config.drawarea.height() +
config.samplearea.width() * config.samplearea.height();
// Do an individual copy if the union is larger than the sum of individual areas.
if (size_union > size_indiv)
{
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
CopyAndBind(ProcessCopyArea(rtsize, config.samplearea));
}
else
{
CopyAndBind(ProcessCopyArea(rtsize, union_rect));
}
}
else
{
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
}
}
Draw(config);
}
+3 -3
View File
@@ -4364,8 +4364,8 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
GSTexture12* draw_ds_as_rt = static_cast<GSTexture12*>(m_ds_as_rt);
const bool feedback_rt = draw_rt && (((config.require_one_barrier || (config.require_full_barrier && m_features.texture_barrier)) && (config.ps.IsFeedbackLoopRT() ||
config.alpha_second_pass.ps.IsFeedbackLoopRT())));
const bool feedback_rt = draw_rt && (((config.require_one_barrier || (config.require_full_barrier && m_features.texture_barrier)) && (config.IsFeedbackLoopRT(config.ps) ||
config.IsFeedbackLoopRT(config.alpha_second_pass.ps))));
const bool feedback_depth = draw_ds_as_rt != nullptr;
if (feedback_rt && !m_features.texture_barrier)
@@ -4541,7 +4541,7 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
if (feedback_rt || feedback_depth)
{
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !(config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]]
if ((one_barrier || full_barrier) && !(config.IsFeedbackLoopRT(config.ps) || config.IsFeedbackLoopDepth(config.ps))) [[unlikely]]
Console.Warning("D3D12: Possible unnecessary barrier detected.");
#endif
if ((one_barrier || full_barrier) && feedback_rt)
+21 -6
View File
@@ -1351,13 +1351,20 @@ GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Sou
return half_offset;
}
GSVector4i GSRendererHW::ComputeBoundingBox(const GSVector2i& rtsize, float rtscale)
GSVector4i GSRendererHW::ComputeBoundingBoxRT(const GSVector2i& rtsize, float rtscale)
{
const GSVector4 offset = GSVector4(-1.0f, 1.0f); // Round value
const GSVector4 box = m_vt.m_min.p.upld(m_vt.m_max.p) + offset.xxyy();
return GSVector4i(box * GSVector4(rtscale)).rintersect(GSVector4i(0, 0, rtsize.x, rtsize.y));
}
GSVector4i GSRendererHW::ComputeBoundingBoxTex(const GSVector2i& texsize, const GSVector4i& region, float texscale)
{
const GSVector4 offset = GSVector4(region.xyxy()) + GSVector4(-1.0f, -1.0f, 1.0f, 1.0f); // Region offset + round value
const GSVector4 box = m_vt.m_min.t.upld(m_vt.m_max.t) + offset;
return GSVector4i(box * GSVector4(texscale)).rintersect(GSVector4i(0, 0, texsize.x, texsize.y));
}
void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
{
// Upscaling hack to avoid various line/grid issues
@@ -7884,7 +7891,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
auto HandleBarrierHazard = [&](bool src_empty) {
if (rt && m_conf.tex == m_conf.rt)
{
m_conf.ps.tex_hazard = 1;
m_conf.tex_hazard = GSHWDrawConfig::TEX_HAZARD_RT;
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
@@ -7900,7 +7907,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
// 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;
m_conf.tex_hazard = GSHWDrawConfig::TEX_HAZARD_DEPTH;
if (m_prim_overlap == PRIM_OVERLAP_NO || src_empty || m_channel_shuffle)
m_conf.require_one_barrier = true;
else
@@ -8046,7 +8053,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.tex_hazard = 1;
m_conf.tex_hazard = GSHWDrawConfig::TEX_HAZARD_RT;
target_region = false;
source_region.bits = 0;
@@ -8722,7 +8729,7 @@ void GSRendererHW::EmulateAlphaTestSecondPass()
{
m_conf.alpha_second_pass.ps.DisableDepthOutput();
}
if (m_conf.alpha_second_pass.ps.IsFeedbackLoopRT() || m_conf.alpha_second_pass.ps.IsFeedbackLoopDepth())
if (m_conf.IsFeedbackLoopRT(m_conf.alpha_second_pass.ps) || m_conf.IsFeedbackLoopDepth(m_conf.alpha_second_pass.ps))
{
m_conf.alpha_second_pass.require_one_barrier = m_conf.require_one_barrier;
m_conf.alpha_second_pass.require_full_barrier = m_conf.require_full_barrier;
@@ -8968,6 +8975,9 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
const float rtscale = rt_or_ds->GetScale();
const GSVector2i rtsize = rt_or_ds->GetTexture()->GetSize();
const GSVector2i rt_unscaled_size = rt_or_ds->GetUnscaledSize();
const float texscale = tex ? tex->GetScale() : 0.0f;
const GSVector2i texsize = tex ? tex->GetTexture()->GetSize() : GSVector2i(0, 0);
// Vertex shader config
float vs_scale_x, vs_scale_y;
@@ -9014,7 +9024,12 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
const GSVector4i hacked_scissor = m_channel_shuffle ? GSVector4i::cxpr(0, 0, 1024, 1024) : m_context->scissor.in;
const GSVector4i scissor(GSVector4i(GSVector4(rtscale) * GSVector4(hacked_scissor)).rintersect(GSVector4i::loadh(rtsize)));
m_conf.drawarea = m_channel_shuffle ? scissor : scissor.rintersect(ComputeBoundingBox(rtsize, rtscale));
m_conf.drawarea = m_channel_shuffle ? scissor : scissor.rintersect(ComputeBoundingBoxRT(rtsize, rtscale));
const GSVector4i tex_region = tex ? tex->GetRegionRect() : GSVector4i::zero();
m_conf.samplearea = m_channel_shuffle ? scissor :
GSVector4i::loadh(texsize).rintersect(ComputeBoundingBoxTex(texsize, tex_region, texscale));
m_conf.scissor = (date_options.enabled && !date_options.barrier) ? m_conf.drawarea : scissor;
HandleProvokingVertexFirst();
+2 -1
View File
@@ -349,7 +349,8 @@ public:
bool VerifyIndices();
void ExpandLineIndices();
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
GSVector4i ComputeBoundingBox(const GSVector2i& rtsize, float rtscale);
GSVector4i ComputeBoundingBoxRT(const GSVector2i& rtsize, float rtscale);
GSVector4i ComputeBoundingBoxTex(const GSVector2i& texsize, const GSVector4i& region, float texscale);
void MergeSprite(GSTextureCache::Source* tex);
float GetTextureScaleFactor() override;
GSVector2i GetValidSize(const GSTextureCache::Source* tex = nullptr, const bool is_shuffle = false);
+26 -5
View File
@@ -2828,7 +2828,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
// Pretty sure GL is supposed to guarantee that the blend unit is coherent with previous pixel write out, so calling this a bug.
bool broken_blend_coherency_barrier = false;
if (m_bugs.broken_blend_coherency)
broken_blend_coherency_barrier = (psel.ps.IsFeedbackLoopRT() || psel.ps.blend_c == 1) && GLState::rt == config.rt;
broken_blend_coherency_barrier = (config.IsFeedbackLoopRT(psel.ps) || psel.ps.blend_c == 1) && GLState::rt == config.rt;
if (config.require_one_barrier || !m_features.texture_barrier)
{
broken_blend_coherency_barrier = false;
@@ -2929,8 +2929,8 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
glTextureBarrier();
}
const bool rt_feedbackloop_pass1 = config.ps.IsFeedbackLoopRT();
const bool rt_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopRT();
const bool rt_feedbackloop_pass1 = config.IsFeedbackLoopRT(config.ps);
const bool rt_feedbackloop_pass2 = config.IsFeedbackLoopRT(config.alpha_second_pass.ps);
if (draw_rt && !m_features.texture_barrier && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(rt_feedbackloop_pass1 || rt_feedbackloop_pass2))))
{
@@ -3039,7 +3039,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
{
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) &&
!((!m_features.texture_barrier && config.tex && config.tex == draw_rt) || config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]]
!((!m_features.texture_barrier && config.tex && config.tex == draw_rt) || config.IsFeedbackLoopRT(config.ps) || config.IsFeedbackLoopDepth(config.ps))) [[unlikely]]
Console.Warning("OpenGL: Possible unnecessary barrier detected.");
#endif
@@ -3104,7 +3104,28 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
else
{
// Optimization: For alpha second pass we can reuse the copy snapshot from the first pass.
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
if (config.tex_hazard)
{
GSVector4i union_rect = config.drawarea.runion(config.samplearea);
u32 size_union = union_rect.width() * union_rect.height();
u32 size_indiv = config.drawarea.width() * config.drawarea.height() +
config.samplearea.width() * config.samplearea.height();
// Do an individual copy if the union is larger than the sum of individual areas.
if (size_union > size_indiv)
{
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
CopyAndBind(ProcessCopyArea(rtsize, config.samplearea));
}
else
{
CopyAndBind(ProcessCopyArea(rtsize, union_rect));
}
}
else
{
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
}
}
}
+4 -4
View File
@@ -5994,7 +5994,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
pipe.feedback_loop_flags |= m_current_framebuffer_feedback_loop;
}
if (draw_rt && ((config.require_one_barrier && (config.ps.IsFeedbackLoopRT() || config.alpha_second_pass.ps.IsFeedbackLoopRT()))) && !m_features.texture_barrier)
if (draw_rt && ((config.require_one_barrier && (config.IsFeedbackLoopRT(config.ps) || config.IsFeedbackLoopRT(config.alpha_second_pass.ps)))) && !m_features.texture_barrier)
{
// Requires a copy of the RT.
draw_rt_clone = static_cast<GSTextureVK*>(CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true));
@@ -6222,10 +6222,10 @@ void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config, PipelineSelect
pipe.feedback_loop_flags = FeedbackLoopFlag_None;
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier))
{
if (config.ps.IsFeedbackLoopRT())
if (config.IsFeedbackLoopRT(config.ps))
pipe.feedback_loop_flags |= FeedbackLoopFlag_ReadAndWriteRT;
if (config.ps.IsFeedbackLoopDepth())
if (config.IsFeedbackLoopDepth(config.ps))
pipe.feedback_loop_flags |= FeedbackLoopFlag_ReadAndWriteDepth;
}
if (!(pipe.feedback_loop_flags & FeedbackLoopFlag_ReadAndWriteDepth))
@@ -6297,7 +6297,7 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
}
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !(m_pipeline_selector.ps.IsFeedbackLoopRT() || m_pipeline_selector.ps.IsFeedbackLoopDepth())) [[unlikely]]
if ((one_barrier || full_barrier) && !(config.IsFeedbackLoopRT(m_pipeline_selector.ps) || config.IsFeedbackLoopDepth(m_pipeline_selector.ps))) [[unlikely]]
Console.Warning("VK: Possible unnecessary barrier detected.");
#endif
VkDependencyFlags barrier_flags = GetFeedbackBarrierDependencyFlags();