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; }