221 lines
6.7 KiB
C++
221 lines
6.7 KiB
C++
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
|
|
#if GRAPHICS_API_DIRECTX12
|
|
|
|
#include "QueryHeapDX12.h"
|
|
#include "GPUDeviceDX12.h"
|
|
#include "GPUContextDX12.h"
|
|
#include "../RenderToolsDX.h"
|
|
|
|
QueryHeapDX12::QueryHeapDX12(GPUDeviceDX12* device, const D3D12_QUERY_HEAP_TYPE& queryHeapType, int32 queryHeapCount)
|
|
: _device(device)
|
|
, _queryHeap(nullptr)
|
|
, _resultBuffer(nullptr)
|
|
, _queryHeapType(queryHeapType)
|
|
, _currentIndex(0)
|
|
, _queryHeapCount(queryHeapCount)
|
|
{
|
|
if (queryHeapType == D3D12_QUERY_HEAP_TYPE_OCCLUSION)
|
|
{
|
|
_resultSize = sizeof(uint64);
|
|
_queryType = D3D12_QUERY_TYPE_OCCLUSION;
|
|
}
|
|
else if (queryHeapType == D3D12_QUERY_HEAP_TYPE_TIMESTAMP)
|
|
{
|
|
_resultSize = sizeof(uint64);
|
|
_queryType = D3D12_QUERY_TYPE_TIMESTAMP;
|
|
}
|
|
else
|
|
{
|
|
MISSING_CODE("Not support D3D12 query heap type.");
|
|
}
|
|
}
|
|
|
|
bool QueryHeapDX12::Init()
|
|
{
|
|
_resultData.Resize(_resultSize * _queryHeapCount);
|
|
|
|
// Create the query heap
|
|
D3D12_QUERY_HEAP_DESC heapDesc;
|
|
heapDesc.Type = _queryHeapType;
|
|
heapDesc.Count = _queryHeapCount;
|
|
heapDesc.NodeMask = 0;
|
|
HRESULT result = _device->GetDevice()->CreateQueryHeap(&heapDesc, IID_PPV_ARGS(&_queryHeap));
|
|
LOG_DIRECTX_RESULT_WITH_RETURN(result, true);
|
|
DX_SET_DEBUG_NAME(_queryHeap, "Query Heap");
|
|
|
|
// Create the result buffer
|
|
D3D12_HEAP_PROPERTIES heapProperties;
|
|
heapProperties.Type = D3D12_HEAP_TYPE_READBACK;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
heapProperties.CreationNodeMask = 1;
|
|
heapProperties.VisibleNodeMask = 1;
|
|
D3D12_RESOURCE_DESC resourceDesc;
|
|
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
resourceDesc.Alignment = 0;
|
|
resourceDesc.Width = _resultData.Count();
|
|
resourceDesc.Height = 1;
|
|
resourceDesc.DepthOrArraySize = 1;
|
|
resourceDesc.MipLevels = 1;
|
|
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
|
resourceDesc.SampleDesc.Count = 1;
|
|
resourceDesc.SampleDesc.Quality = 0;
|
|
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
|
result = _device->GetDevice()->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&_resultBuffer));
|
|
LOG_DIRECTX_RESULT_WITH_RETURN(result, true);
|
|
DX_SET_DEBUG_NAME(_resultBuffer, "Query Heap Result Buffer");
|
|
|
|
// Start out with an open query batch
|
|
_currentBatch.Open = false;
|
|
StartQueryBatch();
|
|
|
|
return false;
|
|
}
|
|
|
|
void QueryHeapDX12::Destroy()
|
|
{
|
|
SAFE_RELEASE(_resultBuffer);
|
|
SAFE_RELEASE(_queryHeap);
|
|
_currentBatch.Clear();
|
|
_resultData.SetCapacity(0);
|
|
}
|
|
|
|
void QueryHeapDX12::EndQueryBatchAndResolveQueryData(GPUContextDX12* context)
|
|
{
|
|
ASSERT(_currentBatch.Open);
|
|
if (_currentBatch.Count == 0)
|
|
return;
|
|
|
|
// Close the current batch
|
|
_currentBatch.Open = false;
|
|
|
|
// Resolve the batch
|
|
const int32 offset = _currentBatch.Start * _resultSize;
|
|
context->GetCommandList()->ResolveQueryData(_queryHeap, _queryType, _currentBatch.Start, _currentBatch.Count, _resultBuffer, offset);
|
|
_currentBatch.Sync = _device->GetCommandQueue()->GetSyncPoint();
|
|
|
|
// Begin a new query batch
|
|
_batches.Add(_currentBatch);
|
|
StartQueryBatch();
|
|
}
|
|
|
|
void QueryHeapDX12::AllocQuery(GPUContextDX12* context, ElementHandle& handle)
|
|
{
|
|
ASSERT(_currentBatch.Open);
|
|
|
|
// Check if need to start from the buffer head
|
|
if (_currentIndex >= GetQueryHeapCount())
|
|
{
|
|
// We're in the middle of a batch, but we're at the end of the heap so split the batch in two
|
|
EndQueryBatchAndResolveQueryData(context);
|
|
}
|
|
|
|
// Allocate element into the current batch
|
|
handle = _currentIndex++;
|
|
_currentBatch.Count++;
|
|
}
|
|
|
|
void QueryHeapDX12::BeginQuery(GPUContextDX12* context, ElementHandle& handle)
|
|
{
|
|
AllocQuery(context, handle);
|
|
|
|
context->GetCommandList()->BeginQuery(_queryHeap, _queryType, handle);
|
|
}
|
|
|
|
void QueryHeapDX12::EndQuery(GPUContextDX12* context, ElementHandle& handle)
|
|
{
|
|
AllocQuery(context, handle);
|
|
|
|
context->GetCommandList()->EndQuery(_queryHeap, _queryType, handle);
|
|
}
|
|
|
|
bool QueryHeapDX12::IsReady(ElementHandle& handle)
|
|
{
|
|
// Current batch is not ready (not ended)
|
|
if (_currentBatch.ContainsElement(handle))
|
|
return false;
|
|
|
|
for (int32 i = 0; i < _batches.Count(); i++)
|
|
{
|
|
auto& batch = _batches[i];
|
|
if (batch.ContainsElement(handle))
|
|
{
|
|
ASSERT(batch.Sync.IsValid());
|
|
return batch.Sync.IsComplete();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void* QueryHeapDX12::ResolveQuery(ElementHandle& handle)
|
|
{
|
|
// Prevent queries from the current batch
|
|
ASSERT(!_currentBatch.ContainsElement(handle));
|
|
|
|
// Find the batch that contains this element to resolve it
|
|
for (int32 i = 0; i < _batches.Count(); i++)
|
|
{
|
|
auto& batch = _batches[i];
|
|
if (batch.ContainsElement(handle))
|
|
{
|
|
ASSERT(batch.Sync.IsValid());
|
|
|
|
// Ensure that end point has been already executed
|
|
if (!batch.Sync.IsComplete())
|
|
{
|
|
if (batch.Sync.IsOpen())
|
|
{
|
|
// The query is on a command list that hasn't been submitted yet
|
|
LOG(Warning, "Stalling the rendering and flushing GPU commands to wait for a query that hasn't been submitted to the GPU yet.");
|
|
_device->WaitForGPU();
|
|
}
|
|
|
|
batch.Sync.WaitForCompletion();
|
|
}
|
|
|
|
// Map the query values readback buffer
|
|
D3D12_RANGE range;
|
|
range.Begin = batch.Start * _resultSize;
|
|
range.End = range.Begin + batch.Count * _resultSize;
|
|
void* mapped = nullptr;
|
|
VALIDATE_DIRECTX_CALL(_resultBuffer->Map(0, &range, &mapped));
|
|
|
|
// Copy the results data
|
|
Platform::MemoryCopy(_resultData.Get() + range.Begin, (byte*)mapped + range.Begin, batch.Count * _resultSize);
|
|
|
|
// Unmap with an empty range to indicate nothing was written by the CPU
|
|
_resultBuffer->Unmap(0, nullptr);
|
|
|
|
// All elements got its results so we can remove this batch
|
|
_batches.RemoveAt(i);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return _resultData.Get() + handle * _resultSize;
|
|
}
|
|
|
|
void QueryHeapDX12::StartQueryBatch()
|
|
{
|
|
ASSERT(!_currentBatch.Open);
|
|
|
|
// Clear the current batch
|
|
_currentBatch.Clear();
|
|
|
|
// Loop active index on overflow
|
|
if (_currentIndex >= GetQueryHeapCount())
|
|
{
|
|
_currentIndex = 0;
|
|
}
|
|
|
|
// Start a new batch
|
|
_currentBatch.Start = _currentIndex;
|
|
_currentBatch.Open = true;
|
|
}
|
|
|
|
#endif
|