Files
FlaxEngine/Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp

277 lines
9.9 KiB
C++

// Copyright (c) Wojciech Figat. All rights reserved.
#if GRAPHICS_API_WEBGPU
#include "GPUPipelineStateWebGPU.h"
#include "GPUVertexLayoutWebGPU.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
WGPUCompareFunction ToCompareFunction(ComparisonFunc value)
{
switch (value)
{
case ComparisonFunc::Never:
return WGPUCompareFunction_Never;
case ComparisonFunc::Less:
return WGPUCompareFunction_Less;
case ComparisonFunc::Equal:
return WGPUCompareFunction_Equal;
case ComparisonFunc::LessEqual:
return WGPUCompareFunction_LessEqual;
case ComparisonFunc::Greater:
return WGPUCompareFunction_Greater;
case ComparisonFunc::NotEqual:
return WGPUCompareFunction_NotEqual;
case ComparisonFunc::GreaterEqual:
return WGPUCompareFunction_GreaterEqual;
case ComparisonFunc::Always:
return WGPUCompareFunction_Always;
default:
return WGPUCompareFunction_Undefined;
}
}
WGPUStencilOperation ToStencilOperation(StencilOperation value)
{
switch (value)
{
case StencilOperation::Keep:
return WGPUStencilOperation_Keep;
case StencilOperation::Zero:
return WGPUStencilOperation_Zero;
case StencilOperation::Replace:
return WGPUStencilOperation_Replace;
case StencilOperation::IncrementSaturated:
return WGPUStencilOperation_IncrementClamp;
case StencilOperation::DecrementSaturated:
return WGPUStencilOperation_DecrementClamp;
case StencilOperation::Invert:
return WGPUStencilOperation_Invert;
case StencilOperation::Increment:
return WGPUStencilOperation_IncrementWrap;
case StencilOperation::Decrement:
return WGPUStencilOperation_DecrementWrap;
default:
return WGPUStencilOperation_Undefined;
}
}
WGPUBlendFactor ToBlendFactor(BlendingMode::Blend value)
{
switch (value)
{
case BlendingMode::Blend::Zero:
return WGPUBlendFactor_Zero;
case BlendingMode::Blend::One:
return WGPUBlendFactor_One;
case BlendingMode::Blend::SrcColor:
return WGPUBlendFactor_Src;
case BlendingMode::Blend::InvSrcColor:
return WGPUBlendFactor_OneMinusSrc;
case BlendingMode::Blend::SrcAlpha:
return WGPUBlendFactor_SrcAlpha;
case BlendingMode::Blend::InvSrcAlpha:
return WGPUBlendFactor_OneMinusSrcAlpha;
case BlendingMode::Blend::DestAlpha:
return WGPUBlendFactor_DstAlpha;
case BlendingMode::Blend::InvDestAlpha:
return WGPUBlendFactor_OneMinusDstAlpha;
case BlendingMode::Blend::DestColor:
return WGPUBlendFactor_Dst;
case BlendingMode::Blend::InvDestColor:
return WGPUBlendFactor_OneMinusDst;
case BlendingMode::Blend::SrcAlphaSat:
return WGPUBlendFactor_SrcAlphaSaturated;
case BlendingMode::Blend::BlendFactor:
return WGPUBlendFactor_Constant;
case BlendingMode::Blend::BlendInvFactor:
return WGPUBlendFactor_OneMinusConstant;
case BlendingMode::Blend::Src1Color:
return WGPUBlendFactor_Src1;
case BlendingMode::Blend::InvSrc1Color:
return WGPUBlendFactor_OneMinusSrc1;
case BlendingMode::Blend::Src1Alpha:
return WGPUBlendFactor_Src1Alpha;
case BlendingMode::Blend::InvSrc1Alpha:
return WGPUBlendFactor_OneMinusSrc1Alpha;
default:
return WGPUBlendFactor_Undefined;
}
}
WGPUBlendComponent ToBlendComponent(BlendingMode::Operation blendOp, BlendingMode::Blend srcBlend, BlendingMode::Blend dstBlend)
{
WGPUBlendComponent result;
switch (blendOp)
{
case BlendingMode::Operation::Add:
result.operation = WGPUBlendOperation_Add;
break;
case BlendingMode::Operation::Subtract:
result.operation = WGPUBlendOperation_Subtract;
break;
case BlendingMode::Operation::RevSubtract:
result.operation = WGPUBlendOperation_ReverseSubtract;
break;
case BlendingMode::Operation::Min:
result.operation = WGPUBlendOperation_Min;
break;
case BlendingMode::Operation::Max:
result.operation = WGPUBlendOperation_Max;
break;
default:
result.operation = WGPUBlendOperation_Undefined;
break;
}
result.srcFactor = ToBlendFactor(srcBlend);
result.dstFactor = ToBlendFactor(dstBlend);
return result;
}
void GPUPipelineStateWebGPU::OnReleaseGPU()
{
VS = nullptr;
PS = nullptr;
for (auto& e : _pipelines)
wgpuRenderPipelineRelease(e.Value);
_pipelines.Clear();
}
uint32 GetHash(const GPUPipelineStateWebGPU::Key& key)
{
static_assert(sizeof(GPUPipelineStateWebGPU::Key) == sizeof(uint64) * 4, "Invalid PSO key size.");
uint32 hash = GetHash(key.Packed[0]);
CombineHash(hash, GetHash(key.Packed[1]));
return hash;
}
WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const Key& key)
{
WGPURenderPipeline pipeline;
if (_pipelines.TryGet(key, pipeline))
return pipeline;
PROFILE_CPU();
PROFILE_MEM(GraphicsCommands);
#if GPU_ENABLE_RESOURCE_NAMING
ZoneText(_debugName.Get(), _debugName.Count() - 1);
#endif
// Build final pipeline description
_depthStencilDesc.format = (WGPUTextureFormat)key.DepthStencilFormat;
PipelineDesc.multisample.count = key.MultiSampleCount;
_fragmentDesc.targetCount = key.RenderTargetCount;
for (int32 i = 0; i < _fragmentDesc.targetCount; i++)
_colorTargets[i].format = (WGPUTextureFormat)key.RenderTargetFormats[i];
WGPUVertexBufferLayout buffers[GPU_MAX_VB_BINDED];
for (int32 i = 0; i < PipelineDesc.vertex.bufferCount; i++)
buffers[i] = *key.VertexBuffers[i];
PipelineDesc.vertex.buffers = buffers;
// Create object
pipeline = wgpuDeviceCreateRenderPipeline(_device->Device, &PipelineDesc);
if (!pipeline)
{
#if GPU_ENABLE_RESOURCE_NAMING
LOG(Error, "wgpuDeviceCreateRenderPipeline failed for {}", String(_debugName.Get(), _debugName.Count() - 1));
#endif
return nullptr;
}
// Cache it
_pipelines.Add(key, pipeline);
return pipeline;
}
bool GPUPipelineStateWebGPU::IsValid() const
{
return _memoryUsage != 0;
}
bool GPUPipelineStateWebGPU::Init(const Description& desc)
{
if (IsValid())
OnReleaseGPU();
// Cache shaders
VS = (GPUShaderProgramVSWebGPU*)desc.VS;
PS = (GPUShaderProgramPSWebGPU*)desc.PS;
// Initialize description (without dynamic state from context such as render targets, vertex buffers, etc.)
PipelineDesc = WGPU_RENDER_PIPELINE_DESCRIPTOR_INIT;
#if GPU_ENABLE_RESOURCE_NAMING
GetDebugName(_debugName);
PipelineDesc.label = { _debugName.Get(), (size_t)_debugName.Count() - 1 };
#endif
PipelineDesc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
PipelineDesc.primitive.frontFace = WGPUFrontFace_CW;
switch (desc.CullMode)
{
case CullMode::Normal:
PipelineDesc.primitive.cullMode = WGPUCullMode_Back;
break;
case CullMode::Inverted:
PipelineDesc.primitive.cullMode = WGPUCullMode_Front;
break;
case CullMode::TwoSided:
PipelineDesc.primitive.cullMode = WGPUCullMode_None;
break;
}
PipelineDesc.primitive.unclippedDepth = !desc.DepthClipEnable && _device->Limits.HasDepthClip;
if (desc.DepthEnable || desc.StencilEnable)
{
PipelineDesc.depthStencil = &_depthStencilDesc;
_depthStencilDesc = WGPU_DEPTH_STENCIL_STATE_INIT;
_depthStencilDesc.depthWriteEnabled = desc.DepthEnable && desc.DepthWriteEnable ? WGPUOptionalBool_True : WGPUOptionalBool_False;
_depthStencilDesc.depthCompare = ToCompareFunction(desc.DepthFunc);
if (desc.StencilEnable)
{
_depthStencilDesc.stencilFront.compare = ToCompareFunction(desc.StencilFunc);
_depthStencilDesc.stencilFront.failOp = ToStencilOperation(desc.StencilFailOp);
_depthStencilDesc.stencilFront.depthFailOp = ToStencilOperation(desc.StencilDepthFailOp);
_depthStencilDesc.stencilFront.passOp = ToStencilOperation(desc.StencilPassOp);
_depthStencilDesc.stencilBack = _depthStencilDesc.stencilFront;
_depthStencilDesc.stencilReadMask = desc.StencilReadMask;
_depthStencilDesc.stencilWriteMask = desc.StencilWriteMask;
}
}
PipelineDesc.multisample.alphaToCoverageEnabled = desc.BlendMode.AlphaToCoverageEnable;
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);
WGPUColorWriteMask writeMask = WGPUColorWriteMask_All;
if (desc.BlendMode.RenderTargetWriteMask != BlendingMode::ColorWrite::All)
{
writeMask = 0;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Red))
writeMask |= WGPUColorWriteMask_Red;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Green))
writeMask |= WGPUColorWriteMask_Green;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Blue))
writeMask |= WGPUColorWriteMask_Blue;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Alpha))
writeMask |= WGPUColorWriteMask_Alpha;
}
for (auto& e : _colorTargets)
{
e = WGPU_COLOR_TARGET_STATE_INIT;
e.blend = &_blendState;
e.writeMask = writeMask;
}
// TODO: set resources binding into PipelineDesc.layout
// TODO: set vertex shader into PipelineDesc.vertex
// TODO: set pixel shader into PipelineDesc.fragment
_memoryUsage = 1;
return GPUPipelineState::Init(desc);
}
#endif