From 6814a43418b16f3ca06c911e472e6b63ccb2edb7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Mar 2026 14:51:15 +0100 Subject: [PATCH] Various changes for WebGPU and hardware instancing support --- Source/Engine/Core/Log.cpp | 8 +++++ .../WebGPU/GPUContextWebGPU.cpp | 15 ++++---- .../GraphicsDevice/WebGPU/GPUContextWebGPU.h | 21 ++++++------ .../GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp | 5 +-- .../WebGPU/GPUPipelineStateWebGPU.cpp | 34 +++++++++++++++---- .../WebGPU/GPUTextureWebGPU.cpp | 8 +---- Source/Engine/Platform/Web/WebPlatform.cpp | 4 +++ .../WebGPU/ShaderCompilerWebGPU.cpp | 2 +- 8 files changed, 63 insertions(+), 34 deletions(-) diff --git a/Source/Engine/Core/Log.cpp b/Source/Engine/Core/Log.cpp index 30f838371..1520ad57c 100644 --- a/Source/Engine/Core/Log.cpp +++ b/Source/Engine/Core/Log.cpp @@ -155,6 +155,7 @@ void Log::Logger::Write(const StringView& msg, LogType type) Platform::Log(msg, (int32)type); #endif +#if LOG_ENABLE_FILE // Write message to log file constexpr int32 LogMaxWriteSize = 1 * 1024 * 1024; // 1GB if (LogAfterInit && LogTotalWriteSize < LogMaxWriteSize) @@ -171,6 +172,7 @@ void Log::Logger::Write(const StringView& msg, LogType type) LogFile->Flush(); #endif } +#endif IsDuringLog = false; LogLocker.Unlock(); @@ -187,7 +189,11 @@ void Log::Logger::Dispose() // Write ending info WriteFloor(); +#if LOG_ENABLE_FILE Write(String::Format(TEXT(" Total errors: {0}\n Closing file"), LogTotalErrorsCnt, DateTime::Now().ToString())); +#else + Write(String::Format(TEXT(" Total errors: {0}"), LogTotalErrorsCnt)); +#endif WriteFloor(); // Close @@ -213,10 +219,12 @@ bool Log::Logger::IsLogEnabled() void Log::Logger::Flush() { +#if LOG_ENABLE_FILE LogLocker.Lock(); if (LogFile) LogFile->Flush(); LogLocker.Unlock(); +#endif } void Log::Logger::WriteFloor() diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp index 568af4128..b190d9d86 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp @@ -319,9 +319,10 @@ void GPUContextWebGPU::BindVB(const Span& vertexBuffers, const uint3 for (int32 i = 0; i < vertexBuffers.Length(); i++) { auto vbWebGPU = (GPUBufferWebGPU*)vertexBuffers.Get()[i]; - _vertexBuffers[i].Buffer = vbWebGPU ? vbWebGPU->Buffer : nullptr; - _vertexBuffers[i].Offset = vertexBuffersOffsets ? vertexBuffersOffsets[i] : 0; - _vertexBuffers[i].Size = vbWebGPU ? vbWebGPU->GetSize() : 0; + if (vbWebGPU) + _vertexBuffers[i] = { vbWebGPU->Buffer, vbWebGPU->GetSize(), vertexBuffersOffsets ? vertexBuffersOffsets[i] : 0 }; + else + _vertexBuffers[i] = { _device->DefaultBuffer, 64, 0 }; // Fallback } } @@ -969,7 +970,8 @@ void GPUContextWebGPU::FlushBindGroup() // Build descriptors for the bind group auto entriesCount = descriptors->DescriptorTypesCount; - _dynamicOffsets.Clear(); + uint32 dynamicOffsets[4]; + uint32 dynamicOffsetsCount = 0; static_assert(ARRAY_COUNT(key.Entries) == SpirvShaderDescriptorInfo::MaxDescriptors, "Invalid size of bind group entries array."); static_assert(ARRAY_COUNT(key.Versions) == SpirvShaderDescriptorInfo::MaxDescriptors, "Invalid size of bind group versions array."); key.EntriesCount = entriesCount; @@ -1060,7 +1062,7 @@ void GPUContextWebGPU::FlushBindGroup() if (descriptor.DescriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) entry.offset = uniform->Allocation.Offset; else - _dynamicOffsets.Add(uniform->Allocation.Offset); + dynamicOffsets[dynamicOffsetsCount++] = uniform->Allocation.Offset; } else LOG(Fatal, "Missing constant buffer at slot {}", descriptor.Slot); @@ -1089,11 +1091,12 @@ void GPUContextWebGPU::FlushBindGroup() LOG(Error, " > buffer: {}", (uint32)e.buffer); } } + ASSERT(dynamicOffsetsCount <= ARRAY_COUNT(dynamicOffsets)); #endif // Bind group WGPUBindGroup bindGroup = _pipelineState->GetBindGroup(key); - wgpuRenderPassEncoderSetBindGroup(_renderPass, groupIndex, bindGroup, _dynamicOffsets.Count(), _dynamicOffsets.Get()); + wgpuRenderPassEncoderSetBindGroup(_renderPass, groupIndex, bindGroup, dynamicOffsetsCount, dynamicOffsets); } } diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.h b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.h index 00f515e47..29c4c8544 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.h +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.h @@ -43,19 +43,18 @@ private: GPUDeviceWebGPU* _device; uint32 _minUniformBufferOffsetAlignment; - Array _dynamicOffsets; // State tracking - int32 _renderPassDirty : 1; - int32 _pipelineDirty : 1; - int32 _bindGroupDirty : 1; - int32 _vertexBufferDirty : 1; - int32 _indexBufferDirty : 1; - int32 _indexBuffer32Bit : 1; - int32 _blendFactorDirty : 1; - int32 _blendFactorSet : 1; - int32 _renderTargetCount : 4; - int32 _vertexBufferCount : 3; + uint32 _renderPassDirty : 1; + uint32 _pipelineDirty : 1; + uint32 _bindGroupDirty : 1; + uint32 _vertexBufferDirty : 1; + uint32 _indexBufferDirty : 1; + uint32 _indexBuffer32Bit : 1; + uint32 _blendFactorDirty : 1; + uint32 _blendFactorSet : 1; + uint32 _renderTargetCount : 4; + uint32 _vertexBufferCount : 4; uint32 _stencilRef; Float4 _blendFactor; Viewport _viewport; diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp index f07d01739..f16475fd5 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp @@ -167,6 +167,7 @@ bool GPUDeviceWebGPU::Init() if (wgpuAdapterGetLimits(Adapter->Adapter, &limits) == WGPUStatus_Success) { MinUniformBufferOffsetAlignment = limits.minUniformBufferOffsetAlignment; + Limits.HasInstancing = true; Limits.HasDrawIndirect = true; Limits.HasDepthAsSRV = true; Limits.HasReadOnlyDepth = true; @@ -446,7 +447,7 @@ bool GPUDeviceWebGPU::Init() } static int32 LogSpamLeft = 20; if (LogSpamLeft-- < 0) - CRASH; // Too many errors + LOG(Fatal, "Too many WebGPU errors"); #endif }; @@ -500,7 +501,7 @@ bool GPUDeviceWebGPU::Init() #if GPU_ENABLE_RESOURCE_NAMING bufferDesc.label = WEBGPU_STR("DefaultBuffer"); #endif - bufferDesc.usage = WGPUBufferUsage_Storage; + bufferDesc.usage = WGPUBufferUsage_Storage | WGPUBufferUsage_Vertex; bufferDesc.size = 64; DefaultBuffer = wgpuDeviceCreateBuffer(Device, &bufferDesc); } diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp index 571837c4b..112368cd1 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp @@ -2,6 +2,10 @@ #if GRAPHICS_API_WEBGPU +#define WEBGPU_LOG_PSO 0 +//#define WEBGPU_LOG_PSO_NAME "PS_GBuffer" // Debug log for PSOs with specific name +#define WEBGPU_LOG_BIND_GROUPS 0 + #include "GPUPipelineStateWebGPU.h" #include "GPUTextureWebGPU.h" #include "GPUVertexLayoutWebGPU.h" @@ -12,9 +16,9 @@ #include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Utilities/Crc.h" - -#define WEBGPU_LOG_PSO 0 -#define WEBGPU_LOG_BIND_GROUPS 0 +#if WEBGPU_LOG_PSO +#include "Engine/Scripting/Enums.h" +#endif WGPUCompareFunction ToCompareFunction(ComparisonFunc value) { @@ -198,6 +202,11 @@ WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const PipelineKey& key, G #endif #if WEBGPU_LOG_PSO LOG(Info, "[WebGPU] GetPipeline: '{}'", String(_debugName.Get(), _debugName.Count() - 1)); +#ifdef WEBGPU_LOG_PSO_NAME + const bool log = StringAnsiView(_debugName.Get(), _debugName.Count() - 1).Contains(WEBGPU_LOG_PSO_NAME); +#else + const bool log = true; +#endif #endif // Lazy-init layout (cannot do it during Init as texture samplers that access eg. depth need to explicitly use UnfilterableFloat) @@ -222,7 +231,7 @@ WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const PipelineKey& key, G if (!mergedVertexLayout) mergedVertexLayout = (GPUVertexLayoutWebGPU*)VS->Layout; // Fallback to shader-specified layout (if using old APIs) if (VS->InputLayout) - mergedVertexLayout = (GPUVertexLayoutWebGPU*)GPUVertexLayout::Merge(mergedVertexLayout, VS->InputLayout, false, true, -1, true); + mergedVertexLayout = (GPUVertexLayoutWebGPU*)GPUVertexLayout::Merge(mergedVertexLayout, VS->InputLayout, true, true, -1, true); // Build attributes list WGPUVertexAttribute attributes[GPU_MAX_VS_ELEMENTS]; @@ -230,6 +239,10 @@ WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const PipelineKey& key, G PipelineDesc.vertex.buffers = buffers; int32 attributeIndex = 0; auto& elements = mergedVertexLayout->GetElements(); +#if WEBGPU_LOG_PSO + if (log) + LOG(Info, " > Vertex elements: {}", elements.Count()); +#endif for (int32 bufferIndex = 0; bufferIndex < GPU_MAX_VB_BINDED; bufferIndex++) { auto& buffer = buffers[bufferIndex]; @@ -253,6 +266,10 @@ WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const PipelineKey& key, G buffer.stepMode = WGPUVertexStepMode_Instance; buffer.arrayStride = Math::Max(buffer.arrayStride, element.Offset + PixelFormatExtensions::SizeInBytes(element.Format)); PipelineDesc.vertex.bufferCount = Math::Max(PipelineDesc.vertex.bufferCount, bufferIndex + 1); +#if WEBGPU_LOG_PSO + if (log) + LOG(Info, " > [{}] slot {}: {} ({} bytes at offset {}) at shader location: {} (per-instance: {})", attributeIndex - 1, element.Slot, ScriptingEnum::ToString(element.Format), PixelFormatExtensions::SizeInBytes(element.Format), element.Offset, dst.shaderLocation, element.PerInstance); +#endif } } } @@ -366,7 +383,7 @@ WGPUBindGroup GPUPipelineStateWebGPU::GetBindGroup(BindGroupKey& key) } } if (collisions > 1) - LOG(Error, "> Hash colllision! {}/{} (capacity: {}), equalLayout: {}, equalEntries: {}, equalVersions: {}", collisions, _bindGroups.Count(), _bindGroups.Capacity(), equalLayout, equalEntries, equalVersions); + LOG(Error, "> Hash collision! {}/{} (capacity: {}), equalLayout: {}, equalEntries: {}, equalVersions: {}", collisions, _bindGroups.Count(), _bindGroups.Capacity(), equalLayout, equalEntries, equalVersions); #endif // Cache it @@ -377,8 +394,11 @@ WGPUBindGroup GPUPipelineStateWebGPU::GetBindGroup(BindGroupKey& key) void GPUPipelineStateWebGPU::InitLayout(GPUResourceView* shaderResources[GPU_MAX_SR_BINDED]) { #if WEBGPU_LOG_PSO - // Debug log for PSOs with specific name - const bool log = true;// StringAnsiView(_debugName.Get(), _debugName.Count() - 1).Contains("PS_HalfDepth"); +#ifdef WEBGPU_LOG_PSO_NAME + const bool log = StringAnsiView(_debugName.Get(), _debugName.Count() - 1).Contains(WEBGPU_LOG_PSO_NAME); +#else + const bool log = true; +#endif #endif // Count the biggest bind group entries (for all shaders) to allocate reused memory diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUTextureWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUTextureWebGPU.cpp index 39c4d7aed..58cea6317 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUTextureWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUTextureWebGPU.cpp @@ -265,22 +265,16 @@ void GPUTextureWebGPU::InitHandles() // Init per slice views _handlesPerSlice.Resize(Depth(), false); - //viewDesc.dimension = WGPUTextureViewDimension_2DArray; if (_desc.HasPerSliceViews() && IsRenderTarget()) { for (int32 sliceIndex = 0; sliceIndex < Depth(); sliceIndex++) { - //viewDesc.baseArrayLayer = sliceIndex; - //viewDesc.arrayLayerCount = 1; auto& view = _handlesPerSlice[sliceIndex]; view.Init(this, format, msaa); view.Create(Texture, viewDesc); view.DepthSlice = sliceIndex; } } - //viewDesc.baseArrayLayer = 0; - //viewDesc.arrayLayerCount = MipLevels(); - //viewDesc.dimension = _viewDimension; } else if (IsArray()) { @@ -370,7 +364,7 @@ void GPUTextureWebGPU::InitHandles() bool GPUTextureWebGPU::GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) { - // TODO: no implemented + // TODO: not implemented return true; } diff --git a/Source/Engine/Platform/Web/WebPlatform.cpp b/Source/Engine/Platform/Web/WebPlatform.cpp index 863f8ea30..2bc9d4c8d 100644 --- a/Source/Engine/Platform/Web/WebPlatform.cpp +++ b/Source/Engine/Platform/Web/WebPlatform.cpp @@ -13,6 +13,7 @@ #include "Engine/Platform/CPUInfo.h" #include "Engine/Platform/MemoryStats.h" #include "Engine/Profiler/ProfilerCPU.h" +#include "Engine/Engine/Engine.h" #include "Engine/Engine/Web/WebGame.h" #include "Engine/Utilities/StringConverter.h" #include @@ -252,6 +253,9 @@ void WebPlatform::Tick() void WebPlatform::Exit() { + // Exit runtime + emscripten_cancel_main_loop(); + emscripten_force_exit(Engine::ExitCode); } extern char** environ; diff --git a/Source/Engine/ShadersCompilation/WebGPU/ShaderCompilerWebGPU.cpp b/Source/Engine/ShadersCompilation/WebGPU/ShaderCompilerWebGPU.cpp index 3828be55a..abaee7a81 100644 --- a/Source/Engine/ShadersCompilation/WebGPU/ShaderCompilerWebGPU.cpp +++ b/Source/Engine/ShadersCompilation/WebGPU/ShaderCompilerWebGPU.cpp @@ -9,7 +9,7 @@ #include "Engine/GraphicsDevice/Vulkan/Types.h" #include "Engine/Platform/CreateProcessSettings.h" #include "Engine/Platform/File.h" -#include "Engine/Platform/Windows/WindowsFileSystem.h" +#include "Engine/Platform/FileSystem.h" #include #include