diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp index 839169f81..9b328e2ed 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp @@ -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; diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp index 8bb29c825..92833d874 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp @@ -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 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(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(Device, type, size); + auto set = New(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; diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.h b/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.h index dcffc8e82..829920300 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.h +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.h @@ -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; diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp index bdf54c602..f5e937044 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp @@ -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)