|
|
|
|
@@ -3,6 +3,7 @@
|
|
|
|
|
#if GRAPHICS_API_WEBGPU
|
|
|
|
|
|
|
|
|
|
#include "GPUPipelineStateWebGPU.h"
|
|
|
|
|
#include "GPUTextureWebGPU.h"
|
|
|
|
|
#include "GPUVertexLayoutWebGPU.h"
|
|
|
|
|
#include "RenderToolsWebGPU.h"
|
|
|
|
|
#include "Engine/Core/Log.h"
|
|
|
|
|
@@ -157,13 +158,14 @@ void GPUPipelineStateWebGPU::OnReleaseGPU()
|
|
|
|
|
|
|
|
|
|
uint32 GetHash(const GPUPipelineStateWebGPU::Key& key)
|
|
|
|
|
{
|
|
|
|
|
static_assert(sizeof(GPUPipelineStateWebGPU::Key) == sizeof(uint64) * 4, "Invalid PSO key size.");
|
|
|
|
|
static_assert(sizeof(GPUPipelineStateWebGPU::Key) == sizeof(uint64) * 3, "Invalid PSO key size.");
|
|
|
|
|
uint32 hash = GetHash(key.Packed[0]);
|
|
|
|
|
CombineHash(hash, GetHash(key.Packed[1]));
|
|
|
|
|
CombineHash(hash, GetHash(key.Packed[2]));
|
|
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const Key& key)
|
|
|
|
|
WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const Key& key, GPUResourceView* shaderResources[GPU_MAX_SR_BINDED])
|
|
|
|
|
{
|
|
|
|
|
WGPURenderPipeline pipeline;
|
|
|
|
|
if (_pipelines.TryGet(key, pipeline))
|
|
|
|
|
@@ -174,6 +176,10 @@ WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const Key& key)
|
|
|
|
|
ZoneText(_debugName.Get(), _debugName.Count() - 1);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Lazy-init layout (cannot do it during Init as texture samplers that access eg. depth need to explicitly use UnfilterableFloat)
|
|
|
|
|
if (!PipelineDesc.layout)
|
|
|
|
|
InitLayout(shaderResources);
|
|
|
|
|
|
|
|
|
|
// Build final pipeline description
|
|
|
|
|
_depthStencilDesc.format = (WGPUTextureFormat)key.DepthStencilFormat;
|
|
|
|
|
PipelineDesc.multisample.count = key.MultiSampleCount;
|
|
|
|
|
@@ -245,6 +251,119 @@ WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const Key& key)
|
|
|
|
|
return pipeline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GPUPipelineStateWebGPU::InitLayout(GPUResourceView* shaderResources[GPU_MAX_SR_BINDED])
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// Count the biggest bind group entries (for all shaders) to allocate reused memory
|
|
|
|
|
int32 maxEntriesCount = 0;
|
|
|
|
|
for (int32 groupIndex = 0; groupIndex < ARRAY_COUNT(BindGroupDescriptors); groupIndex++)
|
|
|
|
|
{
|
|
|
|
|
auto descriptors = BindGroupDescriptors[groupIndex];
|
|
|
|
|
if (descriptors && maxEntriesCount < descriptors->DescriptorTypesCount)
|
|
|
|
|
maxEntriesCount = (int32)descriptors->DescriptorTypesCount;
|
|
|
|
|
}
|
|
|
|
|
Array<WGPUBindGroupLayoutEntry, InlinedAllocation<8>> entries;
|
|
|
|
|
entries.Resize(maxEntriesCount);
|
|
|
|
|
|
|
|
|
|
// Setup bind groups
|
|
|
|
|
WGPUBindGroupLayoutEntry* entriesPtr = entries.Get();
|
|
|
|
|
for (int32 groupIndex = 0; groupIndex < ARRAY_COUNT(BindGroupDescriptors); groupIndex++)
|
|
|
|
|
{
|
|
|
|
|
auto descriptors = BindGroupDescriptors[groupIndex];
|
|
|
|
|
if (!descriptors || descriptors->DescriptorTypesCount == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
int32 entriesCount = descriptors->DescriptorTypesCount;
|
|
|
|
|
Platform::MemoryClear(entries.Get(), sizeof(WGPUBindGroupLayoutEntry) * entriesCount);
|
|
|
|
|
auto visibility = groupIndex == 0 ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment;
|
|
|
|
|
for (int32 index = 0; index < entriesCount; index++)
|
|
|
|
|
{
|
|
|
|
|
auto& descriptor = descriptors->DescriptorTypes[index];
|
|
|
|
|
auto& entry = entriesPtr[index];
|
|
|
|
|
entry.binding = descriptor.Binding;
|
|
|
|
|
entry.bindingArraySize = descriptor.Count;
|
|
|
|
|
entry.visibility = visibility;
|
|
|
|
|
switch (descriptor.DescriptorType)
|
|
|
|
|
{
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
|
|
|
|
entry.sampler.type = WGPUSamplerBindingType_Undefined;
|
|
|
|
|
break;
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
|
|
|
|
entry.texture.sampleType = WGPUTextureSampleType_Undefined;
|
|
|
|
|
if (shaderResources[descriptor.Slot])
|
|
|
|
|
{
|
|
|
|
|
// Hack to use the sample type directly from the view which allows to fix incorrect Depth Buffer reading that allows only manual Load when UnfilterableFloat is used (see SAMPLE_RT_LOAD)
|
|
|
|
|
auto ptr = (GPUResourceViewPtrWebGPU*)shaderResources[descriptor.Slot]->GetNativePtr();
|
|
|
|
|
if (ptr && ptr->TextureView)
|
|
|
|
|
entry.texture.sampleType = ptr->TextureView->SampleType;
|
|
|
|
|
}
|
|
|
|
|
switch (descriptor.ResourceType)
|
|
|
|
|
{
|
|
|
|
|
case SpirvShaderResourceType::Texture1D:
|
|
|
|
|
entry.texture.viewDimension = WGPUTextureViewDimension_1D;
|
|
|
|
|
break;
|
|
|
|
|
case SpirvShaderResourceType::Texture2D:
|
|
|
|
|
entry.texture.viewDimension = WGPUTextureViewDimension_2D;
|
|
|
|
|
break;
|
|
|
|
|
case SpirvShaderResourceType::Texture3D:
|
|
|
|
|
entry.texture.viewDimension = WGPUTextureViewDimension_3D;
|
|
|
|
|
break;
|
|
|
|
|
case SpirvShaderResourceType::TextureCube:
|
|
|
|
|
entry.texture.viewDimension = WGPUTextureViewDimension_Cube;
|
|
|
|
|
break;
|
|
|
|
|
case SpirvShaderResourceType::Texture1DArray:
|
|
|
|
|
CRASH; // Not supported TODO: add error at compile time (in ShaderCompilerWebGPU::Write)
|
|
|
|
|
break;
|
|
|
|
|
case SpirvShaderResourceType::Texture2DArray:
|
|
|
|
|
entry.texture.viewDimension = WGPUTextureViewDimension_2DArray;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
|
|
|
|
entry.buffer.hasDynamicOffset = true;
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
|
|
|
|
if (descriptor.BindingType == SpirvShaderResourceBindingType::SRV)
|
|
|
|
|
entry.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
|
|
|
|
|
else
|
|
|
|
|
entry.buffer.type = WGPUBufferBindingType_Storage;
|
|
|
|
|
break;
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
|
|
|
|
entry.buffer.hasDynamicOffset = true;
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
|
|
|
|
entry.buffer.type = WGPUBufferBindingType_Uniform;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
#if GPU_ENABLE_DIAGNOSTICS
|
|
|
|
|
LOG(Fatal, "Unknown descriptor type: {} used as {} in '{}'", (uint32)descriptor.DescriptorType, (uint32)descriptor.BindingType, String(_debugName.Get(), _debugName.Count() - 1));
|
|
|
|
|
#else
|
|
|
|
|
CRASH;
|
|
|
|
|
#endif
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a bind group layout
|
|
|
|
|
WGPUBindGroupLayoutDescriptor bindGroupLayoutDesc = WGPU_BIND_GROUP_LAYOUT_DESCRIPTOR_INIT;
|
|
|
|
|
bindGroupLayoutDesc.entryCount = entriesCount;
|
|
|
|
|
bindGroupLayoutDesc.entries = entriesPtr;
|
|
|
|
|
BindGroupLayouts[groupIndex] = wgpuDeviceCreateBindGroupLayout(_device->Device, &bindGroupLayoutDesc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the pipeline layout
|
|
|
|
|
WGPUPipelineLayoutDescriptor layoutDesc = WGPU_PIPELINE_LAYOUT_DESCRIPTOR_INIT;
|
|
|
|
|
#if GPU_ENABLE_RESOURCE_NAMING
|
|
|
|
|
layoutDesc.label = PipelineDesc.label;
|
|
|
|
|
#endif
|
|
|
|
|
layoutDesc.bindGroupLayoutCount = GPUBindGroupsWebGPU::GraphicsMax;
|
|
|
|
|
layoutDesc.bindGroupLayouts = BindGroupLayouts;
|
|
|
|
|
PipelineDesc.layout = wgpuDeviceCreatePipelineLayout(_device->Device, &layoutDesc);
|
|
|
|
|
if (!PipelineDesc.layout)
|
|
|
|
|
{
|
|
|
|
|
LOG(Error, "wgpuDeviceCreatePipelineLayout failed");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GPUPipelineStateWebGPU::IsValid() const
|
|
|
|
|
{
|
|
|
|
|
return _memoryUsage != 0;
|
|
|
|
|
@@ -338,108 +457,6 @@ bool GPUPipelineStateWebGPU::Init(const Description& desc)
|
|
|
|
|
_fragmentDesc.module = PS->ShaderModule;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Count the biggest bind group entries (for all shaders) to allocate reused memory
|
|
|
|
|
int32 maxEntriesCount = 0;
|
|
|
|
|
for (int32 groupIndex = 0; groupIndex < ARRAY_COUNT(BindGroupDescriptors); groupIndex++)
|
|
|
|
|
{
|
|
|
|
|
auto descriptors = BindGroupDescriptors[groupIndex];
|
|
|
|
|
if (descriptors && maxEntriesCount < descriptors->DescriptorTypesCount)
|
|
|
|
|
maxEntriesCount = (int32)descriptors->DescriptorTypesCount;
|
|
|
|
|
}
|
|
|
|
|
Array<WGPUBindGroupLayoutEntry, InlinedAllocation<8>> entries;
|
|
|
|
|
entries.Resize(maxEntriesCount);
|
|
|
|
|
|
|
|
|
|
// Setup bind groups
|
|
|
|
|
WGPUBindGroupLayoutEntry* entriesPtr = entries.Get();
|
|
|
|
|
for (int32 groupIndex = 0; groupIndex < ARRAY_COUNT(BindGroupDescriptors); groupIndex++)
|
|
|
|
|
{
|
|
|
|
|
auto descriptors = BindGroupDescriptors[groupIndex];
|
|
|
|
|
if (!descriptors || descriptors->DescriptorTypesCount == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
int32 entriesCount = descriptors->DescriptorTypesCount;
|
|
|
|
|
Platform::MemoryClear(entries.Get(), sizeof(WGPUBindGroupLayoutEntry) * entriesCount);
|
|
|
|
|
auto visibility = groupIndex == 0 ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment;
|
|
|
|
|
for (int32 index = 0; index < entriesCount; index++)
|
|
|
|
|
{
|
|
|
|
|
auto& descriptor = descriptors->DescriptorTypes[index];
|
|
|
|
|
auto& entry = entriesPtr[index];
|
|
|
|
|
entry.binding = descriptor.Binding;
|
|
|
|
|
entry.bindingArraySize = descriptor.Count;
|
|
|
|
|
entry.visibility = visibility;
|
|
|
|
|
switch (descriptor.DescriptorType)
|
|
|
|
|
{
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
|
|
|
|
entry.sampler.type = WGPUSamplerBindingType_Undefined;
|
|
|
|
|
break;
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
|
|
|
|
entry.texture.sampleType = WGPUTextureSampleType_Undefined;
|
|
|
|
|
switch (descriptor.ResourceType)
|
|
|
|
|
{
|
|
|
|
|
case SpirvShaderResourceType::Texture1D:
|
|
|
|
|
entry.texture.viewDimension = WGPUTextureViewDimension_1D;
|
|
|
|
|
break;
|
|
|
|
|
case SpirvShaderResourceType::Texture2D:
|
|
|
|
|
entry.texture.viewDimension = WGPUTextureViewDimension_2D;
|
|
|
|
|
break;
|
|
|
|
|
case SpirvShaderResourceType::Texture3D:
|
|
|
|
|
entry.texture.viewDimension = WGPUTextureViewDimension_3D;
|
|
|
|
|
break;
|
|
|
|
|
case SpirvShaderResourceType::TextureCube:
|
|
|
|
|
entry.texture.viewDimension = WGPUTextureViewDimension_Cube;
|
|
|
|
|
break;
|
|
|
|
|
case SpirvShaderResourceType::Texture1DArray:
|
|
|
|
|
CRASH; // Not supported TODO: add error at compile time (in ShaderCompilerWebGPU::Write)
|
|
|
|
|
break;
|
|
|
|
|
case SpirvShaderResourceType::Texture2DArray:
|
|
|
|
|
entry.texture.viewDimension = WGPUTextureViewDimension_2DArray;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
|
|
|
|
entry.buffer.hasDynamicOffset = true;
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
|
|
|
|
if (descriptor.BindingType == SpirvShaderResourceBindingType::SRV)
|
|
|
|
|
entry.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
|
|
|
|
|
else
|
|
|
|
|
entry.buffer.type = WGPUBufferBindingType_Storage;
|
|
|
|
|
break;
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
|
|
|
|
entry.buffer.hasDynamicOffset = true;
|
|
|
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
|
|
|
|
entry.buffer.type = WGPUBufferBindingType_Uniform;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
#if GPU_ENABLE_DIAGNOSTICS
|
|
|
|
|
LOG(Fatal, "Unknown descriptor type: {} used as {} in '{}'", (uint32)descriptor.DescriptorType, (uint32)descriptor.BindingType, String(_debugName.Get(), _debugName.Count() - 1));
|
|
|
|
|
#else
|
|
|
|
|
CRASH;
|
|
|
|
|
#endif
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a bind group layout
|
|
|
|
|
WGPUBindGroupLayoutDescriptor bindGroupLayoutDesc = WGPU_BIND_GROUP_LAYOUT_DESCRIPTOR_INIT;
|
|
|
|
|
bindGroupLayoutDesc.entryCount = entriesCount;
|
|
|
|
|
bindGroupLayoutDesc.entries = entriesPtr;
|
|
|
|
|
BindGroupLayouts[groupIndex] = wgpuDeviceCreateBindGroupLayout(_device->Device, &bindGroupLayoutDesc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the pipeline layout
|
|
|
|
|
WGPUPipelineLayoutDescriptor layoutDesc = WGPU_PIPELINE_LAYOUT_DESCRIPTOR_INIT;
|
|
|
|
|
#if GPU_ENABLE_RESOURCE_NAMING
|
|
|
|
|
layoutDesc.label = PipelineDesc.label;
|
|
|
|
|
#endif
|
|
|
|
|
layoutDesc.bindGroupLayoutCount = GPUBindGroupsWebGPU::GraphicsMax;
|
|
|
|
|
layoutDesc.bindGroupLayouts = BindGroupLayouts;
|
|
|
|
|
PipelineDesc.layout = wgpuDeviceCreatePipelineLayout(_device->Device, &layoutDesc);
|
|
|
|
|
if (!PipelineDesc.layout)
|
|
|
|
|
{
|
|
|
|
|
LOG(Error, "wgpuDeviceCreatePipelineLayout failed");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_memoryUsage = 1;
|
|
|
|
|
return GPUPipelineState::Init(desc);
|
|
|
|
|
}
|
|
|
|
|
|