// Copyright (c) Wojciech Figat. All rights reserved. #pragma once #if GRAPHICS_API_DIRECTX12 class TimerQueryDX12; class GPUDeviceDX12; class GPUContextDX12; class GPUBuffer; #include "CommandQueueDX12.h" #include "Engine/Graphics/Enums.h" /// /// GPU query ID packed into 64-bits. /// struct GPUQueryDX12 { union { struct { uint16 Type; uint16 Heap; uint16 Element; uint16 SecondaryElement; }; uint64 Raw; }; static int32 GetQueriesCount(GPUQueryType type) { // Timer queries need to know duration via GPU timer queries difference return type == GPUQueryType::Timer ? 2 : 1; } }; /// /// GPU queries heap for DirectX 12 backend. /// class QueryHeapDX12 { public: /// /// The query element handle. /// typedef uint16 ElementHandle; private: struct QueryBatch { /// /// The synchronization point when query has been submitted to be executed. /// SyncPointDX12 Sync; /// /// The first element in the batch (inclusive). /// uint32 Start = 0; /// /// The amount of elements added to this batch. /// uint32 Count = 0; /// /// The GPU clock frequency for timer queries. /// uint64 TimestampFrequency = 0; /// /// Is the batch still open for more begin/end queries. /// bool Open = false; /// /// Checks if this query batch contains a given element contains the element. /// /// The index of the element. /// True if element is in this query, otherwise false. bool ContainsElement(uint32 elementIndex) const { return elementIndex >= Start && elementIndex < Start + Count; } }; private: GPUDeviceDX12* _device = nullptr; ID3D12Resource* _resultBuffer = nullptr; uint32 _currentIndex = 0; uint32 _resultSize = 0; uint32 _queryHeapCount = 0; QueryBatch _currentBatch; Array _batches; Array _resultData; uint64 _timestampFrequency; public: /// /// Initializes this instance. /// /// The device. /// Type of the query heap. /// The size of the heap. /// True if failed, otherwise false. bool Init(GPUDeviceDX12* device, GPUQueryType type, uint32 size); /// /// Destroys this instance. /// void Destroy(); public: GPUQueryType Type; ID3D12QueryHeap* QueryHeap = nullptr; D3D12_QUERY_TYPE QueryType = D3D12_QUERY_TYPE_OCCLUSION; /// /// Gets the query heap capacity. /// FORCE_INLINE uint32 GetQueryHeapCount() const { return _queryHeapCount; } /// /// Gets the size of the result value (in bytes). /// FORCE_INLINE uint32 GetResultSize() const { return _resultSize; } /// /// Gets the result buffer (CPU readable via Map/Unmap). /// FORCE_INLINE ID3D12Resource* GetResultBuffer() const { return _resultBuffer; } public: /// /// Stops tracking the current batch of begin/end query calls that will be resolved together. This implicitly starts a new batch. /// /// The context. void EndQueryBatchAndResolveQueryData(GPUContextDX12* context); /// /// Checks if can alloc a new query (without rolling the existing batch). /// /// How many elements to allocate? /// True if can alloc new query within the same batch. bool CanAlloc(int32 count = 1) const; /// /// Allocates the query heap element. /// /// The result handle. void Alloc(ElementHandle& handle); /// /// Determines whether the specified query handle is ready to read data (command list has been executed by the GPU). /// /// The handle. /// true if the specified query handle is ready; otherwise, false. bool IsReady(ElementHandle& handle); /// /// Resolves the query (or skips if already resolved). /// /// The result handle. /// The optional pointer to GPU timestamps frequency value to store. /// The pointer to the resolved query data. void* Resolve(ElementHandle& handle, uint64* timestampFrequency = nullptr); private: /// /// Starts tracking a new batch of begin/end query calls that will be resolved together /// void StartQueryBatch(); }; #endif