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