Various changes for WebGPU and hardware instancing support

This commit is contained in:
Wojtek Figat
2026-03-03 14:51:15 +01:00
parent 6891256afe
commit 6814a43418
8 changed files with 63 additions and 34 deletions

View File

@@ -319,9 +319,10 @@ void GPUContextWebGPU::BindVB(const Span<GPUBuffer*>& 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);
}
}

View File

@@ -43,19 +43,18 @@ private:
GPUDeviceWebGPU* _device;
uint32 _minUniformBufferOffsetAlignment;
Array<uint32> _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;

View File

@@ -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);
}

View File

@@ -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<uint64>(buffer.arrayStride, element.Offset + PixelFormatExtensions::SizeInBytes(element.Format));
PipelineDesc.vertex.bufferCount = Math::Max<size_t>(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

View File

@@ -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;
}