GS/DX11: Implement depth testing and sampling.

When tex is depth, instead of copying the depth if it needs to be read only
we can instead simultaneously bind a read only depth stencil view and
shader resource view at the same time with no cost avoiding copies.

If the z buffer is read only then no copies are created and can be safely read.
If the z buffer isn't safe to be read then a copy was already created on a previous pass
so we don't have to create another copy on the following pass for reading and doing testing.
This commit is contained in:
lightningterror 2025-08-25 15:12:36 +02:00
parent 49f4c36b0e
commit 2534cb2c9d
5 changed files with 42 additions and 26 deletions

View File

@ -55,7 +55,7 @@ GSDevice11::GSDevice11()
m_features.framebuffer_fetch = false;
m_features.stencil_buffer = true;
m_features.cas_sharpening = true;
m_features.test_and_sample_depth = false;
m_features.test_and_sample_depth = true;
}
GSDevice11::~GSDevice11() = default;
@ -978,7 +978,7 @@ void GSDevice11::EndPresent()
KickTimestampQuery();
// clear out the swap chain view, it might get resized..
OMSetRenderTargets(nullptr, nullptr, nullptr);
OMSetRenderTargets(nullptr, nullptr, nullptr, nullptr);
}
bool GSDevice11::CreateTimestampQueries()
@ -2453,7 +2453,7 @@ void GSDevice11::OMSetBlendState(ID3D11BlendState* bs, u8 bf)
}
}
void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor)
void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor, ID3D11DepthStencilView* read_only_dsv)
{
ID3D11RenderTargetView* rtv = nullptr;
ID3D11DepthStencilView* dsv = nullptr;
@ -2463,7 +2463,11 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
CommitClear(rt);
rtv = *static_cast<GSTexture11*>(rt);
}
if (ds)
if (read_only_dsv)
{
dsv = read_only_dsv;
}
else if (ds)
{
CommitClear(ds);
dsv = *static_cast<GSTexture11*>(ds);
@ -2698,24 +2702,14 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
GSTexture* draw_ds_clone = nullptr;
if (config.tex && config.tex == config.ds)
{
// DX requires a copy when sampling the depth buffer.
draw_ds_clone = CreateDepthStencil(rtsize.x, rtsize.y, config.ds->GetFormat(), false);
if (draw_ds_clone)
{
CopyRect(config.ds, draw_ds_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
PSSetShaderResource(0, draw_ds_clone);
}
else
Console.Warning("D3D11: Failed to allocate temp texture for DS copy.");
}
SetupVS(config.vs, &config.cb_vs);
SetupPS(config.ps, &config.cb_ps, config.sampler);
// Depth testing and sampling, bind resource as dsv read only and srv at the same time without the need of a copy.
ID3D11DepthStencilView* read_only_dsv = nullptr;
if (config.tex && config.tex == config.ds)
read_only_dsv = static_cast<GSTexture11*>(config.ds)->ReadOnlyDepthStencilView();
if (primid_texture)
{
OMDepthStencilSelector dss = config.depth;
@ -2723,7 +2717,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const OMBlendSelector blend(GSHWDrawConfig::ColorMaskSelector(1),
GSHWDrawConfig::BlendState(true, CONST_ONE, CONST_ONE, 3 /* MIN */, CONST_ONE, CONST_ZERO, false, 0));
SetupOM(dss, blend, 0);
OMSetRenderTargets(primid_texture, config.ds, &config.scissor);
OMSetRenderTargets(primid_texture, config.ds, &config.scissor, read_only_dsv);
DrawIndexedPrimitive();
config.ps.date = 3;
@ -2749,7 +2743,11 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
draw_ds = m_state.cached_dsv;
}
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor);
// Update again as it may have changed.
if (config.tex && config.tex == config.ds)
read_only_dsv = static_cast<GSTexture11*>(draw_ds)->ReadOnlyDepthStencilView();
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor, read_only_dsv);
SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend), config.blend.constant);
DrawIndexedPrimitive();
@ -2782,9 +2780,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (draw_rt_clone)
Recycle(draw_rt_clone);
if (draw_ds_clone)
Recycle(draw_ds_clone);
if (primid_texture)
Recycle(primid_texture);

View File

@ -334,7 +334,7 @@ public:
void OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref);
void OMSetBlendState(ID3D11BlendState* bs, u8 bf);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = nullptr);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = nullptr, ID3D11DepthStencilView* read_only_dsv = nullptr);
void SetViewport(const GSVector2i& viewport);
void SetScissor(const GSVector4i& scissor);

View File

@ -173,6 +173,24 @@ GSTexture11::operator ID3D11UnorderedAccessView*()
return m_uav.get();
}
ID3D11DepthStencilView* GSTexture11::ReadOnlyDepthStencilView()
{
if (!m_read_only_dsv)
{
if (m_desc.Format == DXGI_FORMAT_R32G8X24_TYPELESS || m_desc.Format == DXGI_FORMAT_D32_FLOAT_S8X24_UINT)
{
D3D11_DEPTH_STENCIL_VIEW_DESC desc = {};
desc.Format = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
desc.Flags = D3D11_DSV_READ_ONLY_DEPTH;
GSDevice11::GetInstance()->GetD3DDevice()->CreateDepthStencilView(m_texture.get(), &desc, m_read_only_dsv.put());
}
}
return m_read_only_dsv.get();
}
GSDownloadTexture11::GSDownloadTexture11(wil::com_ptr_nothrow<ID3D11Texture2D> tex, u32 width, u32 height, GSTexture::Format format)
: GSDownloadTexture(width, height, format)
, m_texture(std::move(tex))

View File

@ -17,6 +17,7 @@ class GSTexture11 final : public GSTexture
wil::com_ptr_nothrow<ID3D11RenderTargetView> m_rtv;
wil::com_ptr_nothrow<ID3D11DepthStencilView> m_dsv;
wil::com_ptr_nothrow<ID3D11UnorderedAccessView> m_uav;
wil::com_ptr_nothrow<ID3D11DepthStencilView> m_read_only_dsv;
D3D11_TEXTURE2D_DESC m_desc;
public:
@ -41,6 +42,8 @@ public:
operator ID3D11RenderTargetView*();
operator ID3D11DepthStencilView*();
operator ID3D11UnorderedAccessView*();
ID3D11DepthStencilView* ReadOnlyDepthStencilView();
};
class GSDownloadTexture11 final : public GSDownloadTexture

View File

@ -6787,7 +6787,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
// Be careful of single page channel shuffles where depth is the source but it's not going to the same place, we can't read this directly.
else if (ds && m_conf.tex == m_conf.ds && (!m_channel_shuffle || (rt && static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) == static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0))))
{
// GL, Vulkan (in General layout), not DirectX!
// 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.