Cogs.Core
ShaderBuilderES3.cpp
1#include "ShaderBuilder.h"
2#include "Context.h"
3#include "Utilities/Strings.h"
4#include "Utilities/Preprocessor.h"
5#include "ResourceStore.h"
6#include "Renderer/EnginePermutations.h"
7
8#include "Rendering/IGraphicsDevice.h"
9
10#include "Foundation/Logging/Logger.h"
11#include "Foundation/StringUtilities.h"
12#include "Foundation/StringView.h"
13
14
15namespace {
16
17 using namespace Cogs::Core;
18 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("ShaderBuilderES3");
19
21 constexpr size_t InitialBufferCapasity = 4096u;
22
23 struct {
25 Cogs::Core::StringRef defineName;
26 } engineTextures[] = {
27 { Strings::add("environmentSky"), Strings::add("ENVIRONMENT_SKY")},
28 { Strings::add("environmentRadiance"), Strings::add("ENVIRONMENT_RADIANCE")},
29 { Strings::add("environmentIrradiance"), Strings::add("ENVIRONMENT_IRRADIANCE")},
30 { Strings::add("ambientIrradiance"), Strings::add("AMBIENT_IRRADIANCE")},
31 { Strings::add("brdfLUT"), Strings::add("BRDF_LUT")},
32 { Strings::add("cascadedShadowMap"), Strings::add("CASCADEDSHADOWMAP")},
33 { Strings::add("cubeShadowMap"), Strings::add("CUBESHADOWMAP")}
34 };
35
36 struct EngineBufferMember
37 {
40 const char* suffix;
41 };
42
43 Cogs::Core::StringRef sceneBufferMembers[] = {
44 Strings::add("projectionMatrix"),
45 Strings::add("viewMatrix"),
46 Strings::add("inverseViewMatrix"),
47 Strings::add("inverseProjectionMatrix"),
48 Strings::add("worldToClipMatrix"),
49 Strings::add("projectionParameters"),
50 Strings::add("clippingPlanes"),
51 Strings::add("blueNoiseOffset"),
52 Strings::add("viewportOrigin"),
53 Strings::add("viewportSize"),
54 Strings::add("viewportSizeRcp"),
55 Strings::add("shadowDepthClamp"),
56 Strings::add("animationTime"),
57 Strings::add("exposure"),
58 Strings::add("sceneFlags"),
59 Strings::add("clientFlags"),
60 Strings::add("environmentRadianceMips"),
61 Strings::add("environmentIrradianceMips")
62 };
63
64 Cogs::Core::StringRef viewGetters[] = {
65 Strings::add("getClipFromViewMatrix"),
66 Strings::add("getClipFromWorldMatrix"),
67 Strings::add("getViewFromWorldMatrix"),
68 Strings::add("getViewFromClipMatrix"),
69 Strings::add("getWorldFromViewMatrix")
70 };
71
72 Cogs::Core::StringRef objectBufferMembers[] = {
73 Strings::add("worldMatrix"),
74 Strings::add("objectId")
75 };
76
77 Cogs::Core::StringRef animationBufferMembers[] = {
78 Strings::add("boneTransforms")
79 };
80
81 Cogs::Core::StringRef lightBufferMembers[] = {
82 Strings::add("lightPositions"),
83 Strings::add("lightDirections"),
84 Strings::add("lightColorIntensity"),
85 Strings::add("lightParameters"),
86 Strings::add("numLights"),
87 Strings::add("eyePosition"),
88 Strings::add("fogColor"),
89 Strings::add("fogDistance"),
90 Strings::add("fogAmount"),
91 Strings::add("fogEnabled"),
92 Strings::add("ambientIntensity"),
93 Strings::add("ambientColor"),
94 Strings::add("environmentBrightness"),
95 Strings::add("skyMultiplier"),
96 Strings::add("seaFlags"),
97 Strings::add("flags")
98 };
99
100 Cogs::Core::StringRef shadowBufferMembers[] = {
101 Strings::add("shadows"),
102 Strings::add("cascadeOffsets")
103 };
104
105 struct EngineBuffer {
106 Cogs::Core::StringRef* members;
107 size_t count;
109 } engineBuffers[] = {
110 { sceneBufferMembers, sizeof(sceneBufferMembers) / sizeof(sceneBufferMembers[0]), Strings::add("SCENEBUFFER") },
111 { viewGetters, sizeof(viewGetters) / sizeof(viewGetters[0]), Strings::add("VIEWGETTERS") },
112 { objectBufferMembers, sizeof(objectBufferMembers) / sizeof(objectBufferMembers[0]), Strings::add("OBJECTBUFFER") },
113 { animationBufferMembers, sizeof(animationBufferMembers) / sizeof(animationBufferMembers[0]), Strings::add("ANIMATIONBUFFER") },
114 { lightBufferMembers, sizeof(lightBufferMembers) / sizeof(lightBufferMembers[0]), Strings::add("LIGHTBUFFER") },
115 { shadowBufferMembers, sizeof(shadowBufferMembers) / sizeof(shadowBufferMembers[0]), Strings::add("SHADOWBUFFER") }
116 };
117
118
119
120 void addInclude(std::string& content, const Cogs::StringView& prefix, const Cogs::StringView& path)
121 {
122 content.append("#include \"");
123 if (prefix.size()) content.append(prefix.to_string_view());
124 content.append(path.to_string_view());
125 content.append("\"\n");
126 }
127
128 void changeSuffix(std::string& dst, const std::string_view& from, const std::string_view& to)
129 {
130 auto pos = dst.find(from);
131 if (pos == std::string::npos) return;
132 dst.replace(pos, to.length(), to);
133 }
134
135 void addEffectDefinesAndStuff(std::string& output,
136 const std::vector<std::pair<std::string, std::string>>& definitions,
137 uint32_t multiViewCount, bool isVertexShader)
138 {
139 output.append("#version 300 es\n");
140 if (multiViewCount) {
141 std::string multiViewCountString = std::to_string(multiViewCount);
142 if (isVertexShader) {
143 output.append("#extension GL_OVR_multiview : require\nlayout(num_views=");
144 output.append(multiViewCountString);
145 output.append(") in;\n");
146 }
147 output.append("#define COGS_MULTIVIEW ");
148 output.append(multiViewCountString);
149 output.append("\n");
150 }
151 output.append("precision highp float;\n"
152 "precision highp int;\n"
153 "precision highp sampler2DArrayShadow;\n"
154 "precision highp samplerCubeShadow;\n"
155 "precision mediump sampler2DArray;\n"
156 "precision highp usampler2D;\n"
157 "layout (std140) uniform;\n");
158 for (const std::pair<std::string,std::string>& define : definitions) {
159 output.append("#define ");
160 output.append(define.first);
161 output.append(" ");
162 output.append(define.second);
163 output.append("\n");
164 }
165 }
166
167 [[nodiscard]] std::string_view attributeVaryingType(const MaterialDataType type)
168 {
169 switch (type) {
170 case MaterialDataType::Float: return "float "; break;
171 case MaterialDataType::Float2: return "vec2 "; break;
172 case MaterialDataType::Float3: return "vec3 "; break;
173 case MaterialDataType::Float4: return "vec4 "; break;
174 case MaterialDataType::Float4x4: return "mat4 "; break;
175 case MaterialDataType::Int: return "int "; break;
176 case MaterialDataType::Int2: return "ivec4 "; break;
177 case MaterialDataType::Int3: return "ivec3 "; break;
178 case MaterialDataType::Int4: return "ivec4 "; break;
179 case MaterialDataType::UInt: return "uint "; break;
180 case MaterialDataType::UInt2: return "uvec4 "; break;
181 case MaterialDataType::UInt3: return "uvec3 "; break;
182 case MaterialDataType::UInt4: return "uvec4 "; break;
183 case MaterialDataType::SV_IsFrontFace: return "bool "; break;
184 case MaterialDataType::VFACE: return "float "; break;
185 case MaterialDataType::Position: return "vec4 "; break;
186 default:
187 LOG_ERROR(logger, "Unsupported attribute type %d", int(type));
188 return "<illegal>";
189 break;
190 }
191 }
192
193 const char* semanticNames[]
194 {
195 "a_POSITION",
196 "a_NORMAL",
197 "a_COLOR",
198 "a_TEXCOORD",
199 "a_TANGENT",
200 "a_INSTANCEVECTOR",
201 "a_INSTANCEMATRIX",
202 };
203 static_assert(sizeof(semanticNames) == sizeof(semanticNames[0]) * (size_t(ShaderInterfaceMemberDefinition::SemanticName::FirstSystemValueSemantic) - 1));
204
205 void addAttributes(std::string& shaderSource, const ShaderInterfaceDefinition& iface)
206 {
207 for (const ShaderInterfaceMemberDefinition& attribute : iface.members) {
208
209 if ((attribute.semantic.name != ShaderInterfaceMemberDefinition::SemanticName::None)
210 && (size_t(attribute.semantic.name) < size_t(ShaderInterfaceMemberDefinition::SemanticName::FirstSystemValueSemantic)))
211 {
212 shaderSource.append("in ");
213 shaderSource.append(attributeVaryingType(attribute.type));
214 shaderSource.append(semanticNames[size_t(attribute.semantic.name) - 1]);
215 shaderSource.append(std::to_string(attribute.semantic.slot));
216 shaderSource.append(";\n");
217 }
218 else {
219 switch (attribute.semantic.name) {
220 case ShaderInterfaceMemberDefinition::SemanticName::SV_VertexID: [[fallthrough]];
221 case ShaderInterfaceMemberDefinition::SemanticName::SV_InstanceID:
222 break;
223 default:
224 LOG_WARNING(logger, "Unexpected semantic name '%.*s'",
225 StringViewFormat(ShaderInterfaceMemberDefinition::semanticNameString(attribute.semantic.name)));
226 }
227 }
228 }
229 shaderSource.append("\n");
230 }
231
232 void addInterfaceStruct(std::string& shaderSource, const std::string_view& name, const ShaderInterfaceDefinition& iface)
233 {
234 shaderSource.append("struct ");
235 shaderSource.append(name);
236 shaderSource.append(" {\n");
237 for (const auto& attribute : iface.members) {
238 shaderSource.append(" ");
239 shaderSource.append(attributeVaryingType(attribute.type));
240 shaderSource.append(attribute.name);
241 shaderSource.append(";\n");
242 }
243 shaderSource.append("};\n\n");
244 }
245
246 void addAttributeImportFunc(std::string& shaderSource, const std::string_view& name, const ShaderInterfaceDefinition& iface)
247 {
248 shaderSource.append(name);
249 shaderSource.append(" importAttributes() {\n");
250 shaderSource.append(" ");
251 shaderSource.append(name);
252 shaderSource.append(" t;\n");
253 for (const auto& attribute : iface.members) {
254
255
256 if ((attribute.semantic.name != ShaderInterfaceMemberDefinition::SemanticName::None)
257 && (size_t(attribute.semantic.name) < size_t(ShaderInterfaceMemberDefinition::SemanticName::FirstSystemValueSemantic)))
258 {
259 shaderSource.append(" t.");
260 shaderSource.append(attribute.name);
261 shaderSource.append(" = ");
262 shaderSource.append(semanticNames[size_t(attribute.semantic.name) - 1]);
263 shaderSource.append(std::to_string(attribute.semantic.slot));
264 shaderSource.append(";\n");
265 }
266 else {
267 switch (attribute.semantic.name) {
268 case ShaderInterfaceMemberDefinition::SemanticName::SV_VertexID:
269 shaderSource.append(" t.");
270 shaderSource.append(attribute.name);
271 shaderSource.append(" = ");
272 shaderSource.append(attributeVaryingType(attribute.type));
273 shaderSource.append("(gl_VertexID);\n");
274 break;
275 case ShaderInterfaceMemberDefinition::SemanticName::SV_InstanceID:
276 shaderSource.append(" t.");
277 shaderSource.append(attribute.name);
278 shaderSource.append(" = ");
279 shaderSource.append(attributeVaryingType(attribute.type));
280 shaderSource.append("(gl_InstanceID);\n");
281 break;
282 default:
283 break;
284 }
285 }
286 }
287 shaderSource.append(" return t;\n}\n\n");
288 }
289
290 void addInOutVariables(std::string& shaderSource, const std::string_view& prefix, const ShaderInterfaceDefinition& iface, bool in)
291 {
292 if (iface.members.empty()) return;
293 for (const auto& out : iface.members) {
294 switch (out.type) {
295 case MaterialDataType::SV_IsFrontFace:
296 case MaterialDataType::VFACE:
297 // skip
298 break;
299 default:
300 if (out.modifiers & ShaderInterfaceMemberDefinition::CentroidModifier) {
301 shaderSource.append("centroid ");
302 }
303 if (out.modifiers & ShaderInterfaceMemberDefinition::NointerpolationModifier) {
304 shaderSource.append("flat ");
305 }
306 shaderSource.append(in ? "in " : "out ");
307 shaderSource.append(attributeVaryingType(out.type));
308 shaderSource.append(prefix);
309 shaderSource.append(out.name);
310 shaderSource.append(";\n");
311 break;
312 }
313 }
314 shaderSource.append("\n");
315 }
316
317 void addInterfaceConstructor(std::string& shaderSource, const std::string_view& name, const ShaderInterfaceDefinition& iface)
318 {
319 shaderSource.append(name);
320 shaderSource.append(" create");
321 shaderSource.append(name);
322 shaderSource.append("() {\n");
323 shaderSource.append(" ");
324 shaderSource.append(name);
325 shaderSource.append(" t;\n");
326 for (const auto& attribute : iface.members) {
327 const char* initializer = nullptr;
328 switch (attribute.type) {
329 case MaterialDataType::Float: initializer = "0.0;\n"; break;
330 case MaterialDataType::Float2: initializer = "vec2(0);\n"; break;
331 case MaterialDataType::Float3: initializer = "vec3(0);\n"; break;
332 case MaterialDataType::Float4: initializer = "vec4(0);\n"; break;
333 case MaterialDataType::Float4x4: initializer = "mat4(0);\n"; break;
334 default:
335 break;
336 }
337 if (initializer) {
338 shaderSource.append(" t.");
339 shaderSource.append(attribute.name);
340 shaderSource.append(" = ");
341 shaderSource.append(initializer);
342 }
343 }
344 shaderSource.append(" return t;\n}\n\n");
345 }
346
347 void addExportOutFunc(std::string& shaderSource, const std::string_view& name, const ShaderInterfaceDefinition& iface)
348 {
349 shaderSource.append("void exportOut(");
350 shaderSource.append(name);
351 shaderSource.append(" vertexOut) {\n");
352 for (const auto& attribute : iface.members) {
353 switch (attribute.type) {
354 case MaterialDataType::SV_IsFrontFace:
355 case MaterialDataType::VFACE:
356 // skip
357 break;
358 default:
359 shaderSource.append(" vsfs_");
360 shaderSource.append(attribute.name);
361 shaderSource.append(" = vertexOut.");
362 shaderSource.append(attribute.name);
363 shaderSource.append(";\n");
364 break;
365 }
366 }
367 shaderSource.append("}\n");
368 }
369
370 void addImportInFunc(std::string& shaderSource, const std::string_view& name, const ShaderInterfaceDefinition& iface)
371 {
372 shaderSource.append(name);
373 shaderSource.append(" importIn() {\n ");
374 shaderSource.append(name);
375 shaderSource.append(" t;\n");
376 for (const auto& attribute : iface.members) {
377 switch (attribute.type) {
378 case MaterialDataType::SV_IsFrontFace: // type bool
379 shaderSource.append(" t.");
380 shaderSource.append(attribute.name);
381 shaderSource.append(" = gl_FrontFacing;\n");
382 break;
383 case MaterialDataType::VFACE: // type float
384 shaderSource.append(" t.");
385 shaderSource.append(attribute.name);
386 shaderSource.append(" = gl_FrontFacing ? 1.0 : -1.0;\n");
387 break;
388 case MaterialDataType::Position: // type vec4
389 shaderSource.append(" t.");
390 shaderSource.append(attribute.name);
391 shaderSource.append(" = gl_FragCoord;\n");
392 break;
393 default:
394 shaderSource.append(" t.");
395 shaderSource.append(attribute.name);
396 shaderSource.append(" = vsfs_");
397 shaderSource.append(attribute.name);
398 shaderSource.append(";\n");
399 break;
400 }
401 }
402 shaderSource.append(" return t;\n}\n");
403 }
404
405 void addTransferFunc(std::string& shaderSource, const ShaderDefinition& sourceDefinition, const ShaderDefinition& destinationDefinition)
406 {
407 shaderSource.append("void transferAttributes(in VertexIn vertexIn, inout VertexOut vertexOut) {\n");
408 for (const ShaderInterfaceMemberDefinition& member : sourceDefinition.shaderInterface.members) {
409 if (member.semantic.name == ShaderInterfaceMemberDefinition::SemanticName::SV_VertexID) continue;
410 for (const ShaderInterfaceMemberDefinition& dMember : destinationDefinition.shaderInterface.members) {
411 if (dMember.name == member.name) {
412 shaderSource.append(" vertexOut.");
413 shaderSource.append(member.name);
414 shaderSource.append(" = ");
415 if (dMember.type == MaterialDataType::Float3 && member.type == MaterialDataType::Float2 && member.semantic.name == ShaderInterfaceMemberDefinition::SemanticName::Normal) {
416 shaderSource.append("octDecode(vertexIn.");
417 shaderSource.append(member.name);
418 shaderSource.append("); \n");
419 break;
420 }
421 if (dMember.type == MaterialDataType::Float4 && member.type == MaterialDataType::Float3) {
422 shaderSource.append("vec4(vertexIn.");
423 shaderSource.append(member.name);
424 shaderSource.append(", 1.0);\n");
425 break;
426 }
427 if (dMember.type == MaterialDataType::Float4 && member.type == MaterialDataType::Float2) {
428 shaderSource.append("vec4(vertexIn.");
429 shaderSource.append(member.name);
430 shaderSource.append(", 0.0, 1.0);\n");
431 break;
432 }
433 bool close = false;
434 if (dMember.type != member.type) {
435 switch (dMember.type) {
436 case MaterialDataType::Float: shaderSource.append("float("); close = true; break;
437 case MaterialDataType::Float2: shaderSource.append("vec2("); close = true; break;
438 case MaterialDataType::Float3: shaderSource.append("vec3("); close = true; break;
439 case MaterialDataType::Float4: shaderSource.append("vec4("); close = true; break;
440 case MaterialDataType::Float4x4: shaderSource.append("mat4("); close = true; break;
441 case MaterialDataType::Int: shaderSource.append("int("); close = true; break;
442 case MaterialDataType::Int2: shaderSource.append("ivec2("); close = true; break;
443 case MaterialDataType::Int3: shaderSource.append("ivec3("); close = true; break;
444 case MaterialDataType::Int4: shaderSource.append("ivec4("); close = true; break;
445 case MaterialDataType::UInt: shaderSource.append("uint("); close = true; break;
446 case MaterialDataType::UInt2: shaderSource.append("uvec2("); close = true; break;
447 case MaterialDataType::UInt3: shaderSource.append("uvec3("); close = true; break;
448 case MaterialDataType::UInt4: shaderSource.append("uvec4("); close = true; break;
449 default:
450 break;
451 }
452 }
453 shaderSource.append("vertexIn.");
454 shaderSource.append(member.name);
455 if (close) shaderSource.append(1, ')');
456 shaderSource.append(";\n");
457 break;
458 }
459 }
460 }
461 shaderSource.append("}\n");
462 }
463
464
465 void addEngineUniformBuffer(std::string& shaderSource, const std::unordered_set<Cogs::Core::StringRef>& identifiersSeen)
466 {
467 for (const EngineBuffer& engineBuffer : engineBuffers) {
468
469 // Check if any member of buffer has been referenced
470 bool referenced = false;
471 for (size_t i = 0; i < engineBuffer.count; i++) {
472 if (identifiersSeen.contains(engineBuffer.members[i])) {
473 referenced = true;
474 break;
475 }
476 }
477
478 // WebGL requires defined, yet unused, uniform blocks to have bound uniform backing.
479 // Hence we omit defining these unless they are in use.
480 if (referenced) {
481 shaderSource.append("#define COGS_");
482 shaderSource.append(Strings::get(engineBuffer.name).to_string_view());
483 shaderSource.append("_REFERENCED 1\n");
484 }
485 }
486 }
487
488 void addUniformBuffer(std::string& shaderSource,
489 const std::unordered_set<Cogs::Core::StringRef>& identifiersSeen,
490 const ConstantBufferDefinition& definition)
491 {
492 if (definition.values.empty()) return;
493
494 // Check if any member of buffer has been referenced
495 bool isInUse = false;
496 for (const ConstantBufferVariableDefinition& member : definition.values) {
497 if (identifiersSeen.contains(Strings::add(member.name))) {
498 isInUse = true;
499 break;
500 }
501 }
502 if (!isInUse) return;
503
504 // At least one member is in use, include
505 shaderSource.append("uniform ");
506 shaderSource.append(definition.name);
507 shaderSource.append(" {\n");
508 for (const ConstantBufferVariableDefinition& member : definition.values) {
509 shaderSource.append(2, ' ');
510 switch (member.type) {
511 case MaterialDataType::Float: shaderSource.append("float"); break;
512 case MaterialDataType::Float2: shaderSource.append("vec2"); break;
513 case MaterialDataType::Float3: shaderSource.append("vec3"); break;
514 case MaterialDataType::Float4: shaderSource.append("vec4"); break;
515 case MaterialDataType::Float4x4: shaderSource.append("mat4"); break;
516 case MaterialDataType::Float4Array: shaderSource.append("vec4"); break;
517 case MaterialDataType::Float4x4Array: shaderSource.append("mat4"); break;
518 case MaterialDataType::Int: shaderSource.append("int"); break;
519 case MaterialDataType::Int2: shaderSource.append("ivec2"); break;
520 case MaterialDataType::Int3: shaderSource.append("ivec3"); break;
521 case MaterialDataType::Int4: shaderSource.append("ivec4"); break;
522 case MaterialDataType::UInt: shaderSource.append("uint"); break;
523 case MaterialDataType::UInt2: shaderSource.append("uvec2"); break;
524 case MaterialDataType::UInt3: shaderSource.append("uvec3"); break;
525 case MaterialDataType::UInt4: shaderSource.append("uvec4"); break;
526 case MaterialDataType::Bool: shaderSource.append("bool"); break;
527 default:
528 shaderSource.append("<invalid type>");
529 break;
530 }
531 shaderSource.append(1, ' ');
532 shaderSource.append(member.name);
533 if (member.type == MaterialDataType::Float4Array || member.type == MaterialDataType::Float4x4Array) {
534 assert(member.dimension != static_cast<size_t>(-1));
535 shaderSource.append("[");
536 shaderSource.append(std::to_string(member.dimension));
537 shaderSource.append("]");
538 }
539 shaderSource.append(";\n");
540 }
541 shaderSource.append("};\n");
542 }
543
544#if 0
545 void addEngineUniforms(std::string& shaderSource, const std::unordered_set<StringRef>& identifiersSeen)
546 {
547 for (auto& item : engineUniforms) {
548 bool active = identifiersSeen.count(item.name);
549 for (auto dependee : item.dependees) {
550 active = active || identifiersSeen.count(dependee);
551 }
552 if (active) {
553 shaderSource.append("uniform ");
554 switch (item.type) {
555 case MaterialDataType::Float4:
556 shaderSource.append("vec4 ");
557 break;
558 case MaterialDataType::Float4x4:
559 shaderSource.append("mat4 ");
560 break;
561 default:
562 assert(false && "Unhandled material data type");
563 }
564 auto name = Strings::get(item.name);
565 shaderSource.append(name.to_string_view());
566 shaderSource.append(";\n");
567 }
568 }
569 if (identifiersSeen.count(objectId)) shaderSource.append("float objectId() { return objectData.x; }\n");
570 if (identifiersSeen.count(animationTime)) shaderSource.append("float animationTime() { return objectData.y; }\n");
571 if (identifiersSeen.count(environmentRadianceMips)) shaderSource.append("float environmentRadianceMips() { return environmentInfo.x; }\n");
572 if (identifiersSeen.count(environmentIrradianceMips)) shaderSource.append("float environmentIrradianceMips() { return environmentInfo.y; }\n");
573 if (identifiersSeen.count(exposure)) shaderSource.append("float exposure() { return environmentInfo.z; }\n");
574 if (identifiersSeen.count(environmentBrightness)) shaderSource.append("float environmentBrightness() { return environmentInfo.w; }\n");
575 }
576
577#endif
578
579 void addTextureDeclaration(std::string& shaderSource, Cogs::StringView name, TextureDimensions kind, MaterialDataType format)
580 {
581 shaderSource.append("uniform ");
582
583 switch (format) {
584
585 case MaterialDataType::Unknown:
586 case MaterialDataType::Float:
587 case MaterialDataType::Float2:
588 case MaterialDataType::Float3:
589 case MaterialDataType::Float4:
590 break;
591
592 case MaterialDataType::Int:
593 case MaterialDataType::Int2:
594 case MaterialDataType::Int3:
595 case MaterialDataType::Int4:
596 shaderSource.append("i");
597 break;
598
599 case MaterialDataType::UInt:
600 case MaterialDataType::UInt2:
601 case MaterialDataType::UInt3:
602 case MaterialDataType::UInt4:
603 shaderSource.append("u");
604 break;
605 default:
606 LOG_ERROR(logger, "Unexpected texture format: %s", DataTypeNames[static_cast<size_t>(format) < std::size(DataTypeNames) ? static_cast<size_t>(format) : 0]);
607 break;
608 }
609
610 switch (kind) {
611 case TextureDimensions::Texture2D:
612 shaderSource.append("sampler2D ");
613 break;
614 case TextureDimensions::TexureCube:
615 shaderSource.append("samplerCube ");
616 break;
617 case TextureDimensions::Texture2DArray:
618 shaderSource.append("sampler2DArray ");
619 break;
620 case TextureDimensions::Texture3D:
621 shaderSource.append("sampler3D ");
622 break;
623 default:
624 LOG_ERROR(logger, "Unsupported texture type %d", int(kind));
625 break;
626 }
627 shaderSource.append(name.to_string_view());
628 shaderSource.append(";\n");
629 }
630
631 void addTextures(std::string& shaderSource,
632 const std::unordered_set<Cogs::Core::StringRef>& identifiersSeen,
633 const std::vector<MaterialTextureDefinition>& definition)
634 {
635 for (auto& texture : engineTextures) {
636 if (identifiersSeen.contains(texture.name)) {
637 shaderSource.append("#define COGS_");
638 shaderSource.append(Strings::get(texture.defineName).to_string_view());
639 shaderSource.append("_REFERENCED 1\n");
640 }
641 }
642 for (auto& texture : definition) {
643 if (identifiersSeen.contains(Strings::add(texture.name))) {
644 addTextureDeclaration(shaderSource, texture.name, texture.dimensions, texture.format);
645 }
646 }
647 shaderSource.append("\n");
648 }
649
650}
651
652
653bool Cogs::Core::buildEffectES3(Context* context,
654 std::vector<std::pair<std::string, std::string>>& definitions,
655 MaterialDefinition& materialDefinition,
656 const EnginePermutation& permutation,
657 uint32_t multiViewCount)
658{
659 if (!materialDefinition.effect.geometryShader.entryPoint.empty()) {
660 LOG_ERROR(logger, "%s: Geometry shader not allowed in GLES3.", materialDefinition.name.c_str());
661 return false;
662 }
663 if (!materialDefinition.effect.hullShader.entryPoint.empty()) {
664 LOG_ERROR(logger, "%s: Hull shader not allowed in GLES3.", materialDefinition.name.c_str());
665 return false;
666 }
667 if (!materialDefinition.effect.domainShader.entryPoint.empty()) {
668 LOG_ERROR(logger, "%s: Domain shader not allowed in GLES3.", materialDefinition.name.c_str());
669 return false;
670 }
671 if (!materialDefinition.effect.computeShader.entryPoint.empty()) {
672 LOG_ERROR(logger, "%s: Compute shader not allowed in GLES3.", materialDefinition.name.c_str());
673 return false;
674 }
675
676 changeSuffix(materialDefinition.effect.vertexShader.customSourcePath, ".hlsl", ".es30.glsl");
677 changeSuffix(materialDefinition.effect.pixelShader.customSourcePath, ".hlsl", ".es30.glsl");
678 std::string_view prefix = materialDefinition.name;
679
680 { // Vertex shader
681
683 pp.processed.reserve(::InitialBufferCapasity);
684
685 std::string vsheader;
686 vsheader.reserve(::InitialBufferCapasity);
687 addEffectDefinesAndStuff(vsheader, definitions, multiViewCount, true);
688 vsheader.append("#define COGS_VERTEX_SHADER 1\n");
689 if (!pp.process(context, vsheader)) return false;
690 vsheader.swap(pp.processed);
691 pp.processed.clear();
692
693 std::string vsbody;
694 vsbody.reserve(::InitialBufferCapasity);
695 addAttributes(vsbody, materialDefinition.effect.vertexShader.shaderInterface);
696 addInterfaceStruct(vsbody, "VertexIn", materialDefinition.effect.vertexShader.shaderInterface);
697 addAttributeImportFunc(vsbody, "VertexIn", materialDefinition.effect.vertexShader.shaderInterface);
698 addInOutVariables(vsbody, "vsfs_", materialDefinition.effect.pixelShader.shaderInterface, false);
699 addInterfaceStruct(vsbody, "VertexOut", materialDefinition.effect.pixelShader.shaderInterface);
700 addInterfaceConstructor(vsbody, "VertexOut", materialDefinition.effect.pixelShader.shaderInterface);
701 addExportOutFunc(vsbody, "VertexOut", materialDefinition.effect.pixelShader.shaderInterface);
702 addTransferFunc(vsbody, materialDefinition.effect.vertexShader, materialDefinition.effect.pixelShader);
703 addInclude(vsbody, Cogs::StringView(), materialDefinition.effect.vertexShader.customSourcePath);
704
705 vsbody.append("#define MATERIAL_VERTEX_FUNCTION ");
706 vsbody.append(materialDefinition.effect.vertexShader.entryPoint.empty() ? "vertexFunction" : materialDefinition.effect.vertexShader.entryPoint);
707 vsbody.append("\n");
708 addInclude(vsbody, Cogs::StringView(), "Engine/EngineVS.es30.glsl");
709
710 vsbody.append("#define VERTEX_FUNCTION invokeMaterial\n");
711 std::string permutationVS = permutation.getDefinition()->vertexShader;
712 changeSuffix(permutationVS, ".hlsl", ".es30.glsl");
713 addInclude(vsbody, nullptr, permutationVS);
714
715 if (!pp.process(context, vsbody)) return false;
716 vsbody.swap(pp.processed);
717 pp.processed.clear();
718
719 std::string vsuniforms;
720 vsuniforms.reserve(::InitialBufferCapasity);
721 addEngineUniformBuffer(vsuniforms, pp.identifiersSeen);
722 for (const ConstantBufferDefinition& buffer : permutation.getDefinition()->properties.buffers) {
723 addUniformBuffer(vsuniforms, pp.identifiersSeen, buffer);
724 }
725 for (const ConstantBufferDefinition& buffer : materialDefinition.properties.buffers) {
726 addUniformBuffer(vsuniforms, pp.identifiersSeen, buffer);
727 }
728 addTextures(vsuniforms, pp.identifiersSeen, materialDefinition.properties.textures);
729 addInclude(vsuniforms, Cogs::StringView(), "Engine/Common.es30.glsl");
730
731 if (!pp.process(context, vsuniforms)) return false;
732 vsuniforms.swap(pp.processed);
733 pp.processed.clear();
734
735 const std::string shaderName = Cogs::stringConcatenate({ prefix, "VertexShader", permutation.getDefinition()->name, ".es30.glsl" });
736 const std::string all = Cogs::stringConcatenate({ vsheader, vsuniforms, vsbody });
737
738#if 0
739#ifndef EMSCRIPTEN
740 FILE* f = fopen(shaderName.c_str(), "wb");
741 fwrite(all.c_str(), all.length(), 1, f);
742 fclose(f);
743 LOG_DEBUG(logger, "Generated %s", shaderName.c_str());
744#endif
745#endif
746
747 std::string vspath = "Shaders/" + shaderName;
748 context->resourceStore->addResource(vspath, all);
749 }
750
751 { // Fragment shader
753 pp.processed.reserve(::InitialBufferCapasity);
754
755 std::string fsheader;
756 fsheader.reserve(::InitialBufferCapasity);
757 addEffectDefinesAndStuff(fsheader, definitions, multiViewCount, false);
758 fsheader.append("#define COGS_FRAGMENT_SHADER 1\n");
759 if (!pp.process(context, fsheader)) return false;
760 fsheader.swap(pp.processed);
761 pp.processed.clear();
762
763 std::string fsbody;
764 fsbody.reserve(::InitialBufferCapasity);
765 addInOutVariables(fsbody, "vsfs_", materialDefinition.effect.pixelShader.shaderInterface, true);
766 addInterfaceStruct(fsbody, "VertexIn", materialDefinition.effect.pixelShader.shaderInterface);
767 addImportInFunc(fsbody, "VertexIn", materialDefinition.effect.pixelShader.shaderInterface);
768 addInclude(fsbody, nullptr, materialDefinition.effect.pixelShader.customSourcePath);
769
770 fsbody.append("#define MATERIAL_SURFACE_FUNCTION ");
771 fsbody.append(materialDefinition.effect.pixelShader.entryPoint.empty() ? "surfaceFunction" : materialDefinition.effect.pixelShader.entryPoint);
772 fsbody.append("\n");
773 addInclude(fsbody, Cogs::StringView(), "Engine/EnginePS.es30.glsl");
774
775 fsbody.append("#define SURFACE_FUNCTION invokeMaterial\n");
776 std::string permutationPS = permutation.getDefinition()->pixelShader;
777 changeSuffix(permutationPS, ".hlsl", ".es30.glsl");
778 addInclude(fsbody, nullptr, permutationPS);
779
780 if (!pp.process(context, fsbody)) return false;
781 fsbody.swap(pp.processed);
782 pp.processed.clear();
783
784 std::string fsuniforms;
785 fsuniforms.reserve(::InitialBufferCapasity);
786 addEngineUniformBuffer(fsuniforms, pp.identifiersSeen);
787 for (const ConstantBufferDefinition& buffer : permutation.getDefinition()->properties.buffers) {
788 addUniformBuffer(fsuniforms, pp.identifiersSeen, buffer);
789 }
790 for (const ConstantBufferDefinition& buffer : materialDefinition.properties.buffers) {
791 addUniformBuffer(fsuniforms, pp.identifiersSeen, buffer);
792 }
793 addTextures(fsuniforms, pp.identifiersSeen, materialDefinition.properties.textures);
794 addInclude(fsuniforms, nullptr, "Engine/Common.es30.glsl");
795
796 if (!pp.process(context, fsuniforms)) return false;
797 fsuniforms.swap(pp.processed);
798 pp.processed.clear();
799
800 const std::string shaderName = Cogs::stringConcatenate({ prefix, "PixelShader", permutation.getDefinition()->name, ".es30.glsl" });
801 const std::string all = Cogs::stringConcatenate({ fsheader, fsuniforms, fsbody });
802
803#if 0
804#ifndef EMSCRIPTEN
805 FILE* f = fopen(shaderName.c_str(), "wb");
806 fwrite(all.c_str(), all.length(), 1, f);
807 fclose(f);
808 LOG_DEBUG(logger, "Generated %s", shaderName.c_str());
809#endif
810#endif
811
812 std::string fspath = "Shaders/" + shaderName;
813 context->resourceStore->addResource(fspath, all);
814 }
815
816 definitions.clear();
817
818 return true;
819}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class ResourceStore > resourceStore
ResourceStore service instance.
Definition: Context.h:210
Log implementation class.
Definition: LogManager.h:139
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr size_t size() const noexcept
Get the size of the string.
Definition: StringView.h:178
constexpr std::string_view to_string_view() const noexcept
Create a standard library string_view of the same view.
Definition: StringView.h:161
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
TextureDimensions
Texture dimensions.
MaterialDataType
Defines available data types for material properties.
Definition: MaterialTypes.h:20
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Partial C preprocessor.
Definition: Preprocessor.h:42
bool process(Context *context, const StringView input)
Run a text block through the preprocessor.
std::unordered_set< StringRef > identifiersSeen
Set of identifiers encountered in active text.
Definition: Preprocessor.h:51
std::string processed
Resulting processed text.
Definition: Preprocessor.h:48