Better DRED output for DX12 device-removal crashes - #63
Conversation
Improves DRED breadcrumb reporting so a device-removal log identifies exactly where the GPU stopped, instead of an unordered list of anonymous ops. Barrier flushes and render steps now emit markers that DRED attaches to the breadcrumb stream, so each resource barrier reports its resources and state transitions by name, and the dump tags the in-flight op, names the command list and queue. Fixes several bugs in the existing output loop.
There was a problem hiding this comment.
Cpp-linter Review
Used clang-format v21.1.8
Click here for the full clang-format patch
diff --git a/trinity/TriDevice12.cpp b/trinity/TriDevice12.cpp
index ea6e507..3b7cc4b 100644
--- a/trinity/TriDevice12.cpp
+++ b/trinity/TriDevice12.cpp
@@ -226,4 +226,6 @@ void TriDevice::HandleRenderTick( Be::Time realTime, Be::Time simTime )
- pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "<unnamed>",
- pNode->pCommandList,
- pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "<unnamed>",
- lastCompletedOp, pNode->BreadcrumbCount, pNode->BreadcrumbContextsCount );
+ pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "<unnamed>",
+ pNode->pCommandList,
+ pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "<unnamed>",
+ lastCompletedOp,
+ pNode->BreadcrumbCount,
+ pNode->BreadcrumbContextsCount );
@@ -256 +258,2 @@ void TriDevice::HandleRenderTick( Be::Time realTime, Be::Time simTime )
- char const* status = op == lastCompletedOp ? " - IN FLIGHT" : ( op + 1 == lastCompletedOp ) ? " - Last completed" : "";
+ char const* status = op == lastCompletedOp ? " - IN FLIGHT" : ( op + 1 == lastCompletedOp ) ? " - Last completed" :
+ "";
diff --git a/trinityal/dx12/Tr2RenderContextDx12.cpp b/trinityal/dx12/Tr2RenderContextDx12.cpp
index c848840..c743f08 100644
--- a/trinityal/dx12/Tr2RenderContextDx12.cpp
+++ b/trinityal/dx12/Tr2RenderContextDx12.cpp
@@ -1581,3 +1581,3 @@ void SetDredMarker( ID3D12GraphicsCommandList* commandList, const char* text )
- blob[0] = PIXEvent_SetMarker_NoArgs << 10; // timestamp 0, event type
- blob[1] = 0xFF000000; // ARGB color
- blob[2] = ( UINT64( 8 ) << 55 ) | ( UINT64( 1 ) << 54 ); // string info: copy chunk 8, isANSI
+ blob[0] = PIXEvent_SetMarker_NoArgs << 10; // timestamp 0, event type
+ blob[1] = 0xFF000000; // ARGB color
+ blob[2] = ( UINT64( 8 ) << 55 ) | ( UINT64( 1 ) << 54 ); // string info: copy chunk 8, isANSI
@@ -1763 +1763,2 @@ void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_
- ID3D12Resource* resource = barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ? barrier.UAV.pResource : barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_ALIASING ? barrier.Aliasing.pResourceAfter : barrier.Transition.pResource;
+ ID3D12Resource* resource = barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ? barrier.UAV.pResource : barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_ALIASING ? barrier.Aliasing.pResourceAfter :
+ barrier.Transition.pResource;
Have any feedback or feature suggestions? Share it here.
| pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "<unnamed>", | ||
| pNode->pCommandList, | ||
| pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "<unnamed>", | ||
| lastCompletedOp, pNode->BreadcrumbCount, pNode->BreadcrumbContextsCount ); |
There was a problem hiding this comment.
clang-format suggestion
| pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "<unnamed>", | |
| pNode->pCommandList, | |
| pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "<unnamed>", | |
| lastCompletedOp, pNode->BreadcrumbCount, pNode->BreadcrumbContextsCount ); | |
| pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "<unnamed>", | |
| pNode->pCommandList, | |
| pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "<unnamed>", | |
| lastCompletedOp, | |
| pNode->BreadcrumbCount, | |
| pNode->BreadcrumbContextsCount ); |
|
|
||
| char const* opName = DredBreadcrumbOpName( breadcrumbOp ); | ||
| CCP_LOGERR( "\tOp: %d, %s%ls%s", op, opName, contextString.c_str(), ( op + 1 == lastCompletedOp ) ? " - Last completed" : "" ); | ||
| char const* status = op == lastCompletedOp ? " - IN FLIGHT" : ( op + 1 == lastCompletedOp ) ? " - Last completed" : ""; |
There was a problem hiding this comment.
clang-format suggestion
| char const* status = op == lastCompletedOp ? " - IN FLIGHT" : ( op + 1 == lastCompletedOp ) ? " - Last completed" : ""; | |
| char const* status = op == lastCompletedOp ? " - IN FLIGHT" : ( op + 1 == lastCompletedOp ) ? " - Last completed" : | |
| ""; |
| blob[0] = PIXEvent_SetMarker_NoArgs << 10; // timestamp 0, event type | ||
| blob[1] = 0xFF000000; // ARGB color | ||
| blob[2] = ( UINT64( 8 ) << 55 ) | ( UINT64( 1 ) << 54 ); // string info: copy chunk 8, isANSI |
There was a problem hiding this comment.
clang-format suggestion
| blob[0] = PIXEvent_SetMarker_NoArgs << 10; // timestamp 0, event type | |
| blob[1] = 0xFF000000; // ARGB color | |
| blob[2] = ( UINT64( 8 ) << 55 ) | ( UINT64( 1 ) << 54 ); // string info: copy chunk 8, isANSI | |
| blob[0] = PIXEvent_SetMarker_NoArgs << 10; // timestamp 0, event type | |
| blob[1] = 0xFF000000; // ARGB color | |
| blob[2] = ( UINT64( 8 ) << 55 ) | ( UINT64( 1 ) << 54 ); // string info: copy chunk 8, isANSI |
| for( size_t i = 0; i < count && pos < sizeof( buf ) - 1; ++i ) | ||
| { | ||
| const auto& barrier = barriers[i]; | ||
| ID3D12Resource* resource = barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ? barrier.UAV.pResource : barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_ALIASING ? barrier.Aliasing.pResourceAfter : barrier.Transition.pResource; |
There was a problem hiding this comment.
clang-format suggestion
| ID3D12Resource* resource = barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ? barrier.UAV.pResource : barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_ALIASING ? barrier.Aliasing.pResourceAfter : barrier.Transition.pResource; | |
| ID3D12Resource* resource = barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ? barrier.UAV.pResource : barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_ALIASING ? barrier.Aliasing.pResourceAfter : | |
| barrier.Transition.pResource; |
There was a problem hiding this comment.
Pull request overview
This PR improves DirectX 12 device-removal diagnostics by enabling DRED breadcrumb context strings and emitting richer GPU markers (including barrier details) so breadcrumb dumps can identify what the GPU was doing at the time of removal.
Changes:
- Adds a global toggle (
g_dredBreadcrumbsEnabled) and enables DRED breadcrumb contexts when the DX12 debug layer is requested. - Names key DX12 objects (command list, primary direct queue) to improve DRED output.
- Emits PIX3-blob markers for DRED contexts, including barrier-specific breadcrumb strings, and improves the DRED dump loop output formatting.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| trinityal/Tr2RenderContextEnum.cpp | Adds global flag to gate DRED breadcrumb-context marker emission. |
| trinityal/dx12/Tr2RenderContextDx12.cpp | Adds PIX3-blob marker emission and barrier breadcrumb context strings; names the DX12 command list. |
| trinityal/dx12/Tr2PrimaryRenderContextDx12.cpp | Enables DRED breadcrumb contexts under debug-layer option; names the primary command queue. |
| trinity/TriDevice12.cpp | Improves DRED breadcrumb dump iteration and output to include queue/list names and context strings in a bounded window. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_TRANSITION ) | ||
| { | ||
| pos += size_t( snprintf( buf + pos, sizeof( buf ) - pos, " %s(0x%x->0x%x)", name, barrier.Transition.StateBefore, barrier.Transition.StateAfter ) ); | ||
| } | ||
| else if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ) | ||
| { | ||
| pos += size_t( snprintf( buf + pos, sizeof( buf ) - pos, " UAV(%s)", name ) ); | ||
| } | ||
| else | ||
| { | ||
| pos += size_t( snprintf( buf + pos, sizeof( buf ) - pos, " Alias(%s)", name ) ); | ||
| } |
There was a problem hiding this comment.
Cpp-linter Review
Used clang-format v21.1.8
Click here for the full clang-format patch
diff --git a/trinity/TriDevice12.cpp b/trinity/TriDevice12.cpp
index 20bbeef..9d54772 100644
--- a/trinity/TriDevice12.cpp
+++ b/trinity/TriDevice12.cpp
@@ -226,4 +226,6 @@ void TriDevice::HandleRenderTick( Be::Time realTime, Be::Time simTime )
- pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "<unnamed>",
- pNode->pCommandList,
- pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "<unnamed>",
- lastCompletedOp, pNode->BreadcrumbCount, pNode->BreadcrumbContextsCount );
+ pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "<unnamed>",
+ pNode->pCommandList,
+ pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "<unnamed>",
+ lastCompletedOp,
+ pNode->BreadcrumbCount,
+ pNode->BreadcrumbContextsCount );
@@ -257 +259,2 @@ void TriDevice::HandleRenderTick( Be::Time realTime, Be::Time simTime )
- char const* status = op == lastCompletedOp ? " - IN FLIGHT" : ( op + 1 == lastCompletedOp ) ? " - Last completed" : "";
+ char const* status = op == lastCompletedOp ? " - IN FLIGHT" : ( op + 1 == lastCompletedOp ) ? " - Last completed" :
+ "";
diff --git a/trinityal/dx12/Tr2RenderContextDx12.cpp b/trinityal/dx12/Tr2RenderContextDx12.cpp
index d693ee5..97a3a51 100644
--- a/trinityal/dx12/Tr2RenderContextDx12.cpp
+++ b/trinityal/dx12/Tr2RenderContextDx12.cpp
@@ -1581,3 +1581,3 @@ void SetDredMarker( ID3D12GraphicsCommandList* commandList, const char* text )
- blob[0] = PIXEvent_SetMarker_NoArgs << 10; // timestamp 0, event type
- blob[1] = 0xFF000000; // ARGB color
- blob[2] = ( UINT64( 8 ) << 55 ) | ( UINT64( 1 ) << 54 ); // string info: copy chunk 8, isANSI
+ blob[0] = PIXEvent_SetMarker_NoArgs << 10; // timestamp 0, event type
+ blob[1] = 0xFF000000; // ARGB color
+ blob[2] = ( UINT64( 8 ) << 55 ) | ( UINT64( 1 ) << 54 ); // string info: copy chunk 8, isANSI
@@ -1811 +1811,2 @@ void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_
- ID3D12Resource* resource = barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ? barrier.UAV.pResource : barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_ALIASING ? barrier.Aliasing.pResourceAfter : barrier.Transition.pResource;
+ ID3D12Resource* resource = barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ? barrier.UAV.pResource : barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_ALIASING ? barrier.Aliasing.pResourceAfter :
+ barrier.Transition.pResource;
@@ -1822,3 +1823 @@ void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_
- pos += size_t( snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name,
- FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ),
- FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ) ) );
+ pos += size_t( snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ), FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ) ) );
Have any feedback or feature suggestions? Share it here.
| pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "<unnamed>", | ||
| pNode->pCommandList, | ||
| pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "<unnamed>", | ||
| lastCompletedOp, pNode->BreadcrumbCount, pNode->BreadcrumbContextsCount ); |
There was a problem hiding this comment.
clang-format suggestion
| pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "<unnamed>", | |
| pNode->pCommandList, | |
| pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "<unnamed>", | |
| lastCompletedOp, pNode->BreadcrumbCount, pNode->BreadcrumbContextsCount ); | |
| pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "<unnamed>", | |
| pNode->pCommandList, | |
| pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "<unnamed>", | |
| lastCompletedOp, | |
| pNode->BreadcrumbCount, | |
| pNode->BreadcrumbContextsCount ); |
| CCP_LOGERR( "\tOp: %d, %s%ls%s", op, opName, contextString.c_str(), ( op + 1 == lastCompletedOp ) ? " - Last completed" : "" ); | ||
| // Markers with a context string are our own annotations, not GPU work | ||
| char const* opName = breadcrumbOp == D3D12_AUTO_BREADCRUMB_OP_SETMARKER && !contextString.empty() ? "[Trinity]" : DredBreadcrumbOpName( breadcrumbOp ); | ||
| char const* status = op == lastCompletedOp ? " - IN FLIGHT" : ( op + 1 == lastCompletedOp ) ? " - Last completed" : ""; |
There was a problem hiding this comment.
clang-format suggestion
| char const* status = op == lastCompletedOp ? " - IN FLIGHT" : ( op + 1 == lastCompletedOp ) ? " - Last completed" : ""; | |
| char const* status = op == lastCompletedOp ? " - IN FLIGHT" : ( op + 1 == lastCompletedOp ) ? " - Last completed" : | |
| ""; |
| blob[0] = PIXEvent_SetMarker_NoArgs << 10; // timestamp 0, event type | ||
| blob[1] = 0xFF000000; // ARGB color | ||
| blob[2] = ( UINT64( 8 ) << 55 ) | ( UINT64( 1 ) << 54 ); // string info: copy chunk 8, isANSI |
There was a problem hiding this comment.
clang-format suggestion
| blob[0] = PIXEvent_SetMarker_NoArgs << 10; // timestamp 0, event type | |
| blob[1] = 0xFF000000; // ARGB color | |
| blob[2] = ( UINT64( 8 ) << 55 ) | ( UINT64( 1 ) << 54 ); // string info: copy chunk 8, isANSI | |
| blob[0] = PIXEvent_SetMarker_NoArgs << 10; // timestamp 0, event type | |
| blob[1] = 0xFF000000; // ARGB color | |
| blob[2] = ( UINT64( 8 ) << 55 ) | ( UINT64( 1 ) << 54 ); // string info: copy chunk 8, isANSI |
| for( size_t i = 0; i < count && pos < sizeof( buf ) - 1; ++i ) | ||
| { | ||
| const auto& barrier = barriers[i]; | ||
| ID3D12Resource* resource = barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ? barrier.UAV.pResource : barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_ALIASING ? barrier.Aliasing.pResourceAfter : barrier.Transition.pResource; |
There was a problem hiding this comment.
clang-format suggestion
| ID3D12Resource* resource = barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ? barrier.UAV.pResource : barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_ALIASING ? barrier.Aliasing.pResourceAfter : barrier.Transition.pResource; | |
| ID3D12Resource* resource = barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ? barrier.UAV.pResource : barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_ALIASING ? barrier.Aliasing.pResourceAfter : | |
| barrier.Transition.pResource; |
| pos += size_t( snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, | ||
| FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ), | ||
| FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ) ) ); |
There was a problem hiding this comment.
clang-format suggestion
| pos += size_t( snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, | |
| FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ), | |
| FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ) ) ); | |
| pos += size_t( snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ), FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ) ) ); |
Improves DRED breadcrumb reporting so a device-removal log identifies exactly where the GPU stopped, instead of an unordered list of anonymous ops. Barrier flushes and render steps now emit markers that DRED attaches to the breadcrumb stream, so each resource barrier reports its resources and state transitions by name, and the dump tags the in-flight op, names the command list and queue. Fixes several bugs in the existing output loop.