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 sta = WGPUStorageTextureAccess_ReadOnly;
121 }
122 else if (starts_with(type_it, end, "write", type_it)) {
123 sta = WGPUStorageTextureAccess_WriteOnly;
124 }
125 else if (starts_with(type_it, end, "readwrite", type_it)) {
126 sta = WGPUStorageTextureAccess_ReadWrite;
127 }
128 return sta;
129 }
130
131 std::vector<Cogs::WebGPUConstantBufferBinding> extractConstantBindingLayout(std::string shaderSource, WGPUShaderStage usage) {
132 std::vector<Cogs::WebGPUConstantBufferBinding> result;
133 std::istringstream iss(shaderSource);
134 std::string expr = R"(^\s*@group\‍(([0-9]+)\) @binding\‍(([0-9]+)\)\s+var(<uniform>)?\s+([^\s]+)[\s]*:\s?(.*)\s?;)";
135 std::regex regex_expression(expr);
136
137 for (std::string line; std::getline(iss, line); )
138 {
139 std::smatch match;
140 if (std::regex_search(line, match, regex_expression))
141 {
142 size_t group = std::stoi(match.str(1));
143 unsigned int loc = std::stoi(match.str(2));
144 bool isUniform = match.str(3) == "<uniform>";
145 std::string name = match.str(4);
146 std::string type = match.str(5);
147
148 std::string::const_iterator type_it = type.begin();
149 size_t nameHash = Cogs::hash(name);
150 // Cogs::ConstantBufferBindingHandle location = (Cogs::ConstantBufferBindingHandle)loc;
151 // Cogs::WebGPUConstantBufferType bufferType = Cogs::WebGPUConstantBufferType::UniformBuffer;
152 bool alreadyInserted = false;
153 for (auto& e : result) {
154 if (e.group == group && e.bg_ent.binding == loc) {
155 e.bg_ent.visibility |= usage;
156 alreadyInserted = true;
157 break;
158 }
159 }
160 if (alreadyInserted) {
161 continue;
162 }
163 WGPUBindGroupLayoutEntry bg_ent = {};
164 bg_ent.binding = static_cast<uint32_t>(loc);
165 bg_ent.visibility = (WGPUShaderStage)usage;
166
167 if (isUniform) {
168 bg_ent.buffer.type = WGPUBufferBindingType_Uniform;
169 // bufferType = Cogs::WebGPUConstantBufferType::UniformBuffer;
170 }
171 else if (starts_with(type_it, type.end(), "texture_storage_", type_it)) { // type.starts_with("texture_")
172 bg_ent.storageTexture.viewDimension = extractViewDimention(type_it, type.end());
173 eat_white(type_it, type.end());
174 if (!starts_with(type_it, type.end(), "<", type_it)) {
175 LOG_DEBUG(logger, "Texture type %s not yet supported", type.c_str());
176 }
177 bg_ent.storageTexture.format = extractTextureFormat(type_it, type.end());
178 eat_white(type_it, type.end());
179 if (!starts_with(type_it, type.end(), ",", type_it)) {
180 LOG_DEBUG(logger, "Texture type %s not yet supported", type.c_str());
181 }
182 eat_white(type_it, type.end());
183 bg_ent.storageTexture.access = extractStorageTextureAccess(type_it, type.end());
184 eat_white(type_it, type.end());
185 if (!starts_with(type_it, type.end(), ">", type_it)) {
186 LOG_DEBUG(logger, "Texture type %s not yet supported", type.c_str());
187 }
188 }
189 else if (starts_with(type_it, type.end(), "texture_", type_it)) {
190 bool isDepth = false;
191 // bufferType = Cogs::WebGPUConstantBufferType::Texture;
192 bg_ent.texture.sampleType = WGPUTextureSampleType_Float;
193 bg_ent.texture.viewDimension = WGPUTextureViewDimension_2D;
194 bg_ent.texture.multisampled = 0;
195 if (starts_with(type_it, type.end(), "depth_", type_it)) {
196 isDepth = true;
197 }
198 bg_ent.texture.viewDimension = extractViewDimention(type_it, type.end());
199 eat_white(type_it, type.end());
200 if (isDepth) {
201 bg_ent.texture.sampleType = WGPUTextureSampleType_Depth;
202 } else if (starts_with(type_it, type.end(), "<f32>", type_it)) {
203 bg_ent.texture.sampleType = WGPUTextureSampleType_Float;
204 }
205 else if (starts_with(type_it, type.end(), "<i32>", type_it)) {
206 bg_ent.texture.sampleType = WGPUTextureSampleType_Sint;
207 }
208 else if (starts_with(type_it, type.end(), "<u32>", type_it)) {
209 bg_ent.texture.sampleType = WGPUTextureSampleType_Uint;
210 }
211 else {
212 LOG_DEBUG(logger, "Texture type %s not yet supported", type.c_str());
213 }
214 }
215 else if (type == "sampler") {
216 // bufferType = Cogs::WebGPUConstantBufferType::Sampler;
217 bg_ent.sampler.type = WGPUSamplerBindingType_Filtering;
218 }
219 else if (type == "sampler_comparison") {
220 // bufferType = Cogs::WebGPUConstantBufferType::Sampler;
221 bg_ent.sampler.type = WGPUSamplerBindingType_Comparison;
222 }
223 else {
224 LOG_DEBUG(logger, "Unknown uniform type%s", type.c_str());
225 }
226
227 Cogs::WebGPUConstantBufferBinding binding{.group = group, .nameHash = nameHash, .bg_ent = bg_ent};
228 result.push_back(binding);
229 }
230 }
231 return result;
232 }
233
234 const char* semanticNames[]
235 {
236 "a_POSITION",
237 "a_NORMAL",
238 "a_COLOR",
239 "a_TEXCOORD",
240 "a_TANGENT",
241 "a_INSTANCEVECTOR",
242 "a_INSTANCEMATRIX",
243 };
244
245 WGPUVertexFormat stringToVertexFormat(const Cogs::StringView &s) {
246 WGPUVertexFormat format = static_cast<WGPUVertexFormat>(0);
247 switch (Cogs::hash(s))
248 {
249 case Cogs::hash("f32"): format = WGPUVertexFormat::WGPUVertexFormat_Float32; break;
250 case Cogs::hash("vec2f"): format = WGPUVertexFormat::WGPUVertexFormat_Float32x2; break;
251 case Cogs::hash("vec3f"): format = WGPUVertexFormat::WGPUVertexFormat_Float32x3; break;
252 case Cogs::hash("vec4f"): format = WGPUVertexFormat::WGPUVertexFormat_Float32x4; break;
253 case Cogs::hash("i32"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32; break;
254 case Cogs::hash("vec2i"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32x2; break;
255 case Cogs::hash("vec3i"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32x3; break;
256 case Cogs::hash("vec4i"): format = WGPUVertexFormat::WGPUVertexFormat_Sint32x4; break;
257 case Cogs::hash("u32"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32; break;
258 case Cogs::hash("vec2u"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32x2; break;
259 case Cogs::hash("vec3u"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32x3; break;
260 case Cogs::hash("vec4u"): format = WGPUVertexFormat::WGPUVertexFormat_Uint32x4; break;
261 default:
262 LOG_ERROR(logger, "Unsupported vertex attribute type %s", std::string(s).c_str());
263 break;
264 }
265 return format;
266 }
267
268 // size_t extractVertexAttribLocation_old(std::string shaderSource, Cogs::SemanticSlotBinding bindings[], size_t /*maxBindings*/) {
269 // std::istringstream iss(shaderSource);
270 // std::string expr = R"(\s*const ([^\s^0-9]+)([0-9]+)_LOC\s*=\s*([0-9]+))";
271 // std::regex regex_expression(expr);
272 // size_t nAttribs = 0;
273 // for (std::string line; std::getline(iss, line); )
274 // {
275 // std::smatch match;
276 // if (std::regex_search(line, match, regex_expression))
277 // {
278 // std::string semanticName = match.str(1);
279 // uint8_t slot = static_cast<uint8_t>(std::stoi(match.str(2)));
280 // uint8_t loc = static_cast<uint8_t>(std::stoi(match.str(3)));
281 // for (uint8_t i = 0; i < std::size(semanticNames); i++) {
282 // if (strcmp(semanticName.c_str(), semanticNames[i]) == 0) {
283 // Cogs::SemanticSlotBinding& b = bindings[nAttribs++];
284 // b.semantic = i;
285 // b.slot = slot;
286 // b.binding = loc;
287 // break;
288 // }
289 // }
290 // }
291 // }
292 // return nAttribs;
293 // }
294
295 size_t extractVertexAttribLocation(std::string shaderSource, Cogs::SemanticSlotBinding bindings[], size_t /*maxBindings*/) {
296 std::istringstream iss(shaderSource);
297 std::string expr = R"(\s*const ([^\s^0-9]+)([0-9]+)_LOC\s*=\s*([0-9]+))";
298 std::regex regex_expression(expr);
299 size_t nAttribs = 0;
300 for (std::string line; std::getline(iss, line); )
301 {
302 std::smatch match;
303 if (std::regex_search(line, match, regex_expression))
304 {
305 std::string semanticName = match.str(1);
306 uint8_t slot = static_cast<uint8_t>(std::stoi(match.str(2)));
307 uint8_t loc = static_cast<uint8_t>(std::stoi(match.str(3)));
308 for (uint8_t i = 0; i < std::size(semanticNames); i++) {
309 if (strcmp(semanticName.c_str(), semanticNames[i]) == 0) {
310 Cogs::SemanticSlotBinding& b = bindings[nAttribs++];
311 b.semantic = i;
312 b.slot = slot;
313 b.binding = loc;
314 break;
315 }
316 }
317 }
318 }
319
320 iss.clear();
321 iss.seekg(0, std::ios::beg);
322 std::string expr_location = R"(@location\‍(\s*([^\s\)\d]+)([\d]+)_LOC\s*\)\s*([^\s:]+)\s*:\s*([\da-zA-Z]+)[,\s\n])";
323 std::regex regex_location(expr_location);
324 for (std::string line; std::getline(iss, line); )
325 {
326 std::smatch match;
327 if (std::regex_search(line, match, regex_location))
328 {
329 std::string semanticName = match.str(1);
330 uint8_t slot = static_cast<uint8_t>(std::stoi(match.str(2)));
331 std::string name = match.str(3);
332 std::string type = match.str(4);
333 uint8_t semantic = std::numeric_limits<uint8_t>::max();
334 for (uint8_t i = 0; i < std::size(semanticNames); i++) {
335 if (strcmp(semanticName.c_str(), semanticNames[i]) == 0) {
336 semantic = i;
337 break;
338 }
339 }
340 if (semantic == std::numeric_limits<uint8_t>::max()) {
341 LOG_DEBUG(logger, "unknown semantic name for vertex attribute %s", semanticName.c_str());
342 continue;
343 }
344 for (size_t i = 0; i < nAttribs; i++) {
345 if (bindings[i].semantic == semantic && bindings[i].slot == slot) {
346 bindings[i].format = stringToVertexFormat(Cogs::StringView(type));
347 bindings[i].nameHash = Cogs::hash(name);
348 break;
349 }
350 }
351 }
352 }
353 return nAttribs;
354 }
355
356 Cogs::ConstantBufferBindingHandle encodeConstantBufferBindingHandle(Cogs::WebGPUConstantBufferBinding binding) {
357 size_t i = (binding.group << 16) + (binding.bg_ent.binding + 1 );
359 }
360
361 void dumpSource(const std::string& source, int firstLine = 1, int lastLine = std::numeric_limits<int>::max())
362 {
363 const char* start = source.data();
364 const char* curr = start;
365 int line = 1;
366
367 while ((*curr != '\0') && (line <= lastLine)) {
368 const char* p = curr;
369 while ((*p != '\0') && (*p != '\n') && (*p != '\r')) { p++; }
370
371 if (firstLine <= line) {
372 LOG_DEBUG(logger, "%3d: %s", line, std::string(curr, p - curr).c_str());
373 }
374
375 line = line + 1;
376
377 curr = p;
378 if (*curr != '\0') { curr++; };
379 if ((*curr != '\0') && (*curr != *p) && ((*curr == '\n') || (*curr == '\r'))) { curr++; }
380 }
381 }
382
383 void printShaderError(struct WGPUCompilationInfo const* compilationInfo, const std::string* source = nullptr)
384 {
385 for (size_t i = 0; i < compilationInfo->messageCount; i++) {
386 const WGPUCompilationMessage& message = compilationInfo->messages[i];
387 if (source) {
388 dumpSource(*source, ((int)message.lineNum) - 3, ((int)message.lineNum));
389 }
391 std::string category_str;
392 switch (message.type) {
393 case WGPUCompilationMessageType::WGPUCompilationMessageType_Info:
394 category = Cogs::Logging::Category::Info;
395 category_str = "Info";
396 break;
397 case WGPUCompilationMessageType::WGPUCompilationMessageType_Error:
398 category = Cogs::Logging::Category::Error;
399 category_str = "Error";
400 break;
401 case WGPUCompilationMessageType::WGPUCompilationMessageType_Force32:
402 category = Cogs::Logging::Category::Error;
403 category_str = "Force32";
404 break;
405 default:
406 category = Cogs::Logging::Category::Error;
407 category_str = "Unknown";
408 }
409
410 logger.log(category, Cogs::Logging::ErrorGroup::Unspecified, "WGSL %s (%" PRIu64 ", %" PRIu64 "): %.*s", category_str.c_str(), message.lineNum, message.linePos, WGPUStringViewFormat(message.message));
411 }
412 }
413
414 void compile_callback(WGPUCompilationInfoRequestStatus status, struct WGPUCompilationInfo const* compilationInfo, void* userdata1, void* /* userdata2 */) // Does not seem to be called if the shader does not compile
415 {
416 const std::string* source = reinterpret_cast<std::string*>(userdata1);
417 if (status != WGPUCompilationInfoRequestStatus_Success || (compilationInfo != nullptr && compilationInfo->messageCount != 0))
418 {
419 printShaderError(compilationInfo, source);
420 }
421 }
422}
423
424namespace Cogs{
425 void EffectsWebGPU::initialize(GraphicsDeviceWebGPU *device_in, IBuffers * buffers_in)
426 {
427 EffectsCommon::initialize(buffers_in);
428 graphicsDevice = device_in;
429 }
430
432 {
433 }
434
436 {
437 if (!HandleIsValid(handle)) return;
438 EffectWebGPU &effect = this->effects[handle];
439 wgpuShaderModuleRelease(effect.vs_module);
440 wgpuShaderModuleRelease(effect.fs_module);
441 this->effects.removeResource(handle);
442 }
443
445 {
446 PreprocessorDefinitions defines = {};
447 return loadComputeEffect(fileName, defines, effectFlags);
448 }
449
451 {
452 WGPUDevice device = graphicsDevice->device;
453
454 ProcessedContent csSource;
455 Utilities::readFile(handler, fileName, csSource);
456
457 EffectWebGPU effect = {};
458 effect.cs_entry = "main";
459 effect.name = std::string(fileName);
460
461 std::string cs_source;
462 for(auto &def : definitions){
463 cs_source += "const " + def.first + " = " + def.second + ";\n";
464 }
465 cs_source += csSource.content;
466
468 LOG_INFO(logger, "Compiling WebGPU CS:\n%s", cs_source.c_str());
469 }
470
471 WGPUShaderModuleDescriptor descriptor = {};
472 descriptor.label = {fileName.data(), WGPU_STRLEN};
473 WGPUShaderSourceWGSL wgsl_desc = {};
474 wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
475 wgsl_desc.code = {cs_source.c_str(), WGPU_STRLEN};
476 descriptor.nextInChain = (WGPUChainedStruct*)&wgsl_desc;
477
478 effect.cs_module = wgpuDeviceCreateShaderModule(device, &descriptor);
479 // effect.cs_module = wgpuDeviceCreateErrorShaderModule(device, &descriptor, char const * errorMessage);
480
481 std::vector<Cogs::WebGPUConstantBufferBinding> bindings = extractConstantBindingLayout(cs_source, WGPUShaderStage_Compute);
482 addConstantBufferBindings(effect, bindings);
483
484 WGPUCompilationInfoCallbackInfo callbackInfo = {};
485 callbackInfo.nextInChain = nullptr;
486 callbackInfo.callback = compile_callback;
487 callbackInfo.mode = WGPUCallbackMode_AllowProcessEvents;
488 callbackInfo.userdata1 = static_cast<void*>(&cs_source);
489
490 wgpuShaderModuleGetCompilationInfo(effect.cs_module, callbackInfo);
491
492 return this->effects.addResource(std::move(effect));
493 }
494
495 EffectHandle EffectsWebGPU::load(const ProcessedContent& vsSource,
496 const ProcessedContent& hsSource,
497 const ProcessedContent& dsSource,
498 const ProcessedContent& gsSource,
499 const ProcessedContent& fsSource,
500 const StringView& vsEntryPoint,
501 const StringView& /*hsEntryPoint*/,
502 const StringView& /*dsEntryPoint*/,
503 const StringView& /*gsEntryPoint*/,
504 const StringView& fsEntryPoint,
505 const EffectDescription& effect_desc)
506 {
507 EffectFlags::EEffectFlags effectFlags = effect_desc.flags;
508 bool useSortedDefines = false;
509 PreprocessorDefinitions unique_definitions = effect_desc.definitions;
510 std::sort(unique_definitions.begin(), unique_definitions.end());
511 unique_definitions.erase(std::unique(unique_definitions.begin(), unique_definitions.end()), unique_definitions.end());
512 if (unique_definitions.size() != effect_desc.definitions.size()) {
513 useSortedDefines = true;
514 LOG_WARNING(logger, "Redefinition of precompiler defines not supported in WebGPU backend");
515 }
516
517 // WebGPU does only support vertex and pixel shaders
518 assert(!hsSource.origin.size());
519 assert(!dsSource.origin.size());
520 assert(!gsSource.origin.size());
521
522 WGPUDevice device = graphicsDevice->device;
523
524 EffectWebGPU effect = {};
525 effect.vs_entry = std::string(vsEntryPoint);
526 effect.fs_entry = std::string(fsEntryPoint);
527 effect.name = std::string(effect_desc.name);
529 LOG_INFO(logger, "Compiling WebGPU Effect: %s", effect.name.c_str());
530 }
531 std::string definitions_source;
532 for (auto& def : useSortedDefines ? unique_definitions : effect_desc.definitions) {
533 definitions_source += "const " + def.first + " = " + def.second + ";\n";
534 }
535 std::string vs_source = definitions_source + vsSource.content;
536 {
538 LOG_INFO(logger, "Compiling WebGPU VS:\n%s", vs_source.c_str());
539 }
540
541 WGPUShaderModuleDescriptor descriptor = {};
542 descriptor.label = {effect_desc.name.data(), WGPU_STRLEN};
543 WGPUShaderSourceWGSL wgsl_desc = {};
544 wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
545 wgsl_desc.code = {vs_source.c_str(), WGPU_STRLEN};
546 descriptor.nextInChain = (WGPUChainedStruct*)&wgsl_desc;
547
548 effect.vs_module = wgpuDeviceCreateShaderModule(device, &descriptor);
549 // effect.vs_module = wgpuDeviceCreateErrorShaderModule(device, &descriptor, char const * errorMessage);
550
551 WGPUCompilationInfoCallbackInfo callbackInfo = {};
552 callbackInfo.nextInChain = nullptr;
553 callbackInfo.callback = compile_callback;
554 callbackInfo.mode = WGPUCallbackMode_AllowProcessEvents;
555 callbackInfo.userdata1 = static_cast<void*>(& vs_source);
556 wgpuShaderModuleGetCompilationInfo(effect.vs_module, callbackInfo);
557
558 // wgpuShaderModuleSetLabel(effect.vs_module, effect_desc.name.data());
559 std::vector<Cogs::WebGPUConstantBufferBinding> bindings = extractConstantBindingLayout(vs_source, WGPUShaderStage_Vertex);
560 addConstantBufferBindings(effect, bindings);
561 {
562 effect.num_attribs = extractVertexAttribLocation(vs_source, effect.semanticSlotBindings, effect.maxVertexAttribs);
563 }
564 }
565 // TODO only create one module if they are equal?
566 if (fsSource.content.size()) {
567 std::string fs_source;
568 fs_source = definitions_source + fsSource.content;
569
571 LOG_INFO(logger, "Compiling WebGPU FS:\n%s", fs_source.c_str());
572 }
573
574 WGPUShaderModuleDescriptor descriptor = {};
575 descriptor.label = {effect_desc.name.data(), WGPU_STRLEN};
576 WGPUShaderSourceWGSL wgsl_desc = {};
577 wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
578 wgsl_desc.code = {fs_source.c_str(), WGPU_STRLEN};
579 descriptor.nextInChain = (WGPUChainedStruct*)&wgsl_desc;
580
581 effect.fs_module = wgpuDeviceCreateShaderModule(device, &descriptor);
582 // effect.fs_module = wgpuDeviceCreateErrorShaderModule(device, &descriptor, char const * errorMessage);
583
584 WGPUCompilationInfoCallbackInfo callbackInfo = {};
585 callbackInfo.nextInChain = nullptr;
586 callbackInfo.callback = compile_callback;
587 callbackInfo.mode = WGPUCallbackMode_AllowProcessEvents;
588 callbackInfo.userdata1 = static_cast<void*>(& fs_source);
589 wgpuShaderModuleGetCompilationInfo(effect.fs_module, callbackInfo);
590
591 // wgpuShaderModuleSetLabel(effect.fs_module, effect_desc.name.data());
592 std::vector<Cogs::WebGPUConstantBufferBinding> bindings = extractConstantBindingLayout(fs_source, WGPUShaderStage_Fragment);
593 addConstantBufferBindings(effect, bindings);
594 }
595 return this->effects.addResource(std::move(effect));
596 }
598 {
599 auto nameHash = Cogs::hash(name);
600 const EffectWebGPU& effect = effects[effectHandle];
601 {
602 for (size_t i = 0; i < effect.num_bindings; i++) { // Does not report error if maxConstantBuffers is exceeded
603 if (effect.constantBufferBindings[i].nameHash == nameHash) {
604 return encodeConstantBufferBindingHandle(effect.constantBufferBindings[i]);
605 }
606 }
607 }
609 }
610
612 {
613 ConstantBufferBindingHandle constantBuffer = getConstantBufferBinding(effectHandle, name);
614 if (HandleIsValid(constantBuffer)) {
615 return BufferBindingHandle(constantBuffer.handle);
616 }
618 }
619
620 bool EffectsWebGPU::addConstantBufferBindings(EffectWebGPU& effect, const std::vector<Cogs::WebGPUConstantBufferBinding>& bindings) {
621 for (auto binding : bindings) {
622 size_t slot;
623 bool exists = false;
624 for (slot = 0; slot < effect.num_bindings; slot++) { // Does not report error if maxConstantBuffers is exceeded
625 auto& curr = effect.constantBufferBindings[slot];
626 if (curr.nameHash == binding.nameHash) {
627 if (curr.group == binding.group && curr.bg_ent.binding == binding.bg_ent.binding) {
628 curr.bg_ent.visibility = curr.bg_ent.visibility | binding.bg_ent.visibility;
629 exists = true;
630 }
631 else {
632 LOG_ERROR(logger, "Inconsistent binding location");
633 }
634 }
635 }
636 if (exists) {
637 continue;
638 }
639 if (effect.num_bindings < EffectWebGPU::maxConstantBuffers) {
640 effect.constantBufferBindings[effect.num_bindings++] = binding;
641 }
642 else {
643 LOG_ERROR(logger, "Number of bindings exceed EffectWebGPU::maxConstantBuffers=%zu", EffectWebGPU::maxConstantBuffers);
644 return false;
645 }
646 }
647 return true;
648 }
649
650
651 const WebGPUConstantBufferBinding* EffectsWebGPU::getConstantBufferBindings(EffectHandle effectHandle, size_t& num_bindings) {
652 const EffectWebGPU& effect = effects[effectHandle];
653 num_bindings = effect.num_bindings;
654 return effect.constantBufferBindings;
655 }
656
657 TextureBindingHandle EffectsWebGPU::getTextureBinding(EffectHandle effectHandle, const StringView& name, const unsigned int /*slot*/) {
658 std::string constantBufferName = std::string(name);
659 ConstantBufferBindingHandle constantBuffer = getConstantBufferBinding(effectHandle, StringView(constantBufferName));
660 if (HandleIsValid(constantBuffer)) {
661 return TextureBindingHandle(constantBuffer.handle);
662 }
664 }
665
667 std::string constantBufferName = std::string(name);
668 if (!constantBufferName.ends_with("Sampler")) {
669 constantBufferName += "Sampler";
670 }
671
672 ConstantBufferBindingHandle constantBuffer = getConstantBufferBinding(effectHandle, StringView(constantBufferName));
673 if (HandleIsValid(constantBuffer)) {
674 return SamplerStateBindingHandle(constantBuffer.handle);
675 }
676
677 if (constantBufferName.ends_with("TextureSampler")) { // Try again with shortened name
678 constantBufferName.erase(constantBufferName.length() - strlen("TextureSampler"));
679 StringView shortenedName(constantBufferName);
680 return getSamplerStateBinding(effectHandle, shortenedName, slot);
681 }
682
684 }
685}
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:140
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:157
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:181
@ 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:78
handle_type handle
Internal resource handle.
Definition: Common.h:75