1#include "EffectsWebGPU.h"
3#include "GraphicsDeviceWebGPU.h"
5#include "Foundation/Logging/Logger.h"
16 bool starts_with(std::string::const_iterator begin, std::string::const_iterator end,
const char* s, std::string::const_iterator& next) {
17 std::string::const_iterator it = begin;
19 while (it != end && *s !=
'\0') {
33 void eat_white(std::string::const_iterator& it, std::string::const_iterator end) {
34 while (it != end && std::isspace(*it)) {
40 WGPUTextureViewDimension extractViewDimention_native(std::string::const_iterator& type_it, std::string::const_iterator end) {
41 WGPUTextureViewDimension tvd = WGPUTextureViewDimension_Undefined;
42 if (starts_with(type_it, end,
"2d_array", type_it)) {
43 tvd = WGPUTextureViewDimension_2DArray;
45 else if (starts_with(type_it, end,
"2d", type_it)) {
46 tvd = WGPUTextureViewDimension_2D;
48 else if (starts_with(type_it, end,
"cube", type_it)) {
49 tvd = WGPUTextureViewDimension_Cube;
51 else if (starts_with(type_it, end,
"1d", type_it)) {
52 tvd = WGPUTextureViewDimension_1D;
54 else if (starts_with(type_it, end,
"cube_array", type_it)) {
55 tvd = WGPUTextureViewDimension_CubeArray;
57 else if (starts_with(type_it, end,
"3d", type_it)) {
58 tvd = WGPUTextureViewDimension_3D;
63 WGPUTextureFormat extractTextureFormat_native(std::string::const_iterator& type_it, std::string::const_iterator end) {
64 WGPUTextureFormat tf = WGPUTextureFormat_Undefined;
65 if (starts_with(type_it, end,
"rgba8unorm", type_it)) {
66 tf = WGPUTextureFormat_RGBA8Unorm;
68 else if (starts_with(type_it, end,
"rgba8snorm", type_it)) {
69 tf = WGPUTextureFormat_RGBA8Snorm;
71 else if (starts_with(type_it, end,
"rgba8uint", type_it)) {
72 tf = WGPUTextureFormat_RGBA8Uint;
74 else if (starts_with(type_it, end,
"rgba8sint", type_it)) {
75 tf = WGPUTextureFormat_RGBA8Sint;
77 else if (starts_with(type_it, end,
"rgba16uint", type_it)) {
78 tf = WGPUTextureFormat_RGBA16Uint;
80 else if (starts_with(type_it, end,
"rgba16sint", type_it)) {
81 tf = WGPUTextureFormat_RGBA16Sint;
83 else if (starts_with(type_it, end,
"rgba16float", type_it)) {
84 tf = WGPUTextureFormat_RGBA16Float;
86 else if (starts_with(type_it, end,
"r32uint", type_it)) {
87 tf = WGPUTextureFormat_R32Uint;
89 else if (starts_with(type_it, end,
"r32sint", type_it)) {
90 tf = WGPUTextureFormat_R32Sint;
92 else if (starts_with(type_it, end,
"r32float", type_it)) {
93 tf = WGPUTextureFormat_R32Float;
95 else if (starts_with(type_it, end,
"rg32uint", type_it)) {
96 tf = WGPUTextureFormat_RG32Uint;
98 else if (starts_with(type_it, end,
"rg32sint", type_it)) {
99 tf = WGPUTextureFormat_RG32Sint;
101 else if (starts_with(type_it, end,
"rg32float", type_it)) {
102 tf = WGPUTextureFormat_RG32Float;
104 else if (starts_with(type_it, end,
"rgba32uint", type_it)) {
105 tf = WGPUTextureFormat_RGBA32Uint;
107 else if (starts_with(type_it, end,
"rgba32sint", type_it)) {
108 tf = WGPUTextureFormat_RGBA32Sint;
110 else if (starts_with(type_it, end,
"rgba32float", type_it)) {
111 tf = WGPUTextureFormat_RGBA32Float;
113 else if (starts_with(type_it, end,
"bgra8unorm", type_it)) {
114 tf = WGPUTextureFormat_BGRA8Unorm;
119 WGPUStorageTextureAccess extractStorageTextureAccess_native(std::string::const_iterator& type_it, std::string::const_iterator end) {
120 WGPUStorageTextureAccess sta = WGPUStorageTextureAccess_Undefined;
121 if (starts_with(type_it, end,
"read", type_it)) {
122 sta = WGPUStorageTextureAccess_ReadOnly;
124 else if (starts_with(type_it, end,
"write", type_it)) {
125 sta = WGPUStorageTextureAccess_WriteOnly;
127 else if (starts_with(type_it, end,
"readwrite", type_it)) {
128 sta = WGPUStorageTextureAccess_ReadWrite;
133 std::vector<Cogs::WebGPUConstantBufferBinding> extractConstantBinding(std::string shaderSource, WGPUShaderStage usage) {
134 std::vector<Cogs::WebGPUConstantBufferBinding> result;
135 std::istringstream iss(shaderSource);
136 std::string expr = R
"(^\s*@group\(([0-9]+)\) @binding\(([0-9]+)\)\s+var(<uniform>)?\s+([^\s]+)[\s]*:\s?(.*)\s?;)";
137 std::regex regex_expression(expr);
139 for (std::string line; std::getline(iss, line); )
142 if (std::regex_search(line, match, regex_expression))
144 size_t group = std::stoi(match.str(1));
145 unsigned int loc = std::stoi(match.str(2));
146 bool isUniform = match.str(3) ==
"<uniform>";
147 std::string name = match.str(4);
148 std::string type = match.str(5);
150 std::string::const_iterator type_it = type.begin();
154 bool alreadyInserted =
false;
155 for (
auto& e : result) {
156 if (e.group == group && e.bg_ent.binding == loc) {
157 e.bg_ent.visibility |= usage;
158 alreadyInserted =
true;
162 if (alreadyInserted) {
165 WGPUBindGroupLayoutEntry bg_ent = {};
166 bg_ent.binding =
static_cast<uint32_t
>(loc);
167 bg_ent.visibility = (WGPUShaderStage)usage;
170 bg_ent.buffer.type = WGPUBufferBindingType_Uniform;
173 else if (starts_with(type_it, type.end(),
"texture_storage_", type_it)) {
174 bg_ent.storageTexture.viewDimension = extractViewDimention_native(type_it, type.end());
175 eat_white(type_it, type.end());
176 if (!starts_with(type_it, type.end(),
"<", type_it)) {
177 LOG_DEBUG(logger,
"Texture type %s not yet supported", type.c_str());
179 bg_ent.storageTexture.format = extractTextureFormat_native(type_it, type.end());
180 eat_white(type_it, type.end());
181 if (!starts_with(type_it, type.end(),
",", type_it)) {
182 LOG_DEBUG(logger,
"Texture type %s not yet supported", type.c_str());
184 eat_white(type_it, type.end());
185 bg_ent.storageTexture.access = extractStorageTextureAccess_native(type_it, type.end());
186 eat_white(type_it, type.end());
187 if (!starts_with(type_it, type.end(),
">", type_it)) {
188 LOG_DEBUG(logger,
"Texture type %s not yet supported", type.c_str());
191 else if (starts_with(type_it, type.end(),
"texture_", type_it)) {
192 bool isDepth =
false;
195 bg_ent.texture.sampleType = WGPUTextureSampleType_Float;
196 bg_ent.texture.viewDimension = WGPUTextureViewDimension_2D;
197 bg_ent.texture.multisampled = 0;
198 if (starts_with(type_it, type.end(),
"depth_", type_it)) {
201 if (starts_with(type_it, type.end(),
"multisampled_", type_it)) {
202 bg_ent.texture.multisampled = 1;
205 bg_ent.texture.viewDimension = extractViewDimention_native(type_it, type.end());
206 eat_white(type_it, type.end());
208 bg_ent.texture.sampleType = WGPUTextureSampleType_Depth;
210 else if (starts_with(type_it, type.end(),
"<f32>", type_it)) {
212 bg_ent.texture.sampleType = WGPUTextureSampleType_UnfilterableFloat;
215 bg_ent.texture.sampleType = WGPUTextureSampleType_Float;
218 else if (starts_with(type_it, type.end(),
"<i32>", type_it)) {
219 bg_ent.texture.sampleType = WGPUTextureSampleType_Sint;
221 else if (starts_with(type_it, type.end(),
"<u32>", type_it)) {
222 bg_ent.texture.sampleType = WGPUTextureSampleType_Uint;
225 LOG_DEBUG(logger,
"Texture type %s not yet supported", type.c_str());
228 else if (type ==
"sampler") {
230 bg_ent.sampler.type = WGPUSamplerBindingType_Filtering;
232 else if (type ==
"sampler_comparison") {
234 bg_ent.sampler.type = WGPUSamplerBindingType_Comparison;
237 LOG_DEBUG(logger,
"Unknown uniform type%s", type.c_str());
241 result.push_back(binding);
250 Cogs::BindingStorageTextureAccess extractStorageTextureAccess(std::string::const_iterator& type_it, std::string::const_iterator end) {
251 Cogs::BindingStorageTextureAccess sta = Cogs::BindingStorageTextureAccess::WriteOnly;
252 if (starts_with(type_it, end,
"read_write", type_it) || starts_with(type_it, end,
"readwrite", type_it)) {
253 sta = Cogs::BindingStorageTextureAccess::ReadWrite;
255 else if (starts_with(type_it, end,
"read", type_it)) {
256 sta = Cogs::BindingStorageTextureAccess::ReadOnly;
258 else if (starts_with(type_it, end,
"write", type_it)) {
259 sta = Cogs::BindingStorageTextureAccess::WriteOnly;
266 Cogs::Format extractTextureFormat(std::string::const_iterator& type_it, std::string::const_iterator end) {
267 Cogs::Format tf = Cogs::Format::Unknown;
268 if (starts_with(type_it, end,
"rgba8unorm", type_it)) {
269 tf = Cogs::Format::R8G8B8A8_UNORM;
271 else if (starts_with(type_it, end,
"rgba8snorm", type_it)) {
272 tf = Cogs::Format::R8G8B8A8_SNORM;
274 else if (starts_with(type_it, end,
"rgba8uint", type_it)) {
275 tf = Cogs::Format::R8G8B8A8_UINT;
277 else if (starts_with(type_it, end,
"rgba8sint", type_it)) {
278 tf = Cogs::Format::R8G8B8A8_SINT;
280 else if (starts_with(type_it, end,
"rgba16uint", type_it)) {
281 tf = Cogs::Format::R16G16B16A16_UINT;
283 else if (starts_with(type_it, end,
"rgba16sint", type_it)) {
284 tf = Cogs::Format::R16G16B16A16_SINT;
286 else if (starts_with(type_it, end,
"rgba16float", type_it)) {
287 tf = Cogs::Format::R16G16B16A16_FLOAT;
289 else if (starts_with(type_it, end,
"r32uint", type_it)) {
290 tf = Cogs::Format::R32_UINT;
292 else if (starts_with(type_it, end,
"r32sint", type_it)) {
293 tf = Cogs::Format::R32_SINT;
295 else if (starts_with(type_it, end,
"r32float", type_it)) {
296 tf = Cogs::Format::R32_FLOAT;
298 else if (starts_with(type_it, end,
"rg32uint", type_it)) {
299 tf = Cogs::Format::R32G32_UINT;
301 else if (starts_with(type_it, end,
"rg32sint", type_it)) {
302 tf = Cogs::Format::R32G32_SINT;
304 else if (starts_with(type_it, end,
"rg32float", type_it)) {
305 tf = Cogs::Format::R32G32_FLOAT;
307 else if (starts_with(type_it, end,
"rgba32uint", type_it)) {
308 tf = Cogs::Format::R32G32B32A32_UINT;
310 else if (starts_with(type_it, end,
"rgba32sint", type_it)) {
311 tf = Cogs::Format::R32G32B32A32_SINT;
313 else if (starts_with(type_it, end,
"rgba32float", type_it)) {
314 tf = Cogs::Format::R32G32B32A32_FLOAT;
316 else if (starts_with(type_it, end,
"bgra8unorm", type_it)) {
317 tf = Cogs::Format::B8G8R8A8;
322 Cogs::ResourceDimensions extractViewDimention(std::string::const_iterator& type_it, std::string::const_iterator end) {
323 Cogs::ResourceDimensions tvd = Cogs::ResourceDimensions::Unknown;
324 if (starts_with(type_it, end,
"2d_array", type_it)) {
325 tvd = Cogs::ResourceDimensions::Texture2DArray;
327 else if (starts_with(type_it, end,
"2d", type_it)) {
328 tvd = Cogs::ResourceDimensions::Texture2D;
330 else if (starts_with(type_it, end,
"cube", type_it)) {
331 tvd = Cogs::ResourceDimensions::TextureCube;
333 else if (starts_with(type_it, end,
"1d", type_it)) {
334 tvd = Cogs::ResourceDimensions::Texture1D;
336 else if (starts_with(type_it, end,
"cube_array", type_it)) {
337 tvd = Cogs::ResourceDimensions::Unknown;
338 LOG_ERROR_ONCE(logger,
"Texture cube array not yet supported");
340 else if (starts_with(type_it, end,
"3d", type_it)) {
341 tvd = Cogs::ResourceDimensions::Texture3D;
347 std::istringstream iss(shaderSource);
348 std::string expr = R
"(^\s*@group\(([0-9]+)\) @binding\(([0-9]+)\)\s+var(<uniform>)?\s+([^\s]+)[\s]*:\s?(.*)\s?;)";
349 std::regex regex_expression(expr);
351 for (std::string line; std::getline(iss, line); )
354 if (std::regex_search(line, match, regex_expression))
356 uint32_t group = std::stoi(match.str(1));
357 unsigned int loc = std::stoi(match.str(2));
358 bool isUniform = match.str(3) ==
"<uniform>";
359 std::string name = match.str(4);
360 std::string type = match.str(5);
363 std::string::const_iterator type_it = type.begin();
368 bool alreadyInserted =
false;
369 if (group >= Cogs::MaxBindGroups) {
370 LOG_ERROR(logger,
"Group number %u is out of range", group);
373 if (layoutSet.numGroups <= group) layoutSet.numGroups =
static_cast<uint16_t
>(group + 1);
375 for (
size_t i = 0; i < bindGroup.numEntries; ++i) {
376 if (bindGroup.entries[i].binding != loc)
continue;
377 if (bindGroup.entries[i].nameHash != nameHash) {
378 LOG_ERROR(logger,
"Conflicting declarations for @group(%u) @binding(%u): '%s' does not match the resource already bound there.",
379 static_cast<unsigned int>(group), loc, name.c_str());
382 bindGroup.entries[i].visibility |= usage;
383 alreadyInserted =
true;
387 if (alreadyInserted)
continue;
388 if (loc >= Cogs::MaxBindGroupEntries) {
389 LOG_ERROR(logger,
"Binding number %d is out of range", loc);
394 entry.binding =
static_cast<uint32_t
>(loc);
395 entry.visibility = usage;
396 entry.nameHash = nameHash;
399 entry.resourceType = Cogs::BindingResourceType::UniformBuffer;
401 else if (starts_with(type_it, type.end(),
"texture_storage_", type_it)) {
402 entry.resourceType = Cogs::BindingResourceType::StorageTexture;
403 entry.textureDimension = extractViewDimention(type_it, type.end());
404 eat_white(type_it, type.end());
405 if (!starts_with(type_it, type.end(),
"<", type_it)) {
406 LOG_DEBUG(logger,
"Texture type %s not yet supported", type.c_str());
408 entry.format = extractTextureFormat(type_it, type.end());
409 eat_white(type_it, type.end());
410 if (!starts_with(type_it, type.end(),
",", type_it)) {
411 LOG_DEBUG(logger,
"Texture type %s not yet supported", type.c_str());
413 eat_white(type_it, type.end());
414 entry.storageTextureAccess = extractStorageTextureAccess(type_it, type.end());
415 eat_white(type_it, type.end());
416 if (!starts_with(type_it, type.end(),
">", type_it)) {
417 LOG_DEBUG(logger,
"Texture type %s not yet supported", type.c_str());
420 else if (starts_with(type_it, type.end(),
"texture_", type_it)) {
421 entry.resourceType = Cogs::BindingResourceType::Texture;
422 entry.textureDimension = Cogs::ResourceDimensions::Texture2D;
423 entry.textureSampleType = Cogs::BindingTextureSampleType::Float;
424 entry.isDepthTexture =
false;
425 entry.multisampled =
false;
426 if (starts_with(type_it, type.end(),
"depth_", type_it)) {
427 entry.isDepthTexture =
true;
429 if (starts_with(type_it, type.end(),
"multisampled_", type_it)) {
430 entry.multisampled =
true;
432 entry.textureDimension = extractViewDimention(type_it, type.end());
433 eat_white(type_it, type.end());
434 if (entry.isDepthTexture) {
435 entry.textureSampleType = Cogs::BindingTextureSampleType::Depth;
437 else if (starts_with(type_it, type.end(),
"<f32>", type_it)) {
438 if (entry.multisampled) {
439 entry.textureSampleType = Cogs::BindingTextureSampleType::UnfilterableFloat;
442 entry.textureSampleType = Cogs::BindingTextureSampleType::Float;
445 else if (starts_with(type_it, type.end(),
"<i32>", type_it)) {
446 entry.textureSampleType = Cogs::BindingTextureSampleType::Sint;
448 else if (starts_with(type_it, type.end(),
"<u32>", type_it)) {
449 entry.textureSampleType = Cogs::BindingTextureSampleType::Uint;
452 LOG_DEBUG(logger,
"Texture type %s not yet supported", type.c_str());
455 else if (type ==
"sampler") {
456 entry.resourceType = Cogs::BindingResourceType::Sampler;
457 entry.samplerBindingType = Cogs::BindingSamplerBindingType::Filtering;
459 else if (type ==
"sampler_comparison") {
460 entry.resourceType = Cogs::BindingResourceType::Sampler;
461 entry.samplerBindingType = Cogs::BindingSamplerBindingType::Comparison;
464 LOG_DEBUG(logger,
"Unknown uniform type%s", type.c_str());
474 WGPUShaderStage bindingVisibilityToShaderStage(
const uint32_t visibility)
476 uint32_t effectiveVisibility = visibility;
477 if (effectiveVisibility == Cogs::BindingVisibilityNone) {
478 effectiveVisibility = Cogs::BindingVisibilityVertex | Cogs::BindingVisibilityFragment;
481 uint32_t shaderStage = 0;
482 if ((effectiveVisibility & Cogs::BindingVisibilityVertex) != 0) {
483 shaderStage |= WGPUShaderStage_Vertex;
485 if ((effectiveVisibility & Cogs::BindingVisibilityFragment) != 0) {
486 shaderStage |= WGPUShaderStage_Fragment;
488 if ((effectiveVisibility & Cogs::BindingVisibilityCompute) != 0) {
489 shaderStage |= WGPUShaderStage_Compute;
492 return static_cast<WGPUShaderStage
>(shaderStage);
496 WGPUTextureViewDimension ResourceDimensionsToViewDimension(
const Cogs::ResourceDimensions dimension)
499 case Cogs::ResourceDimensions::Texture1D:
500 return WGPUTextureViewDimension_1D;
501 case Cogs::ResourceDimensions::Texture2D:
502 return WGPUTextureViewDimension_2D;
503 case Cogs::ResourceDimensions::Texture2DArray:
504 return WGPUTextureViewDimension_2DArray;
505 case Cogs::ResourceDimensions::Texture3D:
506 return WGPUTextureViewDimension_3D;
507 case Cogs::ResourceDimensions::TextureCube:
508 return WGPUTextureViewDimension_Cube;
510 return WGPUTextureViewDimension_Undefined;
515 WGPUStorageTextureAccess bindingStorageTextureAccessToWebGPU(Cogs::BindingStorageTextureAccess access) {
518 case Cogs::BindingStorageTextureAccess::WriteOnly:
519 return WGPUStorageTextureAccess_WriteOnly;
520 case Cogs::BindingStorageTextureAccess::ReadOnly:
521 return WGPUStorageTextureAccess_ReadOnly;
522 case Cogs::BindingStorageTextureAccess::ReadWrite:
523 return WGPUStorageTextureAccess_ReadWrite;
525 return WGPUStorageTextureAccess_WriteOnly;
531 WGPUTextureSampleType bindingTextureSampleTypeToWebGPU(
532 const Cogs::BindingTextureSampleType sampleType,
533 const bool isDepthTexture,
534 const bool multisampled)
536 switch (sampleType) {
537 case Cogs::BindingTextureSampleType::Float:
538 return multisampled ? WGPUTextureSampleType_UnfilterableFloat : WGPUTextureSampleType_Float;
539 case Cogs::BindingTextureSampleType::UnfilterableFloat:
540 return WGPUTextureSampleType_UnfilterableFloat;
541 case Cogs::BindingTextureSampleType::Depth:
542 return WGPUTextureSampleType_Depth;
543 case Cogs::BindingTextureSampleType::Sint:
544 return WGPUTextureSampleType_Sint;
545 case Cogs::BindingTextureSampleType::Uint:
546 return WGPUTextureSampleType_Uint;
548 return isDepthTexture ? WGPUTextureSampleType_Depth : WGPUTextureSampleType_Float;
553 WGPUSamplerBindingType bindingSamplerTypeToWebGPU(
554 const Cogs::BindingSamplerBindingType samplerType,
555 const bool isDepthTexture,
556 const size_t nameHash)
558 switch (samplerType) {
559 case Cogs::BindingSamplerBindingType::Filtering:
560 return WGPUSamplerBindingType_Filtering;
561 case Cogs::BindingSamplerBindingType::NonFiltering:
562 return WGPUSamplerBindingType_NonFiltering;
563 case Cogs::BindingSamplerBindingType::Comparison:
564 return WGPUSamplerBindingType_Comparison;
566 return (isDepthTexture || nameHash ==
Cogs::hash(
"shadowSampler"))
567 ? WGPUSamplerBindingType_Comparison
568 : WGPUSamplerBindingType_Filtering;
573 WGPUTextureFormat bindingTextureFormatToWebGPU(Cogs::Format format) {
575 case Cogs::Format::R8G8B8A8_UNORM:
576 return WGPUTextureFormat_RGBA8Unorm;
577 case Cogs::Format::R8G8B8A8_SNORM:
578 return WGPUTextureFormat_RGBA8Snorm;
579 case Cogs::Format::R8G8B8A8_UINT:
580 return WGPUTextureFormat_RGBA8Uint;
581 case Cogs::Format::R8G8B8A8_SINT:
582 return WGPUTextureFormat_RGBA8Sint;
583 case Cogs::Format::R16G16B16A16_UINT:
584 return WGPUTextureFormat_RGBA16Uint;
585 case Cogs::Format::R16G16B16A16_SINT:
586 return WGPUTextureFormat_RGBA16Sint;
587 case Cogs::Format::R16G16B16A16_FLOAT:
588 return WGPUTextureFormat_RGBA16Float;
589 case Cogs::Format::R32_UINT:
590 return WGPUTextureFormat_R32Uint;
591 case Cogs::Format::R32_SINT:
592 return WGPUTextureFormat_R32Sint;
593 case Cogs::Format::R32_FLOAT:
594 return WGPUTextureFormat_R32Float;
595 case Cogs::Format::R32G32_UINT:
596 return WGPUTextureFormat_RG32Uint;
597 case Cogs::Format::R32G32_SINT:
598 return WGPUTextureFormat_RG32Sint;
599 case Cogs::Format::R32G32_FLOAT:
600 return WGPUTextureFormat_RG32Float;
601 case Cogs::Format::R32G32B32A32_UINT:
602 return WGPUTextureFormat_RGBA32Uint;
603 case Cogs::Format::R32G32B32A32_SINT:
604 return WGPUTextureFormat_RGBA32Sint;
605 case Cogs::Format::R32G32B32A32_FLOAT:
606 return WGPUTextureFormat_RGBA32Float;
607 case Cogs::Format::B8G8R8A8:
608 return WGPUTextureFormat_BGRA8Unorm;
610 return WGPUTextureFormat_Undefined;
616 bool getWebGPUConstantBufferBindingsFromMetadata(
618 std::vector<Cogs::WebGPUConstantBufferBinding>& outBindings)
623 for (uint16_t groupIdx = 0; groupIdx < layoutSet->numGroups; groupIdx++) {
625 if (group.numEntries == 0)
continue;
626 for (uint16_t entryIdx = 0; entryIdx < group.numEntries; entryIdx++) {
628 WGPUBindGroupLayoutEntry bg_ent = WGPU_BIND_GROUP_LAYOUT_ENTRY_INIT;
629 bg_ent.binding = entry.binding;
630 bg_ent.visibility = bindingVisibilityToShaderStage(entry.visibility);
632 switch (entry.resourceType) {
633 case Cogs::BindingResourceType::UniformBuffer:
634 bg_ent.buffer.type = WGPUBufferBindingType_Uniform;
636 case Cogs::BindingResourceType::StorageBuffer:
637 bg_ent.buffer.type = WGPUBufferBindingType_Storage;
639 case Cogs::BindingResourceType::Texture:
640 bg_ent.texture.viewDimension = ResourceDimensionsToViewDimension(entry.textureDimension);
641 bg_ent.texture.multisampled = entry.multisampled ? 1 : 0;
642 bg_ent.texture.sampleType = bindingTextureSampleTypeToWebGPU(entry.textureSampleType,
643 entry.isDepthTexture,
646 case Cogs::BindingResourceType::Sampler:
647 bg_ent.sampler.type = bindingSamplerTypeToWebGPU(entry.samplerBindingType,
648 entry.isDepthTexture,
651 case Cogs::BindingResourceType::StorageTexture:
652 bg_ent.storageTexture.viewDimension = ResourceDimensionsToViewDimension(entry.textureDimension);
653 bg_ent.storageTexture.access = bindingStorageTextureAccessToWebGPU(entry.storageTextureAccess);
654 bg_ent.storageTexture.format = bindingTextureFormatToWebGPU(entry.format);
658 "Unsupported bind-group resource type %d for entry '%d''.",
659 int(entry.resourceType),
665 binding.group = groupIdx;
666 binding.nameHash = entry.nameHash;
668 binding.bg_ent = bg_ent;
669 outBindings.push_back(std::move(binding));
679 const char* semanticNames[]
691 WGPUVertexFormat format =
static_cast<WGPUVertexFormat
>(0);
694 case Cogs::hash(
"f32"): format = WGPUVertexFormat::WGPUVertexFormat_Float32;
break;
695 case Cogs::hash(
"vec2f"): format = WGPUVertexFormat::WGPUVertexFormat_Float32x2;
break;
696 case Cogs::hash(
"vec3f"): format = WGPUVertexFormat::WGPUVertexFormat_Float32x3;
break;
697 case Cogs::hash(
"vec4f"): format = WGPUVertexFormat::WGPUVertexFormat_Float32x4;
break;
698 case Cogs::hash(
"i32"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32;
break;
699 case Cogs::hash(
"vec2i"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32x2;
break;
700 case Cogs::hash(
"vec3i"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32x3;
break;
701 case Cogs::hash(
"vec4i"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32x4;
break;
702 case Cogs::hash(
"u32"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32;
break;
703 case Cogs::hash(
"vec2u"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32x2;
break;
704 case Cogs::hash(
"vec3u"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32x3;
break;
705 case Cogs::hash(
"vec4u"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32x4;
break;
707 LOG_ERROR(logger,
"Unsupported vertex attribute type %s", std::string(s).c_str());
741 std::istringstream iss(shaderSource);
742 std::string expr = R
"(\s*const ([^\s^0-9]+)([0-9]+)_LOC\s*=\s*([0-9]+))";
743 std::regex regex_expression(expr);
745 for (std::string line; std::getline(iss, line); )
748 if (std::regex_search(line, match, regex_expression))
750 std::string semanticName = match.str(1);
751 uint8_t slot =
static_cast<uint8_t
>(std::stoi(match.str(2)));
752 uint8_t loc =
static_cast<uint8_t
>(std::stoi(match.str(3)));
753 for (uint8_t i = 0; i < std::size(semanticNames); i++) {
754 if (strcmp(semanticName.c_str(), semanticNames[i]) == 0) {
766 iss.seekg(0, std::ios::beg);
767 std::string expr_location = R
"(@location\(\s*([^\s\)\d]+)([\d]+)_LOC\s*\)\s*([^\s:]+)\s*:\s*([\da-zA-Z]+)[,\s\n])";
768 std::regex regex_location(expr_location);
769 for (std::string line; std::getline(iss, line); )
772 if (std::regex_search(line, match, regex_location))
774 std::string semanticName = match.str(1);
775 uint8_t slot =
static_cast<uint8_t
>(std::stoi(match.str(2)));
776 std::string name = match.str(3);
777 std::string type = match.str(4);
778 uint8_t semantic = std::numeric_limits<uint8_t>::max();
779 for (uint8_t i = 0; i < std::size(semanticNames); i++) {
780 if (strcmp(semanticName.c_str(), semanticNames[i]) == 0) {
785 if (semantic == std::numeric_limits<uint8_t>::max()) {
786 LOG_DEBUG(logger,
"unknown semantic name for vertex attribute %s", semanticName.c_str());
789 for (
size_t i = 0; i < nAttribs; i++) {
790 if (bindings[i].semantic == semantic && bindings[i].slot == slot) {
802 size_t i = (
static_cast<size_t>(groupIdx) << 16) + (binding + 1);
808 int64_t i = handle.
handle;
809 group =
static_cast<size_t>(i >> 16);
810 binding =
static_cast<uint32_t
>((i & 0xFFFF) - 1);
813 Cogs::BindingResourceType bindingResourceTypeFromWebGPU(
const WGPUBindGroupLayoutEntry& entry)
815 if (entry.buffer.type != WGPUBufferBindingType_Undefined) {
816 return (entry.buffer.type == WGPUBufferBindingType_Uniform) ? Cogs::BindingResourceType::UniformBuffer : Cogs::BindingResourceType::StorageBuffer;
818 if (entry.texture.sampleType != WGPUTextureSampleType_Undefined) {
819 return Cogs::BindingResourceType::Texture;
821 if (entry.sampler.type != WGPUSamplerBindingType_Undefined) {
822 return Cogs::BindingResourceType::Sampler;
824 if (entry.storageTexture.access != WGPUStorageTextureAccess_Undefined) {
825 return Cogs::BindingResourceType::StorageTexture;
827 return Cogs::BindingResourceType::UniformBuffer;
830 Cogs::ResourceDimensions textureDimensionFromWebGPU(WGPUTextureViewDimension dim)
833 case WGPUTextureViewDimension_1D:
return Cogs::ResourceDimensions::Texture1D;
834 case WGPUTextureViewDimension_2D:
return Cogs::ResourceDimensions::Texture2D;
835 case WGPUTextureViewDimension_2DArray:
return Cogs::ResourceDimensions::Texture2DArray;
836 case WGPUTextureViewDimension_3D:
return Cogs::ResourceDimensions::Texture3D;
837 case WGPUTextureViewDimension_Cube:
838 case WGPUTextureViewDimension_CubeArray:
839 return Cogs::ResourceDimensions::TextureCube;
841 return Cogs::ResourceDimensions::Unknown;
845 Cogs::BindingTextureSampleType textureSampleTypeFromWebGPU(WGPUTextureSampleType sampleType)
847 switch (sampleType) {
848 case WGPUTextureSampleType_Float:
return Cogs::BindingTextureSampleType::Float;
849 case WGPUTextureSampleType_UnfilterableFloat:
return Cogs::BindingTextureSampleType::UnfilterableFloat;
850 case WGPUTextureSampleType_Depth:
return Cogs::BindingTextureSampleType::Depth;
851 case WGPUTextureSampleType_Sint:
return Cogs::BindingTextureSampleType::Sint;
852 case WGPUTextureSampleType_Uint:
return Cogs::BindingTextureSampleType::Uint;
854 return Cogs::BindingTextureSampleType::Unknown;
858 Cogs::BindingSamplerBindingType samplerTypeFromWebGPU(WGPUSamplerBindingType samplerType)
860 switch (samplerType) {
861 case WGPUSamplerBindingType_Filtering:
return Cogs::BindingSamplerBindingType::Filtering;
862 case WGPUSamplerBindingType_NonFiltering:
return Cogs::BindingSamplerBindingType::NonFiltering;
863 case WGPUSamplerBindingType_Comparison:
return Cogs::BindingSamplerBindingType::Comparison;
865 return Cogs::BindingSamplerBindingType::Unknown;
869 bool bindGroupEntryEquals(
const WGPUBindGroupEntry& a,
const WGPUBindGroupEntry& b)
871 return a.binding == b.binding &&
872 a.buffer == b.buffer &&
873 a.offset == b.offset &&
875 a.sampler == b.sampler &&
876 a.textureView == b.textureView;
879 void dumpSource(
const std::string& source,
int firstLine = 1,
int lastLine = std::numeric_limits<int>::max())
881 const char* start = source.data();
882 const char* curr = start;
885 while ((*curr !=
'\0') && (line <= lastLine)) {
886 const char* p = curr;
887 while ((*p !=
'\0') && (*p !=
'\n') && (*p !=
'\r')) { p++; }
889 if (firstLine <= line) {
890 LOG_DEBUG(logger,
"%3d: %s", line, std::string(curr, p - curr).c_str());
896 if (*curr !=
'\0') { curr++; };
897 if ((*curr !=
'\0') && (*curr != *p) && ((*curr ==
'\n') || (*curr ==
'\r'))) { curr++; }
901 void printShaderError(
struct WGPUCompilationInfo
const* compilationInfo,
const std::string* source =
nullptr)
903 for (
size_t i = 0; i < compilationInfo->messageCount; i++) {
904 const WGPUCompilationMessage& message = compilationInfo->messages[i];
906 dumpSource(*source, ((
int)message.lineNum) - 3, ((
int)message.lineNum));
909 std::string category_str;
910 switch (message.type) {
911 case WGPUCompilationMessageType::WGPUCompilationMessageType_Info:
912 category = Cogs::Logging::Category::Info;
913 category_str =
"Info";
915 case WGPUCompilationMessageType::WGPUCompilationMessageType_Error:
916 category = Cogs::Logging::Category::Error;
917 category_str =
"Error";
919 case WGPUCompilationMessageType::WGPUCompilationMessageType_Force32:
920 category = Cogs::Logging::Category::Error;
921 category_str =
"Force32";
924 category = Cogs::Logging::Category::Error;
925 category_str =
"Unknown";
932 void compile_callback(WGPUCompilationInfoRequestStatus status,
struct WGPUCompilationInfo
const* compilationInfo,
void* userdata1,
void* )
934 const std::string* source =
reinterpret_cast<std::string*
>(userdata1);
935 if (status != WGPUCompilationInfoRequestStatus_Success || (compilationInfo !=
nullptr && compilationInfo->messageCount != 0))
937 if(status == WGPUCompilationInfoRequestStatus_CallbackCancelled){
938 LOG_INFO(logger,
"WGPUCompilationInfoRequestStatus_CallbackCancelled");
941 printShaderError(compilationInfo, source);
948 void EffectsWebGPU::initialize(GraphicsDeviceWebGPU *device_in, IBuffers * buffers_in)
950 EffectsCommon::initialize(buffers_in);
951 graphicsDevice = device_in;
956 std::vector<EffectHandle> handles;
957 for (
auto& resource : effects) {
958 handles.push_back(effects.getHandle(resource));
960 for (
auto& handle : handles) {
967 if (!HandleIsValid(handle))
return;
971 if(effect.vs_module){
972 wgpuShaderModuleRelease(effect.vs_module);
973 counters.shader_module--;
975 if(effect.fs_module){
976 wgpuShaderModuleRelease(effect.fs_module);
977 counters.shader_module--;
979 if(effect.cs_module){
980 wgpuShaderModuleRelease(effect.cs_module);
981 counters.shader_module--;
983 this->effects.removeResource(handle);
994 WGPUDevice device = graphicsDevice->device;
998 Utilities::readFile(handler, fileName, csSource);
1001 effect.cs_entry =
"main";
1002 effect.name = std::string(fileName);
1004 std::string cs_source;
1005 for(
auto &def : definitions){
1006 cs_source +=
"const " + def.first +
" = " + def.second +
";\n";
1008 cs_source += csSource.content;
1011 LOG_INFO(logger,
"Compiling WebGPU CS:\n%s", cs_source.c_str());
1014 WGPUShaderModuleDescriptor descriptor = WGPU_SHADER_MODULE_DESCRIPTOR_INIT;
1015 descriptor.label = {fileName.data(), WGPU_STRLEN};
1016 WGPUShaderSourceWGSL wgsl_desc = WGPU_SHADER_SOURCE_WGSL_INIT;
1017 wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
1018 wgsl_desc.code = {cs_source.c_str(), WGPU_STRLEN};
1019 descriptor.nextInChain = (WGPUChainedStruct*)&wgsl_desc;
1021 effect.cs_module = wgpuDeviceCreateShaderModule(device, &descriptor);
1022 counters.shader_module++;
1026 if (!extractBindingLayout(cs_source, Cogs::BindingVisibilityFlags::BindingVisibilityCompute, effect.bindGroupSetDescription)) {
1031 std::vector<Cogs::WebGPUConstantBufferBinding> bindings = extractConstantBinding(cs_source, WGPUShaderStage_Compute);
1032 addConstantBufferBindings(effect, bindings);
1034 WGPUCompilationInfoCallbackInfo callbackInfo = WGPU_COMPILATION_INFO_CALLBACK_INFO_INIT;
1035 callbackInfo.nextInChain =
nullptr;
1036 callbackInfo.callback = compile_callback;
1037 callbackInfo.mode = WGPUCallbackMode_AllowProcessEvents;
1038 callbackInfo.userdata1 =
static_cast<void*
>(&cs_source);
1040 wgpuShaderModuleGetCompilationInfo(effect.cs_module, callbackInfo);
1043 return this->effects.addResource(std::move(effect));
1059 bool useSortedDefines =
false;
1061 std::sort(unique_definitions.begin(), unique_definitions.end());
1062 unique_definitions.erase(std::unique(unique_definitions.begin(), unique_definitions.end()), unique_definitions.end());
1063 if (unique_definitions.size() != effect_desc.
definitions.size()) {
1064 useSortedDefines =
true;
1065 LOG_WARNING(logger,
"Redefinition of precompiler defines not supported in WebGPU backend");
1069 assert(!hsSource.origin.size());
1070 assert(!dsSource.origin.size());
1071 assert(!gsSource.origin.size());
1073 WGPUDevice device = graphicsDevice->device;
1074 ResourceCountersWebGPU &counters = graphicsDevice->counters;
1076 EffectWebGPU effect = {};
1077 effect.vs_entry = std::string(vsEntryPoint);
1078 effect.fs_entry = std::string(fsEntryPoint);
1079 effect.name = std::string(effect_desc.
name);
1081 LOG_INFO(logger,
"Compiling WebGPU Effect: %s", effect.name.c_str());
1083 std::string definitions_source;
1084 for (
auto& def : useSortedDefines ? unique_definitions : effect_desc.
definitions) {
1085 definitions_source +=
"const " + def.first +
" = " + def.second +
";\n";
1092 hasBindingMetadata =
true;
1094 LOG_INFO_ONCE(logger,
"Effect %s missing binding metadata parsing shader source as fallback", effect.name.c_str());
1097 std::string vs_source = definitions_source + vsSource.content;
1100 LOG_INFO(logger,
"Compiling WebGPU VS:\n%s", vs_source.c_str());
1103 WGPUShaderModuleDescriptor descriptor = WGPU_SHADER_MODULE_DESCRIPTOR_INIT;
1104 descriptor.label = { effect_desc.
name.
data(), WGPU_STRLEN };
1105 WGPUShaderSourceWGSL wgsl_desc = WGPU_SHADER_SOURCE_WGSL_INIT;
1106 wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
1107 wgsl_desc.code = { vs_source.c_str(), WGPU_STRLEN };
1108 descriptor.nextInChain = (WGPUChainedStruct*)&wgsl_desc;
1110 effect.vs_module = wgpuDeviceCreateShaderModule(device, &descriptor);
1111 counters.shader_module++;
1113 WGPUCompilationInfoCallbackInfo callbackInfo = WGPU_COMPILATION_INFO_CALLBACK_INFO_INIT;
1114 callbackInfo.nextInChain =
nullptr;
1115 callbackInfo.callback = compile_callback;
1116 callbackInfo.mode = WGPUCallbackMode_AllowProcessEvents;
1117 callbackInfo.userdata1 =
static_cast<void*
>(&vs_source);
1118 wgpuShaderModuleGetCompilationInfo(effect.vs_module, callbackInfo);
1122 effect.num_attribs = extractVertexAttribLocation(vs_source, effect.semanticSlotBindings, effect.maxVertexAttribs);
1125 if (!hasBindingMetadata) {
1126 if (!extractBindingLayout(vs_source, Cogs::BindingVisibilityFlags::BindingVisibilityVertex, effect.bindGroupSetDescription)) {
1132 if (fsSource.content.size()) {
1133 std::string fs_source;
1134 fs_source = definitions_source + fsSource.content;
1137 LOG_INFO(logger,
"Compiling WebGPU FS:\n%s", fs_source.c_str());
1140 WGPUShaderModuleDescriptor descriptor = WGPU_SHADER_MODULE_DESCRIPTOR_INIT;
1141 descriptor.label = {effect_desc.
name.
data(), WGPU_STRLEN};
1142 WGPUShaderSourceWGSL wgsl_desc = WGPU_SHADER_SOURCE_WGSL_INIT;
1143 wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
1144 wgsl_desc.code = {fs_source.c_str(), WGPU_STRLEN};
1145 descriptor.nextInChain = (WGPUChainedStruct*)&wgsl_desc;
1147 effect.fs_module = wgpuDeviceCreateShaderModule(device, &descriptor);
1148 counters.shader_module++;
1150 WGPUCompilationInfoCallbackInfo callbackInfo = WGPU_COMPILATION_INFO_CALLBACK_INFO_INIT;
1151 callbackInfo.nextInChain =
nullptr;
1152 callbackInfo.callback = compile_callback;
1153 callbackInfo.mode = WGPUCallbackMode_AllowProcessEvents;
1154 callbackInfo.userdata1 =
static_cast<void*
>(& fs_source);
1155 wgpuShaderModuleGetCompilationInfo(effect.fs_module, callbackInfo);
1157 if (!hasBindingMetadata) {
1158 if (!extractBindingLayout(fs_source, Cogs::BindingVisibilityFlags::BindingVisibilityFragment, effect.bindGroupSetDescription)) {
1165 std::vector<Cogs::WebGPUConstantBufferBinding> metadataBindings;
1166 if (!getWebGPUConstantBufferBindingsFromMetadata(&effect.bindGroupSetDescription, metadataBindings)) {
1170 if (!addConstantBufferBindings(effect, metadataBindings)) {
1176 effect.updateBindGroupDescHashes();
1177 return this->effects.addResource(std::move(effect));
1183 for (uint16_t g = 0; g < effect.bindGroupSetDescription.numGroups; g++) {
1185 for (uint16_t i = 0; i < group.numEntries; i++) {
1186 if (group.entries[i].nameHash == nameHash) {
1187 return encodeConstantBufferBindingHandle(g, i);
1195 if (!HandleIsValid(effectHandle) || !effects.hasResource(effectHandle)) {
1196 return IEffects::getBindGroupDescription(effectHandle, groupIndex);
1198 const EffectWebGPU& effect = effects[effectHandle];
1199 if (groupIndex >= effect.bindGroupSetDescription.numGroups) {
1200 return IEffects::getBindGroupDescription(effectHandle, groupIndex);
1202 return effect.bindGroupSetDescription.groups[groupIndex];
1206 static const size_t emptyBindGroupDescHash = BindGroupDescription().hash();
1209 if (!HandleIsValid(effectHandle) || !effects.hasResource(effectHandle)) {
1210 return emptyBindGroupDescHash;
1213 if (groupIndex >= effect.bindGroupSetDescription.numGroups) {
1214 return emptyBindGroupDescHash;
1222 if (HandleIsValid(constantBuffer)) {
1228 bool EffectsWebGPU::addConstantBufferBindings(
EffectWebGPU& effect,
const std::vector<Cogs::WebGPUConstantBufferBinding>& bindings) {
1229 for (
auto binding : bindings) {
1231 bool exists =
false;
1232 for (slot = 0; slot < effect.num_bindings; slot++) {
1233 auto& curr = effect.constantBufferBindings[slot];
1234 if (curr.nameHash == binding.nameHash) {
1235 if (curr.group == binding.group && curr.bg_ent.binding == binding.bg_ent.binding) {
1236 curr.bg_ent.visibility = curr.bg_ent.visibility | binding.bg_ent.visibility;
1240 LOG_ERROR(logger,
"Inconsistent binding location");
1247 if (effect.num_bindings < EffectWebGPU::maxConstantBuffers) {
1248 effect.constantBufferBindings[effect.num_bindings++] = binding;
1251 LOG_ERROR(logger,
"Number of bindings exceed EffectWebGPU::maxConstantBuffers=%zu", EffectWebGPU::maxConstantBuffers);
1260 std::string constantBufferName = std::string(name);
1262 if (HandleIsValid(constantBuffer)) {
1269 std::string constantBufferName = std::string(name);
1270 if (!constantBufferName.ends_with(
"Sampler")) {
1271 constantBufferName +=
"Sampler";
1275 if (HandleIsValid(constantBuffer)) {
1279 if (constantBufferName.ends_with(
"TextureSampler")) {
1280 constantBufferName.erase(constantBufferName.length() - strlen(
"TextureSampler"));
1281 StringView shortenedName(constantBufferName);
1331 if (!HandleIsValid(effectHandle) || !effects.hasResource(effectHandle)) {
1335 return effect.bindGroupSetDescription.numGroups;
uint32_t getNumBindGroups(EffectHandle effectHandle) override
Query the number of bind-group layouts exposed by the given effect.
TextureBindingHandle getTextureBinding(EffectHandle effectHandle, const StringView &name, const unsigned int slot) override
Get a handle to a texture object binding, mapping how to bind textures to the given effect.
virtual void releaseEffect(EffectHandle effectHandle) override
Release the effect with the given handle, freeing all resources generated during program loading.
BufferBindingHandle getBufferBinding(EffectHandle effectHandle, const StringView &name) override
Get a handle to a buffer binding.
virtual EffectHandle loadComputeEffect(const StringView &, EffectFlags::EEffectFlags) override
Load the compute shader with the given file name and create an effect.
ConstantBufferBindingHandle getConstantBufferBinding(EffectHandle effectHandle, const StringView &name) override
Get a handle to a constant buffer binding, mapping how to bind a constant buffer to the given effect.
virtual void releaseResources() override
Release all allocated effect resources.
SamplerStateBindingHandle getSamplerStateBinding(EffectHandle effectHandle, const StringView &, const unsigned int slot) override
Get a handle to a sampler state object binding, mapping how to bind the sampler state to the given ef...
size_t getBindGroupDescHash(EffectHandle effectHandle, uint32_t groupIndex)
Cached hash of the description returned by getBindGroupDescription, matching it for out-of-range grou...
Log implementation class.
void log(const Category category, uint32_t errorNumber, _Printf_format_string_ const char *fmt,...) const VALIDATE_ARGS(4)
Log a formatted message.
Provides a weakly referenced view over the contents of a string.
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
@ Unspecified
The default error number for legacy logger usage.
Category
Logging categories used to filter log messages.
Contains all Cogs related functionality.
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
std::vector< PreprocessorDefinition > PreprocessorDefinitions
A set of preprocessor definitions.
Contains an effect description used to load a single effect.
EffectFlags::EEffectFlags flags
Effect loading flags.
BindGroupSetDescription bindGroupLayoutDesc
Optional bind-group layout metadata. Backends may use this instead of source introspection.
PreprocessorDefinitions definitions
Definitions.
StringView name
Name of the effect. Used for tracking purposes, like naming shader dumps.
EEffectFlags
Effect source flags.
@ LogShaderSource
Log the contents of the shader on error.
void updateBindGroupDescHashes()
Refresh bindGroupDescHash. Call once bindGroupSetDescription is final.
size_t bindGroupDescHash[MaxBindGroups]
Hash of each entry in bindGroupSetDescription.groups, letting bind group compatibility be tested with...
static const Handle_t NoHandle
Represents a handle to nothing.
handle_type handle
Internal resource handle.