Cogs.Core
PipelineStatesWebGPU.cpp
1#include "PipelineStatesWebGPU.h"
2
3#include "GraphicsDeviceWebGPU.h"
4
5#include "Foundation/Logging/Logger.h"
6
7namespace{
8 Cogs::Logging::Log logger = Cogs::Logging::getLogger("PipelineStatesWebGPU");
9
10 WGPUBlendOperation wgpu(Cogs::BlendState::BlendOperation opp)
11 {
12 if(opp == Cogs::BlendState::BlendOperation::Add)
13 return WGPUBlendOperation_Add;
14 if(opp == Cogs::BlendState::BlendOperation::Subtract)
15 return WGPUBlendOperation_Subtract;
16 if(opp == Cogs::BlendState::BlendOperation::ReverseSubtract)
17 return WGPUBlendOperation_ReverseSubtract;
18 if(opp == Cogs::BlendState::BlendOperation::Min)
19 return WGPUBlendOperation_Min;
20 if(opp == Cogs::BlendState::BlendOperation::Max)
21 return WGPUBlendOperation_Max;
22 assert(false);
23 return WGPUBlendOperation_Add;
24 }
25
26 WGPUBlendFactor wgpu(Cogs::BlendState::Blend blend)
27 {
28 if(blend == Cogs::BlendState::Blend::Zero)
29 return WGPUBlendFactor_Zero;
30 if(blend == Cogs::BlendState::Blend::One)
31 return WGPUBlendFactor_One;
32 if(blend == Cogs::BlendState::Blend::SourceColor)
33 return WGPUBlendFactor_Src;
34 if(blend == Cogs::BlendState::Blend::InverseSourceColor)
35 return WGPUBlendFactor_OneMinusSrc;
36 if(blend == Cogs::BlendState::Blend::SourceAlpha)
37 return WGPUBlendFactor_SrcAlpha;
38 if(blend == Cogs::BlendState::Blend::InverseSourceAlpha)
39 return WGPUBlendFactor_OneMinusSrcAlpha;
40 if(blend == Cogs::BlendState::Blend::DestinationAlpha)
41 return WGPUBlendFactor_DstAlpha;
42 if(blend == Cogs::BlendState::Blend::InverseDestinationAlpha)
43 return WGPUBlendFactor_OneMinusDstAlpha;
44 if(blend == Cogs::BlendState::Blend::DestinationColor)
45 return WGPUBlendFactor_Dst;
46 if(blend == Cogs::BlendState::Blend::InverseDestinationColor)
47 return WGPUBlendFactor_OneMinusDst;
48 if(blend == Cogs::BlendState::Blend::SourceAlphaSaturate)
49 return WGPUBlendFactor_SrcAlphaSaturated;
50 if(blend == Cogs::BlendState::Blend::BlendFactor)
51 return WGPUBlendFactor_Constant;
52 if(blend == Cogs::BlendState::Blend::InverseBlendFactor)
53 return WGPUBlendFactor_OneMinusConstant;
54 assert(false);
55 return WGPUBlendFactor_Zero;
56 }
57}
58
59namespace Cogs{
60
61 void PipelineStatesWebGPU::initialize(GraphicsDeviceWebGPU *device)
62 {
63 graphics_device = device;
64 }
65
66 size_t PipelineStatesWebGPU::renderPipelineHash(EffectHandle effectHandle,
67 InputLayoutHandle inputLayoutHandle,
68 PrimitiveType primitiveType,
69 RasterizerStateHandle rasterizeStateHandle,
70 DepthStencilStateHandle depthStencilStateHandle,
71 BlendStateHandle blendStateHandle,
72 RenderTargetHandle renderTargetHandle,
73 DepthStencilHandle depthStencilHandle)
74 {
75 EffectsWebGPU &effects = graphics_device->effects;
76 BuffersWebGPU &buffers = graphics_device->buffers;
77 RenderTargetsWebGPU &render_targets = graphics_device->renderTargets;
78
79 size_t pso_hash = Cogs::hash();
80 if (HandleIsValid(effectHandle)) {
81 EffectWebGPU& effect = effects.effects[effectHandle];
82 pso_hash = effect.hash(pso_hash);
83 }
84 if (HandleIsValid(inputLayoutHandle)) {
85 InputLayoutWebGPU& layout = buffers.inputLayouts[inputLayoutHandle];
86 pso_hash = layout.hash(pso_hash);
87 }
88 pso_hash = Cogs::hash(primitiveType, pso_hash);
89 pso_hash = Cogs::hash(rasterizeStateHandle.handle, pso_hash);
90 pso_hash = Cogs::hash(depthStencilStateHandle.handle, pso_hash);
91 pso_hash = Cogs::hash(blendStateHandle.handle, pso_hash);
92 if (HandleIsValid(renderTargetHandle)) {
93 RenderTargetWebGPU& render_target = render_targets.render_targets[renderTargetHandle];
94 pso_hash = render_target.hash(pso_hash);
95 }
96 if (HandleIsValid(depthStencilHandle)) {
97 DepthStencilTargetWebGPU& depth_target = render_targets.depth_stencil_targets[depthStencilHandle];
98 pso_hash = depth_target.hash(pso_hash);
99 }
100 return pso_hash;
101 }
102 RenderPipelineHandle PipelineStatesWebGPU::loadRenderPipeline(EffectHandle effectHandle,
103 InputLayoutHandle inputLayoutHandle,
104 PrimitiveType primitiveType,
105 RasterizerStateHandle rasterizeStateHandle,
106 DepthStencilStateHandle depthStencilStateHandle,
107 BlendStateHandle blendStateHandle,
108 RenderTargetHandle renderTargetHandle,
109 DepthStencilHandle depthStencilHandle)
110 {
111 WGPUDevice device = graphics_device->device;
112 BuffersWebGPU &buffers = graphics_device->buffers;
113 RenderTargetsWebGPU &renderTargets = graphics_device->renderTargets;
114
115 size_t pso_hash = renderPipelineHash(effectHandle,
116 inputLayoutHandle,
117 primitiveType,
118 rasterizeStateHandle,
119 depthStencilStateHandle,
120 blendStateHandle,
121 renderTargetHandle,
122 depthStencilHandle);
123 auto iter = renderPipelineHashMap.find(pso_hash);
124 if(iter != renderPipelineHashMap.end()){
125 return iter->second;
126 }
127
128 EffectWebGPU &effect = graphics_device->effects.effects[effectHandle];
129 const RasterizerState &rs =
130 rasterizeStateHandle ?
131 *reinterpret_cast<RasterizerState*>(rasterizeStateHandle.handle):
133 // assert(rs.wireFrame == false); // TODO
134 // assert(rs.multiSample == true); // TODO
135 // assert(rs.scissor == true); // TODO
136 //assert(rs.noDepthClip == false); // TODO O requres optional feature "depth-clip-control"
137 const DepthStencilState &ds =
138 depthStencilStateHandle ?
139 *reinterpret_cast<DepthStencilState*>(depthStencilStateHandle.handle) :
141 const BlendState &bsc =
142 blendStateHandle ?
143 reinterpret_cast<BlendState*>(blendStateHandle.handle)[0] :
145 const BlendState &bsa =
146 blendStateHandle ?
147 reinterpret_cast<BlendState*>(blendStateHandle.handle)[1] :
149
150 LOG_INFO(logger, "PipelineStateLoad %s", effect.name.c_str());
151
152 WGPURenderPipelineDescriptor desc = {};
153 desc.label = {effect.name.c_str(), WGPU_STRLEN};
154 desc.layout = nullptr;
155 WGPUBindGroupLayout bind_group_layout;
156 {
157 WGPUBindGroupLayoutEntry layoutEntryList[EffectWebGPU::maxConstantBuffers];
158 size_t numConstantBufferBindings = 0;
159 const WebGPUConstantBufferBinding* bindings;
160 bindings = graphics_device->effects.getConstantBufferBindings(effectHandle, numConstantBufferBindings);
161 for (size_t i = 0; i < numConstantBufferBindings; i++) {
162 assert(bindings[i].group == 0);
163 layoutEntryList[i] = bindings[i].bg_ent;
164 }
165 WGPUBindGroupLayoutDescriptor bind_group_layout_desc = {};
166 bind_group_layout_desc.label = {effect.name.c_str(), WGPU_STRLEN};
167 bind_group_layout_desc.entryCount = numConstantBufferBindings;
168 bind_group_layout_desc.entries = layoutEntryList;
169 bind_group_layout = wgpuDeviceCreateBindGroupLayout(device, &bind_group_layout_desc);
170
171 WGPUPipelineLayoutDescriptor pipeline_layout_desc = {};
172 pipeline_layout_desc.bindGroupLayoutCount = 1;
173 pipeline_layout_desc.bindGroupLayouts = &bind_group_layout;
174 WGPUPipelineLayout pipeline_layout = wgpuDeviceCreatePipelineLayout(device, &pipeline_layout_desc);
175 desc.label = {effect.name.c_str(), WGPU_STRLEN};
176 desc.layout = pipeline_layout;
177 }
178
179 WGPUPrimitiveState &primitive_state = desc.primitive;
180 {
181 assert(primitiveType < PrimitiveType::TriangleListAdjacency); // WebGPU does not support advanced primitives
182 if(primitiveType == PrimitiveType::PointList){
183 primitive_state.topology = WGPUPrimitiveTopology_PointList;
184 primitive_state.stripIndexFormat = WGPUIndexFormat_Undefined;
185 }
186 else if(primitiveType == PrimitiveType::LineList){
187 primitive_state.topology = WGPUPrimitiveTopology_LineList;
188 primitive_state.stripIndexFormat = WGPUIndexFormat_Undefined;
189 }
190 else if(primitiveType == PrimitiveType::LineStrip){
191 primitive_state.topology = WGPUPrimitiveTopology_LineStrip;
192 primitive_state.stripIndexFormat = WGPUIndexFormat_Uint32; // TODO
193 }
194 else if(primitiveType == PrimitiveType::TriangleList){
195 primitive_state.topology = WGPUPrimitiveTopology_TriangleList;
196 primitive_state.stripIndexFormat = WGPUIndexFormat_Undefined;
197 }
198 else if(primitiveType == PrimitiveType::TriangleStrip){
199 primitive_state.topology = WGPUPrimitiveTopology_TriangleStrip;
200 primitive_state.stripIndexFormat = WGPUIndexFormat_Uint32; // TODO
201 }
202 else{
203 assert(false);
204 }
205
206 if(rs.frontCounterClockwise)
207 primitive_state.frontFace = WGPUFrontFace_CCW;
208 else
209 primitive_state.frontFace = WGPUFrontFace_CW;
210 if(rs.cullMode == RasterizerState::Front)
211 primitive_state.cullMode = WGPUCullMode_Front;
212 else if(rs.cullMode == RasterizerState::Back)
213 primitive_state.cullMode = WGPUCullMode_Back;
214 else
215 primitive_state.cullMode = WGPUCullMode_None;
216 }
217
218 WGPUVertexState &vertex_state = desc.vertex;
219 {
220 vertex_state.module = effect.vs_module;
221 vertex_state.entryPoint = {effect.vs_entry.c_str(), WGPU_STRLEN};
222 vertex_state.constantCount = 0;
223 vertex_state.constants = nullptr;
224 if (HandleIsValid(inputLayoutHandle)) {
225 InputLayoutWebGPU& layout = buffers.inputLayouts[inputLayoutHandle];
226 vertex_state.bufferCount = (uint32_t)layout.vertex_buffer_layout.size();
227 vertex_state.buffers = layout.vertex_buffer_layout.data();
228 }
229 }
230
231 bool use_swap_chain = !HandleIsValid(renderTargetHandle) && !HandleIsValid(depthStencilHandle);
232
233 uint32_t samples = 0;
234 WGPUDepthStencilState depthStencil = {};
235 if(HandleIsValid(depthStencilHandle) || use_swap_chain){
236 if(use_swap_chain){
237 SwapChainWebGPU &defaultSwapChain = graphics_device->defaultSwapChain;
238 depthStencil.format = defaultSwapChain.depth_format;
239 samples = defaultSwapChain.samples;
240 }
241 else{
242 DepthStencilTargetWebGPU &dst = renderTargets.depth_stencil_targets[depthStencilHandle];
243 depthStencil.format = dst.format;
244 samples = dst.samples;
245 }
246
247 if(ds.depthEnabled){
248 depthStencil.depthWriteEnabled = static_cast<WGPUOptionalBool>(ds.writeEnabled);
249
250 if(ds.depthFunction == DepthStencilState::Never)
251 depthStencil.depthCompare = WGPUCompareFunction_Never;
252 else if(ds.depthFunction == DepthStencilState::Less)
253 depthStencil.depthCompare = WGPUCompareFunction_Less;
254 else if(ds.depthFunction == DepthStencilState::LessOrEqual)
255 depthStencil.depthCompare = WGPUCompareFunction_LessEqual;
256 else if(ds.depthFunction == DepthStencilState::Equal)
257 depthStencil.depthCompare = WGPUCompareFunction_Equal;
258 else if(ds.depthFunction == DepthStencilState::GreaterOrEqual)
259 depthStencil.depthCompare = WGPUCompareFunction_GreaterEqual;
260 else if(ds.depthFunction == DepthStencilState::Greater)
261 depthStencil.depthCompare = WGPUCompareFunction_Greater;
262 else if(ds.depthFunction == DepthStencilState::NotEqual)
263 depthStencil.depthCompare = WGPUCompareFunction_NotEqual;
264 else if(ds.depthFunction == DepthStencilState::Always)
265 depthStencil.depthCompare = WGPUCompareFunction_Always;
266 else
267 assert(false);
268 }
269 else{
270 depthStencil.depthWriteEnabled = WGPUOptionalBool(false);
271 depthStencil.depthCompare = WGPUCompareFunction_Always;
272 }
273
274 depthStencil.stencilFront.compare = WGPUCompareFunction_Always;
275 depthStencil.stencilFront.failOp = WGPUStencilOperation_Keep;
276 depthStencil.stencilFront.depthFailOp = WGPUStencilOperation_Keep;
277 depthStencil.stencilFront.passOp = WGPUStencilOperation_Keep;
278
279 depthStencil.stencilBack.compare = WGPUCompareFunction_Always;
280 depthStencil.stencilBack.failOp = WGPUStencilOperation_Keep;
281 depthStencil.stencilBack.depthFailOp = WGPUStencilOperation_Keep;
282 depthStencil.stencilBack.passOp = WGPUStencilOperation_Keep;
283
284 depthStencil.stencilReadMask = 0;
285 depthStencil.stencilWriteMask = 0;
286
287 depthStencil.depthBias = (int32_t)rs.depthBias;
288 depthStencil.depthBiasSlopeScale = rs.slopeScaledDepthBias;
289 depthStencil.depthBiasClamp = rs.depthBiasClamp;
290
291 desc.depthStencil = &depthStencil;
292 }
293
294 WGPUColorTargetState color_target[8] = {};
295 uint32_t color_target_count = 0;
296 if(HandleIsValid(renderTargetHandle)){
297 RenderTargetWebGPU &rt = renderTargets.render_targets[renderTargetHandle];
298 samples = glm::max(rt.samples, samples);
299 color_target_count = rt.count;
300 for(uint32_t i=0; i<rt.count; i++){
301 color_target[i].format = rt.format[i];
302 color_target[i].writeMask = WGPUColorWriteMask_All;
303 }
304 }
305 else if(use_swap_chain){
306 SwapChainWebGPU &defaultSwapChain = graphics_device->defaultSwapChain;
307 color_target_count = 1;
308 color_target[0].format = defaultSwapChain.color_format;
309 color_target[0].writeMask = WGPUColorWriteMask_All;
310 }
311
312 WGPUBlendState blend_state = {};
313 if(bsc.enabled || bsa.enabled){
314// assert(bsc.enabled && bsa.enabled);
315 blend_state.color.operation = wgpu(bsc.operation);
316 blend_state.color.srcFactor = wgpu(bsc.sourceBlend);
317 blend_state.color.dstFactor = wgpu(bsc.destinationBlend);
318 blend_state.alpha.operation = wgpu(bsa.operation);
319 blend_state.alpha.srcFactor = wgpu(bsa.sourceBlend);
320 blend_state.alpha.dstFactor = wgpu(bsa.destinationBlend);
321 for(uint32_t i=0; i<color_target_count; i++){
322 color_target[i].blend = &blend_state; // TODO different blend states?
323 }
324 }
325
326 WGPUFragmentState fragment = {};
327 if(effect.fs_module){
328 fragment.module = effect.fs_module;
329 fragment.entryPoint = {effect.fs_entry.c_str(), WGPU_STRLEN};
330 fragment.constantCount = 0;
331 fragment.constants = nullptr;
332 if(color_target_count > 0){
333 fragment.targetCount = color_target_count;
334 fragment.targets = color_target;
335 }
336 desc.fragment = &fragment;
337 }
338
339 WGPUMultisampleState &multisample = desc.multisample;
340 {
341 multisample.count = samples;
342 multisample.mask = ~0x00000000u;
343 multisample.alphaToCoverageEnabled = false;
344 }
345
346 WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(device, &desc);
347 // TODO async pipeline creation
348
349 RenderPipelineWebGPU pipelineState = {};
350 pipelineState.hash = pso_hash;
351 pipelineState.effect = effectHandle;
352 pipelineState.inputLayoutHandle = inputLayoutHandle;
353 pipelineState.primitiveType = primitiveType;
354 pipelineState.rasterizeStateHandle = rasterizeStateHandle;
355 pipelineState.depthStencilStateHandle = depthStencilStateHandle;
356 pipelineState.blendStateHandle = blendStateHandle;
357 pipelineState.renderTargetHandle = renderTargetHandle;
358 pipelineState.depthStencilHandle = depthStencilHandle;
359
360 pipelineState.pipeline = pipeline;
361 pipelineState.layout = bind_group_layout;
362
363 auto handle = this->renderPipeline.addResource(std::move(pipelineState));
364 renderPipelineHashMap.insert({pso_hash, handle});
365 return handle;
366 }
367
368 ComputePipelineHandle PipelineStatesWebGPU::loadComputePipeline(EffectHandle effectHandle)
369 {
370 WGPUDevice device = graphics_device->device;
371
372 size_t pso_hash = Cogs::hash();
373 pso_hash = Cogs::hash(effectHandle.handle, pso_hash);
374 auto iter = computePipelineHashMap.find(pso_hash);
375 if(iter != computePipelineHashMap.end()){
376 return iter->second;
377 }
378
379 EffectWebGPU &effect = graphics_device->effects.effects[effectHandle];
380
381 LOG_INFO(logger, "PipelineStateLoad %s", effect.name.c_str());
382
383 WGPUComputePipelineDescriptor desc = {};
384 desc.label = {effect.name.c_str(), WGPU_STRLEN};
385 desc.layout = nullptr; // Let WebGPU figure out layout?
386 WGPUComputeState &compute = desc.compute;
387 compute.module = effect.cs_module;
388 compute.entryPoint = {effect.cs_entry.c_str(), WGPU_STRLEN};
389 compute.constantCount = 0; // TODO
390 compute.constants = nullptr; // TODO
391 WGPUComputePipeline pipeline = wgpuDeviceCreateComputePipeline(device, &desc);
392 // TODO async pipeline creation
393
394 uint32_t groupIndex = 0;
395 WGPUBindGroupLayout layout = wgpuComputePipelineGetBindGroupLayout(pipeline, groupIndex);
396 // TODO create our own bind groups?
397
398 ComputePipelineWebGPU pipelineState = {};
399 pipelineState.hash = pso_hash;
400 pipelineState.effect = effectHandle;
401
402 pipelineState.pipeline = pipeline;
403 pipelineState.layout = layout;
404
405 auto handle = this->computePipeline.addResource(std::move(pipelineState));
406 computePipelineHashMap.insert({pso_hash, handle});
407 return handle;
408 }
409
410}
Log implementation class.
Definition: LogManager.h:140
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
PrimitiveType
Primitive types for interpreting vertex data sent to the graphics pipeline.
Definition: Common.h:112
@ PointList
List of points.
@ TriangleStrip
Triangle strip.
@ LineList
List of lines.
@ TriangleListAdjacency
List of triangles with adjacency.
@ LineStrip
Line strip.
@ TriangleList
List of triangles.
static BlendState DefaultState()
Creates a blend state object initialized with the default settings.
Definition: BlendState.h:55
Blend
Options for blend functions.
Definition: BlendState.h:14
@ Never
Never evaluates to true. When using this, all objects will fail depth testing.
@ GreaterOrEqual
Greater or equal depth.
@ NotEqual
Depth not equal evaluates to true.
@ Always
Always evaluates to true.
@ LessOrEqual
Less or equal depth.
static DepthStencilState DefaultState()
Constructs a depth stencil state object initialized with the default values.
static RasterizerState DefaultState()
Constructs a rasterizer state initialized with the default values.
@ Back
Cull back facing primitives.
@ Front
Cull front facing primitives.