From 5756e5c9211ac79376514fc7f724ad327bfa774c Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 26 Aug 2019 18:08:05 +0200 Subject: [PATCH] [d3d11] Check for shader resource view hazards Fixes incorrect behaviour in games that try to use a currently bound UAV or render target as a shader resource at the same time. Fixes visual artifacts in Shining Resonance Refrain on AMD hardware. --- src/d3d11/d3d11_context.cpp | 109 ++++++++++++++++++++++++++++++++++-- src/d3d11/d3d11_context.h | 17 ++++++ src/d3d11/d3d11_view.h | 5 ++ src/d3d11/d3d11_view_srv.h | 4 ++ 4 files changed, 131 insertions(+), 4 deletions(-) diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index 09693039..34223112 100644 --- a/src/d3d11/d3d11_context.cpp +++ b/src/d3d11/d3d11_context.cpp @@ -2492,6 +2492,8 @@ namespace dxvk { BindUnorderedAccessView( uavSlotId + i, uav, ctrSlotId + i, ctr); + + TestCsSrvHazards(uav); } } } @@ -2601,13 +2603,25 @@ namespace dxvk { if (!ValidateRenderTargets(NumRTVs, ppRenderTargetViews, pDepthStencilView)) return; - for (uint32_t i = 0; i < m_state.om.renderTargetViews.size(); i++) - m_state.om.renderTargetViews[i] = i < NumRTVs + for (uint32_t i = 0; i < m_state.om.renderTargetViews.size(); i++) { + auto rtv = i < NumRTVs ? static_cast(ppRenderTargetViews[i]) : nullptr; + + if (m_state.om.renderTargetViews[i] != rtv) { + m_state.om.renderTargetViews[i] = rtv; + TestOmSrvHazards(rtv); + } + } + + auto dsv = static_cast(pDepthStencilView); + + if (m_state.om.depthStencilView != dsv) { + m_state.om.depthStencilView = dsv; + TestOmSrvHazards(dsv); + } - m_state.om.depthStencilView = static_cast(pDepthStencilView); - m_state.om.maxRtv = NumRTVs; + m_state.om.maxRtv = NumRTVs; } bool spillRenderPass = false; @@ -2635,6 +2649,8 @@ namespace dxvk { BindUnorderedAccessView( uavSlotId + i, uav, ctrSlotId + i, ctr); + + TestOmSrvHazards(uav); spillRenderPass = true; } @@ -3535,6 +3551,16 @@ namespace dxvk { auto resView = static_cast(ppResources[i]); if (Bindings.views[StartSlot + i] != resView) { + if (unlikely(resView && resView->TestHazards())) { + if (TestSrvHazards(resView)) + resView = nullptr; + + // Only set if necessary, but don't reset it on every + // bind as this would be more expensive than a few + // redundant checks in OMSetRenderTargets and friends. + Bindings.hazardous.set(StartSlot + i, resView); + } + Bindings.views[StartSlot + i] = resView; BindShaderResource(slotId + i, resView); } @@ -3711,6 +3737,81 @@ namespace dxvk { } + template + bool D3D11DeviceContext::TestSrvHazards( + D3D11ShaderResourceView* pView) { + bool hazard = false; + + if (ShaderStage == DxbcProgramType::ComputeShader) { + int32_t uav = m_state.cs.uavMask.findNext(0); + + while (uav >= 0 && !hazard) { + hazard = CheckViewOverlap(pView, m_state.cs.unorderedAccessViews[uav].ptr()); + uav = m_state.cs.uavMask.findNext(uav + 1); + } + } else { + hazard = CheckViewOverlap(pView, m_state.om.depthStencilView.ptr()); + + for (uint32_t i = 0; !hazard && i < m_state.om.maxRtv; i++) + hazard = CheckViewOverlap(pView, m_state.om.renderTargetViews[i].ptr()); + + for (uint32_t i = 0; !hazard && i < m_state.om.maxUav; i++) + hazard = CheckViewOverlap(pView, m_state.ps.unorderedAccessViews[i].ptr()); + } + + return hazard; + } + + + template + void D3D11DeviceContext::TestSrvHazards( + T* pView, + D3D11ShaderResourceBindings& Bindings) { + uint32_t slotId = computeSrvBinding(ShaderStage, 0); + int32_t srvId = Bindings.hazardous.findNext(0); + + while (srvId >= 0) { + auto srv = Bindings.views[srvId].ptr(); + + if (likely(srv && srv->TestHazards())) { + bool hazard = CheckViewOverlap(pView, srv); + + if (unlikely(hazard)) { + Bindings.views[srvId] = nullptr; + Bindings.hazardous.clr(srvId); + + BindShaderResource(slotId + srvId, nullptr); + } + } else { + // Avoid further redundant iterations + Bindings.hazardous.clr(srvId); + } + + srvId = Bindings.hazardous.findNext(srvId + 1); + } + } + + + template + void D3D11DeviceContext::TestOmSrvHazards( + T* pView) { + if (!pView) return; + TestSrvHazards (pView, m_state.vs.shaderResources); + TestSrvHazards (pView, m_state.hs.shaderResources); + TestSrvHazards (pView, m_state.ds.shaderResources); + TestSrvHazards (pView, m_state.gs.shaderResources); + TestSrvHazards (pView, m_state.ps.shaderResources); + } + + + template + void D3D11DeviceContext::TestCsSrvHazards( + T* pView) { + if (!pView) return; + TestSrvHazards (pView, m_state.cs.shaderResources); + } + + bool D3D11DeviceContext::ValidateRenderTargets( UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, diff --git a/src/d3d11/d3d11_context.h b/src/d3d11/d3d11_context.h index 81efeb76..86899576 100644 --- a/src/d3d11/d3d11_context.h +++ b/src/d3d11/d3d11_context.h @@ -800,6 +800,23 @@ namespace dxvk { const D3D11CommonTexture* pTexture, VkImageSubresource Subresource); + template + bool TestSrvHazards( + D3D11ShaderResourceView* pView); + + template + void TestSrvHazards( + T* pView, + D3D11ShaderResourceBindings& Bindings); + + template + void TestOmSrvHazards( + T* pView); + + template + void TestCsSrvHazards( + T* pView); + bool ValidateRenderTargets( UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, diff --git a/src/d3d11/d3d11_view.h b/src/d3d11/d3d11_view.h index eab73c5a..50fcef10 100644 --- a/src/d3d11/d3d11_view.h +++ b/src/d3d11/d3d11_view.h @@ -73,4 +73,9 @@ namespace dxvk { } } + template + bool CheckViewOverlap(const T1* a, const T2* b) { + return a && b && CheckViewOverlap(a->GetViewInfo(), b->GetViewInfo()); + } + } \ No newline at end of file diff --git a/src/d3d11/d3d11_view_srv.h b/src/d3d11/d3d11_view_srv.h index 58ec8d36..fc24718e 100644 --- a/src/d3d11/d3d11_view_srv.h +++ b/src/d3d11/d3d11_view_srv.h @@ -37,6 +37,10 @@ namespace dxvk { return m_info; } + BOOL TestHazards() const { + return m_info.BindFlags & (D3D11_BIND_RENDER_TARGET | D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_UNORDERED_ACCESS); + } + D3D11_RESOURCE_DIMENSION GetResourceType() const { D3D11_RESOURCE_DIMENSION type; m_resource->GetType(&type);