From 2534cb2c9dda511acf94ca74721a192d7aa3d835 Mon Sep 17 00:00:00 2001 From: lightningterror <18107717+lightningterror@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:12:36 +0200 Subject: [PATCH] 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. --- pcsx2/GS/Renderers/DX11/GSDevice11.cpp | 43 +++++++++++-------------- pcsx2/GS/Renderers/DX11/GSDevice11.h | 2 +- pcsx2/GS/Renderers/DX11/GSTexture11.cpp | 18 +++++++++++ pcsx2/GS/Renderers/DX11/GSTexture11.h | 3 ++ pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 2 +- 5 files changed, 42 insertions(+), 26 deletions(-) diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp index 76758d0492..e135e6d0c7 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp @@ -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(rt); } - if (ds) + if (read_only_dsv) + { + dsv = read_only_dsv; + } + else if (ds) { CommitClear(ds); dsv = *static_cast(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(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(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); diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.h b/pcsx2/GS/Renderers/DX11/GSDevice11.h index 91a231f02a..9132f2c05d 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.h +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.h @@ -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); diff --git a/pcsx2/GS/Renderers/DX11/GSTexture11.cpp b/pcsx2/GS/Renderers/DX11/GSTexture11.cpp index 2d52cfc788..a199025b2c 100644 --- a/pcsx2/GS/Renderers/DX11/GSTexture11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSTexture11.cpp @@ -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 tex, u32 width, u32 height, GSTexture::Format format) : GSDownloadTexture(width, height, format) , m_texture(std::move(tex)) diff --git a/pcsx2/GS/Renderers/DX11/GSTexture11.h b/pcsx2/GS/Renderers/DX11/GSTexture11.h index 37ef01c4b0..31c26bc76d 100644 --- a/pcsx2/GS/Renderers/DX11/GSTexture11.h +++ b/pcsx2/GS/Renderers/DX11/GSTexture11.h @@ -17,6 +17,7 @@ class GSTexture11 final : public GSTexture wil::com_ptr_nothrow m_rtv; wil::com_ptr_nothrow m_dsv; wil::com_ptr_nothrow m_uav; + wil::com_ptr_nothrow 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 diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 576dd89abe..e14b15b015 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -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(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) == static_cast(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.