From c266c14f94782b63eac5c19ab0b51d61c4c37067 Mon Sep 17 00:00:00 2001 From: JohnGreenCCP Date: Fri, 14 Aug 2026 15:40:41 +0100 Subject: [PATCH 01/11] Better DRED output for DX12 device-removal crashes 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. --- trinity/TriDevice12.cpp | 39 ++++++----- trinityal/Tr2RenderContextEnum.cpp | 1 + .../dx12/Tr2PrimaryRenderContextDx12.cpp | 12 +++- trinityal/dx12/Tr2RenderContextDx12.cpp | 68 +++++++++++++++++++ 4 files changed, 101 insertions(+), 19 deletions(-) diff --git a/trinity/TriDevice12.cpp b/trinity/TriDevice12.cpp index cded55596..ea6e5072e 100644 --- a/trinity/TriDevice12.cpp +++ b/trinity/TriDevice12.cpp @@ -212,22 +212,33 @@ void TriDevice::HandleRenderTick( Be::Time realTime, Be::Time simTime ) CCP_LOGERR( "[DRED] Last tracked GPU operations:" ); std::map contextStrings; - D3D12_AUTO_BREADCRUMB_NODE1 const* pNode = dredAutoBreadcrumbsOutput.pHeadAutoBreadcrumbNode; - while( pNode && pNode->pLastBreadcrumbValue ) + for( D3D12_AUTO_BREADCRUMB_NODE1 const* pNode = dredAutoBreadcrumbsOutput.pHeadAutoBreadcrumbNode; pNode; pNode = pNode->pNext ) { + if( !pNode->pLastBreadcrumbValue ) + { + continue; + } UINT lastCompletedOp = *pNode->pLastBreadcrumbValue; - if( lastCompletedOp != (int)pNode->BreadcrumbCount && lastCompletedOp != 0 ) + // Only lists in flight at removal time; 0 = never started, BreadcrumbCount = fully retired + if( lastCompletedOp != pNode->BreadcrumbCount && lastCompletedOp != 0 ) { - CCP_LOGERR( "[DRED] Commandlist completed %d of %d commands", lastCompletedOp, pNode->BreadcrumbCount ); + CCP_LOGERR( "[DRED] Commandlist '%s' (%p) on queue '%s' completed %d of %d commands (%d contexts)", + pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "", + pNode->pCommandList, + pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "", + lastCompletedOp, pNode->BreadcrumbCount, pNode->BreadcrumbContextsCount ); - UINT firstOp = std::max( lastCompletedOp - 100, 0 ); + UINT firstOp = lastCompletedOp > 100 ? lastCompletedOp - 100 : 0; UINT lastOp = std::min( lastCompletedOp + 20, UINT( pNode->BreadcrumbCount ) - 1 ); contextStrings.clear(); - for( UINT breadcrumbContext = firstOp; breadcrumbContext < pNode->BreadcrumbContextsCount; ++breadcrumbContext ) + for( UINT breadcrumbContext = 0; breadcrumbContext < pNode->BreadcrumbContextsCount; ++breadcrumbContext ) { const D3D12_DRED_BREADCRUMB_CONTEXT& context = pNode->pBreadcrumbContexts[breadcrumbContext]; - contextStrings[context.BreadcrumbIndex] = context.pContextString; + if( context.BreadcrumbIndex >= firstOp && context.BreadcrumbIndex <= lastOp ) + { + contextStrings[context.BreadcrumbIndex] = context.pContextString; + } } for( UINT op = firstOp; op <= lastOp; ++op ) @@ -242,27 +253,21 @@ void TriDevice::HandleRenderTick( Be::Time realTime, Be::Time simTime ) } 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" : ""; + CCP_LOGERR( "\tOp: %d, %s %ls%s", op, opName, contextString.c_str(), status ); } } - pNode = pNode->pNext; } } if( SUCCEEDED( pDred->GetPageFaultAllocationOutput1( &dredPageFaultOutput ) ) ) { for( auto node = dredPageFaultOutput.pHeadExistingAllocationNode; node != nullptr; node = node->pNext ) { - if( node->ObjectNameW ) - { - CCP_LOGERR( "Page Fault Allocation on: %ls", node->ObjectNameW ); - } + CCP_LOGERR( "Page Fault Allocation on: %ls (type %d)", node->ObjectNameW ? node->ObjectNameW : L"", node->AllocationType ); } for( auto node = dredPageFaultOutput.pHeadRecentFreedAllocationNode; node != nullptr; node = node->pNext ) { - if( node->ObjectNameW ) - { - CCP_LOGERR( "Page Fault Free on: %ls", node->ObjectNameW ); - } + CCP_LOGERR( "Page Fault Free on: %ls (type %d)", node->ObjectNameW ? node->ObjectNameW : L"", node->AllocationType ); } } } diff --git a/trinityal/Tr2RenderContextEnum.cpp b/trinityal/Tr2RenderContextEnum.cpp index 8e756c70e..b30a89962 100644 --- a/trinityal/Tr2RenderContextEnum.cpp +++ b/trinityal/Tr2RenderContextEnum.cpp @@ -6,6 +6,7 @@ #include "ALLog.h" bool g_requestDebugMarkers = false; +bool g_dredBreadcrumbsEnabled = false; bool g_skipNvidiaStreamline = false; bool g_brokenMacOSNvidiaDrivers = false; diff --git a/trinityal/dx12/Tr2PrimaryRenderContextDx12.cpp b/trinityal/dx12/Tr2PrimaryRenderContextDx12.cpp index 68c3e5ac3..a661c077d 100644 --- a/trinityal/dx12/Tr2PrimaryRenderContextDx12.cpp +++ b/trinityal/dx12/Tr2PrimaryRenderContextDx12.cpp @@ -15,6 +15,7 @@ extern bool g_requestDeviceDebugLayer; extern bool g_requestDebugMarkers; +extern bool g_dredBreadcrumbsEnabled; bool g_gatherPipelineStatistics = false; extern ICrashReporter* TrinityALCrashes; @@ -36,7 +37,7 @@ CCP_STATS_DECLARE( dx12GpuMemoryBudgetLocal, "Trinity/AL/gpuMemory/budgetLocal", namespace { -bool EnableDebugLayer() +void EnableDredBreadcrumbs() { CComPtr pDredSettings; if( SUCCEEDED( D3D12GetDebugInterface( IID_PPV_ARGS( &pDredSettings ) ) ) ) @@ -44,9 +45,14 @@ bool EnableDebugLayer() // Turn on auto-breadcrumbs and page fault reporting. pDredSettings->SetAutoBreadcrumbsEnablement( D3D12_DRED_ENABLEMENT_FORCED_ON ); pDredSettings->SetPageFaultEnablement( D3D12_DRED_ENABLEMENT_FORCED_ON ); + // Capture SetMarker/BeginEvent strings alongside the breadcrumb ops + pDredSettings->SetBreadcrumbContextEnablement( D3D12_DRED_ENABLEMENT_FORCED_ON ); + g_dredBreadcrumbsEnabled = true; } +} - +bool EnableDebugLayer() +{ CComPtr debugInterface; CComPtr debugInterface1; if( SUCCEEDED( D3D12GetDebugInterface( IID_PPV_ARGS( &debugInterface ) ) ) ) @@ -300,6 +306,7 @@ ALResult Tr2PrimaryRenderContextAL::CreateDevice( bool hasDebugLayer = false; if( g_requestDeviceDebugLayer ) { + EnableDredBreadcrumbs(); hasDebugLayer = EnableDebugLayer(); } @@ -393,6 +400,7 @@ ALResult Tr2PrimaryRenderContextAL::CreateDevice( desc.NodeMask = 0; CR_RETURN_HR( CreateCommandQueue( device, &desc, commandQueue ) ); + TrinityALImpl::SetDebugName( commandQueue, "PrimaryDirectQueue" ); const bool isWindowless = ( focusWindow == 0 ) && presentationParameters.software; diff --git a/trinityal/dx12/Tr2RenderContextDx12.cpp b/trinityal/dx12/Tr2RenderContextDx12.cpp index e13747566..c84884033 100644 --- a/trinityal/dx12/Tr2RenderContextDx12.cpp +++ b/trinityal/dx12/Tr2RenderContextDx12.cpp @@ -17,6 +17,7 @@ #include "util/AmdExtDevice.h" extern bool g_requestDebugMarkers; +extern bool g_dredBreadcrumbsEnabled; CCP_STATS_DECLARE( primitiveCount, "Trinity/AL/primitiveCount", true, CST_COUNTER_HIGH, "Primitive count in DrawPrimitive calls." ); CCP_STATS_DECLARE( vertexCount, "Trinity/AL/vertexCount", true, CST_COUNTER_HIGH, "Vertex count in DrawPrimitive calls." ); @@ -139,6 +140,7 @@ ALResult Tr2RenderContextAL::CreateDx12( ID3D12CommandAllocator* commandAllocato commandAllocator, nullptr, IID_PPV_ARGS( &m_commandList ) ) ); + TrinityALImpl::SetDebugName( m_commandList, "Tr2RenderContext CommandList" ); CR_RETURN_HR( m_commandList->Close() ); m_commandList.QueryInterface( &m_commandList2 ); @@ -1568,6 +1570,25 @@ void Tr2RenderContextAL::ResetDx12() m_srgbWriteEnable = false; } +namespace +{ +// DRED breadcrumb contexts are only captured from PIX3-blob markers (Metadata=2, +// WinPixEventRuntime encoding); legacy ANSI/unicode markers are ignored +void SetDredMarker( ID3D12GraphicsCommandList* commandList, const char* text ) +{ + constexpr UINT64 PIXEvent_SetMarker_NoArgs = 0x008; + UINT64 blob[64]; + 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 + size_t lenBytes = strlen( text ) + 1; + size_t qwords = std::min( ( lenBytes + 7 ) / 8, size_t( 60 ) ); + memset( &blob[3], 0, qwords * 8 ); + memcpy( &blob[3], text, std::min( lenBytes, qwords * 8 - 1 ) ); + commandList->SetMarker( 2, blob, UINT( ( 3 + qwords ) * 8 ) ); +} +} + void Tr2RenderContextAL::AddGpuMarker( const char* marker ) { m_ownerDevice->GetMarkerBuffer().PutMarker( m_commandList2, marker ); @@ -1576,6 +1597,10 @@ void Tr2RenderContextAL::AddGpuMarker( const char* marker ) { crashTracker->PutMarker( m_commandList2, marker ); } + if( g_dredBreadcrumbsEnabled ) + { + SetDredMarker( m_commandList, marker ); + } } void Tr2RenderContextAL::PushGpuMarker( const char* marker ) @@ -1720,10 +1745,51 @@ void Tr2RenderContextAL::ResourceBarrierDx12( const D3D12_RESOURCE_BARRIER& barr ResourceBarrierDx12( 1, &barrier ); } +namespace +{ +// Recorded immediately before each ResourceBarrier so the DRED breadcrumb context +// identifies which resources/states the otherwise anonymous RESOURCEBARRIER op contains +void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_RESOURCE_BARRIER* barriers, size_t count ) +{ + if( !g_dredBreadcrumbsEnabled ) + { + return; + } + char buf[512]; + size_t pos = size_t( snprintf( buf, sizeof( buf ), "Barriers:" ) ); + 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; + char name[128]; + UINT nameSize = sizeof( name ) - 1; + if( !resource || FAILED( resource->GetPrivateData( WKPDID_D3DDebugObjectName, &nameSize, name ) ) || nameSize >= sizeof( name ) ) + { + nameSize = UINT( snprintf( name, sizeof( name ), "%p", resource ) ); + } + name[nameSize] = 0; + 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 ) ); + } + } + SetDredMarker( commandList, buf ); +} +} + void Tr2RenderContextAL::FlushBarriersDx12() { if( !m_barriers.empty() ) { + EmitBarrierBreadcrumb( m_commandList, m_barriers.data(), m_barriers.size() ); m_commandList->ResourceBarrier( UINT( m_barriers.size() ), m_barriers.data() ); m_barriers.clear(); } @@ -1771,6 +1837,7 @@ void Tr2RenderContextAL::FlushBarriersDx12( size_t count, ID3D12Resource** resou } if( barrierCount ) { + EmitBarrierBreadcrumb( m_commandList, barriers, barrierCount ); m_commandList->ResourceBarrier( UINT( barrierCount ), barriers ); } } @@ -1797,6 +1864,7 @@ void Tr2RenderContextAL::FlushBarriersDx12( size_t count, ID3D12Resource** resou } if( !barriers.empty() ) { + EmitBarrierBreadcrumb( m_commandList, barriers.data(), barriers.size() ); m_commandList->ResourceBarrier( UINT( barriers.size() ), barriers.data() ); } } From a1d9cd46f840be47878fb641b28ecc60893c3a33 Mon Sep 17 00:00:00 2001 From: JohnGreenCCP Date: Tue, 18 Aug 2026 16:31:36 +0100 Subject: [PATCH 02/11] Output resource state names instead of hex in DRED barrier breadcrumbs --- trinityal/dx12/Tr2RenderContextDx12.cpp | 53 ++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/trinityal/dx12/Tr2RenderContextDx12.cpp b/trinityal/dx12/Tr2RenderContextDx12.cpp index c84884033..471f6c2b0 100644 --- a/trinityal/dx12/Tr2RenderContextDx12.cpp +++ b/trinityal/dx12/Tr2RenderContextDx12.cpp @@ -1747,6 +1747,54 @@ void Tr2RenderContextAL::ResourceBarrierDx12( const D3D12_RESOURCE_BARRIER& barr namespace { +const char* FormatResourceStates( char* buf, size_t size, D3D12_RESOURCE_STATES states ) +{ + if( states == D3D12_RESOURCE_STATE_COMMON ) + { + return "COMMON"; + } + if( states == D3D12_RESOURCE_STATE_GENERIC_READ ) + { + return "GENERIC_READ"; + } + static const struct + { + D3D12_RESOURCE_STATES bit; + const char* name; + } s_stateNames[] = { + { D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, "VB_CB" }, + { D3D12_RESOURCE_STATE_INDEX_BUFFER, "IB" }, + { D3D12_RESOURCE_STATE_RENDER_TARGET, "RT" }, + { D3D12_RESOURCE_STATE_UNORDERED_ACCESS, "UAV" }, + { D3D12_RESOURCE_STATE_DEPTH_WRITE, "DEPTH_W" }, + { D3D12_RESOURCE_STATE_DEPTH_READ, "DEPTH_R" }, + { D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, "SRV_NONPX" }, + { D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, "SRV_PX" }, + { D3D12_RESOURCE_STATE_STREAM_OUT, "STREAM_OUT" }, + { D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, "INDIRECT" }, + { D3D12_RESOURCE_STATE_COPY_DEST, "COPY_DST" }, + { D3D12_RESOURCE_STATE_COPY_SOURCE, "COPY_SRC" }, + { D3D12_RESOURCE_STATE_RESOLVE_DEST, "RESOLVE_DST" }, + { D3D12_RESOURCE_STATE_RESOLVE_SOURCE, "RESOLVE_SRC" }, + { D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, "RTAS" }, + }; + size_t pos = 0; + UINT remaining = UINT( states ); + for( const auto& state : s_stateNames ) + { + if( ( remaining & UINT( state.bit ) ) == UINT( state.bit ) ) + { + pos += size_t( snprintf( buf + pos, size - pos, "%s%s", pos ? "|" : "", state.name ) ); + remaining &= ~UINT( state.bit ); + } + } + if( remaining ) + { + snprintf( buf + pos, size - pos, "%s0x%x", pos ? "|" : "", remaining ); + } + return buf; +} + // Recorded immediately before each ResourceBarrier so the DRED breadcrumb context // identifies which resources/states the otherwise anonymous RESOURCEBARRIER op contains void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_RESOURCE_BARRIER* barriers, size_t count ) @@ -1770,7 +1818,10 @@ void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_ name[nameSize] = 0; 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 ) ); + char before[96], after[96]; + 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 ) ) ); } else if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ) { From 050311f95a2944205fafd3b0dd9b57c22250fbf2 Mon Sep 17 00:00:00 2001 From: JohnGreenCCP Date: Tue, 18 Aug 2026 17:06:51 +0100 Subject: [PATCH 03/11] Improved DRED marker formatting --- trinity/TriDevice12.cpp | 3 ++- trinityal/dx12/Tr2RenderContextDx12.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/trinity/TriDevice12.cpp b/trinity/TriDevice12.cpp index ea6e5072e..20bbeef16 100644 --- a/trinity/TriDevice12.cpp +++ b/trinity/TriDevice12.cpp @@ -252,7 +252,8 @@ void TriDevice::HandleRenderTick( Be::Time realTime, Be::Time simTime ) contextString = it->second; } - char const* opName = DredBreadcrumbOpName( breadcrumbOp ); + // 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" : ""; CCP_LOGERR( "\tOp: %d, %s %ls%s", op, opName, contextString.c_str(), status ); } diff --git a/trinityal/dx12/Tr2RenderContextDx12.cpp b/trinityal/dx12/Tr2RenderContextDx12.cpp index 471f6c2b0..d693ee5dd 100644 --- a/trinityal/dx12/Tr2RenderContextDx12.cpp +++ b/trinityal/dx12/Tr2RenderContextDx12.cpp @@ -1804,7 +1804,7 @@ void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_ return; } char buf[512]; - size_t pos = size_t( snprintf( buf, sizeof( buf ), "Barriers:" ) ); + size_t pos = size_t( snprintf( buf, sizeof( buf ), "[Barrier]" ) ); for( size_t i = 0; i < count && pos < sizeof( buf ) - 1; ++i ) { const auto& barrier = barriers[i]; From d72b816083a0096aa0ea4fa71a3e24fe254e6677 Mon Sep 17 00:00:00 2001 From: JohnGreenFC <152402736+JohnGreenFC@users.noreply.github.com> Date: Wed, 19 Aug 2026 13:17:33 +0100 Subject: [PATCH 04/11] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- trinity/TriDevice12.cpp | 10 ++++++---- trinityal/dx12/Tr2RenderContextDx12.cpp | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/trinity/TriDevice12.cpp b/trinity/TriDevice12.cpp index 20bbeef16..661e1967d 100644 --- a/trinity/TriDevice12.cpp +++ b/trinity/TriDevice12.cpp @@ -223,10 +223,12 @@ void TriDevice::HandleRenderTick( Be::Time realTime, Be::Time simTime ) if( lastCompletedOp != pNode->BreadcrumbCount && lastCompletedOp != 0 ) { CCP_LOGERR( "[DRED] Commandlist '%s' (%p) on queue '%s' completed %d of %d commands (%d contexts)", - pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "", - pNode->pCommandList, - pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "", - lastCompletedOp, pNode->BreadcrumbCount, pNode->BreadcrumbContextsCount ); + pNode->pCommandListDebugNameA ? pNode->pCommandListDebugNameA : "", + pNode->pCommandList, + pNode->pCommandQueueDebugNameA ? pNode->pCommandQueueDebugNameA : "", + lastCompletedOp, + pNode->BreadcrumbCount, + pNode->BreadcrumbContextsCount ); UINT firstOp = lastCompletedOp > 100 ? lastCompletedOp - 100 : 0; UINT lastOp = std::min( lastCompletedOp + 20, UINT( pNode->BreadcrumbCount ) - 1 ); diff --git a/trinityal/dx12/Tr2RenderContextDx12.cpp b/trinityal/dx12/Tr2RenderContextDx12.cpp index d693ee5dd..c04466620 100644 --- a/trinityal/dx12/Tr2RenderContextDx12.cpp +++ b/trinityal/dx12/Tr2RenderContextDx12.cpp @@ -1578,9 +1578,9 @@ void SetDredMarker( ID3D12GraphicsCommandList* commandList, const char* text ) { constexpr UINT64 PIXEvent_SetMarker_NoArgs = 0x008; UINT64 blob[64]; - 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 size_t lenBytes = strlen( text ) + 1; size_t qwords = std::min( ( lenBytes + 7 ) / 8, size_t( 60 ) ); memset( &blob[3], 0, qwords * 8 ); From 98e82901a412550b92ef0ae9ec17852e4e63ca33 Mon Sep 17 00:00:00 2001 From: JohnGreenCCP Date: Wed, 19 Aug 2026 13:24:03 +0100 Subject: [PATCH 05/11] Fixed buffer overrun when formatting truncated resource state names snprintf returns the untruncated length, so pos could pass the end of the buffer and the next snprintf(buf + pos, size - pos, ...) wrote out of bounds (size - pos underflows). Clamp pos to the terminator after every append. --- trinityal/dx12/Tr2RenderContextDx12.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/trinityal/dx12/Tr2RenderContextDx12.cpp b/trinityal/dx12/Tr2RenderContextDx12.cpp index c04466620..4cb7fb567 100644 --- a/trinityal/dx12/Tr2RenderContextDx12.cpp +++ b/trinityal/dx12/Tr2RenderContextDx12.cpp @@ -1747,6 +1747,12 @@ void Tr2RenderContextAL::ResourceBarrierDx12( const D3D12_RESOURCE_BARRIER& barr namespace { +// snprintf returns the untruncated length; clamp so pos never passes the terminator +size_t AdvanceFormatPos( size_t pos, int written, size_t size ) +{ + return written < 0 || pos + size_t( written ) >= size ? size - 1 : pos + size_t( written ); +} + const char* FormatResourceStates( char* buf, size_t size, D3D12_RESOURCE_STATES states ) { if( states == D3D12_RESOURCE_STATE_COMMON ) @@ -1784,7 +1790,7 @@ const char* FormatResourceStates( char* buf, size_t size, D3D12_RESOURCE_STATES { if( ( remaining & UINT( state.bit ) ) == UINT( state.bit ) ) { - pos += size_t( snprintf( buf + pos, size - pos, "%s%s", pos ? "|" : "", state.name ) ); + pos = AdvanceFormatPos( pos, snprintf( buf + pos, size - pos, "%s%s", pos ? "|" : "", state.name ), size ); remaining &= ~UINT( state.bit ); } } @@ -1804,7 +1810,7 @@ void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_ return; } char buf[512]; - size_t pos = size_t( snprintf( buf, sizeof( buf ), "[Barrier]" ) ); + size_t pos = AdvanceFormatPos( 0, snprintf( buf, sizeof( buf ), "[Barrier]" ), sizeof( buf ) ); for( size_t i = 0; i < count && pos < sizeof( buf ) - 1; ++i ) { const auto& barrier = barriers[i]; @@ -1819,17 +1825,17 @@ void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_ if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_TRANSITION ) { char before[96], after[96]; - pos += size_t( snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, + pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ), - FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ) ) ); + FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ) ), sizeof( buf ) ); } else if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ) { - pos += size_t( snprintf( buf + pos, sizeof( buf ) - pos, " UAV(%s)", name ) ); + pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " UAV(%s)", name ), sizeof( buf ) ); } else { - pos += size_t( snprintf( buf + pos, sizeof( buf ) - pos, " Alias(%s)", name ) ); + pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " Alias(%s)", name ), sizeof( buf ) ); } } SetDredMarker( commandList, buf ); From 5c9bcd49fc94a11ce6367780e78ecdaa36d93b9a Mon Sep 17 00:00:00 2001 From: JohnGreenCCP Date: Wed, 19 Aug 2026 14:30:12 +0100 Subject: [PATCH 06/11] Extracted state name formatting out of the breadcrumb format call --- trinityal/dx12/Tr2RenderContextDx12.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/trinityal/dx12/Tr2RenderContextDx12.cpp b/trinityal/dx12/Tr2RenderContextDx12.cpp index 4cb7fb567..9a69f5a65 100644 --- a/trinityal/dx12/Tr2RenderContextDx12.cpp +++ b/trinityal/dx12/Tr2RenderContextDx12.cpp @@ -1825,9 +1825,9 @@ void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_ if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_TRANSITION ) { char before[96], after[96]; - pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, - FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ), - FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ) ), sizeof( buf ) ); + const char* beforeStates = FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ); + const char* afterStates = FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ); + pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, beforeStates, afterStates ), sizeof( buf ) ); } else if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ) { From 86a777df89510e3f01ace5814995a017f33cffd4 Mon Sep 17 00:00:00 2001 From: JohnGreenCCP Date: Wed, 19 Aug 2026 14:31:17 +0100 Subject: [PATCH 07/11] Extracted state name formatting out of the breadcrumb format call --- trinityal/dx12/Tr2RenderContextDx12.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/trinityal/dx12/Tr2RenderContextDx12.cpp b/trinityal/dx12/Tr2RenderContextDx12.cpp index 4cb7fb567..9a69f5a65 100644 --- a/trinityal/dx12/Tr2RenderContextDx12.cpp +++ b/trinityal/dx12/Tr2RenderContextDx12.cpp @@ -1825,9 +1825,9 @@ void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_ if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_TRANSITION ) { char before[96], after[96]; - pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, - FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ), - FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ) ), sizeof( buf ) ); + const char* beforeStates = FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ); + const char* afterStates = FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ); + pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, beforeStates, afterStates ), sizeof( buf ) ); } else if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ) { From f885c9bf21ee08dac1a74f1b89d658f3a1225fe7 Mon Sep 17 00:00:00 2001 From: JohnGreenFC <152402736+JohnGreenFC@users.noreply.github.com> Date: Wed, 19 Aug 2026 14:40:16 +0100 Subject: [PATCH 08/11] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- trinity/TriDevice12.cpp | 3 ++- trinityal/dx12/Tr2RenderContextDx12.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/trinity/TriDevice12.cpp b/trinity/TriDevice12.cpp index 661e1967d..9d5477210 100644 --- a/trinity/TriDevice12.cpp +++ b/trinity/TriDevice12.cpp @@ -256,7 +256,8 @@ void TriDevice::HandleRenderTick( Be::Time realTime, Be::Time simTime ) // 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" : ""; + char const* status = op == lastCompletedOp ? " - IN FLIGHT" : ( op + 1 == lastCompletedOp ) ? " - Last completed" : + ""; CCP_LOGERR( "\tOp: %d, %s %ls%s", op, opName, contextString.c_str(), status ); } } diff --git a/trinityal/dx12/Tr2RenderContextDx12.cpp b/trinityal/dx12/Tr2RenderContextDx12.cpp index 9a69f5a65..445d54446 100644 --- a/trinityal/dx12/Tr2RenderContextDx12.cpp +++ b/trinityal/dx12/Tr2RenderContextDx12.cpp @@ -1814,7 +1814,8 @@ void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_ 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; + 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; char name[128]; UINT nameSize = sizeof( name ) - 1; if( !resource || FAILED( resource->GetPrivateData( WKPDID_D3DDebugObjectName, &nameSize, name ) ) || nameSize >= sizeof( name ) ) From cd826a9b922e42f6c1c3d18cc303ec9fc0c84973 Mon Sep 17 00:00:00 2001 From: JohnGreenCCP Date: Thu, 20 Aug 2026 11:07:47 +0100 Subject: [PATCH 09/11] Log ANSI debug names in DRED page fault allocation output --- trinity/TriDevice12.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/trinity/TriDevice12.cpp b/trinity/TriDevice12.cpp index 9d5477210..1ef8cc289 100644 --- a/trinity/TriDevice12.cpp +++ b/trinity/TriDevice12.cpp @@ -265,13 +265,24 @@ void TriDevice::HandleRenderTick( Be::Time realTime, Be::Time simTime ) } if( SUCCEEDED( pDred->GetPageFaultAllocationOutput1( &dredPageFaultOutput ) ) ) { + // Engine names are ANSI (WKPDID_D3DDebugObjectName), so DRED fills ObjectNameA; ObjectNameW only holds names set via SetName + auto logAllocationNode = []( const char* prefix, const D3D12_DRED_ALLOCATION_NODE1* node ) { + if( node->ObjectNameA ) + { + CCP_LOGERR( "%s: %s (type %d)", prefix, node->ObjectNameA, node->AllocationType ); + } + else + { + CCP_LOGERR( "%s: %ls (type %d)", prefix, node->ObjectNameW ? node->ObjectNameW : L"", node->AllocationType ); + } + }; for( auto node = dredPageFaultOutput.pHeadExistingAllocationNode; node != nullptr; node = node->pNext ) { - CCP_LOGERR( "Page Fault Allocation on: %ls (type %d)", node->ObjectNameW ? node->ObjectNameW : L"", node->AllocationType ); + logAllocationNode( "Page Fault Allocation on", node ); } for( auto node = dredPageFaultOutput.pHeadRecentFreedAllocationNode; node != nullptr; node = node->pNext ) { - CCP_LOGERR( "Page Fault Free on: %ls (type %d)", node->ObjectNameW ? node->ObjectNameW : L"", node->AllocationType ); + logAllocationNode( "Page Fault Free on", node ); } } } From deaeb3faed6462499b6f3d4620329adf0768152e Mon Sep 17 00:00:00 2001 From: JohnGreenCCP Date: Thu, 20 Aug 2026 11:07:47 +0100 Subject: [PATCH 10/11] Emit DRED barrier breadcrumbs for barriers issued outside the flush path --- trinity/Tr2IndirectDrawBuffer.cpp | 7 +- trinityal/dx12/Tr2RenderContextDx12.cpp | 128 +----------------- ...2RtTopLevelAccelerationStructureALDx12.cpp | 8 +- trinityal/dx12/Tr2TextureALDx12.cpp | 50 +++---- trinityal/dx12/Utilities.cpp | 122 +++++++++++++++++ trinityal/dx12/Utilities.h | 4 + 6 files changed, 164 insertions(+), 155 deletions(-) diff --git a/trinity/Tr2IndirectDrawBuffer.cpp b/trinity/Tr2IndirectDrawBuffer.cpp index 576e451f2..777ce633b 100644 --- a/trinity/Tr2IndirectDrawBuffer.cpp +++ b/trinity/Tr2IndirectDrawBuffer.cpp @@ -4,6 +4,9 @@ #include "Tr2IndirectDrawBuffer.h" #include "Tr2Renderer.h" #include "../trinityal/metal/Tr2ShaderProgramALMetal.h" +#if TRINITY_PLATFORM == TRINITY_DIRECTX12 +#include "../trinityal/dx12/Utilities.h" +#endif CCP_STATS_DECLARE( sceneExecuteIndirectCount, "Trinity/AL/sceneExecuteIndirectCount", true, CST_COUNTER_LOW, "Number of ExecuteIndirect calls." ); @@ -328,7 +331,7 @@ void Tr2IndirectDrawBuffer::CopyArguments() transition }; - renderContext.m_commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( renderContext.m_commandList, 1, &barrier ); } for( int i = 0; i <= copyIndex; i++ ) @@ -352,7 +355,7 @@ void Tr2IndirectDrawBuffer::CopyArguments() transition }; - renderContext.m_commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( renderContext.m_commandList, 1, &barrier ); } #endif } diff --git a/trinityal/dx12/Tr2RenderContextDx12.cpp b/trinityal/dx12/Tr2RenderContextDx12.cpp index 445d54446..dff1c56f9 100644 --- a/trinityal/dx12/Tr2RenderContextDx12.cpp +++ b/trinityal/dx12/Tr2RenderContextDx12.cpp @@ -1570,25 +1570,6 @@ void Tr2RenderContextAL::ResetDx12() m_srgbWriteEnable = false; } -namespace -{ -// DRED breadcrumb contexts are only captured from PIX3-blob markers (Metadata=2, -// WinPixEventRuntime encoding); legacy ANSI/unicode markers are ignored -void SetDredMarker( ID3D12GraphicsCommandList* commandList, const char* text ) -{ - constexpr UINT64 PIXEvent_SetMarker_NoArgs = 0x008; - UINT64 blob[64]; - 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 - size_t lenBytes = strlen( text ) + 1; - size_t qwords = std::min( ( lenBytes + 7 ) / 8, size_t( 60 ) ); - memset( &blob[3], 0, qwords * 8 ); - memcpy( &blob[3], text, std::min( lenBytes, qwords * 8 - 1 ) ); - commandList->SetMarker( 2, blob, UINT( ( 3 + qwords ) * 8 ) ); -} -} - void Tr2RenderContextAL::AddGpuMarker( const char* marker ) { m_ownerDevice->GetMarkerBuffer().PutMarker( m_commandList2, marker ); @@ -1599,7 +1580,7 @@ void Tr2RenderContextAL::AddGpuMarker( const char* marker ) } if( g_dredBreadcrumbsEnabled ) { - SetDredMarker( m_commandList, marker ); + TrinityALImpl::SetDredMarker( m_commandList, marker ); } } @@ -1745,110 +1726,11 @@ void Tr2RenderContextAL::ResourceBarrierDx12( const D3D12_RESOURCE_BARRIER& barr ResourceBarrierDx12( 1, &barrier ); } -namespace -{ -// snprintf returns the untruncated length; clamp so pos never passes the terminator -size_t AdvanceFormatPos( size_t pos, int written, size_t size ) -{ - return written < 0 || pos + size_t( written ) >= size ? size - 1 : pos + size_t( written ); -} - -const char* FormatResourceStates( char* buf, size_t size, D3D12_RESOURCE_STATES states ) -{ - if( states == D3D12_RESOURCE_STATE_COMMON ) - { - return "COMMON"; - } - if( states == D3D12_RESOURCE_STATE_GENERIC_READ ) - { - return "GENERIC_READ"; - } - static const struct - { - D3D12_RESOURCE_STATES bit; - const char* name; - } s_stateNames[] = { - { D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, "VB_CB" }, - { D3D12_RESOURCE_STATE_INDEX_BUFFER, "IB" }, - { D3D12_RESOURCE_STATE_RENDER_TARGET, "RT" }, - { D3D12_RESOURCE_STATE_UNORDERED_ACCESS, "UAV" }, - { D3D12_RESOURCE_STATE_DEPTH_WRITE, "DEPTH_W" }, - { D3D12_RESOURCE_STATE_DEPTH_READ, "DEPTH_R" }, - { D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, "SRV_NONPX" }, - { D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, "SRV_PX" }, - { D3D12_RESOURCE_STATE_STREAM_OUT, "STREAM_OUT" }, - { D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, "INDIRECT" }, - { D3D12_RESOURCE_STATE_COPY_DEST, "COPY_DST" }, - { D3D12_RESOURCE_STATE_COPY_SOURCE, "COPY_SRC" }, - { D3D12_RESOURCE_STATE_RESOLVE_DEST, "RESOLVE_DST" }, - { D3D12_RESOURCE_STATE_RESOLVE_SOURCE, "RESOLVE_SRC" }, - { D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, "RTAS" }, - }; - size_t pos = 0; - UINT remaining = UINT( states ); - for( const auto& state : s_stateNames ) - { - if( ( remaining & UINT( state.bit ) ) == UINT( state.bit ) ) - { - pos = AdvanceFormatPos( pos, snprintf( buf + pos, size - pos, "%s%s", pos ? "|" : "", state.name ), size ); - remaining &= ~UINT( state.bit ); - } - } - if( remaining ) - { - snprintf( buf + pos, size - pos, "%s0x%x", pos ? "|" : "", remaining ); - } - return buf; -} - -// Recorded immediately before each ResourceBarrier so the DRED breadcrumb context -// identifies which resources/states the otherwise anonymous RESOURCEBARRIER op contains -void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_RESOURCE_BARRIER* barriers, size_t count ) -{ - if( !g_dredBreadcrumbsEnabled ) - { - return; - } - char buf[512]; - size_t pos = AdvanceFormatPos( 0, snprintf( buf, sizeof( buf ), "[Barrier]" ), sizeof( buf ) ); - 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; - char name[128]; - UINT nameSize = sizeof( name ) - 1; - if( !resource || FAILED( resource->GetPrivateData( WKPDID_D3DDebugObjectName, &nameSize, name ) ) || nameSize >= sizeof( name ) ) - { - nameSize = UINT( snprintf( name, sizeof( name ), "%p", resource ) ); - } - name[nameSize] = 0; - if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_TRANSITION ) - { - char before[96], after[96]; - const char* beforeStates = FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ); - const char* afterStates = FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ); - pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, beforeStates, afterStates ), sizeof( buf ) ); - } - else if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ) - { - pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " UAV(%s)", name ), sizeof( buf ) ); - } - else - { - pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " Alias(%s)", name ), sizeof( buf ) ); - } - } - SetDredMarker( commandList, buf ); -} -} - void Tr2RenderContextAL::FlushBarriersDx12() { if( !m_barriers.empty() ) { - EmitBarrierBreadcrumb( m_commandList, m_barriers.data(), m_barriers.size() ); - m_commandList->ResourceBarrier( UINT( m_barriers.size() ), m_barriers.data() ); + TrinityALImpl::ResourceBarrier( m_commandList, UINT( m_barriers.size() ), m_barriers.data() ); m_barriers.clear(); } } @@ -1895,8 +1777,7 @@ void Tr2RenderContextAL::FlushBarriersDx12( size_t count, ID3D12Resource** resou } if( barrierCount ) { - EmitBarrierBreadcrumb( m_commandList, barriers, barrierCount ); - m_commandList->ResourceBarrier( UINT( barrierCount ), barriers ); + TrinityALImpl::ResourceBarrier( m_commandList, UINT( barrierCount ), barriers ); } } else @@ -1922,8 +1803,7 @@ void Tr2RenderContextAL::FlushBarriersDx12( size_t count, ID3D12Resource** resou } if( !barriers.empty() ) { - EmitBarrierBreadcrumb( m_commandList, barriers.data(), barriers.size() ); - m_commandList->ResourceBarrier( UINT( barriers.size() ), barriers.data() ); + TrinityALImpl::ResourceBarrier( m_commandList, UINT( barriers.size() ), barriers.data() ); } } } diff --git a/trinityal/dx12/Tr2RtTopLevelAccelerationStructureALDx12.cpp b/trinityal/dx12/Tr2RtTopLevelAccelerationStructureALDx12.cpp index 459186ef1..4039266c4 100644 --- a/trinityal/dx12/Tr2RtTopLevelAccelerationStructureALDx12.cpp +++ b/trinityal/dx12/Tr2RtTopLevelAccelerationStructureALDx12.cpp @@ -78,7 +78,7 @@ ALResult Tr2RtTopLevelAccelerationStructureAL::Create( const size_t count, const uavBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; uavBarrier.UAV.pResource = nullptr; uavBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - renderContext.m_commandList->ResourceBarrier( 1, &uavBarrier ); + TrinityALImpl::ResourceBarrier( renderContext.m_commandList, 1, &uavBarrier ); size_t capacity = Align( count, 128 ); @@ -165,7 +165,7 @@ ALResult Tr2RtTopLevelAccelerationStructureAL::Create( const size_t count, const topLevelUavBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; topLevelUavBarrier.UAV.pResource = buffer.TrinityALImpl_GetObject()->GetGpuResource(); topLevelUavBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - renderContext.m_commandList->ResourceBarrier( 1, &topLevelUavBarrier ); + TrinityALImpl::ResourceBarrier( renderContext.m_commandList, 1, &topLevelUavBarrier ); return S_OK; } @@ -195,7 +195,7 @@ ALResult Tr2RtTopLevelAccelerationStructureAL::Update( const size_t count, const uavBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; uavBarrier.UAV.pResource = nullptr; uavBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - renderContext.m_commandList->ResourceBarrier( 1, &uavBarrier ); + TrinityALImpl::ResourceBarrier( renderContext.m_commandList, 1, &uavBarrier ); CComPtr uploadBuffer; auto completed = m_owner->GetRenderedFrameNumber(); @@ -256,7 +256,7 @@ ALResult Tr2RtTopLevelAccelerationStructureAL::Update( const size_t count, const topLevelUavBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; topLevelUavBarrier.UAV.pResource = m_buffer.TrinityALImpl_GetObject()->GetGpuResource(); topLevelUavBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - renderContext.m_commandList->ResourceBarrier( 1, &topLevelUavBarrier ); + TrinityALImpl::ResourceBarrier( renderContext.m_commandList, 1, &topLevelUavBarrier ); return S_OK; } diff --git a/trinityal/dx12/Tr2TextureALDx12.cpp b/trinityal/dx12/Tr2TextureALDx12.cpp index c51a92174..ac9dd816a 100644 --- a/trinityal/dx12/Tr2TextureALDx12.cpp +++ b/trinityal/dx12/Tr2TextureALDx12.cpp @@ -260,17 +260,17 @@ struct Tr2TextureAL::MipMapGenerator else { auto restore = TrinityALImpl::Transition( m_staging, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST ); - commandList->ResourceBarrier( 1, &restore ); + TrinityALImpl::ResourceBarrier( commandList, 1, &restore ); } staging = m_staging; // Copy the resource to staging auto from = TrinityALImpl::Transition( resource, resourceState, D3D12_RESOURCE_STATE_COPY_SOURCE ); - commandList->ResourceBarrier( 1, &from ); + TrinityALImpl::ResourceBarrier( commandList, 1, &from ); commandList->CopyResource( staging, resource ); auto to = TrinityALImpl::Transition( staging, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE ); - commandList->ResourceBarrier( 1, &to ); + TrinityALImpl::ResourceBarrier( commandList, 1, &to ); } else { @@ -279,7 +279,7 @@ struct Tr2TextureAL::MipMapGenerator if( ( resourceState & D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE ) == 0 ) { auto barrier = TrinityALImpl::Transition( staging, resourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); } else { @@ -390,7 +390,7 @@ struct Tr2TextureAL::MipMapGenerator srv2uavDescs[i].Transition.Subresource = mip + ( i * desc.MipLevels ); uav2srvDescs[i].Transition.Subresource = mip + ( i * desc.MipLevels ); } - commandList->ResourceBarrier( desc.DepthOrArraySize, srv2uavDescs.data() ); + TrinityALImpl::ResourceBarrier( commandList, desc.DepthOrArraySize, srv2uavDescs.data() ); // Bind the mip subresources commandList->SetComputeRootDescriptorTable( TrinityALImpl::GenerateMipsResources::TargetTexture, uavH ); @@ -412,10 +412,10 @@ struct Tr2TextureAL::MipMapGenerator ( mipHeight + TrinityALImpl::GenerateMipsResources::ThreadGroupSize - 1 ) / TrinityALImpl::GenerateMipsResources::ThreadGroupSize, desc.DepthOrArraySize ); - commandList->ResourceBarrier( 1, &barrierUAV ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrierUAV ); // Transition the mip to an SRV - commandList->ResourceBarrier( desc.DepthOrArraySize, uav2srvDescs.data() ); + TrinityALImpl::ResourceBarrier( commandList, desc.DepthOrArraySize, uav2srvDescs.data() ); // Offset the descriptor heap handles uavH.ptr += descriptorSize; @@ -430,18 +430,18 @@ struct Tr2TextureAL::MipMapGenerator TrinityALImpl::Transition( resource, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST ) }; - commandList->ResourceBarrier( 2, barriers ); + TrinityALImpl::ResourceBarrier( commandList, 2, barriers ); // Copy the entire resource back commandList->CopyResource( resource, staging ); // Transition the target resource back to pixel shader resource auto barrier = TrinityALImpl::Transition( resource, D3D12_RESOURCE_STATE_COPY_DEST, resourceState ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); } else if( ( resourceState & D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE ) == 0 ) { auto barrier = TrinityALImpl::Transition( resource, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, resourceState ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); } m_device.DirtyDescriptorCache(); @@ -477,27 +477,27 @@ struct Tr2TextureAL::MipMapGenerator else { auto restore = TrinityALImpl::Transition( m_resourceCopy, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST ); - commandList->ResourceBarrier( 1, &restore ); + TrinityALImpl::ResourceBarrier( commandList, 1, &restore ); } // Copy the resource data auto barrier = TrinityALImpl::Transition( resource, resourceState, D3D12_RESOURCE_STATE_COPY_SOURCE ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); commandList->CopyResource( m_resourceCopy, resource ); barrier = TrinityALImpl::Transition( m_resourceCopy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); // Generate the mips GenerateMips_UnorderedAccessPath( m_resourceCopy, DXGI_FORMAT_R8G8B8A8_UNORM, commandList, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE ); // Direct copy back barrier = TrinityALImpl::Transition( m_resourceCopy, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_SOURCE ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); barrier = TrinityALImpl::Transition( resource, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); commandList->CopyResource( resource, m_resourceCopy ); barrier = TrinityALImpl::Transition( resource, D3D12_RESOURCE_STATE_COPY_DEST, resourceState ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); return S_OK; } @@ -550,33 +550,33 @@ struct Tr2TextureAL::MipMapGenerator else { auto restore = TrinityALImpl::Transition( m_bgrAliasCopy, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST ); - commandList->ResourceBarrier( 1, &restore ); + TrinityALImpl::ResourceBarrier( commandList, 1, &restore ); } // Copy the resource data auto barrier = TrinityALImpl::AliasBarrier( nullptr, m_bgrAliasCopy ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); barrier = TrinityALImpl::Transition( resource, resourceState, D3D12_RESOURCE_STATE_COPY_SOURCE ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); commandList->CopyResource( m_bgrAliasCopy, resource ); // Generate the mips barrier = TrinityALImpl::AliasBarrier( m_bgrAliasCopy, m_bgrResourceCopy ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); barrier = TrinityALImpl::Transition( m_bgrResourceCopy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); FORWARD_HR( GenerateMips_UnorderedAccessPath( m_bgrResourceCopy, DXGI_FORMAT_R8G8B8A8_UNORM, commandList, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE ) ); // Direct copy back barrier = TrinityALImpl::AliasBarrier( m_bgrResourceCopy, m_bgrAliasCopy ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); barrier = TrinityALImpl::Transition( m_bgrAliasCopy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); barrier = TrinityALImpl::Transition( resource, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); commandList->CopyResource( resource, m_bgrAliasCopy ); barrier = TrinityALImpl::Transition( resource, D3D12_RESOURCE_STATE_COPY_DEST, resourceState ); - commandList->ResourceBarrier( 1, &barrier ); + TrinityALImpl::ResourceBarrier( commandList, 1, &barrier ); return S_OK; } diff --git a/trinityal/dx12/Utilities.cpp b/trinityal/dx12/Utilities.cpp index 479be6763..692c90eae 100644 --- a/trinityal/dx12/Utilities.cpp +++ b/trinityal/dx12/Utilities.cpp @@ -8,6 +8,8 @@ #include "ALResult.h" #include "ALLog.h" +extern bool g_dredBreadcrumbsEnabled; + namespace { @@ -1384,6 +1386,126 @@ D3D12_RESOURCE_BARRIER AliasBarrier( return desc; } +// DRED breadcrumb contexts are only captured from PIX3-blob markers (Metadata=2, +// WinPixEventRuntime encoding); legacy ANSI/unicode markers are ignored +void SetDredMarker( ID3D12GraphicsCommandList* commandList, const char* text ) +{ + constexpr UINT64 PIXEvent_SetMarker_NoArgs = 0x008; + UINT64 blob[64]; + 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 + size_t lenBytes = strlen( text ) + 1; + size_t qwords = std::min( ( lenBytes + 7 ) / 8, size_t( 60 ) ); + memset( &blob[3], 0, qwords * 8 ); + memcpy( &blob[3], text, std::min( lenBytes, qwords * 8 - 1 ) ); + commandList->SetMarker( 2, blob, UINT( ( 3 + qwords ) * 8 ) ); +} + +namespace +{ +// snprintf returns the untruncated length; clamp so pos never passes the terminator +size_t AdvanceFormatPos( size_t pos, int written, size_t size ) +{ + return written < 0 || pos + size_t( written ) >= size ? size - 1 : pos + size_t( written ); +} + +const char* FormatResourceStates( char* buf, size_t size, D3D12_RESOURCE_STATES states ) +{ + if( states == D3D12_RESOURCE_STATE_COMMON ) + { + return "COMMON"; + } + if( states == D3D12_RESOURCE_STATE_GENERIC_READ ) + { + return "GENERIC_READ"; + } + static const struct + { + D3D12_RESOURCE_STATES bit; + const char* name; + } s_stateNames[] = { + { D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, "VB_CB" }, + { D3D12_RESOURCE_STATE_INDEX_BUFFER, "IB" }, + { D3D12_RESOURCE_STATE_RENDER_TARGET, "RT" }, + { D3D12_RESOURCE_STATE_UNORDERED_ACCESS, "UAV" }, + { D3D12_RESOURCE_STATE_DEPTH_WRITE, "DEPTH_W" }, + { D3D12_RESOURCE_STATE_DEPTH_READ, "DEPTH_R" }, + { D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, "SRV_NONPX" }, + { D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, "SRV_PX" }, + { D3D12_RESOURCE_STATE_STREAM_OUT, "STREAM_OUT" }, + { D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, "INDIRECT" }, + { D3D12_RESOURCE_STATE_COPY_DEST, "COPY_DST" }, + { D3D12_RESOURCE_STATE_COPY_SOURCE, "COPY_SRC" }, + { D3D12_RESOURCE_STATE_RESOLVE_DEST, "RESOLVE_DST" }, + { D3D12_RESOURCE_STATE_RESOLVE_SOURCE, "RESOLVE_SRC" }, + { D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, "RTAS" }, + }; + size_t pos = 0; + UINT remaining = UINT( states ); + for( const auto& state : s_stateNames ) + { + if( ( remaining & UINT( state.bit ) ) == UINT( state.bit ) ) + { + pos = AdvanceFormatPos( pos, snprintf( buf + pos, size - pos, "%s%s", pos ? "|" : "", state.name ), size ); + remaining &= ~UINT( state.bit ); + } + } + if( remaining ) + { + snprintf( buf + pos, size - pos, "%s0x%x", pos ? "|" : "", remaining ); + } + return buf; +} + +// Recorded immediately before each ResourceBarrier so the DRED breadcrumb context +// identifies which resources/states the otherwise anonymous RESOURCEBARRIER op contains +void EmitBarrierBreadcrumb( ID3D12GraphicsCommandList* commandList, const D3D12_RESOURCE_BARRIER* barriers, size_t count ) +{ + if( !g_dredBreadcrumbsEnabled ) + { + return; + } + char buf[512]; + size_t pos = AdvanceFormatPos( 0, snprintf( buf, sizeof( buf ), "[Barrier]" ), sizeof( buf ) ); + 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; + char name[128]; + UINT nameSize = sizeof( name ) - 1; + if( !resource || FAILED( resource->GetPrivateData( WKPDID_D3DDebugObjectName, &nameSize, name ) ) || nameSize >= sizeof( name ) ) + { + nameSize = UINT( snprintf( name, sizeof( name ), "%p", resource ) ); + } + name[nameSize] = 0; + if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_TRANSITION ) + { + char before[96], after[96]; + const char* beforeStates = FormatResourceStates( before, sizeof( before ), barrier.Transition.StateBefore ); + const char* afterStates = FormatResourceStates( after, sizeof( after ), barrier.Transition.StateAfter ); + pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " %s(%s->%s)", name, beforeStates, afterStates ), sizeof( buf ) ); + } + else if( barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV ) + { + pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " UAV(%s)", name ), sizeof( buf ) ); + } + else + { + pos = AdvanceFormatPos( pos, snprintf( buf + pos, sizeof( buf ) - pos, " Alias(%s)", name ), sizeof( buf ) ); + } + } + SetDredMarker( commandList, buf ); +} +} + +void ResourceBarrier( ID3D12GraphicsCommandList* commandList, UINT count, const D3D12_RESOURCE_BARRIER* barriers ) +{ + EmitBarrierBreadcrumb( commandList, barriers, count ); + commandList->ResourceBarrier( count, barriers ); +} + D3D12_HEAP_PROPERTIES HeapDesc( D3D12_HEAP_TYPE type ) { D3D12_HEAP_PROPERTIES heap = { type, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 0, 0 }; diff --git a/trinityal/dx12/Utilities.h b/trinityal/dx12/Utilities.h index efa37b3c5..9b01aa4f8 100644 --- a/trinityal/dx12/Utilities.h +++ b/trinityal/dx12/Utilities.h @@ -20,6 +20,10 @@ D3D12_RESOURCE_BARRIER AliasBarrier( ID3D12Resource* before, ID3D12Resource* after ); +void SetDredMarker( ID3D12GraphicsCommandList* commandList, const char* text ); + +void ResourceBarrier( ID3D12GraphicsCommandList* commandList, UINT count, const D3D12_RESOURCE_BARRIER* barriers ); + D3D12_HEAP_PROPERTIES HeapDesc( D3D12_HEAP_TYPE type ); void MemcpySubresource( From 50f52f2cdf26dc2333b21ffbac83dc03b91529cd Mon Sep 17 00:00:00 2001 From: JohnGreenCCP Date: Thu, 20 Aug 2026 12:15:07 +0100 Subject: [PATCH 11/11] Print the page fault virtual address in the DRED output --- trinity/TriDevice12.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/trinity/TriDevice12.cpp b/trinity/TriDevice12.cpp index 1ef8cc289..a5ed773c6 100644 --- a/trinity/TriDevice12.cpp +++ b/trinity/TriDevice12.cpp @@ -265,6 +265,7 @@ void TriDevice::HandleRenderTick( Be::Time realTime, Be::Time simTime ) } if( SUCCEEDED( pDred->GetPageFaultAllocationOutput1( &dredPageFaultOutput ) ) ) { + CCP_LOGERR( "[DRED] Page fault VA: 0x%016llX", dredPageFaultOutput.PageFaultVA ); // Engine names are ANSI (WKPDID_D3DDebugObjectName), so DRED fills ObjectNameA; ObjectNameW only holds names set via SetName auto logAllocationNode = []( const char* prefix, const D3D12_DRED_ALLOCATION_NODE1* node ) { if( node->ObjectNameA )