From 8728d88ddeb43249c33a4f21e7d8f710e083f539 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 26 Feb 2026 15:50:36 +0100 Subject: [PATCH] Fix vertex layouts binding to match vertex shader inputs on WebGPU --- .../WebGPU/GPUContextWebGPU.cpp | 4 +- .../GraphicsDevice/WebGPU/GPUContextWebGPU.h | 1 - .../WebGPU/GPUPipelineStateWebGPU.cpp | 59 ++++++++++++++++--- .../WebGPU/GPUPipelineStateWebGPU.h | 3 +- 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp index fc0328b8f..4b1809b8a 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp @@ -41,7 +41,6 @@ GPUContextWebGPU::GPUContextWebGPU(GPUDeviceWebGPU* device) : GPUContext(device) , _device(device) { - _vertexBufferNullLayout = WGPU_VERTEX_BUFFER_LAYOUT_INIT; _minUniformBufferOffsetAlignment = device->MinUniformBufferOffsetAlignment; // Setup descriptor handles tables lookup cache @@ -307,14 +306,13 @@ void GPUContextWebGPU::BindVB(const Span& vertexBuffers, const uint3 ASSERT(vertexBuffers.Length() <= GPU_MAX_VB_BINDED); _vertexBufferDirty = true; _vertexBufferCount = vertexBuffers.Length(); - _pipelineKey.VertexBufferCount = vertexBuffers.Length(); + _pipelineKey.VertexLayout = (GPUVertexLayoutWebGPU*)(vertexLayout ? vertexLayout : GPUVertexLayout::Get(vertexBuffers)); 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; - _pipelineKey.VertexBuffers[i] = vbWebGPU && vbWebGPU->GetVertexLayout() ? &((GPUVertexLayoutWebGPU*)vbWebGPU->GetVertexLayout())->Layout : &_vertexBufferNullLayout; } } diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.h b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.h index 9f392bae2..8d03d11da 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.h +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.h @@ -42,7 +42,6 @@ private: }; GPUDeviceWebGPU* _device; - WGPUVertexBufferLayout _vertexBufferNullLayout; uint32 _minUniformBufferOffsetAlignment; Array _bindGroupEntries; Array _dynamicOffsets; diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp index 0513ee6e7..43cdf89e2 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp @@ -4,9 +4,11 @@ #include "GPUPipelineStateWebGPU.h" #include "GPUVertexLayoutWebGPU.h" +#include "RenderToolsWebGPU.h" #include "Engine/Core/Log.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerMemory.h" +#include "Engine/Graphics/PixelFormatExtensions.h" WGPUCompareFunction ToCompareFunction(ComparisonFunc value) { @@ -179,15 +181,53 @@ WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const Key& key) for (int32 i = 0; i < _fragmentDesc.targetCount; i++) _colorTargets[i].format = (WGPUTextureFormat)key.RenderTargetFormats[i]; WGPUVertexBufferLayout buffers[GPU_MAX_VB_BINDED]; - PipelineDesc.vertex.bufferCount = key.VertexBufferCount; - int32 shaderLocation = 0; - for (int32 i = 0; i < PipelineDesc.vertex.bufferCount; i++) + if (key.VertexLayout) { - buffers[i] = *key.VertexBuffers[i]; - for (int32 j = 0; j < buffers[i].attributeCount; j++) - ((WGPUVertexAttribute&)buffers[i].attributes[j]).shaderLocation = shaderLocation++; + // Combine input layout of Vertex Buffers with the destination layout used by the Vertex Shader + GPUVertexLayoutWebGPU* mergedVertexLayout = key.VertexLayout; + 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); + + // Build attributes list + WGPUVertexAttribute attributes[GPU_MAX_VS_ELEMENTS]; + PipelineDesc.vertex.bufferCount = 0; + PipelineDesc.vertex.buffers = buffers; + int32 attributeIndex = 0; + auto& elements = mergedVertexLayout->GetElements(); + for (int32 bufferIndex = 0; bufferIndex < GPU_MAX_VB_BINDED; bufferIndex++) + { + auto& buffer = buffers[bufferIndex]; + buffer.nextInChain = nullptr; + buffer.stepMode = WGPUVertexStepMode_Vertex; + buffer.arrayStride = 0; + buffer.attributeCount = 0; + buffer.attributes = attributes + attributeIndex; + for (int32 i = 0; i < elements.Count(); i++) + { + const VertexElement& element = elements[i]; + if (element.Slot != bufferIndex) + continue; + WGPUVertexAttribute& dst = attributes[attributeIndex++]; + buffer.attributeCount++; + dst.nextInChain = nullptr; + dst.format = RenderToolsWebGPU::ToVertexFormat(element.Format); + dst.offset = element.Offset; + dst.shaderLocation = i; // Elements are sorted to match Input Layout order of Vertex Shader as provided by GPUVertexLayout::Merge + if (element.PerInstance) + 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); + } + } + } + else + { + // No vertex input + PipelineDesc.vertex.bufferCount = 0; + PipelineDesc.vertex.buffers = nullptr; } - PipelineDesc.vertex.buffers = buffers; // Create object pipeline = wgpuDeviceCreateRenderPipeline(_device->Device, &PipelineDesc); @@ -280,9 +320,10 @@ bool GPUPipelineStateWebGPU::Init(const Description& desc) if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Alpha)) writeMask |= WGPUColorWriteMask_Alpha; } - if (PS) + if (desc.PS) { - for (int32 rtIndex = 0; rtIndex < PS->GetBindings().OutputsCount; rtIndex++) + uint16 outputsCount = desc.PS->GetBindings().OutputsCount; + for (uint16 rtIndex = 0; rtIndex < outputsCount; rtIndex++) _colorTargets[rtIndex].writeMask = writeMask; } diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.h b/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.h index 7a66b1c8a..13cf0ba1d 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.h +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.h @@ -25,9 +25,8 @@ public: uint16 DepthStencilFormat : 7; uint16 MultiSampleCount : 3; uint16 RenderTargetCount : 3; - uint16 VertexBufferCount : 3; uint8 RenderTargetFormats[GPU_MAX_RT_BINDED]; - struct WGPUVertexBufferLayout* VertexBuffers[GPU_MAX_VB_BINDED]; + class GPUVertexLayoutWebGPU* VertexLayout; }; uint64 Packed[4]; };