View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0003499 | NoesisGUI | C++ SDK | public | 2024-07-15 11:43 | 2025-01-11 12:58 |
Reporter | AnKor | Assigned To | jsantos | ||
Priority | normal | Severity | minor | Reproducibility | sometimes |
Status | resolved | Resolution | fixed | ||
Product Version | 3.2.4 | ||||
Target Version | 3.2.7 | Fixed in Version | 3.2.7 | ||
Summary | 0003499: D3D12 debug layer error caused by split barriers | ||||
Description | We have a scenario where render target texture is updated and resolved in one frame, but isn't actually used until the next frame. Version 3.2.4 introduced split barriers and in our case BEGIN_ONLY and END_ONLY barrier halves happen to be in different command lists. I haven't verified thoroughly but at least on one notebook with Win11, Intel 13700H with Xe graphics (no dedicated GPU) this causes debug layer errors: D3D12 ERROR: ID3D12CommandQueue::ExecuteCommandLists: Begin split on ... has no matching END_ONLY barrier in the same ExecuteCommandLists scope. Splitting barriers acrossExecuteCommandLists boundaries is not supported. It also serves no benefit since all syncronization and cache flush operations are guaranteed to be completed in the preceding ExecuteCommandLists scope. [ RESOURCE_MANIPULATION ERROR #535: RESOURCE_BARRIER_UNMATCHED_BEGIN] Discussed in https://www.noesisengine.com/forums/viewtopic.php?t=3377 | ||||
Steps To Reproduce | Update and resolve RT texture like this but don't use it in the same frame: pDevice->SetRenderTarget(pRT); pView->GetRenderer()->Render(false,false); pDevice->ResolveRenderTarget(pRT,0,0); | ||||
Tags | No tags attached. | ||||
Platform | Windows | ||||
The solution for this issue is straightforward: I've attached a patch that introduces a new EndPendingSplitBarriers() function in the device. You need to call this function before invoking ExecuteCommandLists(). However, I'm unsure if this should be officially added to our implementation, as it addresses a very specific aspect of your setup. In our case, the RenderTargets are exclusively used for offscreen rendering, where split barriers are always properly closed. Your use of RenderTargets for capturing a view could be managed on your end by binding and resolving the surface with your own implementation. Adding this to the reference implementation feels like it introduces unnecessary complexity, which could negatively impact other users. What are your thoughts? - EndSplitBarriers.patch (6,633 bytes)
Index: Include/NsRender/D3D12Factory.h =================================================================== --- Include/NsRender/D3D12Factory.h (revision 14871) +++ Include/NsRender/D3D12Factory.h (working copy) @@ -47,6 +47,8 @@ const void* hlsl, uint32_t size); static void ClearPixelShaders(Noesis::RenderDevice* device); + static void EndPendingSplitBarriers(Noesis::RenderDevice* device); + static ID3D12Resource* GetNativePtr(Noesis::Texture* texture); static D3D12_RESOURCE_STATES GetTextureState(Noesis::Texture* texture); static void SetTextureState(Noesis::Texture* texture, D3D12_RESOURCE_STATES state); Index: Src/D3D12RenderDevice.cpp =================================================================== --- Src/D3D12RenderDevice.cpp (revision 14871) +++ Src/D3D12RenderDevice.cpp (working copy) @@ -911,6 +911,7 @@ mCommands = commands; mSafeFenceValue = safeFenceValue; InvalidateStateCache(); + mPendingSplitBarriers.Clear(); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -919,6 +920,7 @@ D3D12Texture* texture = (D3D12Texture*)texture_; PendingRelease p = { texture->srv, 0xFFFF, 0xFFFF, mSafeFenceValue, texture->obj }; mPendingReleases.PushBack(p); + mPendingSplitBarriers.EraseIf([&](Texture* _) { return _ == texture; }); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -941,6 +943,41 @@ } //////////////////////////////////////////////////////////////////////////////////////////////////// +static void EnsureShaderResourceState(ID3D12GraphicsCommandList* commands, D3D12Texture* texture) +{ + if (texture->state != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) + { + D3D12_RESOURCE_BARRIER barrier; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.pResource = texture->obj; + barrier.Transition.StateBefore = texture->state; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + + if (texture->pendingBarrierToSRV) + { + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_END_ONLY; + texture->pendingBarrierToSRV = false; + } + + commands->ResourceBarrier(1, &barrier); + texture->state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void D3D12RenderDevice::EndPendingSplitBarriers() +{ + for (Texture* texture : mPendingSplitBarriers) + { + EnsureShaderResourceState(mCommands, (D3D12Texture*)texture); + } + + mPendingSplitBarriers.Clear(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// ID3D12Resource* D3D12RenderDevice::GetNativePtr(Noesis::Texture* texture) { return ((D3D12Texture*)texture)->obj; @@ -1288,30 +1325,6 @@ } //////////////////////////////////////////////////////////////////////////////////////////////////// -static void EnsureShaderResourceState(ID3D12GraphicsCommandList* commands, D3D12Texture* texture) -{ - if (texture->state != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) - { - D3D12_RESOURCE_BARRIER barrier; - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrier.Transition.pResource = texture->obj; - barrier.Transition.StateBefore = texture->state; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; - - if (texture->pendingBarrierToSRV) - { - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_END_ONLY; - texture->pendingBarrierToSRV = false; - } - - commands->ResourceBarrier(1, &barrier); - texture->state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// void D3D12RenderDevice::SetRenderTarget(RenderTarget* surface_) { D3D12RenderTarget* surface = (D3D12RenderTarget*)surface_; @@ -1440,6 +1453,7 @@ mCommands->ResourceBarrier(1, &barrier); surface->texture->pendingBarrierToSRV = true; + mPendingSplitBarriers.PushBack(surface->texture); DX_RELEASE(commands1); DX_END_EVENT(); @@ -1456,6 +1470,7 @@ mCommands->ResourceBarrier(1, &barrier); surface->texture->pendingBarrierToSRV = true; + mPendingSplitBarriers.PushBack(surface->texture); } if (surface->stencil) Index: Src/D3D12RenderDevice.h =================================================================== --- Src/D3D12RenderDevice.h (revision 14871) +++ Src/D3D12RenderDevice.h (working copy) @@ -54,6 +54,8 @@ void SafeReleaseRenderTarget(Noesis::RenderTarget* surface); void SafeReleaseResource(ID3D12Resource* resource); + void EndPendingSplitBarriers(); + static ID3D12Resource* GetNativePtr(Noesis::Texture* texture); static D3D12_RESOURCE_STATES GetTextureState(Noesis::Texture* texture); static void SetTextureState(Noesis::Texture* texture, D3D12_RESOURCE_STATES state); @@ -250,6 +252,7 @@ }; Noesis::Vector<PendingRelease> mPendingReleases; + Noesis::Vector<Noesis::Texture*> mPendingSplitBarriers; ID3D12RootSignature* mCachedRootSignature; ID3D12PipelineState* mCachedPipelineState; Index: Src/Render.D3D12RenderDevice.cpp =================================================================== --- Src/Render.D3D12RenderDevice.cpp (revision 14871) +++ Src/Render.D3D12RenderDevice.cpp (working copy) @@ -54,6 +54,13 @@ } //////////////////////////////////////////////////////////////////////////////////////////////////// +void D3D12Factory::EndPendingSplitBarriers(RenderDevice* device_) +{ + D3D12RenderDevice* device = (D3D12RenderDevice*)device_; + device->EndPendingSplitBarriers(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// ID3D12Resource* D3D12Factory::GetNativePtr(Texture* texture) { return D3D12RenderDevice::GetNativePtr(texture); |
|
Ok, never mind, I have decided to commit this (r14888) Thanks for the feedback! |
|
Date Modified | Username | Field | Change |
---|---|---|---|
2024-07-15 11:43 | AnKor | New Issue | |
2024-07-16 14:23 | jsantos | Assigned To | => jsantos |
2024-07-16 14:23 | jsantos | Status | new => assigned |
2024-07-16 14:23 | jsantos | Target Version | => 3.2.5 |
2024-10-24 12:22 | jsantos | Target Version | 3.2.5 => 3.2.6 |
2024-11-22 18:19 | jsantos | Target Version | 3.2.6 => 3.2.7 |
2025-01-11 12:34 | jsantos | Note Added: 0010254 | |
2025-01-11 12:34 | jsantos | File Added: EndSplitBarriers.patch | |
2025-01-11 12:34 | jsantos | Status | assigned => feedback |
2025-01-11 12:34 | jsantos | Target Version | 3.2.7 => |
2025-01-11 12:34 | jsantos | Note Edited: 0010254 | |
2025-01-11 12:38 | jsantos | Note Edited: 0010254 | |
2025-01-11 12:38 | jsantos | Note Edited: 0010254 | |
2025-01-11 12:58 | jsantos | Target Version | => 3.2.7 |
2025-01-11 12:58 | jsantos | Status | feedback => resolved |
2025-01-11 12:58 | jsantos | Resolution | open => fixed |
2025-01-11 12:58 | jsantos | Fixed in Version | => 3.2.7 |
2025-01-11 12:58 | jsantos | Note Added: 0010256 |