Fix binding various resources to shaders in WebGPU

This commit is contained in:
Wojtek Figat
2026-02-26 12:23:07 +01:00
parent 20c9ed27fb
commit 239ceb75a9
9 changed files with 51 additions and 19 deletions

View File

@@ -66,7 +66,7 @@ bool GPUBufferWebGPU::OnInit()
bufferDesc.usage |= WGPUBufferUsage_Vertex;
else if (EnumHasAllFlags(_desc.Flags, GPUBufferFlags::Argument))
bufferDesc.usage |= WGPUBufferUsage_Indirect;
if (IsUnorderedAccess())
if (IsUnorderedAccess() || IsShaderResource()) // SRV buffers need to be bind as read-only storage
bufferDesc.usage |= WGPUBufferUsage_Storage;
switch (_desc.Usage)
{

View File

@@ -347,8 +347,10 @@ void GPUContextWebGPU::UpdateCB(GPUConstantBuffer* cb, const void* data)
if (size != 0)
{
// Allocate a chunk of memory in a shared page allocator
auto allocation = _device->DataUploader.Allocate(size, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst, _minUniformBufferOffsetAlignment);
uint32 alignedSize = Math::AlignUp<uint32>(size, 16); // Uniform buffers must be aligned to 16 bytes
auto allocation = _device->DataUploader.Allocate(alignedSize, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst, _minUniformBufferOffsetAlignment);
cbWebGPU->Allocation = allocation;
cbWebGPU->AllocationSize = alignedSize;
// TODO: consider holding CPU-side staging buffer and copying data to the GPU buffer in a single batch for all uniforms (before flushing the active command encoder)
wgpuQueueWriteBuffer(_device->Queue, allocation.Buffer, allocation.Offset, data, size);
_bindGroupDirty = true;
@@ -944,7 +946,13 @@ void GPUContextWebGPU::FlushBindGroup()
if (!entry.textureView)
{
// Fallback
view = _device->DefaultTexture->View(0);
auto defaultTexture = _device->DefaultTexture[(int32)descriptor.ResourceType];
if (!defaultTexture)
{
LOG(Error, "Missing resource {} at slot {} of binding space {}", (int32)descriptor.ResourceType, descriptor.Slot, (int32)descriptor.BindingType);
CRASH;
}
view = defaultTexture->View(0);
ptr = (GPUResourceViewPtrWebGPU*)view->GetNativePtr();
entry.textureView = ptr->TextureView->View;
}
@@ -974,7 +982,7 @@ void GPUContextWebGPU::FlushBindGroup()
if (uniform && uniform->Allocation.Buffer)
{
entry.buffer = uniform->Allocation.Buffer;
entry.size = uniform->GetSize();
entry.size = uniform->AllocationSize;
_dynamicOffsets.Add(uniform->Allocation.Offset);
}
else
@@ -988,7 +996,7 @@ void GPUContextWebGPU::FlushBindGroup()
{
entry.buffer = uniform->Allocation.Buffer;
entry.offset = uniform->Allocation.Offset;
entry.size = uniform->GetSize();
entry.size = uniform->AllocationSize;
}
else
CRASH; // TODO: add dummy buffer as fallback

View File

@@ -75,6 +75,10 @@ private:
Array<PendingClear, FixedAllocation<16>> _pendingClears;
GPUResourceView* _shaderResources[GPU_MAX_SR_BINDED];
GPUResourceView* _storageResources[GPU_MAX_SR_BINDED];
GPUResourceView** _resourceTables[(int32)SpirvShaderResourceBindingType::MAX];
#if ENABLE_ASSERTION
uint32 _resourceTableSizes[(int32)SpirvShaderResourceBindingType::MAX];
#endif
public:
GPUContextWebGPU(GPUDeviceWebGPU* device);

View File

@@ -18,6 +18,7 @@
#if BUILD_DEBUG
#include "Engine/Core/Collections/Sorting.h"
#endif
#include "Engine/GraphicsDevice/Vulkan/Types.h"
#include "Engine/Graphics/PixelFormatExtensions.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Profiler/ProfilerMemory.h"
@@ -182,8 +183,11 @@ bool GPUDeviceWebGPU::Init()
if (wgpuAdapterGetLimits(Adapter->Adapter, &limits) == WGPUStatus_Success)
{
MinUniformBufferOffsetAlignment = limits.minUniformBufferOffsetAlignment;
Limits.HasDepthAsSRV = true;
Limits.HasReadOnlyDepth = true;
Limits.HasDepthClip = features.Contains(WGPUFeatureName_DepthClipControl);
Limits.HasReadOnlyDepth = true;
Limits.MaximumSamplerAnisotropy = 4;
Limits.MaximumTexture1DSize = Math::Min<int32>(GPU_MAX_TEXTURE_SIZE, limits.maxTextureDimension1D);
Limits.MaximumTexture2DSize = Math::Min<int32>(GPU_MAX_TEXTURE_SIZE, limits.maxTextureDimension2D);
Limits.MaximumTexture3DSize = Math::Min<int32>(GPU_MAX_TEXTURE_SIZE, limits.maxTextureDimension3D);
@@ -492,11 +496,20 @@ void GPUDeviceWebGPU::DrawBegin()
DataUploader.DrawBegin();
// Create default texture
if (!DefaultTexture)
static_assert(ARRAY_COUNT(DefaultTexture) == (int32)SpirvShaderResourceType::MAX, "Invalid size of default textures");
if (!DefaultTexture[(int32)SpirvShaderResourceType::Texture2D])
{
DefaultTexture = New<GPUTextureWebGPU>(this, TEXT("DefaultTexture"));
DefaultTexture->Init(GPUTextureDescription::New2D(1, 1, PixelFormat::R8G8B8A8_UNorm, GPUTextureFlags::ShaderResource));
DefaultTexture->SetResidentMipLevels(1);
GPUTextureWebGPU* texture;
#define INIT_TEXTURE(type, desc) \
texture = New<GPUTextureWebGPU>(this, TEXT("DefaultTexture")); \
DefaultTexture[(int32)SpirvShaderResourceType::type] = texture; \
texture->Init(desc); \
texture->SetResidentMipLevels(1)
INIT_TEXTURE(Texture2D, GPUTextureDescription::New2D(1, 1, PixelFormat::R8G8B8A8_UNorm, GPUTextureFlags::ShaderResource));
INIT_TEXTURE(Texture3D, GPUTextureDescription::New3D(1, 1, 1, PixelFormat::R8G8B8A8_UNorm, GPUTextureFlags::ShaderResource));
INIT_TEXTURE(TextureCube, GPUTextureDescription::NewCube(1, 1, PixelFormat::R8G8B8A8_UNorm, GPUTextureFlags::ShaderResource));
#undef INIT_TEXTURE
}
}
@@ -573,7 +586,7 @@ void GPUDeviceWebGPU::Dispose()
// Clear device resources
DataUploader.ReleaseGPU();
SAFE_DELETE_GPU_RESOURCE(DefaultTexture);
SAFE_DELETE_GPU_RESOURCES(DefaultTexture);
SAFE_DELETE_GPU_RESOURCES(DefaultSamplers);
SAFE_DELETE(_mainContext);
SAFE_DELETE(Adapter);

View File

@@ -80,7 +80,7 @@ public:
WGPUDevice Device = nullptr;
WGPUQueue Queue = nullptr;
GPUSamplerWebGPU* DefaultSamplers[6] = {};
GPUTextureWebGPU* DefaultTexture = nullptr;
GPUTextureWebGPU* DefaultTexture[10] = {};
GPUDataUploaderWebGPU DataUploader;
uint32 MinUniformBufferOffsetAlignment = 1;

View File

@@ -258,9 +258,15 @@ bool GPUPipelineStateWebGPU::Init(const Description& desc)
PipelineDesc.fragment = &_fragmentDesc;
_fragmentDesc = WGPU_FRAGMENT_STATE_INIT;
_fragmentDesc.targets = _colorTargets;
_blendState = WGPU_BLEND_STATE_INIT;
_blendState.color = ToBlendComponent(desc.BlendMode.BlendOp, desc.BlendMode.SrcBlend, desc.BlendMode.DestBlend);
_blendState.alpha = ToBlendComponent(desc.BlendMode.BlendOpAlpha, desc.BlendMode.SrcBlendAlpha, desc.BlendMode.DestBlendAlpha);
Platform::MemoryClear(&_colorTargets, sizeof(_colorTargets));
if (desc.BlendMode.BlendEnable)
{
_blendState = WGPU_BLEND_STATE_INIT;
_blendState.color = ToBlendComponent(desc.BlendMode.BlendOp, desc.BlendMode.SrcBlend, desc.BlendMode.DestBlend);
_blendState.alpha = ToBlendComponent(desc.BlendMode.BlendOpAlpha, desc.BlendMode.SrcBlendAlpha, desc.BlendMode.DestBlendAlpha);
for (auto& e : _colorTargets)
e.blend = &_blendState;
}
WGPUColorWriteMask writeMask = WGPUColorWriteMask_All;
if (desc.BlendMode.RenderTargetWriteMask != BlendingMode::ColorWrite::All)
{
@@ -274,12 +280,10 @@ bool GPUPipelineStateWebGPU::Init(const Description& desc)
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Alpha))
writeMask |= WGPUColorWriteMask_Alpha;
}
for (auto& e : _colorTargets)
if (PS)
{
e = WGPU_COLOR_TARGET_STATE_INIT;
if (desc.BlendMode.BlendEnable)
e.blend = &_blendState;
e.writeMask = writeMask;
for (int32 rtIndex = 0; rtIndex < PS->GetBindings().OutputsCount; rtIndex++)
_colorTargets[rtIndex].writeMask = writeMask;
}
// Cache shaders

View File

@@ -14,6 +14,7 @@ GPUConstantBufferWebGPU::GPUConstantBufferWebGPU(GPUDeviceWebGPU* device, uint32
: GPUResourceWebGPU(device, name)
{
_size = _memoryUsage = size;
AllocationSize = 0;
}
GPUShaderProgram* GPUShaderWebGPU::CreateGPUShaderProgram(ShaderStage type, const GPUShaderProgramInitializer& initializer, Span<byte> bytecode, MemoryReadStream& stream)

View File

@@ -18,6 +18,7 @@ public:
public:
GPUDataUploaderWebGPU::Allocation Allocation;
uint32 AllocationSize;
};
/// <summary>