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.
This commit is contained in:
Victor Moya
2016-12-29 10:18:43 +01:00
committed by Baldur Karlsson
parent 366581fb3f
commit 460f785a5b
2 changed files with 136 additions and 44 deletions
+1 -1
View File
@@ -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;
+135 -43
View File
@@ -49,16 +49,16 @@ vector<uint32_t> 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<uint32_t> 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<CounterResult> D3D12Replay::FetchCounters(const vector<uint32_t> &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<CounterResult> D3D12Replay::FetchCounters(const vector<uint32_t> &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;
}