Fix occlusion queries on WebGPU
This commit is contained in:
@@ -445,6 +445,13 @@ uint64 GPUContextWebGPU::BeginQuery(GPUQueryType type)
|
||||
// Begin occlusion query on the active set
|
||||
wgpuRenderPassEncoderBeginOcclusionQuery(_renderPass, query.Index);
|
||||
}
|
||||
else if (_activeOcclusionQuerySet == -1 && !_renderPass)
|
||||
{
|
||||
// Begin new render pass and then begin occlusion query
|
||||
_pendingOcclusionQuerySet = query.Set;
|
||||
FlushRenderPass();
|
||||
wgpuRenderPassEncoderBeginOcclusionQuery(_renderPass, query.Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the next pending occlusion query set to use for the next pass (or frame)
|
||||
@@ -956,6 +963,8 @@ WGPUComputePassEncoder GPUContextWebGPU::OnDispatch(GPUShaderProgramCS* shader)
|
||||
|
||||
void GPUContextWebGPU::EndRenderPass()
|
||||
{
|
||||
_activeOcclusionQuerySet = -1;
|
||||
_pendingOcclusionQuerySet = -1;
|
||||
wgpuRenderPassEncoderEnd(_renderPass);
|
||||
wgpuRenderPassEncoderRelease(_renderPass);
|
||||
_renderPass = nullptr;
|
||||
|
||||
@@ -30,8 +30,9 @@ GPUVertexLayoutWebGPU::GPUVertexLayoutWebGPU(GPUDeviceWebGPU* device, const Elem
|
||||
SetElements(elements, explicitOffsets);
|
||||
}
|
||||
|
||||
GPUQuerySetWebGPU::GPUQuerySetWebGPU(WGPUDevice device, GPUQueryType type, uint32 count)
|
||||
: _device(device)
|
||||
GPUQuerySetWebGPU::GPUQuerySetWebGPU(WGPUInstance instance, WGPUDevice device, GPUQueryType type, uint32 count)
|
||||
: _instance(instance)
|
||||
, _device(device)
|
||||
, _count(count)
|
||||
, Type(type)
|
||||
{
|
||||
@@ -109,28 +110,59 @@ bool GPUQuerySetWebGPU::Read(uint32 index, uint64& result, bool wait)
|
||||
if (_state == Resolved)
|
||||
{
|
||||
// Start mapping the buffer
|
||||
ASSERT(!wait); // TODO: impl wgpuBufferMapAsync with waiting (see GPUBufferWebGPU::Map)
|
||||
WGPUBufferMapCallbackInfo callback = WGPU_BUFFER_MAP_CALLBACK_INFO_INIT;
|
||||
callback.mode = WGPUCallbackMode_AllowSpontaneous;
|
||||
callback.userdata1 = this;
|
||||
callback.userdata2 = this;
|
||||
callback.callback = [](WGPUMapAsyncStatus status, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2)
|
||||
{
|
||||
if (status == WGPUMapAsyncStatus_Success)
|
||||
{
|
||||
auto set = (GPUQuerySetWebGPU*)userdata1;
|
||||
auto set = (GPUQuerySetWebGPU*)userdata2;
|
||||
set->OnRead();
|
||||
}
|
||||
#if !BUILD_RELEASE
|
||||
#if GPU_ENABLE_DEVELOPMENT
|
||||
else
|
||||
{
|
||||
LOG(Error, "Query Set map failed with status {}, {}", (uint32)status, WEBGPU_TO_STR(message));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
wgpuBufferMapAsync(_readBuffer, WGPUMapMode_Read, 0, _index * sizeof(uint64), callback);
|
||||
_state = Mapping;
|
||||
if (wait)
|
||||
{
|
||||
// Run in sync with wait
|
||||
struct QueryData : AsyncCallbackDataWebGPU
|
||||
{
|
||||
WGPUBufferMapCallback Callback = nullptr;
|
||||
};
|
||||
AsyncCallbackWebGPU<WGPUBufferMapCallbackInfo, QueryData> mapRequest(WGPU_BUFFER_MAP_CALLBACK_INFO_INIT);
|
||||
mapRequest.Data.Callback = callback.callback;
|
||||
mapRequest.Info.userdata2 = callback.userdata2;
|
||||
mapRequest.Info.callback = [](WGPUMapAsyncStatus status, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2)
|
||||
{
|
||||
QueryData& userData = *reinterpret_cast<QueryData*>(userdata1);
|
||||
userData.Call(status == WGPUMapAsyncStatus_Success, status, message);
|
||||
userData.Callback(status, message, userdata1, userdata2);
|
||||
};
|
||||
_state = Mapping;
|
||||
wgpuBufferMapAsync(_readBuffer, WGPUMapMode_Read, 0, _index * sizeof(uint64), mapRequest.Info);
|
||||
auto mapRequestResult = mapRequest.Wait(_instance);
|
||||
if (mapRequestResult == WGPUWaitStatus_TimedOut)
|
||||
{
|
||||
LOG(Error, "WebGPU query set buffer map request has timed out after {}s", (int32)mapRequest.Data.WaitTime);
|
||||
return false;
|
||||
}
|
||||
if (mapRequestResult == WGPUWaitStatus_Error)
|
||||
return false;
|
||||
_state = Mapped;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Run in async
|
||||
_state = Mapping;
|
||||
wgpuBufferMapAsync(_readBuffer, WGPUMapMode_Read, 0, _index * sizeof(uint64), callback);
|
||||
}
|
||||
}
|
||||
else if (_state == Mapped)
|
||||
if (_state == Mapped)
|
||||
{
|
||||
// Read the results from mapped buffer
|
||||
if (Type == GPUQueryType::Timer)
|
||||
@@ -142,6 +174,8 @@ bool GPUQuerySetWebGPU::Read(uint32 index, uint64& result, bool wait)
|
||||
{
|
||||
// Occlusion outputs number of fragment samples that pass all the tests (scissor, stencil, depth, etc.)
|
||||
result = _mapped[index];
|
||||
if (result > 1 && Type == GPUQueryType::BinaryOcclusion)
|
||||
result = 1; // Clamp binary result
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -589,7 +623,7 @@ bool GPUDeviceWebGPU::Init()
|
||||
{
|
||||
Platform::Fatal(String::Format(TEXT("WebGPU device run out of memory! {}"), WEBGPU_TO_STR(message)), nullptr, FatalErrorType::GPUOutOfMemory);
|
||||
}
|
||||
#if !BUILD_RELEASE
|
||||
#if GPU_ENABLE_DEVELOPMENT
|
||||
else if (type == WGPUErrorType_Validation)
|
||||
{
|
||||
LOG(Warning, "WebGPU Validation: {}", WEBGPU_TO_STR(message));
|
||||
@@ -847,7 +881,7 @@ GPUQueryWebGPU GPUDeviceWebGPU::AllocateQuery(GPUQueryType type)
|
||||
{
|
||||
if (setIndex == WEBGPU_MAX_QUERY_SETS)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
#if GPU_ENABLE_DEVELOPMENT
|
||||
static bool SingleTimeLog = true;
|
||||
if (SingleTimeLog)
|
||||
{
|
||||
@@ -861,7 +895,7 @@ GPUQueryWebGPU GPUDeviceWebGPU::AllocateQuery(GPUQueryType type)
|
||||
// Allocate a new query heap
|
||||
PROFILE_MEM(GraphicsCommands);
|
||||
uint32 size = type == GPUQueryType::Timer ? 1024 : 4096;
|
||||
auto set = New<GPUQuerySetWebGPU>(Device, type, size);
|
||||
auto set = New<GPUQuerySetWebGPU>(WebGPUInstance, Device, type, size);
|
||||
QuerySets[QuerySetsCount++] = set;
|
||||
}
|
||||
|
||||
@@ -870,6 +904,7 @@ GPUQueryWebGPU GPUDeviceWebGPU::AllocateQuery(GPUQueryType type)
|
||||
{
|
||||
static_assert(sizeof(GPUQueryWebGPU) == sizeof(uint64), "Invalid WebGPU query size.");
|
||||
query.Set = setIndex;
|
||||
query.Padding = 1; // Ensure Raw is never 0, even for the first query
|
||||
query.Index = QuerySets[setIndex]->Allocate();
|
||||
}
|
||||
return query;
|
||||
|
||||
@@ -39,7 +39,8 @@ struct GPUQueryWebGPU
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32 Set;
|
||||
uint16 Set;
|
||||
uint16 Padding;
|
||||
uint32 Index;
|
||||
};
|
||||
uint64 Raw;
|
||||
@@ -52,6 +53,7 @@ struct GPUQueryWebGPU
|
||||
class GPUQuerySetWebGPU
|
||||
{
|
||||
private:
|
||||
WGPUInstance _instance;
|
||||
WGPUDevice _device;
|
||||
uint32 _count;
|
||||
uint32 _index = 0;
|
||||
@@ -74,7 +76,7 @@ public:
|
||||
WGPUQuerySet Set;
|
||||
|
||||
public:
|
||||
GPUQuerySetWebGPU(WGPUDevice device, GPUQueryType type, uint32 count);
|
||||
GPUQuerySetWebGPU(WGPUInstance instance, WGPUDevice device, GPUQueryType type, uint32 count);
|
||||
~GPUQuerySetWebGPU();
|
||||
|
||||
bool CanAllocate() const;
|
||||
|
||||
@@ -707,6 +707,7 @@ bool GPUPipelineStateWebGPU::Init(const Description& desc)
|
||||
GetDebugName(_debugName);
|
||||
PipelineDesc.label = { _debugName.Get(), (size_t)_debugName.Count() - 1 };
|
||||
#endif
|
||||
// WGPUPrimitiveTopology_LineList could be used for wireframe mode but it breaks index buffers usage
|
||||
PipelineDesc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
|
||||
PipelineDesc.primitive.frontFace = WGPUFrontFace_CW;
|
||||
switch (desc.CullMode)
|
||||
|
||||
Reference in New Issue
Block a user