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