From 1ce4c0f16eff651fabaae0b5c34cea1257403e76 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 23 Aug 2026 00:01:11 +0200 Subject: [PATCH] Add `GPUQueryType::BinaryOcclusion` for simpler visibility checks --- Source/Engine/Graphics/Enums.h | 5 +++++ .../DirectX/DX11/GPUContextDX11.cpp | 15 +++++++++++++-- .../GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp | 8 +++++++- .../GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h | 2 +- .../DirectX/DX12/GPUContextDX12.cpp | 2 +- .../GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp | 2 +- .../GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp | 10 +++++----- .../GraphicsDevice/DirectX/DX12/QueryHeapDX12.h | 5 ++++- .../GraphicsDevice/Vulkan/GPUContextVulkan.cpp | 4 ++++ .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 8 ++++++-- .../GraphicsDevice/WebGPU/GPUContextWebGPU.cpp | 2 +- .../GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp | 2 +- 12 files changed, 49 insertions(+), 16 deletions(-) diff --git a/Source/Engine/Graphics/Enums.h b/Source/Engine/Graphics/Enums.h index 639899d9a..28200a895 100644 --- a/Source/Engine/Graphics/Enums.h +++ b/Source/Engine/Graphics/Enums.h @@ -410,6 +410,11 @@ enum class GPUQueryType /// Occlusion = 1, + /// + /// 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. + /// + BinaryOcclusion = 2, + MAX }; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp index 4de8b8ea5..067adab65 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp @@ -303,7 +303,7 @@ void GPUContextDX11::SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureView void GPUContextDX11::SetRenderTarget(GPUTextureView* depthBuffer, const Span& 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(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); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp index aa23c3a0f..b19961ed8 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp @@ -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) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h index 08007a0e3..a298d3b61 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h @@ -93,7 +93,7 @@ private: bool _allowTearing = false; GPUBuffer* _dummyVB = nullptr; Array _queries; - Array _readyQueries[2]; // Timer and Occlusion + Array _readyQueries[3]; // Timer and Occlusion and BinaryOcclusion // Static Samplers ID3D11SamplerState* _samplerLinearClamp = nullptr; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 44e5cb400..bb1cbbb27 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -948,7 +948,7 @@ void GPUContextDX12::SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureView void GPUContextDX12::SetRenderTarget(GPUTextureView* depthBuffer, const Span& 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(depthBuffer); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 1c3f01899..1d65e7bce 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -1061,7 +1061,7 @@ GPUQueryDX12 GPUDeviceDX12::AllocQuery(GPUQueryType type) // Allocate a new query heap PROFILE_MEM(GraphicsCommands); auto heap = New(); - int32 size = type == GPUQueryType::Occlusion ? 4096 : 1024; + int32 size = type == GPUQueryType::Timer ? 1024 : 4096; if (heap->Init(this, type, size)) { Delete(heap); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp index 5a24c5b4c..5d818c070 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp @@ -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); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.h index e8ab92a73..629c8cb51 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.h @@ -153,7 +153,10 @@ public: /// /// How many elements to allocate? /// True if can alloc new query within the same batch. - bool CanAlloc(int32 count = 1) const; + bool CanAlloc(int32 count = 1) const + { + return _currentBatch.Open && _currentIndex + count <= _queryHeapCount; + } /// /// Allocates the query heap element. diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index e835b2f80..5fc45fd22 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -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; } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 084122d77..ac58b3a22 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -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(this, type == GPUQueryType::Occlusion ? 4096 : 1024, type); + auto pool = New(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; } diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp index 8b132dde2..839169f81 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp @@ -229,7 +229,7 @@ void GPUContextWebGPU::SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureVi void GPUContextWebGPU::SetRenderTarget(GPUTextureView* depthBuffer, const Span& 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) { diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp index 85ff85616..8bb29c825 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp @@ -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(Device, type, size); QuerySets[QuerySetsCount++] = set; }