Cogs.Core
EffectsWebGPU.cpp
1#include "EffectsWebGPU.h"
2
3#include "GraphicsDeviceWebGPU.h"
4
5#include "Foundation/Logging/Logger.h"
6
7#include <algorithm>
8#include <cinttypes>
9#include <sstream>
10#include <regex>
11
12namespace{
13 Cogs::Logging::Log logger = Cogs::Logging::getLogger("EffectsWebGPU");
14
15 bool starts_with(std::string::const_iterator begin, std::string::const_iterator end, const char* s, std::string::const_iterator& next) {
16 std::string::const_iterator it = begin;
17 next = begin;
18 while (it != end && *s != '\0') {
19 if (*it != *s) {
20 return false;
21 }
22 s++;
23 it++;
24 }
25 if (*s != '\0') {
26 return false;
27 }
28 next = it;
29 return true;
30 }
31
32 void eat_white(std::string::const_iterator& it, std::string::const_iterator end) {
33 while (it != end && std::isspace(*it)) {
34 it++;
35 }
36 }
37
38 WGPUTextureViewDimension extractViewDimention(std::string::const_iterator& type_it, std::string::const_iterator end) {
39 WGPUTextureViewDimension tvd = WGPUTextureViewDimension_Undefined;
40 if (starts_with(type_it, end, "2d_array", type_it)) {
41 tvd = WGPUTextureViewDimension_2DArray;
42 }
43 else if (starts_with(type_it, end, "2d", type_it)) {
44 tvd = WGPUTextureViewDimension_2D;
45 }
46 else if (starts_with(type_it, end, "cube", type_it)) {
47 tvd = WGPUTextureViewDimension_Cube;
48 }
49 else if (starts_with(type_it, end, "1d", type_it)) {
50 tvd = WGPUTextureViewDimension_1D;
51 }
52 else if (starts_with(type_it, end, "cube_array", type_it)) {
53 tvd = WGPUTextureViewDimension_CubeArray;
54 }
55 else if (starts_with(type_it, end, "3d", type_it)) {
56 tvd = WGPUTextureViewDimension_3D;
57 }
58 return tvd;
59 }
60
61 WGPUTextureFormat extractTextureFormat(std::string::const_iterator& type_it, std::string::const_iterator end) {
62 WGPUTextureFormat tf = WGPUTextureFormat_Undefined;
63 if (starts_with(type_it, end, "rgba8unorm", type_it)) {
64 tf = WGPUTextureFormat_RGBA8Unorm;
65 }
66 else if (starts_with(type_it, end, "rgba8snorm", type_it)) {
67 tf = WGPUTextureFormat_RGBA8Snorm;
68 }
69 else if (starts_with(type_it, end, "rgba8uint", type_it)) {
70 tf = WGPUTextureFormat_RGBA8Uint;
71 }
72 else if (starts_with(type_it, end, "rgba8sint", type_it)) {
73 tf = WGPUTextureFormat_RGBA8Sint;
74 }
75 else if (starts_with(type_it, end, "rgba16uint", type_it)) {
76 tf = WGPUTextureFormat_RGBA16Uint;
77 }
78 else if (starts_with(type_it, end, "rgba16sint", type_it)) {
79 tf = WGPUTextureFormat_RGBA16Sint;
80 }
81 else if (starts_with(type_it, end, "rgba16float", type_it)) {
82 tf = WGPUTextureFormat_RGBA16Float;
83 }
84 else if (starts_with(type_it, end, "r32uint", type_it)) {
85 tf = WGPUTextureFormat_R32Uint;
86 }
87 else if (starts_with(type_it, end, "r32sint", type_it)) {
88 tf = WGPUTextureFormat_R32Sint;
89 }
90 else if (starts_with(type_it, end, "r32float", type_it)) {
91 tf = WGPUTextureFormat_R32Float;
92 }
93 else if (starts_with(type_it, end, "rg32uint", type_it)) {
94 tf = WGPUTextureFormat_RG32Uint;
95 }
96 else if (starts_with(type_it, end, "rg32sint", type_it)) {
97 tf = WGPUTextureFormat_RG32Sint;
98 }
99 else if (starts_with(type_it, end, "rg32float", type_it)) {
100 tf = WGPUTextureFormat_RG32Float;
101 }
102 else if (starts_with(type_it, end, "rgba32uint", type_it)) {
103 tf = WGPUTextureFormat_RGBA32Uint;
104 }
105 else if (starts_with(type_it, end, "rgba32sint", type_it)) {
106 tf = WGPUTextureFormat_RGBA32Sint;
107 }
108 else if (starts_with(type_it, end, "rgba32float", type_it)) {
109 tf = WGPUTextureFormat_RGBA32Float;
110 }
111 else if (starts_with(type_it, end, "bgra8unorm", type_it)) {
112 tf = WGPUTextureFormat_BGRA8Unorm;
113 }
114 return tf;
115 }
116
117 WGPUStorageTextureAccess extractStorageTextureAccess(std::string::const_iterator& type_it, std::string::const_iterator end) {
118 WGPUStorageTextureAccess sta = WGPUStorageTextureAccess_Undefined;
119 if (starts_with(type_it, end, "read", type_it)) {
120#ifdef EMSCRIPTEN
121 assert(false); // TODO update emsdk with newer webgpu implementation
122#else
123 sta = WGPUStorageTextureAccess_ReadOnly;
124#endif
125 }
126 else if (starts_with(type_it, end, "write", type_it)) {
127 sta = WGPUStorageTextureAccess_WriteOnly;
128 }
129 else if (starts_with(type_it, end, "readwrite", type_it)) {
130#ifdef EMSCRIPTEN
131 assert(false); // TODO update emsdk with newer webgpu implementation
132#else
133 sta = WGPUStorageTextureAccess_ReadWrite;
134#endif
135 }
136 return sta;
137 }
138
139 std::vector<Cogs::WebGPUConstantBufferBinding> extractConstantBindingLayout(std::string shaderSource, Cogs::WebGPUConstantBufferUsage usage) {
140 std::vector<Cogs::WebGPUConstantBufferBinding> result;
141 std::istringstream iss(shaderSource);
142 std::string expr = R"(^\s*@group\‍(([0-9]+)\) @binding\‍(([0-9]+)\)\s+var(<uniform>)?\s+([^\s]+)[\s]*:\s?(.*)\s?;)";
143 std::regex regex_expression(expr);
144
145 for (std::string line; std::getline(iss, line); )
146 {
147 std::smatch match;
148 if (std::regex_search(line, match, regex_expression))
149 {
150 size_t group = std::stoi(match.str(1));
151 unsigned int loc = std::stoi(match.str(2));
152 bool isUniform = match.str(3) == "<uniform>";
153 std::string name = match.str(4);
154 std::string type = match.str(5);
155
156 std::string::const_iterator type_it = type.begin();
157 size_t nameHash = Cogs::hash(name);
158 // Cogs::ConstantBufferBindingHandle location = (Cogs::ConstantBufferBindingHandle)loc;
159 // Cogs::WebGPUConstantBufferType bufferType = Cogs::WebGPUConstantBufferType::UniformBuffer;
160 bool alreadyInserted = false;
161 for (auto& e : result) {
162 if (e.group == group && e.bg_ent.binding == loc) {
163 e.bg_ent.visibility |= usage;
164 alreadyInserted = true;
165 break;
166 }
167 }
168 if (alreadyInserted) {
169 continue;
170 }
171 WGPUBindGroupLayoutEntry bg_ent = {};
172 bg_ent.binding = static_cast<uint32_t>(loc);
173 bg_ent.visibility = (WGPUShaderStageFlags)usage;
174
175 if (isUniform) {
176 bg_ent.buffer.type = WGPUBufferBindingType_Uniform;
177 // bufferType = Cogs::WebGPUConstantBufferType::UniformBuffer;
178 }
179 else if (starts_with(type_it, type.end(), "texture_storage_", type_it)) { // type.starts_with("texture_")
180 bg_ent.storageTexture.viewDimension = extractViewDimention(type_it, type.end());
181 eat_white(type_it, type.end());
182 if (!starts_with(type_it, type.end(), "<", type_it)) {
183 LOG_DEBUG(logger, "Texture type %s not yet supported", type.c_str());
184 }
185 bg_ent.storageTexture.format = extractTextureFormat(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());
189 }
190 eat_white(type_it, type.end());
191 bg_ent.storageTexture.access = extractStorageTextureAccess(type_it, type.end());
192 eat_white(type_it, type.end());
193 if (!starts_with(type_it, type.end(), ">", type_it)) {
194 LOG_DEBUG(logger, "Texture type %s not yet supported", type.c_str());
195 }
196 }
197 else if (starts_with(type_it, type.end(), "texture_", type_it)) {
198 bool isDepth = false;
199 // bufferType = Cogs::WebGPUConstantBufferType::Texture;
200 bg_ent.texture.sampleType = WGPUTextureSampleType_Float;
201 bg_ent.texture.viewDimension = WGPUTextureViewDimension_2D;
202 bg_ent.texture.multisampled = 0;
203 if (starts_with(type_it, type.end(), "depth_", type_it)) {
204 isDepth = true;
205 }
206 bg_ent.texture.viewDimension = extractViewDimention(type_it, type.end());
207 eat_white(type_it, type.end());
208 if (isDepth) {
209 bg_ent.texture.sampleType = WGPUTextureSampleType_Depth;
210 } else if (starts_with(type_it, type.end(), "<f32>", type_it)) {
211 bg_ent.texture.sampleType = WGPUTextureSampleType_Float;
212 }
213 else if (starts_with(type_it, type.end(), "<i32>", type_it)) {
214 bg_ent.texture.sampleType = WGPUTextureSampleType_Sint;
215 }
216 else if (starts_with(type_it, type.end(), "<u32>", type_it)) {
217 bg_ent.texture.sampleType = WGPUTextureSampleType_Uint;
218 }
219 else {
220 LOG_DEBUG(logger, "Texture type %s not yet supported", type.c_str());
221 }
222 }
223 else if (type == "sampler") {
224 // bufferType = Cogs::WebGPUConstantBufferType::Sampler;
225 bg_ent.sampler.type = WGPUSamplerBindingType_Filtering;
226 }
227 else if (type == "sampler_comparison") {
228 // bufferType = Cogs::WebGPUConstantBufferType::Sampler;
229 bg_ent.sampler.type = WGPUSamplerBindingType_Comparison;
230 }
231 else {
232 LOG_DEBUG(logger, "Unknown uniform type%s", type.c_str());
233 }
234
235 Cogs::WebGPUConstantBufferBinding binding{.group = group, .nameHash = nameHash, .bg_ent = bg_ent};
236 result.push_back(binding);
237 }
238 }
239 return result;
240 }
241
242 const char* semanticNames[]
243 {
244 "a_POSITION",
245 "a_NORMAL",
246 "a_COLOR",
247 "a_TEXCOORD",
248 "a_TANGENT",
249 "a_INSTANCEVECTOR",
250 "a_INSTANCEMATRIX",
251 };
252
253 WGPUVertexFormat stringToVertexFormat(const Cogs::StringView &s) {
254 WGPUVertexFormat format = WGPUVertexFormat::WGPUVertexFormat_Undefined;
255 switch (Cogs::hash(s))
256 {
257 case Cogs::hash("float"): format = WGPUVertexFormat::WGPUVertexFormat_Float32; break;
258 case Cogs::hash("vec2f"): format = WGPUVertexFormat::WGPUVertexFormat_Float32x2; break;
259 case Cogs::hash("vec3f"): format = WGPUVertexFormat::WGPUVertexFormat_Float32x3; break;
260 case Cogs::hash("vec4f"): format = WGPUVertexFormat::WGPUVertexFormat_Float32x4; break;
261 case Cogs::hash("i32"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32; break;
262 case Cogs::hash("vec2i"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32x2; break;
263 case Cogs::hash("vec3i"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32x3; break;
264 case Cogs::hash("vec4i"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32x4; break;
265 case Cogs::hash("u32"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32; break;
266 case Cogs::hash("vec2u"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32x2; break;
267 case Cogs::hash("vec3u"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32x3; break;
268 case Cogs::hash("vec4u"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32x4; break;
269 default:
270 LOG_ERROR(logger, "Unsupported vertex attribute type %s", std::string(s).c_str());
271 break;
272 }
273 return format;
274 }
275
276 // size_t extractVertexAttribLocation_old(std::string shaderSource, Cogs::SemanticSlotBinding bindings[], size_t /*maxBindings*/) {
277 // std::istringstream iss(shaderSource);
278 // std::string expr = R"(\s*const ([^\s^0-9]+)([0-9]+)_LOC\s*=\s*([0-9]+))";
279 // std::regex regex_expression(expr);
280 // size_t nAttribs = 0;
281 // for (std::string line; std::getline(iss, line); )
282 // {
283 // std::smatch match;
284 // if (std::regex_search(line, match, regex_expression))
285 // {
286 // std::string semanticName = match.str(1);
287 // uint8_t slot = static_cast<uint8_t>(std::stoi(match.str(2)));
288 // uint8_t loc = static_cast<uint8_t>(std::stoi(match.str(3)));
289 // for (uint8_t i = 0; i < std::size(semanticNames); i++) {
290 // if (strcmp(semanticName.c_str(), semanticNames[i]) == 0) {
291 // Cogs::SemanticSlotBinding& b = bindings[nAttribs++];
292 // b.semantic = i;
293 // b.slot = slot;
294 // b.binding = loc;
295 // break;
296 // }
297 // }
298 // }
299 // }
300 // return nAttribs;
301 // }
302
303 size_t extractVertexAttribLocation(std::string shaderSource, Cogs::SemanticSlotBinding bindings[], size_t /*maxBindings*/) {
304 std::istringstream iss(shaderSource);
305 std::string expr = R"(\s*const ([^\s^0-9]+)([0-9]+)_LOC\s*=\s*([0-9]+))";
306 std::regex regex_expression(expr);
307 size_t nAttribs = 0;
308 for (std::string line; std::getline(iss, line); )
309 {
310 std::smatch match;
311 if (std::regex_search(line, match, regex_expression))
312 {
313 std::string semanticName = match.str(1);
314 uint8_t slot = static_cast<uint8_t>(std::stoi(match.str(2)));
315 uint8_t loc = static_cast<uint8_t>(std::stoi(match.str(3)));
316 for (uint8_t i = 0; i < std::size(semanticNames); i++) {
317 if (strcmp(semanticName.c_str(), semanticNames[i]) == 0) {
318 Cogs::SemanticSlotBinding& b = bindings[nAttribs++];
319 b.semantic = i;
320 b.slot = slot;
321 b.binding = loc;
322 break;
323 }
324 }
325 }
326 }
327
328 iss.clear();
329 iss.seekg(0, std::ios::beg);
330 std::string expr_location = R"(@location\‍(\s*([^\s\)\d]+)([\d]+)_LOC\s*\)\s*([^\s:]+)\s*:\s*([\da-zA-Z]+)[,\s\n])";
331 std::regex regex_location(expr_location);
332 for (std::string line; std::getline(iss, line); )
333 {
334 std::smatch match;
335 if (std::regex_search(line, match, regex_location))
336 {
337 std::string semanticName = match.str(1);
338 uint8_t slot = static_cast<uint8_t>(std::stoi(match.str(2)));
339 std::string name = match.str(3);
340 std::string type = match.str(4);
341 uint8_t semantic = std::numeric_limits<uint8_t>::max();
342 for (uint8_t i = 0; i < std::size(semanticNames); i++) {
343 if (strcmp(semanticName.c_str(), semanticNames[i]) == 0) {
344 semantic = i;
345 break;
346 }
347 }
348 if (semantic == std::numeric_limits<uint8_t>::max()) {
349 LOG_DEBUG(logger, "unknown semantic name for vertex attribute %s", semanticName.c_str());
350 continue;
351 }
352 for (size_t i = 0; i < nAttribs; i++) {
353 if (bindings[i].semantic == semantic && bindings[i].slot == slot) {
354 bindings[i].format = stringToVertexFormat(Cogs::StringView(type));
355 bindings[i].nameHash = Cogs::hash(name);
356 break;
357 }
358 }
359 }
360 }
361 return nAttribs;
362 }
363
364#ifndef EMSCRIPTEN
365 void dumpSource(const std::string& source, int firstLine = 1, int lastLine = std::numeric_limits<int>::max())
366 {
367 const char* start = source.data();
368 const char* curr = start;
369 int line = 1;
370
371 while ((*curr != '\0') && (line <= lastLine)) {
372 const char* p = curr;
373 while ((*p != '\0') && (*p != '\n') && (*p != '\r')) { p++; }
374
375 if (firstLine <= line) {
376 LOG_DEBUG(logger, "%3d: %s", line, std::string(curr, p - curr).c_str());
377 }
378
379 line = line + 1;
380
381 curr = p;
382 if (*curr != '\0') { curr++; };
383 if ((*curr != '\0') && (*curr != *p) && ((*curr == '\n') || (*curr == '\r'))) { curr++; }
384 }
385 }
386
387 void printShaderError(struct WGPUCompilationInfo const* compilationInfo, const std::string* source = nullptr)
388 {
389 for (size_t i = 0; i < compilationInfo->messageCount; i++) {
390 const WGPUCompilationMessage& message = compilationInfo->messages[i];
391 if (source) {
392 dumpSource(*source, ((int)message.lineNum) - 3, ((int)message.lineNum));
393 }
395 std::string category_str;
396 switch (message.type) {
397 case WGPUCompilationMessageType::WGPUCompilationMessageType_Info:
398 category = Cogs::Logging::Category::Info;
399 category_str = "Info";
400 break;
401 case WGPUCompilationMessageType::WGPUCompilationMessageType_Error:
402 category = Cogs::Logging::Category::Error;
403 category_str = "Error";
404 break;
405 case WGPUCompilationMessageType::WGPUCompilationMessageType_Force32:
406 category = Cogs::Logging::Category::Error;
407 category_str = "Force32";
408 break;
409 default:
410 category = Cogs::Logging::Category::Error;
411 category_str = "Unknown";
412 }
413
414 logger.log(category, Cogs::Logging::ErrorGroup::Unspecified, "WGSL %s (%" PRIu64 ", %" PRIu64 "): %s", category_str.c_str(), message.lineNum, message.linePos, message.message);
415 }
416 }
417
418 void compile_callback(WGPUCompilationInfoRequestStatus status, struct WGPUCompilationInfo const* compilationInfo, void* userdata) // Does not seem to be called if the shader does not compile
419 {
420 const std::string* source = reinterpret_cast<std::string*>(userdata);
421 if (status != WGPUCompilationInfoRequestStatus_Success || (compilationInfo != nullptr && compilationInfo->messageCount != 0))
422 {
423 printShaderError(compilationInfo, source);
424 }
425 }
426#endif
427}
428
429namespace Cogs{
430 void EffectsWebGPU::initialize(GraphicsDeviceWebGPU *device_in, IBuffers * buffers_in)
431 {
432 EffectsCommon::initialize(buffers_in);
433 graphicsDevice = device_in;
434 }
435
437 {
438 }
439
441 {
442 if (!HandleIsValid(handle)) return;
443 EffectWebGPU &effect = this->effects[handle];
444 wgpuShaderModuleRelease(effect.vs_module);
445 wgpuShaderModuleRelease(effect.fs_module);
446 this->effects.removeResource(handle);
447 }
448
450 {
451 PreprocessorDefinitions defines = {};
452 return loadComputeEffect(fileName, defines, effectFlags);
453 }
454
456 {
457 WGPUDevice device = graphicsDevice->device;
458
459 ProcessedContent csSource;
460 Utilities::readFile(handler, fileName, csSource);
461
462 EffectWebGPU effect = {};
463 effect.cs_entry = "main";
464 effect.name = std::string(fileName);
465
466 std::string cs_source;
467 for(auto &def : definitions){
468 cs_source += "const " + def.first + " = " + def.second + ";\n";
469 }
470 cs_source += csSource.content;
471
473 LOG_INFO(logger, "Compiling WebGPU CS:\n%s", cs_source.c_str());
474 }
475
476 WGPUShaderModuleDescriptor descriptor = {};
477 descriptor.label = fileName.data();
478 WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
479 wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
480 wgsl_desc.code = cs_source.c_str();
481 descriptor.nextInChain = (WGPUChainedStruct*)&wgsl_desc;
482
483 effect.cs_module = wgpuDeviceCreateShaderModule(device, &descriptor);
484 // effect.cs_module = wgpuDeviceCreateErrorShaderModule(device, &descriptor, char const * errorMessage);
485
486 std::vector<Cogs::WebGPUConstantBufferBinding> bindings = extractConstantBindingLayout(cs_source, Cogs::WebGPUConstantBufferUsage::ShaderStage_Compute);
487 addConstantBufferBindings(effect, bindings);
488
489#ifndef EMSCRIPTEN
490 wgpuShaderModuleGetCompilationInfo(effect.cs_module, compile_callback, static_cast<void*>(&cs_source));
491#endif
492 // wgpuShaderModuleSetLabel(effect.cs_module, effect_desc.name.data());
493
494 return this->effects.addResource(std::move(effect));
495 }
496
497 EffectHandle EffectsWebGPU::load(const ProcessedContent& vsSource,
498 const ProcessedContent& hsSource,
499 const ProcessedContent& dsSource,
500 const ProcessedContent& gsSource,
501 const ProcessedContent& fsSource,
502 const StringView& vsEntryPoint,
503 const StringView& /*hsEntryPoint*/,
504 const StringView& /*dsEntryPoint*/,
505 const StringView& /*gsEntryPoint*/,
506 const StringView& fsEntryPoint,
507 const EffectDescription& effect_desc)
508 {
509 EffectFlags::EEffectFlags effectFlags = effect_desc.flags;
510 bool useSortedDefines = false;
511 PreprocessorDefinitions unique_definitions = effect_desc.definitions;
512 std::sort(unique_definitions.begin(), unique_definitions.end());
513 unique_definitions.erase(std::unique(unique_definitions.begin(), unique_definitions.end()), unique_definitions.end());
514 if (unique_definitions.size() != effect_desc.definitions.size()) {
515 useSortedDefines = true;
516 LOG_WARNING(logger, "Redefinition of precompiler defines not supported in WebGPU backend");
517 }
518
519 // WebGPU does only support vertex and pixel shaders
520 assert(!hsSource.origin.size());
521 assert(!dsSource.origin.size());
522 assert(!gsSource.origin.size());
523
524 WGPUDevice device = graphicsDevice->device;
525
526 EffectWebGPU effect = {};
527 effect.vs_entry = std::string(vsEntryPoint);
528 effect.fs_entry = std::string(fsEntryPoint);
529 effect.name = std::string(effect_desc.name);
531 LOG_INFO(logger, "Compiling WebGPU Effect: %s", effect.name.c_str());
532 }
533 std::string definitions_source;
534 for (auto& def : useSortedDefines ? unique_definitions : effect_desc.definitions) {
535 definitions_source += "const " + def.first + " = " + def.second + ";\n";
536 }
537 std::string vs_source = definitions_source + vsSource.content;
538 {
540 LOG_INFO(logger, "Compiling WebGPU VS:\n%s", vs_source.c_str());
541 }
542
543 WGPUShaderModuleDescriptor descriptor = {};
544 descriptor.label = effect_desc.name.data();
545 WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
546 wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
547 wgsl_desc.code = vs_source.c_str();
548 descriptor.nextInChain = (WGPUChainedStruct*)&wgsl_desc;
549
550 effect.vs_module = wgpuDeviceCreateShaderModule(device, &descriptor);
551 // effect.vs_module = wgpuDeviceCreateErrorShaderModule(device, &descriptor, char const * errorMessage);
552
553#ifndef EMSCRIPTEN
554 wgpuShaderModuleGetCompilationInfo(effect.vs_module, compile_callback, static_cast<void*>(& vs_source));
555#endif
556 // wgpuShaderModuleSetLabel(effect.vs_module, effect_desc.name.data());
557 std::vector<Cogs::WebGPUConstantBufferBinding> bindings = extractConstantBindingLayout(vs_source, Cogs::WebGPUConstantBufferUsage::ShaderStage_Vertex);
558 addConstantBufferBindings(effect, bindings);
559 {
560 effect.num_attribs = extractVertexAttribLocation(vs_source, effect.semanticSlotBindings, effect.maxVertexAttribs);
561 }
562 }
563 // TODO only create one module if they are equal?
564 if (fsSource.content.size()) {
565 std::string fs_source;
566 fs_source = definitions_source + fsSource.content;
567
569 LOG_INFO(logger, "Compiling WebGPU FS:\n%s", fs_source.c_str());
570 }
571
572 WGPUShaderModuleDescriptor descriptor = {};
573 descriptor.label = effect_desc.name.data();
574 WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
575 wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
576 wgsl_desc.code = fs_source.c_str();
577 descriptor.nextInChain = (WGPUChainedStruct*)&wgsl_desc;
578
579 effect.fs_module = wgpuDeviceCreateShaderModule(device, &descriptor);
580 // effect.fs_module = wgpuDeviceCreateErrorShaderModule(device, &descriptor, char const * errorMessage);
581
582#ifndef EMSCRIPTEN
583 wgpuShaderModuleGetCompilationInfo(effect.fs_module, compile_callback, static_cast<void*>(& fs_source));
584#endif
585 // wgpuShaderModuleSetLabel(effect.fs_module, effect_desc.name.data());
586 std::vector<Cogs::WebGPUConstantBufferBinding> bindings = extractConstantBindingLayout(fs_source, Cogs::WebGPUConstantBufferUsage::ShaderStage_Fragment);
587 addConstantBufferBindings(effect, bindings);
588 }
589 return this->effects.addResource(std::move(effect));
590 }
592 {
593 auto nameHash = Cogs::hash(name);
594 const EffectWebGPU& effect = effects[effectHandle];
595 {
596 for (size_t i = 0; i < effect.num_bindings; i++) { // Does not report error if maxConstantBuffers is exceeded
597 if (effect.constantBufferBindings[i].nameHash == nameHash) {
598 return (ConstantBufferBindingHandle)effect.constantBufferBindings[i].bg_ent.binding;
599 }
600 }
601 }
603 }
604
606 {
607 ConstantBufferBindingHandle constantBuffer = getConstantBufferBinding(effectHandle, name);
608 if (HandleIsValid(constantBuffer)) {
609 return BufferBindingHandle(constantBuffer.handle);
610 }
612 }
613
614 bool EffectsWebGPU::addConstantBufferBindings(EffectWebGPU& effect, const std::vector<Cogs::WebGPUConstantBufferBinding>& bindings) {
615 for (auto binding : bindings) {
616 size_t slot;
617 bool exists = false;
618 for (slot = 0; slot < effect.num_bindings; slot++) { // Does not report error if maxConstantBuffers is exceeded
619 auto& curr = effect.constantBufferBindings[slot];
620 if (curr.nameHash == binding.nameHash) {
621 if (curr.group == binding.group && curr.bg_ent.binding == binding.bg_ent.binding) {
622 curr.bg_ent.visibility = (WebGPUConstantBufferUsage)(curr.bg_ent.visibility | binding.bg_ent.visibility);
623 exists = true;
624 }
625 else {
626 LOG_ERROR(logger, "Inconsistent binding location");
627 }
628 }
629 }
630 if (exists) {
631 continue;
632 }
633 if (effect.num_bindings < EffectWebGPU::maxConstantBuffers) {
634 effect.constantBufferBindings[effect.num_bindings++] = binding;
635 }
636 else {
637 LOG_ERROR(logger, "Number of bindings exceed EffectWebGPU::maxConstantBuffers=%zu", EffectWebGPU::maxConstantBuffers);
638 return false;
639 }
640 }
641 return true;
642 }
643
644
645 const WebGPUConstantBufferBinding* EffectsWebGPU::getConstantBufferBindings(EffectHandle effectHandle, size_t& num_bindings) {
646 const EffectWebGPU& effect = effects[effectHandle];
647 num_bindings = effect.num_bindings;
648 return effect.constantBufferBindings;
649 }
650
651 TextureBindingHandle EffectsWebGPU::getTextureBinding(EffectHandle effectHandle, const StringView& name, const unsigned int /*slot*/) {
652 std::string constantBufferName = std::string(name);
653 ConstantBufferBindingHandle constantBuffer = getConstantBufferBinding(effectHandle, StringView(constantBufferName));
654 if (HandleIsValid(constantBuffer)) {
655 return TextureBindingHandle(constantBuffer.handle);
656 }
658 }
659
661 std::string constantBufferName = std::string(name);
662 if (!constantBufferName.ends_with("Sampler")) {
663 constantBufferName += "Sampler";
664 }
665
666 ConstantBufferBindingHandle constantBuffer = getConstantBufferBinding(effectHandle, StringView(constantBufferName));
667 if (HandleIsValid(constantBuffer)) {
668 return SamplerStateBindingHandle(constantBuffer.handle);
669 }
670
671 if (constantBufferName.ends_with("TextureSampler")) { // Try again with shortened name
672 constantBufferName.erase(constantBufferName.length() - strlen("TextureSampler"));
673 StringView shortenedName(constantBufferName);
674 return getSamplerStateBinding(effectHandle, shortenedName, slot);
675 }
676
678 }
679}
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...
Log implementation class.
Definition: LogManager.h:139
void log(const Category category, uint32_t errorNumber, _Printf_format_string_ const char *fmt,...) const VALIDATE_ARGS(4)
Log a formatted message.
Definition: LogManager.h:156
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
Definition: StringView.h:171
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
@ Unspecified
The default error number for legacy logger usage.
Definition: LogManager.h:49
Category
Logging categories used to filter log messages.
Definition: LogManager.h:31
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
std::vector< PreprocessorDefinition > PreprocessorDefinitions
A set of preprocessor definitions.
Definition: IEffects.h:13
Contains an effect description used to load a single effect.
Definition: IEffects.h:55
EffectFlags::EEffectFlags flags
Effect loading flags.
Definition: IEffects.h:96
PreprocessorDefinitions definitions
Definitions.
Definition: IEffects.h:93
StringView name
Name of the effect. Used for tracking purposes, like naming shader dumps.
Definition: IEffects.h:90
EEffectFlags
Effect source flags.
Definition: IEffects.h:20
@ LogShaderSource
Log the contents of the shader on error.
Definition: IEffects.h:34
static const Handle_t NoHandle
Represents a handle to nothing.
Definition: Common.h:77
handle_type handle
Internal resource handle.
Definition: Common.h:74