277 lines
9.9 KiB
C++
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
|