From d9d99f51ad456a8da3797c28cc7d1a24dcbdce79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Tue, 6 Jan 2026 20:44:12 -0800 Subject: [PATCH] D3D12: Added pipeline stats. (#3534) --- src/renderer_d3d12.cpp | 128 +++++++++++++++++++++++++++++++++-------- src/renderer_d3d12.h | 51 ++++++++++++++-- 2 files changed, 150 insertions(+), 29 deletions(-) diff --git a/src/renderer_d3d12.cpp b/src/renderer_d3d12.cpp index 08989d4db..ff80a9d94 100644 --- a/src/renderer_d3d12.cpp +++ b/src/renderer_d3d12.cpp @@ -656,6 +656,8 @@ namespace bgfx { namespace d3d12 } } + typedef HRESULT (WINAPI* PFN_D3D12_ENABLE_EXPERIMENTAL_FEATURES)(uint32_t _numFeatures, const IID* _iids, void* _configurationStructs, uint32_t* _configurationStructSizes); + #if USE_D3D12_DYNAMIC_LIB static PFN_D3D12_ENABLE_EXPERIMENTAL_FEATURES D3D12EnableExperimentalFeatures; static PFN_D3D12_CREATE_DEVICE D3D12CreateDevice; @@ -4108,22 +4110,25 @@ namespace bgfx { namespace d3d12 queueDesc.Priority = 0; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.NodeMask = 1; - DX_CHECK(_device->CreateCommandQueue(&queueDesc + DX_CHECK(_device->CreateCommandQueue( + &queueDesc , IID_ID3D12CommandQueue , (void**)&m_commandQueue ) ); m_completedFence = 0; m_currentFence = 0; - DX_CHECK(_device->CreateFence(0 + DX_CHECK(_device->CreateFence( + 0 , D3D12_FENCE_FLAG_NONE , IID_ID3D12Fence , (void**)&m_fence ) ); - for (uint32_t ii = 0; ii < BX_COUNTOF(m_commandList); ++ii) + for (uint32_t ii = 0; ii < kMaxCommandLists; ++ii) { - DX_CHECK(_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT + DX_CHECK(_device->CreateCommandAllocator( + D3D12_COMMAND_LIST_TYPE_DIRECT , IID_ID3D12CommandAllocator , (void**)&m_commandList[ii].m_commandAllocator ) ); @@ -4138,6 +4143,30 @@ namespace bgfx { namespace d3d12 DX_CHECK(m_commandList[ii].m_commandList->Close() ); } + + const D3D12_QUERY_HEAP_DESC queryHeapDesc = + { + .Type = D3D12_QUERY_HEAP_TYPE_PIPELINE_STATISTICS, + .Count = kMaxCommandLists, + .NodeMask = 1, + }; + + DX_CHECK(_device->CreateQueryHeap( + &queryHeapDesc + , IID_ID3D12QueryHeap + , (void**)&m_pipelineStatsQueryHeap + ) ); + setDebugObjectName(m_pipelineStatsQueryHeap, "Pipeline Statistics Query Heap"); + + m_pipelineStatsReadBack = createCommittedResource(_device, HeapProperty::ReadBack, kMaxCommandLists*sizeof(D3D12_QUERY_DATA_PIPELINE_STATISTICS) ); + setDebugObjectName(m_pipelineStatsReadBack, "Pipeline Statistics Read-Back"); + + const D3D12_RANGE range = + { + .Begin = 0, + .End = kMaxCommandLists*sizeof(D3D12_QUERY_DATA_PIPELINE_STATISTICS), + }; + m_pipelineStatsReadBack->Map(0, &range, (void**)&m_pipelineStats); } void CommandQueueD3D12::shutdown() @@ -4146,13 +4175,22 @@ namespace bgfx { namespace d3d12 DX_RELEASE(m_fence, 0); - for (uint32_t ii = 0; ii < BX_COUNTOF(m_commandList); ++ii) + for (uint32_t ii = 0; ii < kMaxCommandLists; ++ii) { DX_RELEASE(m_commandList[ii].m_commandAllocator, 0); DX_RELEASE(m_commandList[ii].m_commandList, 0); } DX_RELEASE(m_commandQueue, 0); + + const D3D12_RANGE range = + { + .Begin = 0, + .End = kMaxCommandLists*sizeof(D3D12_QUERY_DATA_PIPELINE_STATISTICS), + }; + m_pipelineStatsReadBack->Unmap(0, &range); + DX_RELEASE(m_pipelineStatsQueryHeap, 0); + DX_RELEASE(m_pipelineStatsReadBack, 0); } ID3D12GraphicsCommandList* CommandQueueD3D12::alloc() @@ -4165,12 +4203,33 @@ namespace bgfx { namespace d3d12 CommandList& commandList = m_commandList[m_control.m_current]; DX_CHECK(commandList.m_commandAllocator->Reset() ); DX_CHECK(commandList.m_commandList->Reset(commandList.m_commandAllocator, NULL) ); + commandList.m_commandList->BeginQuery( + m_pipelineStatsQueryHeap + , D3D12_QUERY_TYPE_PIPELINE_STATISTICS + , m_control.m_current + ); return commandList.m_commandList; } uint64_t CommandQueueD3D12::kick() { - CommandList& commandList = m_commandList[m_control.m_current]; + const uint32_t currentIdx = m_control.m_current; + CommandList& commandList = m_commandList[currentIdx]; + + commandList.m_commandList->EndQuery( + m_pipelineStatsQueryHeap + , D3D12_QUERY_TYPE_PIPELINE_STATISTICS + , currentIdx + ); + commandList.m_commandList->ResolveQueryData( + m_pipelineStatsQueryHeap + , D3D12_QUERY_TYPE_PIPELINE_STATISTICS + , currentIdx + , 1 + , m_pipelineStatsReadBack + , currentIdx*sizeof(D3D12_QUERY_DATA_PIPELINE_STATISTICS) + ); + DX_CHECK(commandList.m_commandList->Close() ); ID3D12CommandList* commandLists[] = { commandList.m_commandList }; @@ -4247,6 +4306,8 @@ namespace bgfx { namespace d3d12 } ra.clear(); + m_pipelineStatsSum.add(m_pipelineStats[m_control.m_read]); + m_control.consume(1); return true; @@ -6138,24 +6199,32 @@ namespace bgfx { namespace d3d12 void TimerQueryD3D12::init() { - D3D12_QUERY_HEAP_DESC queryHeapDesc; - queryHeapDesc.Count = m_control.m_size * 2; - queryHeapDesc.NodeMask = 1; - queryHeapDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; - DX_CHECK(s_renderD3D12->m_device->CreateQueryHeap(&queryHeapDesc + ID3D12Device* device = s_renderD3D12->m_device; + + D3D12_QUERY_HEAP_DESC queryHeapDesc = + { + .Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP, + .Count = m_control.m_size * 2, + .NodeMask = 1, + }; + + DX_CHECK(device->CreateQueryHeap( + &queryHeapDesc , IID_ID3D12QueryHeap , (void**)&m_queryHeap ) ); + setDebugObjectName(m_queryHeap, "Timer Query Heap"); const uint32_t size = queryHeapDesc.Count*sizeof(uint64_t); - m_readback = createCommittedResource(s_renderD3D12->m_device + m_readback = createCommittedResource(device , HeapProperty::ReadBack , size ); + setDebugObjectName(m_readback, "Timer Query Read-Back"); DX_CHECK(s_renderD3D12->m_cmd.m_commandQueue->GetTimestampFrequency(&m_frequency) ); - D3D12_RANGE range = { 0, size }; + D3D12_RANGE range = { .Begin = 0, .End = size }; m_readback->Map(0, &range, (void**)&m_queryResult); for (uint32_t ii = 0; ii < BX_COUNTOF(m_result); ++ii) @@ -6178,6 +6247,8 @@ namespace bgfx { namespace d3d12 uint32_t TimerQueryD3D12::begin(uint32_t _resultIdx, uint32_t _frameNum) { + ID3D12GraphicsCommandList* commandList = s_renderD3D12->m_commandList; + while (0 == m_control.reserve(1) ) { m_control.consume(1); @@ -6192,8 +6263,6 @@ namespace bgfx { namespace d3d12 query.m_ready = false; query.m_frameNum = _frameNum; - ID3D12GraphicsCommandList* commandList = s_renderD3D12->m_commandList; - uint32_t offset = idx * 2 + 0; commandList->EndQuery(m_queryHeap , D3D12_QUERY_TYPE_TIMESTAMP @@ -6207,13 +6276,13 @@ namespace bgfx { namespace d3d12 void TimerQueryD3D12::end(uint32_t _idx) { + ID3D12GraphicsCommandList* commandList = s_renderD3D12->m_commandList; + Query& query = m_query[_idx]; query.m_ready = true; query.m_fence = s_renderD3D12->m_cmd.m_currentFence - 1; uint32_t offset = _idx * 2; - ID3D12GraphicsCommandList* commandList = s_renderD3D12->m_commandList; - commandList->EndQuery(m_queryHeap , D3D12_QUERY_TYPE_TIMESTAMP , offset + 1 @@ -6276,10 +6345,11 @@ namespace bgfx { namespace d3d12 ) ); const uint32_t size = BX_COUNTOF(m_handle)*sizeof(uint64_t); - m_readback = createCommittedResource(s_renderD3D12->m_device - , HeapProperty::ReadBack - , size - ); + m_readback = createCommittedResource( + s_renderD3D12->m_device + , HeapProperty::ReadBack + , size + ); D3D12_RANGE range = { 0, size }; m_readback->Map(0, &range, (void**)&m_result); @@ -6527,9 +6597,6 @@ namespace bgfx { namespace d3d12 static ViewState viewState; viewState.reset(_render); -// bool wireframe = !!(_render->m_debug&BGFX_DEBUG_WIREFRAME); -// setDebugWireframe(wireframe); - uint16_t currentSamplerStateIdx = kInvalidHandle; ProgramHandle currentProgram = BGFX_INVALID_HANDLE; uint32_t currentBindHash = 0; @@ -7389,7 +7456,6 @@ namespace bgfx { namespace d3d12 { BGFX_D3D12_PROFILER_BEGIN_LITERAL("debugstats", kColorFrame); -// m_needPresent = true; TextVideoMem& tvm = m_textVideoMem; static int64_t next = timeEnd; @@ -7548,6 +7614,17 @@ namespace bgfx { namespace d3d12 ); pos++; + tvm.printf(10, pos++, 0x8b, " Pipeline Statistics:"); + const CommandQueueD3D12::PipelineStats& pipelineStats = m_cmd.m_pipelineStatsSum; + tvm.printf(10, pos++, 0x8b, " IAVertices: %llu", pipelineStats.IAVertices); + tvm.printf(10, pos++, 0x8b, " IAPrimitives: %llu", pipelineStats.IAPrimitives); + tvm.printf(10, pos++, 0x8b, " VSInvocations: %llu", pipelineStats.VSInvocations); + tvm.printf(10, pos++, 0x8b, " CInvocations: %llu", pipelineStats.CInvocations); + tvm.printf(10, pos++, 0x8b, " CPrimitives: %llu", pipelineStats.CPrimitives); + tvm.printf(10, pos++, 0x8b, " PSInvocations: %llu", pipelineStats.PSInvocations); + tvm.printf(10, pos++, 0x8b, " CSInvocations: %llu", pipelineStats.CSInvocations); + pos++; + double captureMs = double(captureElapsed)*toMs; tvm.printf(10, pos++, 0x8b, " Capture: %7.4f [ms] ", captureMs); @@ -7616,6 +7693,7 @@ namespace bgfx { namespace d3d12 #endif // BX_PLATFORM_WINDOWS m_backBufferColorFence[m_backBufferColorIdx] = kick(); + m_cmd.m_pipelineStatsSum.reset(); } } /* namespace d3d12 */ } // namespace bgfx diff --git a/src/renderer_d3d12.h b/src/renderer_d3d12.h index a513817c3..00f161253 100644 --- a/src/renderer_d3d12.h +++ b/src/renderer_d3d12.h @@ -77,8 +77,6 @@ extern "C" uint64_t WINAPI bgfx_PIXEventsReplaceBlock(PIXEven namespace bgfx { namespace d3d12 { - typedef HRESULT (WINAPI* PFN_D3D12_ENABLE_EXPERIMENTAL_FEATURES)(uint32_t _numFeatures, const IID* _iids, void* _configurationStructs, uint32_t* _configurationStructSizes); - struct Rdt { enum Enum @@ -372,6 +370,8 @@ namespace bgfx { namespace d3d12 struct CommandQueueD3D12 { + static constexpr uint32_t kMaxCommandLists = 256; + CommandQueueD3D12() : m_currentFence(0) , m_completedFence(0) @@ -396,14 +396,57 @@ namespace bgfx { namespace d3d12 HANDLE m_event; }; + struct PipelineStats + { + PipelineStats() + { + reset(); + } + + void reset() + { + IAVertices = 0; + IAPrimitives = 0; + VSInvocations = 0; + CInvocations = 0; + CPrimitives = 0; + PSInvocations = 0; + CSInvocations = 0; + } + + void add(const D3D12_QUERY_DATA_PIPELINE_STATISTICS& _stats) + { + IAVertices += _stats.IAVertices; + IAPrimitives += _stats.IAPrimitives; + VSInvocations += _stats.VSInvocations; + CInvocations += _stats.CInvocations; + CPrimitives += _stats.CPrimitives; + PSInvocations += _stats.PSInvocations; + CSInvocations += _stats.CSInvocations; + } + + uint64_t IAVertices; + uint64_t IAPrimitives; + uint64_t VSInvocations; + uint64_t CInvocations; + uint64_t CPrimitives; + uint64_t PSInvocations; + uint64_t CSInvocations; + }; + ID3D12CommandQueue* m_commandQueue; uint64_t m_currentFence; uint64_t m_completedFence; ID3D12Fence* m_fence; - CommandList m_commandList[256]; + CommandList m_commandList[kMaxCommandLists]; typedef stl::vector ResourceArray; - ResourceArray m_release[256]; + ResourceArray m_release[kMaxCommandLists]; bx::RingBufferControl m_control; + + ID3D12Resource* m_pipelineStatsReadBack; + ID3D12QueryHeap* m_pipelineStatsQueryHeap; + D3D12_QUERY_DATA_PIPELINE_STATISTICS* m_pipelineStats; + PipelineStats m_pipelineStatsSum; }; struct BatchD3D12