diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp index 067adab65..6745cd757 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp @@ -627,6 +627,7 @@ uint64 GPUContextDX11::BeginQuery(GPUQueryType type) ASSERT_LOW_LAYER(query.State == GPUQueryDataDX11::Ready); ASSERT_LOW_LAYER(query.Type == type); query.State = GPUQueryDataDX11::Begin; + query.TTL = 50; // Auto-destroy after some frames when user fails to call GetQueryResult auto context = _device->GetIM(); if (type == GPUQueryType::Timer) { diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h index 4923c921b..176316dce 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h @@ -2,6 +2,7 @@ #pragma once +#include "Engine/Core/Math/Vector4.h" #include "Engine/Graphics/GPUContext.h" #include "GPUDeviceDX11.h" #include "GPUPipelineStateDX11.h" diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp index b19961ed8..abd922026 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp @@ -998,13 +998,13 @@ void GPUDeviceDX11::DrawEnd() } #endif - // Auto-return finished queries back to the pool + // Auto-return finished queries back to the pool (or leaked queries) auto* queries = _queries.Get(); int32 queriesCount = _queries.Count(); for (int32 i = 0; i < queriesCount; i++) { auto& query = queries[i]; - if (query.State == GPUQueryDataDX11::Finished) + if (query.State == GPUQueryDataDX11::Finished || (query.State == GPUQueryDataDX11::End && --query.TTL == 0)) { query.State = GPUQueryDataDX11::Ready; query.Result = 0; @@ -1027,7 +1027,8 @@ bool GPUDeviceDX11::GetQueryResult(uint64 queryID, uint64& result, bool wait) result = query.Result; return true; } - ASSERT_LOW_LAYER(query.State == GPUQueryDataDX11::End); + if (query.State != GPUQueryDataDX11::End) + return false; auto context = GetIM(); RETRY: @@ -1062,13 +1063,17 @@ RETRY: } else if (q.Type == (uint16)GPUQueryType::Occlusion) { - hasData = context->GetData(query.Query, &result, sizeof(UINT64), 0) == S_OK; + UINT64 data; + hasData = context->GetData(query.Query, &data, sizeof(data), 0) == S_OK; + if (hasData) + result = data; } else { - BOOL resultBool; - hasData = context->GetData(query.Query, &resultBool, sizeof(BOOL), 0) == S_OK; - result = resultBool ? 1 : 0; + BOOL data; + hasData = context->GetData(query.Query, &data, sizeof(data), 0) == S_OK; + if (hasData) + result = data ? 1 : 0; } if (!hasData && wait) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h index a298d3b61..690b11456 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h @@ -41,7 +41,8 @@ struct GPUQueryDataDX11 ID3D11Query* TimerBeginQuery = nullptr; ID3D11Query* DisjointQuery = nullptr; uint64 Result = 0; - enum States { Ready, Begin, End, Finished } State = Ready; + uint16 TTL = 0; + enum States : uint16 { Ready, Begin, End, Finished } State = Ready; GPUQueryType Type = GPUQueryType::MAX; void Release(); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 1d65e7bce..80f6a84a1 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -1003,7 +1003,7 @@ void GPUDeviceDX12::RenderEnd() // Resolve the queries for (auto heap : QueryHeaps) - heap->EndQueryBatchAndResolveQueryData(_mainContext); + heap->EndFrame(_mainContext); } void GPUDeviceDX12::OnCrash() diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp index 5d818c070..aac344fe8 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp @@ -79,13 +79,20 @@ void QueryHeapDX12::Destroy() _resultData.SetCapacity(0); } -void QueryHeapDX12::EndQueryBatchAndResolveQueryData(GPUContextDX12* context) +void QueryHeapDX12::EndFrame(GPUContextDX12* context) { - ASSERT(_currentBatch.Open); - if (_currentBatch.Count == 0) - return; + // Update existing batches to avoid leaks + for (int32 i = 0; i < _batches.Count(); i++) + { + if (--_batches[i].TTL == 0) + { + _batches.RemoveAt(i--); + } + } - // Close the current batch + // Close the current batch (if not empty) + if (!_currentBatch.Open || _currentBatch.Count == 0) + return; _currentBatch.Open = false; // Resolve the batch @@ -203,6 +210,7 @@ void QueryHeapDX12::StartQueryBatch() // Start a new batch _currentBatch.Start = _currentIndex; _currentBatch.Open = true; + _currentBatch.TTL = 50; // Auto-destroy after some frames when user fails to call GetQueryResult } #endif diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.h index 629c8cb51..c0528ce98 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.h @@ -75,6 +75,8 @@ private: /// bool Open = false; + uint8 TTL = 0; + /// /// Checks if this query batch contains a given element contains the element. /// @@ -146,7 +148,7 @@ 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); + void EndFrame(GPUContextDX12* context); /// /// Checks if can alloc a new query (without rolling the existing batch). diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index ac58b3a22..43d37aebd 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -677,7 +677,6 @@ void QueryPoolVulkan::Reset(CmdBufferVulkan* cmdBuffer) BufferedQueryPoolVulkan::BufferedQueryPoolVulkan(GPUDeviceVulkan* device, int32 capacity, GPUQueryType type) : QueryPoolVulkan(device, capacity, type) - , _lastBeginIndex(0) { _queryOutput.Resize(capacity); _usedQueryBits.AddZeroed((capacity + 63) / 64); @@ -702,10 +701,12 @@ bool BufferedQueryPoolVulkan::AcquireQuery(CmdBufferVulkan* cmdBuffer, uint32& r resultIndex += wordIndex * 64; const uint64 bit = (uint64)1 << (uint64)(resultIndex % 64); _usedQueryBits[wordIndex] = _usedQueryBits[wordIndex] | bit; + _startedQueryBits[wordIndex] &= ~bit; _readResultsBits[wordIndex] &= ~bit; _lastBeginIndex = resultIndex + 1; if (ResetBeforeUse) Reset(cmdBuffer); + _lastUsedFrame = Engine::FrameCount; return true; } } @@ -728,6 +729,7 @@ void BufferedQueryPoolVulkan::ReleaseQuery(uint32 queryIndex) _lastBeginIndex = (uint32)queryIndex; } } + _lastUsedFrame = Engine::FrameCount; if (_usedQueryBits[word] == 0) { // Check if pool got empty and reset the pointer back to start @@ -749,23 +751,21 @@ void BufferedQueryPoolVulkan::MarkQueryAsStarted(uint32 queryIndex) bool BufferedQueryPoolVulkan::GetResults(uint32 index, uint64& result) { - const uint64 bit = (uint64)(index % 64); - const uint64 bitMask = (uint64)1 << bit; + const uint64 bit = (uint64)1 << (index % 64); const uint32 word = index / 64; - if ((_startedQueryBits[word] & bitMask) == 0) + if ((_startedQueryBits[word] & bit) == 0) { // Query never started/ended - result = 0; return true; } - if ((_readResultsBits[word] & bitMask) == 0) + if ((_readResultsBits[word] & bit) == 0) { const VkResult vkResult = vkGetQueryPoolResults(_device->Device, _handle, index, 1, sizeof(uint64), &_queryOutput[index], sizeof(uint64), VK_QUERY_RESULT_64_BIT); if (vkResult == VK_SUCCESS) { - _readResultsBits[word] = _readResultsBits[word] | bitMask; + _readResultsBits[word] = _readResultsBits[word] | bit; #if VULKAN_RESET_QUERY_POOLS // Add to reset @@ -777,7 +777,6 @@ bool BufferedQueryPoolVulkan::GetResults(uint32 index, uint64& result) else if (vkResult == VK_NOT_READY) { // No results yet - result = 0; return false; } else @@ -786,13 +785,15 @@ bool BufferedQueryPoolVulkan::GetResults(uint32 index, uint64& result) } } + // Get result result = _queryOutput[index]; + _lastUsedFrame = Engine::FrameCount; return true; } bool BufferedQueryPoolVulkan::HasRoom() const { - const uint64 allUsedMask = MAX_uint64; + constexpr uint64 allUsedMask = MAX_uint64; if (_lastBeginIndex < _usedQueryBits.Count() * 64) { ASSERT((_usedQueryBits[_lastBeginIndex / 64] & allUsedMask) != allUsedMask); @@ -801,6 +802,49 @@ bool BufferedQueryPoolVulkan::HasRoom() const return false; } +bool BufferedQueryPoolVulkan::TryExpire() +{ + // Auto-destroy pools some frames when user fails to call GetQueryResult + if (Engine::FrameCount - _lastUsedFrame < 50) + return false; + + // Reset back to clean state all unfinished queries + for (uint32 index = 0; index < _queryOutput.Count(); index++) + { + const uint64 bit = (uint64)1 << (index % 64); + const uint32 word = index / 64; + if ((_usedQueryBits[word] & bit) == 0) + continue; + if ((_readResultsBits[word] & bit) == 0) + { + const VkResult vkResult = vkGetQueryPoolResults(_device->Device, _handle, index, 1, sizeof(uint64), &_queryOutput[index], sizeof(uint64), VK_QUERY_RESULT_64_BIT); + if (vkResult == VK_SUCCESS) + { + _readResultsBits[word] = _readResultsBits[word] | bit; + +#if VULKAN_RESET_QUERY_POOLS + // Add to reset + if (!_device->QueriesToReset.Contains(this)) + _device->QueriesToReset.Add(this); + _resetRanges.Add(Range{ index, 1 }); +#endif + } + } + _usedQueryBits[word] = _usedQueryBits[word] & ~bit; + } + constexpr uint64 allUsedMask = MAX_uint64; + for (int32 wordIndex = 0; wordIndex < _usedQueryBits.Count(); wordIndex++) + { + if (_usedQueryBits[wordIndex] != allUsedMask) + { + _lastBeginIndex = wordIndex * 64; + break; + } + } + + return HasRoom(); +} + HelperResourcesVulkan::HelperResourcesVulkan(GPUDeviceVulkan* device) : _device(device) { @@ -1276,7 +1320,7 @@ int32 GPUDeviceVulkan::GetOrCreateQueryPool(GPUQueryType type) for (int32 i = 0; i < QueryPools.Count(); i++) { auto pool = pools[i]; - if (pool->Type == type && pool->HasRoom()) + if (pool->Type == type && (pool->HasRoom() || pool->TryExpire())) return i; } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h index abff89cbb..b1cafeea7 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h @@ -312,7 +312,9 @@ private: Array _readResultsBits; // Last potentially free index in the pool - int32 _lastBeginIndex; + int32 _lastBeginIndex = 0; + // Last frame when pool was used + uint64 _lastUsedFrame = 0; public: BufferedQueryPoolVulkan(GPUDeviceVulkan* device, int32 capacity, GPUQueryType type); @@ -321,6 +323,7 @@ public: void MarkQueryAsStarted(uint32 queryIndex); bool GetResults(uint32 index, uint64& result); bool HasRoom() const; + bool TryExpire(); }; /// diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp index 2a945c2b0..da2361030 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp @@ -642,7 +642,7 @@ void GPUSwapChainVulkan::Present(bool vsync) // Cache a command buffer to wait on its fence before drawing to this backbuffer again auto& acquiredBackBuffer = _backBuffers[_acquiredImageIndex]; - ASSERT(acquiredBackBuffer.SubmitCmdBuffer == nullptr); + ASSERT(acquiredBackBuffer.SubmitCmdBuffer == nullptr || !acquiredBackBuffer.SubmitCmdBuffer->IsSubmitted()); acquiredBackBuffer.SubmitCmdBuffer = context->GetCmdBufferManager()->GetActiveCmdBuffer(); context->GetCmdBufferManager()->SubmitActiveCmdBuffer(_backBuffers[_acquiredImageIndex].RenderingDoneSemaphore); diff --git a/Source/Engine/GraphicsDevice/WebGPU/IncludeWebGPU.h b/Source/Engine/GraphicsDevice/WebGPU/IncludeWebGPU.h index 06c3b3d9f..056abf19c 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/IncludeWebGPU.h +++ b/Source/Engine/GraphicsDevice/WebGPU/IncludeWebGPU.h @@ -17,10 +17,10 @@ #include -// Utiltiy macro to convert WGPUStringView into UTF-16 string (on stack) +// Utility macro to convert WGPUStringView into UTF-16 string (on stack) #define WEBGPU_TO_STR(strView) StringAsUTF16<>(strView.data, strView.data ? strView.length : 0).Get() -// Utiltiy macro to get WGPUStringView for a text constant +// Utility macro to get WGPUStringView for a text constant #define WEBGPU_STR(str) { str, ARRAY_COUNT(str) - 1 } #define WEBGPU_MAX_QUERY_SETS 8