GS/DX11/GL: Adapt full barrier multidraw FB copy for sampling RT/DS hazards.

This commit is contained in:
TJnotJT
2026-05-30 14:52:42 +02:00
committed by lightningterror
parent 02c5f8f648
commit 4d25bdcabb
11 changed files with 213 additions and 99 deletions
+66
View File
@@ -2616,6 +2616,7 @@ void GSState::FlushPrim()
m_quad_check_valid_shuffle = false;
m_drawlist.clear();
m_drawlist_bbox.clear();
m_drawlist_bbox_tex.clear();
if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
@@ -4725,6 +4726,71 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlist(bool save_drawlist, b
}
}
template <u32 primclass, bool fst>
void GSState::GetPrimitiveOverlapDrawlistTextureBBoxImpl(float bbox_scale)
{
pxAssert(m_drawlist_bbox_tex.empty()); // Should only call this once per draw.
constexpr int n = GSUtil::GetClassVertexCount(primclass);
const GSVertex* RESTRICT v = m_vertex->buff;
const u16* RESTRICT index = m_index->buff;
const u32 count = m_index->tail;
for (u32 i = 0; i < count; i += n)
{
const float q = primclass == GS_SPRITE_CLASS ? v[index[i + 1]].RGBAQ.Q : v[index[i]].RGBAQ.Q;
GSVector4 bbox = GetTexCoordsImpl<fst>(v[index[i]], q);
for (u32 j = 1; j < n; j++)
{
const GSVector4 tex = GetTexCoordsImpl<fst>(v[index[i + j]]);
bbox = bbox.min(tex).xyzw(bbox.max(tex));
}
bbox = bbox * bbox_scale; // Account for upscaling.
bbox = bbox.floor().xyzw(bbox.ceil()); // Round.
bbox += GSVector4(-1.0f, -1.0f, 1.0f, 1.0f); // +1 on all sides for bilinear.
m_drawlist_bbox_tex.push_back(GSVector4i(bbox));
}
}
void GSState::GetPrimitiveOverlapDrawlistTextureBBox(float bbox_scale)
{
pxAssert(PRIM->TME);
switch (m_vt.m_primclass)
{
case GS_POINT_CLASS:
if (PRIM->FST)
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_POINT_CLASS, true>(bbox_scale);
else
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_POINT_CLASS, false>(bbox_scale);
break;
case GS_LINE_CLASS:
if (PRIM->FST)
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_LINE_CLASS, true>(bbox_scale);
else
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_LINE_CLASS, false>(bbox_scale);
break;
case GS_TRIANGLE_CLASS:
if (PRIM->FST)
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_TRIANGLE_CLASS, true>(bbox_scale);
else
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_TRIANGLE_CLASS, false>(bbox_scale);
break;
case GS_SPRITE_CLASS:
if (PRIM->FST)
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_SPRITE_CLASS, true>(bbox_scale);
else
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_SPRITE_CLASS, false>(bbox_scale);
break;
default:
pxAssert(false);
return;
}
}
GSState::PRIM_OVERLAP GSState::PrimitiveOverlap(bool save_drawlist)
{
// Assume that 1 triangle, 1 sprite, 1 line, or 3 points can't overlap.
+4
View File
@@ -392,6 +392,7 @@ public:
PRIM_OVERLAP m_prim_overlap = PRIM_OVERLAP_UNKNOW;
std::vector<size_t> m_drawlist;
std::vector<GSVector4i> m_drawlist_bbox;
std::vector<GSVector4i> m_drawlist_bbox_tex;
struct GSPCRTCRegs
{
@@ -535,6 +536,9 @@ public:
PRIM_OVERLAP GetPrimitiveOverlapDrawlistImpl(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f);
PRIM_OVERLAP GetPrimitiveOverlapDrawlist(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f);
PRIM_OVERLAP PrimitiveOverlap(bool save_drawlist = false);
template <u32 primclass, bool fst>
void GetPrimitiveOverlapDrawlistTextureBBoxImpl(float bbox_scale = 1.0f);
void GetPrimitiveOverlapDrawlistTextureBBox(float bbox_scale = 1.0f);
bool SpriteDrawWithoutGaps();
void CalculatePrimitiveCoversWithoutGaps();
GIFRegTEX0 GetTex0Layer(u32 lod);
+59 -47
View File
@@ -3027,6 +3027,58 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
}
void GSDevice11::FeedbackCopyAndBind(const GSHWDrawConfig& config,
GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, const GSVector4i& copyarea)
{
if (rt_clone)
{
CopyRect(rt, rt_clone, copyarea, copyarea.left, copyarea.top);
PSSetShaderResource(2, rt_clone);
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT)
PSSetShaderResource(0, rt_clone);
}
if (ds_clone)
{
CopyRect(ds, ds_clone, copyarea, copyarea.left, copyarea.top);
PSSetShaderResource(4, ds_clone);
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_DEPTH)
PSSetShaderResource(0, ds_clone);
}
}
// Choose the best copy area based on the hazards and whether we need RT and/or DS copies.
void GSDevice11::FeedbackCopyAndBind(const GSHWDrawConfig& config,
GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone,
const GSVector4i& copyarea, const GSVector4i& samplearea)
{
const GSVector4i rtsize = (rt ? rt : ds)->GetRect();
// DX11 can't do partial depth copies so only do attempt individual copies for RT hazards.
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT)
{
const GSVector4i union_rect = config.drawarea.runion(config.samplearea);
const u32 size_union = union_rect.width() * union_rect.height();
const 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)
{
FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.drawarea));
FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.samplearea));
}
else
{
FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, union_rect));
}
}
else
{
// No RT hazards so just need the draw area (or full area for DS).
FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.drawarea));
}
};
void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier)
@@ -3036,27 +3088,6 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
Console.Warning("D3D11: Possible unnecessary copy detected.");
#endif
auto CopyAndBind = [&](GSVector4i drawarea) {
if (draw_rt_clone)
{
CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top);
if ((one_barrier || full_barrier))
PSSetShaderResource(2, draw_rt_clone);
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT)
PSSetShaderResource(0, draw_rt_clone);
}
if (draw_ds_clone)
{
CopyRect(draw_ds, draw_ds_clone, drawarea, drawarea.left, drawarea.top);
if ((one_barrier || full_barrier))
PSSetShaderResource(4, draw_ds_clone);
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_DEPTH)
PSSetShaderResource(0, draw_ds_clone);
}
};
const GSVector4i rtsize(0, 0, (draw_rt ? draw_rt : draw_ds)->GetWidth(), (draw_rt ? draw_rt : draw_ds)->GetHeight());
if (full_barrier)
{
const u32 draw_list_size = static_cast<u32>(config.drawlist->size());
@@ -3067,10 +3098,13 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = (*config.drawlist)[n] * indices_per_prim;
const u32 count = config.drawlist->at(n) * indices_per_prim;
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
const GSVector4i bbox = config.drawlist_bbox->at(n).rintersect(config.drawarea);
const GSVector4i bbox_tex = config.drawlist_bbox_tex ?
config.drawlist_bbox_tex->at(n).rintersect(config.samplearea) : GSVector4i::zero();
FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, bbox, bbox_tex);
Draw(config, p, count);
@@ -3082,29 +3116,7 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
if (one_barrier)
{
// DX11 can't do partial depth copies.
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT)
{
const GSVector4i union_rect = config.drawarea.runion(config.samplearea);
const u32 size_union = union_rect.width() * union_rect.height();
const 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));
}
FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, config.drawarea, config.samplearea);
}
Draw(config);
+6
View File
@@ -372,6 +372,12 @@ public:
void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, u8 afix);
void RenderHW(GSHWDrawConfig& config) override;
void FeedbackCopyAndBind(const GSHWDrawConfig& config,
GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, const GSVector4i& copyarea);
void FeedbackCopyAndBind(const GSHWDrawConfig& config,
GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone,
const GSVector4i& copyarea, const GSVector4i& samplearea);
void SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier);
+1 -1
View File
@@ -4585,7 +4585,7 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = (*config.drawlist)[n] * indices_per_prim;
const u32 count = config.drawlist->at(n) * indices_per_prim;
if (feedback_rt)
FeedbackBarrier(draw_rt);
+12 -2
View File
@@ -6161,7 +6161,7 @@ void GSRendererHW::DetermineVSConfig(GSTextureCache::Target* rt, float rtscale,
m_conf.vs.iip = !IsFlatShaded();
}
void GSRendererHW::DetermineBarriers(GSTextureCache::Target* rt)
void GSRendererHW::DetermineBarriers(GSTextureCache::Target* rt, GSTextureCache::Source* tex)
{
const GSDevice::FeatureSupport& features = g_gs_device->Features();
@@ -6204,6 +6204,16 @@ void GSRendererHW::DetermineBarriers(GSTextureCache::Target* rt)
ComputeDrawlistGetSize(rt->m_scale);
m_conf.drawlist = &m_drawlist;
m_conf.drawlist_bbox = &m_drawlist_bbox;
if (m_conf.tex_hazard != GSHWDrawConfig::TEX_HAZARD_NONE)
{
GetPrimitiveOverlapDrawlistTextureBBox(tex->GetScale());
m_conf.drawlist_bbox_tex = &m_drawlist_bbox_tex;
}
else
{
m_conf.drawlist_bbox_tex = nullptr;
}
}
}
@@ -9027,7 +9037,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
}
// Barriers must be determined before indices are modified via HandleProvokingVertexFirst/SetupIA.
DetermineBarriers(rt);
DetermineBarriers(rt, tex);
// Perform second pass setup here once barriers are determined.
EmulateAlphaTestSecondPass();
+1 -1
View File
@@ -251,7 +251,7 @@ private:
void DetermineVSConfig(GSTextureCache::Target* rt, float rtscale, const GSVector2i& rtsize,
const GSVector2i& unscaled_size, float& vs_scale_x, float& vs_scale_y);
void DetermineBarriers(GSTextureCache::Target* rt);
void DetermineBarriers(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
void SetTCOffset();
bool NextDrawColClip() const;
+1 -1
View File
@@ -2518,7 +2518,7 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder>
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const size_t count = (*config.drawlist)[n] * indices_per_prim;
const size_t count = config.drawlist->at(n) * indices_per_prim;
textureBarrier(enc);
EncodeDraw(enc, topology, count, buffer, off, p);
p += count;
+57 -46
View File
@@ -3033,6 +3033,57 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
}
void GSDeviceOGL::FeedbackCopyAndBind(const GSHWDrawConfig& config,
GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, const GSVector4i& copyarea)
{
if (rt_clone)
{
CopyRect(rt, rt_clone, copyarea, copyarea.left, copyarea.top);
PSSetShaderResource(2, rt_clone);
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT)
PSSetShaderResource(0, rt_clone);
}
if (ds_clone)
{
CopyRect(ds, ds_clone, copyarea, copyarea.left, copyarea.top);
PSSetShaderResource(4, ds_clone);
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_DEPTH)
PSSetShaderResource(0, ds_clone);
}
}
// Choose the best copy area based on the hazards and whether we need RT and/or DS copies.
void GSDeviceOGL::FeedbackCopyAndBind(const GSHWDrawConfig& config,
GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone,
const GSVector4i& copyarea, const GSVector4i& samplearea)
{
const GSVector4i rtsize = (rt ? rt : ds)->GetRect();
if (config.tex_hazard != GSHWDrawConfig::TEX_HAZARD_NONE)
{
const GSVector4i union_rect = config.drawarea.runion(config.samplearea);
const u32 size_union = union_rect.width() * union_rect.height();
const 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)
{
FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.drawarea));
FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.samplearea));
}
else
{
FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, union_rect));
}
}
else
{
// No RT/DS hazards so just need the draw area.
FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.drawarea));
}
}
void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier)
@@ -3042,27 +3093,6 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
Console.Warning("OpenGL: Possible unnecessary barrier detected.");
#endif
auto CopyAndBind = [&](GSVector4i drawarea) {
if (draw_rt_clone)
{
CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top);
if ((one_barrier || full_barrier))
PSSetShaderResource(2, draw_rt_clone);
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT)
PSSetShaderResource(0, draw_rt_clone);
}
if (draw_ds_clone)
{
CopyRect(draw_ds, draw_ds_clone, drawarea, drawarea.left, drawarea.top);
if ((one_barrier || full_barrier))
PSSetShaderResource(4, draw_ds_clone);
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_DEPTH)
PSSetShaderResource(0, draw_ds_clone);
}
};
const GSVector4i rtsize(0, 0, (draw_rt ? draw_rt : draw_ds)->GetWidth(), (draw_rt ? draw_rt : draw_ds)->GetHeight());
if (full_barrier)
{
pxAssert(config.drawlist && !config.drawlist->empty());
@@ -3077,7 +3107,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = (*config.drawlist)[n] * indices_per_prim;
const u32 count = config.drawlist->at(n) * indices_per_prim;
if (m_features.texture_barrier)
{
@@ -3085,8 +3115,10 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
}
else
{
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
const GSVector4i bbox = config.drawlist_bbox->at(n).rintersect(config.drawarea);
const GSVector4i bbox_tex = config.drawlist_bbox_tex ?
config.drawlist_bbox_tex->at(n).rintersect(config.samplearea) : GSVector4i::zero();
FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, bbox, bbox_tex);
}
Draw(config, p, count);
@@ -3106,28 +3138,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
else
{
// Optimization: For alpha second pass we can reuse the copy snapshot from the first pass.
if (config.tex_hazard)
{
const GSVector4i union_rect = config.drawarea.runion(config.samplearea);
const u32 size_union = union_rect.width() * union_rect.height();
const 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));
}
FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, config.drawarea, config.samplearea);
}
}
+5
View File
@@ -352,6 +352,11 @@ public:
void DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, const GSVector2& ds);
void RenderHW(GSHWDrawConfig& config) override;
void FeedbackCopyAndBind(const GSHWDrawConfig& config,
GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, const GSVector4i& copyarea);
void FeedbackCopyAndBind(const GSHWDrawConfig& config,
GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone,
const GSVector4i& copyarea, const GSVector4i& samplearea);
void SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier);
+1 -1
View File
@@ -6370,7 +6370,7 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
{
IssueBarriers();
const u32 count = (*config.drawlist)[n] * indices_per_prim;
const u32 count = config.drawlist->at(n) * indices_per_prim;
Draw(config, p, count);
p += count;
}