Add GPUQueryType::BinaryOcclusion for simpler visibility checks

This commit is contained in:
2026-08-23 00:01:11 +02:00
parent dfb0db0b70
commit 1ce4c0f16e
12 changed files with 49 additions and 16 deletions
+5
View File
@@ -410,6 +410,11 @@ enum class GPUQueryType
/// </summary>
Occlusion = 1,
/// <summary>
/// Tests object visibility by checking depth and stencil tests. Works like Occlusion query, except returns simply a binary 0/1 result: 0 indicates that no samples passed depth and stencil testing; 1 indicates that at least one sample passed.
/// </summary>
BinaryOcclusion = 2,
MAX
};
@@ -303,7 +303,7 @@ void GPUContextDX11::SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureView
void GPUContextDX11::SetRenderTarget(GPUTextureView* depthBuffer, const Span<GPUTextureView*>& rts)
{
ASSERT(Math::IsInRange(rts.Length(), 1, GPU_MAX_RT_BINDED));
ASSERT(Math::IsInRange(rts.Length(), 0, GPU_MAX_RT_BINDED));
auto depthBufferDX11 = static_cast<GPUTextureViewDX11*>(depthBuffer);
ID3D11DepthStencilView* dsv = depthBufferDX11 ? depthBufferDX11->DSV() : nullptr;
@@ -590,7 +590,18 @@ uint64 GPUContextDX11::BeginQuery(GPUQueryType type)
auto& query = _device->_queries.AddOne();
query.Type = type;
D3D11_QUERY_DESC queryDesc;
queryDesc.Query = type == GPUQueryType::Occlusion ? D3D11_QUERY_OCCLUSION : D3D11_QUERY_TIMESTAMP;
switch (type)
{
case GPUQueryType::Timer:
queryDesc.Query = D3D11_QUERY_TIMESTAMP;
break;
case GPUQueryType::Occlusion:
queryDesc.Query = D3D11_QUERY_OCCLUSION;
break;
case GPUQueryType::BinaryOcclusion:
queryDesc.Query = D3D11_QUERY_OCCLUSION_PREDICATE;
break;
}
queryDesc.MiscFlags = 0;
HRESULT hr = _device->GetDevice()->CreateQuery(&queryDesc, &query.Query);
LOG_DIRECTX_RESULT_WITH_RETURN(hr, 0);
@@ -1060,9 +1060,15 @@ RETRY:
}
}
}
else if (q.Type == (uint16)GPUQueryType::Occlusion)
{
hasData = context->GetData(query.Query, &result, sizeof(UINT64), 0) == S_OK;
}
else
{
hasData = context->GetData(query.Query, &result, sizeof(uint64), 0) == S_OK;
BOOL resultBool;
hasData = context->GetData(query.Query, &resultBool, sizeof(BOOL), 0) == S_OK;
result = resultBool ? 1 : 0;
}
if (!hasData && wait)
@@ -93,7 +93,7 @@ private:
bool _allowTearing = false;
GPUBuffer* _dummyVB = nullptr;
Array<GPUQueryDataDX11> _queries;
Array<uint16> _readyQueries[2]; // Timer and Occlusion
Array<uint16> _readyQueries[3]; // Timer and Occlusion and BinaryOcclusion
// Static Samplers
ID3D11SamplerState* _samplerLinearClamp = nullptr;
@@ -948,7 +948,7 @@ void GPUContextDX12::SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureView
void GPUContextDX12::SetRenderTarget(GPUTextureView* depthBuffer, const Span<GPUTextureView*>& rts)
{
ASSERT(Math::IsInRange(rts.Length(), 1, GPU_MAX_RT_BINDED));
ASSERT(Math::IsInRange(rts.Length(), 0, GPU_MAX_RT_BINDED));
const auto depthBufferDX12 = static_cast<GPUTextureViewDX12*>(depthBuffer);
@@ -1061,7 +1061,7 @@ GPUQueryDX12 GPUDeviceDX12::AllocQuery(GPUQueryType type)
// Allocate a new query heap
PROFILE_MEM(GraphicsCommands);
auto heap = New<QueryHeapDX12>();
int32 size = type == GPUQueryType::Occlusion ? 4096 : 1024;
int32 size = type == GPUQueryType::Timer ? 1024 : 4096;
if (heap->Init(this, type, size))
{
Delete(heap);
@@ -28,6 +28,11 @@ bool QueryHeapDX12::Init(GPUDeviceDX12* device, GPUQueryType type, uint32 size)
QueryType = D3D12_QUERY_TYPE_OCCLUSION;
heapDesc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION;
break;
case GPUQueryType::BinaryOcclusion:
_resultSize = sizeof(uint64);
QueryType = D3D12_QUERY_TYPE_BINARY_OCCLUSION;
heapDesc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION;
break;
case GPUQueryType::MAX:
return true;
}
@@ -100,11 +105,6 @@ void QueryHeapDX12::EndQueryBatchAndResolveQueryData(GPUContextDX12* context)
StartQueryBatch();
}
bool QueryHeapDX12::CanAlloc(int32 count) const
{
return _currentBatch.Open && _currentIndex + count <= GetQueryHeapCount();
}
void QueryHeapDX12::Alloc(ElementHandle& handle)
{
ASSERT(_currentBatch.Open);
@@ -153,7 +153,10 @@ public:
/// </summary>
/// <param name="count">How many elements to allocate?</param>
/// <returns>True if can alloc new query within the same batch.</returns>
bool CanAlloc(int32 count = 1) const;
bool CanAlloc(int32 count = 1) const
{
return _currentBatch.Open && _currentIndex + count <= _queryHeapCount;
}
/// <summary>
/// Allocates the query heap element.
@@ -1466,6 +1466,9 @@ uint64 GPUContextVulkan::BeginQuery(GPUQueryType type)
case GPUQueryType::Occlusion:
vkCmdBeginQuery(cmdBuffer->GetHandle(), pool->GetHandle(), query.QueryIndex, 0);
break;
case GPUQueryType::BinaryOcclusion:
vkCmdBeginQuery(cmdBuffer->GetHandle(), pool->GetHandle(), query.QueryIndex, VK_QUERY_CONTROL_PRECISE_BIT);
break;
}
pool->MarkQueryAsStarted(query.QueryIndex);
@@ -1492,6 +1495,7 @@ void GPUContextVulkan::EndQuery(uint64 queryID)
#endif
break;
case GPUQueryType::Occlusion:
case GPUQueryType::BinaryOcclusion:
vkCmdEndQuery(cmdBuffer->GetHandle(), pool->GetHandle(), query.QueryIndex);
break;
}
@@ -642,7 +642,7 @@ QueryPoolVulkan::QueryPoolVulkan(GPUDeviceVulkan* device, int32 capacity, GPUQue
{
VkQueryPoolCreateInfo createInfo;
RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO);
createInfo.queryType = type == GPUQueryType::Occlusion ? VK_QUERY_TYPE_OCCLUSION : VK_QUERY_TYPE_TIMESTAMP;
createInfo.queryType = type != GPUQueryType::Timer ? VK_QUERY_TYPE_TIMESTAMP : VK_QUERY_TYPE_OCCLUSION;
createInfo.queryCount = capacity;
VALIDATE_VULKAN_RESULT(vkCreateQueryPool(device->Device, &createInfo, nullptr, &_handle));
@@ -1282,7 +1282,7 @@ int32 GPUDeviceVulkan::GetOrCreateQueryPool(GPUQueryType type)
PROFILE_CPU_NAMED("Create Create Pool");
PROFILE_MEM(GraphicsCommands);
auto pool = New<BufferedQueryPoolVulkan>(this, type == GPUQueryType::Occlusion ? 4096 : 1024, type);
auto pool = New<BufferedQueryPoolVulkan>(this, type == GPUQueryType::Timer ? 1024 : 4096, type);
QueryPools.Add(pool);
return QueryPools.Count() - 1;
}
@@ -2217,13 +2217,17 @@ RETRY:
}
break;
case GPUQueryType::Occlusion:
case GPUQueryType::BinaryOcclusion:
hasData = pool->GetResults(query.QueryIndex, result);
if (hasData && pool->Type == GPUQueryType::BinaryOcclusion && result > 1)
result = 1; // Clamp binary result
break;
}
if (!hasData && wait)
{
// Wait until data is ready
// TODO: use VK_QUERY_RESULT_WAIT_BIT maybe?
Platform::Yield();
goto RETRY;
}
@@ -229,7 +229,7 @@ void GPUContextWebGPU::SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureVi
void GPUContextWebGPU::SetRenderTarget(GPUTextureView* depthBuffer, const Span<GPUTextureView*>& rts)
{
ASSERT(Math::IsInRange(rts.Length(), 1, GPU_MAX_RT_BINDED));
ASSERT(Math::IsInRange(rts.Length(), 0, GPU_MAX_RT_BINDED));
auto depthBufferGPU = (GPUTextureViewWebGPU*)depthBuffer;
if (_renderTargetCount != rts.Length() || _depthStencil != depthBufferGPU || Platform::MemoryCompare(_renderTargets, rts.Get(), rts.Length() * sizeof(void*)) != 0)
{
@@ -860,7 +860,7 @@ GPUQueryWebGPU GPUDeviceWebGPU::AllocateQuery(GPUQueryType type)
// Allocate a new query heap
PROFILE_MEM(GraphicsCommands);
uint32 size = type == GPUQueryType::Occlusion ? 4096 : 1024;
uint32 size = type == GPUQueryType::Timer ? 1024 : 4096;
auto set = New<GPUQuerySetWebGPU>(Device, type, size);
QuerySets[QuerySetsCount++] = set;
}