From 460f785a5badc6dffaab788b9342dd7f4a04495f Mon Sep 17 00:00:00 2001 From: Victor Moya Date: Thu, 29 Dec 2016 10:18:43 +0100 Subject: [PATCH] Implemented D3D12 counters (occlusion and pipeline stats based) Implemented D3D12 counters based on occlusion and pipeline statistics queries. Display an error message window when SetStablePower is called and Win10 is not in developer mode. --- renderdoc/driver/d3d11/d3d11_counters.cpp | 2 +- renderdoc/driver/d3d12/d3d12_counters.cpp | 178 ++++++++++++++++------ 2 files changed, 136 insertions(+), 44 deletions(-) diff --git a/renderdoc/driver/d3d11/d3d11_counters.cpp b/renderdoc/driver/d3d11/d3d11_counters.cpp index 8f7e90c70..e3d1cd5ed 100644 --- a/renderdoc/driver/d3d11/d3d11_counters.cpp +++ b/renderdoc/driver/d3d11/d3d11_counters.cpp @@ -100,7 +100,7 @@ void D3D11DebugManager::DescribeCounter(uint32_t counterID, CounterDescription & desc.units = eUnits_Absolute; break; case eCounter_RasterizerInvocations: - desc.name = "RasterizerInvocations"; + desc.name = "Rasterizer Invocations"; desc.description = "Number of primitives that were sent to the rasterizer."; desc.resultByteWidth = 8; desc.resultCompType = eCompType_UInt; diff --git a/renderdoc/driver/d3d12/d3d12_counters.cpp b/renderdoc/driver/d3d12/d3d12_counters.cpp index f3a36766d..ca1c3c7ee 100644 --- a/renderdoc/driver/d3d12/d3d12_counters.cpp +++ b/renderdoc/driver/d3d12/d3d12_counters.cpp @@ -49,16 +49,16 @@ vector D3D12Replay::EnumerateCounters() ret.push_back(eCounter_EventGPUDuration); ret.push_back(eCounter_InputVerticesRead); ret.push_back(eCounter_IAPrimitives); - ret.push_back(eCounter_VSInvocations); - ret.push_back(eCounter_GSInvocations); ret.push_back(eCounter_GSPrimitives); ret.push_back(eCounter_RasterizerInvocations); ret.push_back(eCounter_RasterizedPrimitives); - ret.push_back(eCounter_PSInvocations); + ret.push_back(eCounter_SamplesWritten); + ret.push_back(eCounter_VSInvocations); ret.push_back(eCounter_HSInvocations); ret.push_back(eCounter_DSInvocations); + ret.push_back(eCounter_GSInvocations); + ret.push_back(eCounter_PSInvocations); ret.push_back(eCounter_CSInvocations); - ret.push_back(eCounter_SamplesWritten); return ret; } @@ -99,7 +99,7 @@ void D3D12Replay::DescribeCounter(uint32_t counterID, CounterDescription &desc) desc.units = eUnits_Absolute; break; case eCounter_RasterizerInvocations: - desc.name = "RasterizerInvocations"; + desc.name = "Rasterizer Invocations"; desc.description = "Number of primitives that were sent to the rasterizer."; desc.resultByteWidth = 8; desc.resultCompType = eCompType_UInt; @@ -175,20 +175,31 @@ void D3D12Replay::DescribeCounter(uint32_t counterID, CounterDescription &desc) struct D3D12GPUTimerCallback : public D3D12DrawcallCallback { - D3D12GPUTimerCallback(WrappedID3D12Device *dev, D3D12Replay *rp, ID3D12QueryHeap *qh) - : m_pDevice(dev), m_pReplay(rp), m_QueryHeap(qh) + D3D12GPUTimerCallback(WrappedID3D12Device *dev, D3D12Replay *rp, ID3D12QueryHeap *tqh, + ID3D12QueryHeap *psqh, ID3D12QueryHeap *oqh) + : m_pDevice(dev), + m_pReplay(rp), + m_TimerQueryHeap(tqh), + m_PipeStatsQueryHeap(psqh), + m_OcclusionQueryHeap(oqh) { m_pDevice->GetQueue()->GetCommandData()->m_DrawcallCallback = this; } ~D3D12GPUTimerCallback() { m_pDevice->GetQueue()->GetCommandData()->m_DrawcallCallback = NULL; } void PreDraw(uint32_t eid, ID3D12GraphicsCommandList *cmd) { - cmd->EndQuery(m_QueryHeap, D3D12_QUERY_TYPE_TIMESTAMP, (uint32_t)(m_Results.size() * 2 + 0)); + cmd->BeginQuery(m_OcclusionQueryHeap, D3D12_QUERY_TYPE_OCCLUSION, (uint32_t)(m_Results.size())); + cmd->BeginQuery(m_PipeStatsQueryHeap, D3D12_QUERY_TYPE_PIPELINE_STATISTICS, + (uint32_t)(m_Results.size())); + cmd->EndQuery(m_TimerQueryHeap, D3D12_QUERY_TYPE_TIMESTAMP, (uint32_t)(m_Results.size() * 2 + 0)); } bool PostDraw(uint32_t eid, ID3D12GraphicsCommandList *cmd) { - cmd->EndQuery(m_QueryHeap, D3D12_QUERY_TYPE_TIMESTAMP, (uint32_t)(m_Results.size() * 2 + 1)); + cmd->EndQuery(m_TimerQueryHeap, D3D12_QUERY_TYPE_TIMESTAMP, (uint32_t)(m_Results.size() * 2 + 1)); + cmd->EndQuery(m_PipeStatsQueryHeap, D3D12_QUERY_TYPE_PIPELINE_STATISTICS, + (uint32_t)(m_Results.size())); + cmd->EndQuery(m_OcclusionQueryHeap, D3D12_QUERY_TYPE_OCCLUSION, (uint32_t)(m_Results.size())); m_Results.push_back(eid); return false; } @@ -206,7 +217,9 @@ struct D3D12GPUTimerCallback : public D3D12DrawcallCallback WrappedID3D12Device *m_pDevice; D3D12Replay *m_pReplay; - ID3D12QueryHeap *m_QueryHeap; + ID3D12QueryHeap *m_TimerQueryHeap; + ID3D12QueryHeap *m_PipeStatsQueryHeap; + ID3D12QueryHeap *m_OcclusionQueryHeap; vector m_Results; // events which are the 'same' from being the same command buffer resubmitted // multiple times in the frame. We will only get the full callback when we're @@ -231,7 +244,7 @@ vector D3D12Replay::FetchCounters(const vector &counter D3D12_RESOURCE_DESC bufDesc; bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; bufDesc.Alignment = 0; - bufDesc.Width = sizeof(uint64_t) * maxEID * 2; + bufDesc.Width = (sizeof(uint64_t) * 3 + sizeof(D3D12_QUERY_DATA_PIPELINE_STATISTICS)) * maxEID; bufDesc.Height = 1; bufDesc.DepthOrArraySize = 1; bufDesc.MipLevels = 1; @@ -251,83 +264,162 @@ vector D3D12Replay::FetchCounters(const vector &counter return ret; } - D3D12_QUERY_HEAP_DESC queryDesc; - queryDesc.Count = maxEID * 2; - queryDesc.NodeMask = 1; - queryDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; - ID3D12QueryHeap *queryHeap = NULL; - hr = m_pDevice->CreateQueryHeap(&queryDesc, __uuidof(queryHeap), (void **)&queryHeap); + D3D12_QUERY_HEAP_DESC timerQueryDesc; + timerQueryDesc.Count = maxEID * 2; + timerQueryDesc.NodeMask = 1; + timerQueryDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; + ID3D12QueryHeap *timerQueryHeap = NULL; + hr = m_pDevice->CreateQueryHeap(&timerQueryDesc, __uuidof(timerQueryHeap), + (void **)&timerQueryHeap); if(FAILED(hr)) { - RDCERR("Failed to create query heap %08x", hr); + RDCERR("Failed to create timer query heap %08x", hr); return ret; } - m_pDevice->SetStablePowerState(TRUE); + D3D12_QUERY_HEAP_DESC pipestatsQueryDesc; + pipestatsQueryDesc.Count = maxEID; + pipestatsQueryDesc.NodeMask = 1; + pipestatsQueryDesc.Type = D3D12_QUERY_HEAP_TYPE_PIPELINE_STATISTICS; + ID3D12QueryHeap *pipestatsQueryHeap = NULL; + hr = m_pDevice->CreateQueryHeap(&pipestatsQueryDesc, __uuidof(pipestatsQueryHeap), + (void **)&pipestatsQueryHeap); + if(FAILED(hr)) + { + RDCERR("Failed to create pipeline statistics query heap %08x", hr); + return ret; + } - D3D12GPUTimerCallback cb(m_pDevice, this, queryHeap); + D3D12_QUERY_HEAP_DESC occlusionQueryDesc; + occlusionQueryDesc.Count = maxEID; + occlusionQueryDesc.NodeMask = 1; + occlusionQueryDesc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION; + ID3D12QueryHeap *occlusionQueryHeap = NULL; + hr = m_pDevice->CreateQueryHeap(&occlusionQueryDesc, __uuidof(occlusionQueryHeap), + (void **)&occlusionQueryHeap); + if(FAILED(hr)) + { + RDCERR("Failed to create occlusion query heap %08x", hr); + return ret; + } + + // Only supported with developer mode drivers!!! + hr = m_pDevice->SetStablePowerState(TRUE); + if(FAILED(hr)) + MessageBoxA(NULL, + "D3D12 counters require Win10 developer mode enabled: Settings > Update & Security " + "> For Developers > Developer Mode", + "D3D12 Counters Error", MB_ICONWARNING | MB_OK); + + D3D12GPUTimerCallback cb(m_pDevice, this, timerQueryHeap, pipestatsQueryHeap, occlusionQueryHeap); // replay the events to perform all the queries m_pDevice->ReplayLog(0, maxEID, eReplay_Full); + // Only supported with developer mode drivers!!! m_pDevice->SetStablePowerState(FALSE); ID3D12GraphicsCommandList *list = m_pDevice->GetNewList(); - list->ResolveQueryData(queryHeap, D3D12_QUERY_TYPE_TIMESTAMP, 0, maxEID * 2, readbackBuf, 0); + list->ResolveQueryData(timerQueryHeap, D3D12_QUERY_TYPE_TIMESTAMP, 0, maxEID, readbackBuf, 0); + list->ResolveQueryData(pipestatsQueryHeap, D3D12_QUERY_TYPE_PIPELINE_STATISTICS, 0, maxEID, + readbackBuf, sizeof(uint64_t) * 2 * maxEID); + list->ResolveQueryData( + occlusionQueryHeap, D3D12_QUERY_TYPE_OCCLUSION, 0, maxEID, readbackBuf, + sizeof(uint64_t) * 2 + sizeof(D3D12_QUERY_DATA_PIPELINE_STATISTICS) * maxEID); list->Close(); m_pDevice->ExecuteLists(); m_pDevice->FlushLists(); - D3D12_RANGE range = {0, (SIZE_T)bufDesc.Width}; - void *data; - hr = readbackBuf->Map(0, &range, &data); + D3D12_RANGE range; + range.Begin = 0; + range.End = (SIZE_T)bufDesc.Width; + + uint8_t *data; + hr = readbackBuf->Map(0, &range, (void **)&data); if(FAILED(hr)) { - RDCERR("Failed to create query heap %08x", hr); - SAFE_RELEASE(queryHeap); + RDCERR("Failed to read timer query heap data %08x", hr); SAFE_RELEASE(readbackBuf); + SAFE_RELEASE(timerQueryHeap); + SAFE_RELEASE(pipestatsQueryHeap); + SAFE_RELEASE(occlusionQueryHeap); return ret; } uint64_t *timestamps = (uint64_t *)data; + data += maxEID * 2 * sizeof(uint64_t); + D3D12_QUERY_DATA_PIPELINE_STATISTICS *pipelinestats = (D3D12_QUERY_DATA_PIPELINE_STATISTICS *)data; + data += maxEID * sizeof(D3D12_QUERY_DATA_PIPELINE_STATISTICS); + uint64_t *occlusion = (uint64_t *)data; uint64_t freq; m_pDevice->GetQueue()->GetTimestampFrequency(&freq); for(size_t i = 0; i < cb.m_Results.size(); i++) { - CounterResult result; + for(size_t c = 0; c < counters.size(); c++) + { + CounterResult result; - uint64_t delta = timestamps[i * 2 + 1] - timestamps[i * 2 + 0]; + result.eventID = cb.m_Results[i]; + result.counterID = counters[c]; - result.eventID = cb.m_Results[i]; - result.counterID = eCounter_EventGPUDuration; - result.value.d = double(delta) / double(freq); - - ret.push_back(result); + switch(counters[c]) + { + case eCounter_EventGPUDuration: + { + uint64_t delta = timestamps[i * 2 + 1] - timestamps[i * 2 + 0]; + result.value.d = double(delta) / double(freq); + } + break; + case eCounter_InputVerticesRead: result.value.u64 = pipelinestats[i].IAVertices; break; + case eCounter_IAPrimitives: result.value.u64 = pipelinestats[i].IAPrimitives; break; + case eCounter_GSPrimitives: result.value.u64 = pipelinestats[i].GSPrimitives; break; + case eCounter_RasterizerInvocations: + result.value.u64 = pipelinestats[i].CInvocations; + break; + case eCounter_RasterizedPrimitives: result.value.u64 = pipelinestats[i].CPrimitives; break; + case eCounter_SamplesWritten: result.value.u64 = occlusion[i]; break; + case eCounter_VSInvocations: result.value.u64 = pipelinestats[i].VSInvocations; break; + case eCounter_HSInvocations: result.value.u64 = pipelinestats[i].HSInvocations; break; + case eCounter_DSInvocations: result.value.u64 = pipelinestats[i].DSInvocations; break; + case eCounter_GSInvocations: result.value.u64 = pipelinestats[i].GSInvocations; break; + case eCounter_PSInvocations: result.value.u64 = pipelinestats[i].PSInvocations; break; + case eCounter_CSInvocations: result.value.u64 = pipelinestats[i].CSInvocations; break; + } + ret.push_back(result); + } } for(size_t i = 0; i < cb.m_AliasEvents.size(); i++) { - CounterResult search; - search.counterID = eCounter_EventGPUDuration; - search.eventID = cb.m_AliasEvents[i].first; + for(size_t c = 0; c < counters.size(); c++) + { + CounterResult search; + search.counterID = counters[c]; + search.eventID = cb.m_AliasEvents[i].first; - // find the result we're aliasing - auto it = std::find(ret.begin(), ret.end(), search); - RDCASSERT(it != ret.end()); + // find the result we're aliasing + auto it = std::find(ret.begin(), ret.end(), search); + RDCASSERT(it != ret.end()); - // duplicate the result and append - CounterResult aliased = *it; - aliased.eventID = cb.m_AliasEvents[i].second; - ret.push_back(aliased); + // duplicate the result and append + CounterResult aliased = *it; + aliased.eventID = cb.m_AliasEvents[i].second; + ret.push_back(aliased); + } } // sort so that the alias results appear in the right places std::sort(ret.begin(), ret.end()); + SAFE_RELEASE(readbackBuf); + SAFE_RELEASE(timerQueryHeap); + SAFE_RELEASE(pipestatsQueryHeap); + SAFE_RELEASE(occlusionQueryHeap); + return ret; }