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 Strings::add("getViewFromViewportMatrix")
71 };
72
73 Cogs::Core::StringRef objectBufferMembers[] = {
74 Strings::add("worldMatrix"),
75 Strings::add("objectId")
76 };
77
78 Cogs::Core::StringRef animationBufferMembers[] = {
79 Strings::add("boneTransforms")
80 };
81
82 Cogs::Core::StringRef lightBufferMembers[] = {
83 Strings::add("lightPositions"),
84 Strings::add("lightDirections"),
85 Strings::add("lightColorIntensity"),
86 Strings::add("lightParameters"),
87 Strings::add("numLights"),
88 Strings::add("eyePosition"),
89 Strings::add("fogColor"),
90 Strings::add("fogDistance"),
91 Strings::add("fogAmount"),
92 Strings::add("fogEnabled"),
93 Strings::add("ambientIntensity"),
94 Strings::add("ambientColor"),
95 Strings::add("environmentBrightness"),
96 Strings::add("skyMultiplier"),
97 Strings::add("seaFlags"),
98 Strings::add("flags")
99 };
100
101 Cogs::Core::StringRef shadowBufferMembers[] = {
102 Strings::add("shadows"),
103 Strings::add("cascadeOffsets")
104 };
105
106 struct EngineBuffer {
107 Cogs::Core::StringRef* members;
108 size_t count;
110 } engineBuffers[] = {
111 { sceneBufferMembers, sizeof(sceneBufferMembers) / sizeof(sceneBufferMembers[0]), Strings::add("SCENEBUFFER") },
112 { viewGetters, sizeof(viewGetters) / sizeof(viewGetters[0]), Strings::add("VIEWGETTERS") },
113 { objectBufferMembers, sizeof(objectBufferMembers) / sizeof(objectBufferMembers[0]), Strings::add("OBJECTBUFFER") },
114 { animationBufferMembers, sizeof(animationBufferMembers) / sizeof(animationBufferMembers[0]), Strings::add("ANIMATIONBUFFER") },
115 { lightBufferMembers, sizeof(lightBufferMembers) / sizeof(lightBufferMembers[0]), Strings::add("LIGHTBUFFER") },
116 { shadowBufferMembers, sizeof(shadowBufferMembers) / sizeof(shadowBufferMembers[0]), Strings::add("SHADOWBUFFER") }
117 };
118
119
120
121 void addInclude(std::string& content, const Cogs::StringView& prefix, const Cogs::StringView& path)
122 {
123 content.append("#include \"");
124 if (prefix.size()) content.append(prefix.to_string_view());
125 content.append(path.to_string_view());
126 content.append("\"\n");
127 }
128
129 void changeSuffix(std::string& dst, const std::string_view& from, const std::string_view& to)
130 {
131 auto pos = dst.find(from);
132 if (pos == std::string::npos) return;
133 dst.replace(pos, to.length(), to);
134 }
135
136 void addEffectDefinesAndStuff(std::string& output,
137 const std::vector<std::pair<std::string, std::string>>& definitions,
138 uint32_t multiViewCount, bool isVertexShader)
139 {
140 output.append("#version 300 es\n");
141 if (multiViewCount) {
142 std::string multiViewCountString = std::to_string(multiViewCount);
143 if (isVertexShader) {
144 output.append("#extension GL_OVR_multiview : require\nlayout(num_views=");
145 output.append(multiViewCountString);
146 output.append(") in;\n");
147 }
148 output.append("#define COGS_MULTIVIEW ");
149 output.append(multiViewCountString);
150 output.append("\n");
151 }
152 output.append("precision highp float;\n"
153 "precision highp int;\n"
154 "precision highp sampler2DArrayShadow;\n"
155 "precision highp samplerCubeShadow;\n"
156 "precision mediump sampler2DArray;\n"
157 "precision highp isampler2D;\n"
158 "precision highp usampler2D;\n"
159 "precision highp usampler2DArray;\n"
160 "precision highp isampler2DArray;\n"
161 "layout (std140) uniform;\n");
162 for (const std::pair<std::string,std::string>& define : definitions) {
163 output.append("#define ");
164 output.append(define.first);
165 output.append(" ");
166 output.append(define.second);
167 output.append("\n");
168 }
169 }
170
171 [[nodiscard]] std::string_view attributeVaryingType(const MaterialDataType type)
172 {
173 switch (type) {
174 case MaterialDataType::Float: return "float "; break;
175 case MaterialDataType::Float2: return "vec2 "; break;
176 case MaterialDataType::Float3: return "vec3 "; break;
177 case MaterialDataType::Float4: return "vec4 "; break;
178 case MaterialDataType::Float4x4: return "mat4 "; break;
179 case MaterialDataType::Int: return "int "; break;
180 case MaterialDataType::Int2: return "ivec4 "; break;
181 case MaterialDataType::Int3: return "ivec3 "; break;
182 case MaterialDataType::Int4: return "ivec4 "; break;
183 case MaterialDataType::UInt: return "uint "; break;
184 case MaterialDataType::UInt2: return "uvec4 "; break;
185 case MaterialDataType::UInt3: return "uvec3 "; break;
186 case MaterialDataType::UInt4: return "uvec4 "; break;
187 case MaterialDataType::SV_IsFrontFace: return "bool "; break;
188 case MaterialDataType::VFACE: return "float "; break;
189 case MaterialDataType::Position: return "vec4 "; break;
190 default:
191 LOG_ERROR(logger, "Unsupported attribute type %d", int(type));
192 return "<illegal>";
193 break;
194 }
195 }
196
197 const char* semanticNames[]
198 {
199 "a_POSITION",
200 "a_NORMAL",
201 "a_COLOR",
202 "a_TEXCOORD",
203 "a_TANGENT",
204 "a_INSTANCEVECTOR",
205 "a_INSTANCEMATRIX",
206 };
207 static_assert(sizeof(semanticNames) == sizeof(semanticNames[0]) * (size_t(ShaderInterfaceMemberDefinition::SemanticName::FirstSystemValueSemantic) - 1));
208
209 void addAttributes(std::string& shaderSource, const ShaderInterfaceDefinition& iface)
210 {
211 for (const ShaderInterfaceMemberDefinition& attribute : iface.members) {
212
213 if ((attribute.semantic.name != ShaderInterfaceMemberDefinition::SemanticName::None)
214 && (size_t(attribute.semantic.name) < size_t(ShaderInterfaceMemberDefinition::SemanticName::FirstSystemValueSemantic)))
215 {
216 shaderSource.append("in ");
217 shaderSource.append(attributeVaryingType(attribute.type));
218 shaderSource.append(semanticNames[size_t(attribute.semantic.name) - 1]);
219 shaderSource.append(std::to_string(attribute.semantic.slot));
220 shaderSource.append(";\n");
221 }
222 else {
223 switch (attribute.semantic.name) {
224 case ShaderInterfaceMemberDefinition::SemanticName::SV_VertexID: [[fallthrough]];
225 case ShaderInterfaceMemberDefinition::SemanticName::SV_InstanceID:
226 break;
227 default:
228 LOG_WARNING(logger, "Unexpected semantic name '%.*s'",
229 StringViewFormat(ShaderInterfaceMemberDefinition::semanticNameString(attribute.semantic.name)));
230 }
231 }
232 }
233 shaderSource.append("\n");
234 }
235
236 void addInterfaceStruct(std::string& shaderSource, const std::string_view& name, const ShaderInterfaceDefinition& iface)
237 {
238 shaderSource.append("struct ");
239 shaderSource.append(name);
240 shaderSource.append(" {\n");
241 for (const auto& attribute : iface.members) {
242 shaderSource.append(" ");
243 shaderSource.append(attributeVaryingType(attribute.type));
244 shaderSource.append(attribute.name);
245 shaderSource.append(";\n");
246 }
247 shaderSource.append("};\n\n");
248 }
249
250 void addAttributeImportFunc(std::string& shaderSource, const std::string_view& name, const ShaderInterfaceDefinition& iface)
251 {
252 shaderSource.append(name);
253 shaderSource.append(" importAttributes() {\n");
254 shaderSource.append(" ");
255 shaderSource.append(name);
256 shaderSource.append(" t;\n");
257 for (const auto& attribute : iface.members) {
258
259
260 if ((attribute.semantic.name != ShaderInterfaceMemberDefinition::SemanticName::None)
261 && (size_t(attribute.semantic.name) < size_t(ShaderInterfaceMemberDefinition::SemanticName::FirstSystemValueSemantic)))
262 {
263 shaderSource.append(" t.");
264 shaderSource.append(attribute.name);
265 shaderSource.append(" = ");
266 shaderSource.append(semanticNames[size_t(attribute.semantic.name) - 1]);
267 shaderSource.append(std::to_string(attribute.semantic.slot));
268 shaderSource.append(";\n");
269 }
270 else {
271 switch (attribute.semantic.name) {
272 case ShaderInterfaceMemberDefinition::SemanticName::SV_VertexID:
273 shaderSource.append(" t.");
274 shaderSource.append(attribute.name);
275 shaderSource.append(" = ");
276 shaderSource.append(attributeVaryingType(attribute.type));
277 shaderSource.append("(gl_VertexID);\n");
278 break;
279 case ShaderInterfaceMemberDefinition::SemanticName::SV_InstanceID:
280 shaderSource.append(" t.");
281 shaderSource.append(attribute.name);
282 shaderSource.append(" = ");
283 shaderSource.append(attributeVaryingType(attribute.type));
284 shaderSource.append("(gl_InstanceID);\n");
285 break;
286 default:
287 break;
288 }
289 }
290 }
291 shaderSource.append(" return t;\n}\n\n");
292 }
293
294 void addInOutVariables(std::string& shaderSource, const std::string_view& prefix, const ShaderInterfaceDefinition& iface, bool in)
295 {
296 if (iface.members.empty()) return;
297 for (const auto& out : iface.members) {
298 switch (out.type) {
299 case MaterialDataType::SV_IsFrontFace:
300 case MaterialDataType::VFACE:
301 // skip
302 break;
303 default:
304 if (out.modifiers & ShaderInterfaceMemberDefinition::CentroidModifier) {
305 shaderSource.append("centroid ");
306 }
307 if (out.modifiers & ShaderInterfaceMemberDefinition::NointerpolationModifier) {
308 shaderSource.append("flat ");
309 }
310 shaderSource.append(in ? "in " : "out ");
311 shaderSource.append(attributeVaryingType(out.type));
312 shaderSource.append(prefix);
313 shaderSource.append(out.name);
314 shaderSource.append(";\n");
315 break;
316 }
317 }
318 shaderSource.append("\n");
319 }
320
321 void addInterfaceConstructor(std::string& shaderSource, const std::string_view& name, const ShaderInterfaceDefinition& iface)
322 {
323 shaderSource.append(name);
324 shaderSource.append(" create");
325 shaderSource.append(name);
326 shaderSource.append("() {\n");
327 shaderSource.append(" ");
328 shaderSource.append(name);
329 shaderSource.append(" t;\n");
330 for (const auto& attribute : iface.members) {
331 const char* initializer = nullptr;
332 switch (attribute.type) {
333 case MaterialDataType::Float: initializer = "0.0;\n"; break;
334 case MaterialDataType::Float2: initializer = "vec2(0);\n"; break;
335 case MaterialDataType::Float3: initializer = "vec3(0);\n"; break;
336 case MaterialDataType::Float4: initializer = "vec4(0);\n"; break;
337 case MaterialDataType::Float4x4: initializer = "mat4(0);\n"; break;
338 default:
339 break;
340 }
341 if (initializer) {
342 shaderSource.append(" t.");
343 shaderSource.append(attribute.name);
344 shaderSource.append(" = ");
345 shaderSource.append(initializer);
346 }
347 }
348 shaderSource.append(" return t;\n}\n\n");
349 }
350
351 void addExportOutFunc(std::string& shaderSource, const std::string_view& name, const ShaderInterfaceDefinition& iface)
352 {
353 shaderSource.append("void exportOut(");
354 shaderSource.append(name);
355 shaderSource.append(" vertexOut) {\n");
356 for (const auto& attribute : iface.members) {
357 switch (attribute.type) {
358 case MaterialDataType::SV_IsFrontFace:
359 case MaterialDataType::VFACE:
360 // skip
361 break;
362 default:
363 shaderSource.append(" vsfs_");
364 shaderSource.append(attribute.name);
365 shaderSource.append(" = vertexOut.");
366 shaderSource.append(attribute.name);
367 shaderSource.append(";\n");
368 break;
369 }
370 }
371 shaderSource.append("}\n");
372 }
373
374 void addImportInFunc(std::string& shaderSource, const std::string_view& name, const ShaderInterfaceDefinition& iface)
375 {
376 shaderSource.append(name);
377 shaderSource.append(" importIn() {\n ");
378 shaderSource.append(name);
379 shaderSource.append(" t;\n");
380 for (const auto& attribute : iface.members) {
381 switch (attribute.type) {
382 case MaterialDataType::SV_IsFrontFace: // type bool
383 shaderSource.append(" t.");
384 shaderSource.append(attribute.name);
385 shaderSource.append(" = gl_FrontFacing;\n");
386 break;
387 case MaterialDataType::VFACE: // type float
388 shaderSource.append(" t.");
389 shaderSource.append(attribute.name);
390 shaderSource.append(" = gl_FrontFacing ? 1.0 : -1.0;\n");
391 break;
392 case MaterialDataType::Position: // type vec4
393 shaderSource.append(" t.");
394 shaderSource.append(attribute.name);
395 shaderSource.append(" = gl_FragCoord;\n");
396 break;
397 default:
398 shaderSource.append(" t.");
399 shaderSource.append(attribute.name);
400 shaderSource.append(" = vsfs_");
401 shaderSource.append(attribute.name);
402 shaderSource.append(";\n");
403 break;
404 }
405 }
406 shaderSource.append(" return t;\n}\n");
407 }
408
409 void addTransferFunc(std::string& shaderSource, const ShaderDefinition& sourceDefinition, const ShaderDefinition& destinationDefinition)
410 {
411 shaderSource.append("void transferAttributes(in VertexIn vertexIn, inout VertexOut vertexOut) {\n");
412 for (const ShaderInterfaceMemberDefinition& member : sourceDefinition.shaderInterface.members) {
413 if (member.semantic.name == ShaderInterfaceMemberDefinition::SemanticName::SV_VertexID) continue;
414 for (const ShaderInterfaceMemberDefinition& dMember : destinationDefinition.shaderInterface.members) {
415 if (dMember.name == member.name) {
416 shaderSource.append(" vertexOut.");
417 shaderSource.append(member.name);
418 shaderSource.append(" = ");
419 if (dMember.type == MaterialDataType::Float3 && member.type == MaterialDataType::Float2 && member.semantic.name == ShaderInterfaceMemberDefinition::SemanticName::Normal) {
420 shaderSource.append("octDecode(vertexIn.");
421 shaderSource.append(member.name);
422 shaderSource.append("); \n");
423 break;
424 }
425 if (dMember.type == MaterialDataType::Float4 && member.type == MaterialDataType::Float3) {
426 shaderSource.append("vec4(vertexIn.");
427 shaderSource.append(member.name);
428 shaderSource.append(", 1.0);\n");
429 break;
430 }
431 if (dMember.type == MaterialDataType::Float4 && member.type == MaterialDataType::Float2) {
432 shaderSource.append("vec4(vertexIn.");
433 shaderSource.append(member.name);
434 shaderSource.append(", 0.0, 1.0);\n");
435 break;
436 }
437 bool close = false;
438 if (dMember.type != member.type) {
439 switch (dMember.type) {
440 case MaterialDataType::Float: shaderSource.append("float("); close = true; break;
441 case MaterialDataType::Float2: shaderSource.append("vec2("); close = true; break;
442 case MaterialDataType::Float3: shaderSource.append("vec3("); close = true; break;
443 case MaterialDataType::Float4: shaderSource.append("vec4("); close = true; break;
444 case MaterialDataType::Float4x4: shaderSource.append("mat4("); close = true; break;
445 case MaterialDataType::Int: shaderSource.append("int("); close = true; break;
446 case MaterialDataType::Int2: shaderSource.append("ivec2("); close = true; break;
447 case MaterialDataType::Int3: shaderSource.append("ivec3("); close = true; break;
448 case MaterialDataType::Int4: shaderSource.append("ivec4("); close = true; break;
449 case MaterialDataType::UInt: shaderSource.append("uint("); close = true; break;
450 case MaterialDataType::UInt2: shaderSource.append("uvec2("); close = true; break;
451 case MaterialDataType::UInt3: shaderSource.append("uvec3("); close = true; break;
452 case MaterialDataType::UInt4: shaderSource.append("uvec4("); close = true; break;
453 default:
454 break;
455 }
456 }
457 shaderSource.append("vertexIn.");
458 shaderSource.append(member.name);
459 if (close) shaderSource.append(1, ')');
460 shaderSource.append(";\n");
461 break;
462 }
463 }
464 }
465 shaderSource.append("}\n");
466 }
467
468
469 void addEngineUniformBuffer(std::string& shaderSource, const std::unordered_set<Cogs::Core::StringRef>& identifiersSeen)
470 {
471 for (const EngineBuffer& engineBuffer : engineBuffers) {
472
473 // Check if any member of buffer has been referenced
474 bool referenced = false;
475 for (size_t i = 0; i < engineBuffer.count; i++) {
476 if (identifiersSeen.contains(engineBuffer.members[i])) {
477 referenced = true;
478 break;
479 }
480 }
481
482 // WebGL requires defined, yet unused, uniform blocks to have bound uniform backing.
483 // Hence we omit defining these unless they are in use.
484 if (referenced) {
485 shaderSource.append("#define COGS_");
486 shaderSource.append(Strings::get(engineBuffer.name).to_string_view());
487 shaderSource.append("_REFERENCED 1\n");
488 }
489 }
490 }
491
492 void addUniformBuffer(std::string& shaderSource,
493 const std::unordered_set<Cogs::Core::StringRef>& identifiersSeen,
494 const ConstantBufferDefinition& definition)
495 {
496 if (definition.values.empty()) return;
497
498 // Check if any member of buffer has been referenced
499 bool isInUse = false;
500 for (const ConstantBufferVariableDefinition& member : definition.values) {
501 if (identifiersSeen.contains(Strings::add(member.name))) {
502 isInUse = true;
503 break;
504 }
505 }
506 if (!isInUse) return;
507
508 // At least one member is in use, include
509 shaderSource.append("uniform ");
510 shaderSource.append(definition.name);
511 shaderSource.append(" {\n");
512 for (const ConstantBufferVariableDefinition& member : definition.values) {
513 shaderSource.append(2, ' ');
514 switch (member.type) {
515 case MaterialDataType::Float: shaderSource.append("float"); break;
516 case MaterialDataType::Float2: shaderSource.append("vec2"); break;
517 case MaterialDataType::Float3: shaderSource.append("vec3"); break;
518 case MaterialDataType::Float4: shaderSource.append("vec4"); break;
519 case MaterialDataType::Float4x4: shaderSource.append("mat4"); break;
520 case MaterialDataType::Float4Array: shaderSource.append("vec4"); break;
521 case MaterialDataType::Float4x4Array: shaderSource.append("mat4"); break;
522 case MaterialDataType::Int: shaderSource.append("int"); break;
523 case MaterialDataType::Int2: shaderSource.append("ivec2"); break;
524 case MaterialDataType::Int3: shaderSource.append("ivec3"); break;
525 case MaterialDataType::Int4: shaderSource.append("ivec4"); break;
526 case MaterialDataType::UInt: shaderSource.append("uint"); break;
527 case MaterialDataType::UInt2: shaderSource.append("uvec2"); break;
528 case MaterialDataType::UInt3: shaderSource.append("uvec3"); break;
529 case MaterialDataType::UInt4: shaderSource.append("uvec4"); break;
530 case MaterialDataType::Bool: shaderSource.append("bool"); break;
531 default:
532 shaderSource.append("<invalid type>");
533 break;
534 }
535 shaderSource.append(1, ' ');
536 shaderSource.append(member.name);
537 if (member.type == MaterialDataType::Float4Array || member.type == MaterialDataType::Float4x4Array) {
538 assert(member.dimension != static_cast<size_t>(-1));
539 shaderSource.append("[");
540 shaderSource.append(std::to_string(member.dimension));
541 shaderSource.append("]");
542 }
543 shaderSource.append(";\n");
544 }
545 shaderSource.append("};\n");
546 }
547
548#if 0
549 void addEngineUniforms(std::string& shaderSource, const std::unordered_set<StringRef>& identifiersSeen)
550 {
551 for (auto& item : engineUniforms) {
552 bool active = identifiersSeen.count(item.name);
553 for (auto dependee : item.dependees) {
554 active = active || identifiersSeen.count(dependee);
555 }
556 if (active) {
557 shaderSource.append("uniform ");
558 switch (item.type) {
559 case MaterialDataType::Float4:
560 shaderSource.append("vec4 ");
561 break;
562 case MaterialDataType::Float4x4:
563 shaderSource.append("mat4 ");
564 break;
565 default:
566 assert(false && "Unhandled material data type");
567 }
568 auto name = Strings::get(item.name);
569 shaderSource.append(name.to_string_view());
570 shaderSource.append(";\n");
571 }
572 }
573 if (identifiersSeen.count(objectId)) shaderSource.append("float objectId() { return objectData.x; }\n");
574 if (identifiersSeen.count(animationTime)) shaderSource.append("float animationTime() { return objectData.y; }\n");
575 if (identifiersSeen.count(environmentRadianceMips)) shaderSource.append("float environmentRadianceMips() { return environmentInfo.x; }\n");
576 if (identifiersSeen.count(environmentIrradianceMips)) shaderSource.append("float environmentIrradianceMips() { return environmentInfo.y; }\n");
577 if (identifiersSeen.count(exposure)) shaderSource.append("float exposure() { return environmentInfo.z; }\n");
578 if (identifiersSeen.count(environmentBrightness)) shaderSource.append("float environmentBrightness() { return environmentInfo.w; }\n");
579 }
580
581#endif
582
583 void addTextureDeclaration(std::string& shaderSource, Cogs::StringView name, TextureDimensions kind, MaterialDataType format, MaterialTypePrecision precision)
584 {
585 shaderSource.append("uniform ");
586
587 switch (precision) {
588 case MaterialTypePrecision::Default:
589 break;
590 case MaterialTypePrecision::Low:
591 shaderSource.append("lowp ");
592 break;
593 case MaterialTypePrecision::Medium:
594 shaderSource.append("mediump ");
595 break;
596 case MaterialTypePrecision::High:
597 shaderSource.append("highp ");
598 break;
599 default:
600 assert(false && "Invalid enum value");
601 break;
602 }
603
604 switch (format) {
605
606 case MaterialDataType::Unknown:
607 case MaterialDataType::Float:
608 case MaterialDataType::Float2:
609 case MaterialDataType::Float3:
610 case MaterialDataType::Float4:
611 break;
612
613 case MaterialDataType::Int:
614 case MaterialDataType::Int2:
615 case MaterialDataType::Int3:
616 case MaterialDataType::Int4:
617 shaderSource.append("i");
618 break;
619
620 case MaterialDataType::UInt:
621 case MaterialDataType::UInt2:
622 case MaterialDataType::UInt3:
623 case MaterialDataType::UInt4:
624 shaderSource.append("u");
625 break;
626 default:
627 LOG_ERROR(logger, "Unexpected texture format: %s", DataTypeNames[static_cast<size_t>(format) < std::size(DataTypeNames) ? static_cast<size_t>(format) : 0]);
628 break;
629 }
630
631 switch (kind) {
632 case TextureDimensions::Texture2D:
633 shaderSource.append("sampler2D ");
634 break;
635 case TextureDimensions::TexureCube:
636 shaderSource.append("samplerCube ");
637 break;
638 case TextureDimensions::Texture2DArray:
639 shaderSource.append("sampler2DArray ");
640 break;
641 case TextureDimensions::Texture3D:
642 shaderSource.append("sampler3D ");
643 break;
644 default:
645 LOG_ERROR(logger, "Unsupported texture type %d", int(kind));
646 break;
647 }
648 shaderSource.append(name.to_string_view());
649 shaderSource.append(";\n");
650 }
651
652 void addTextures(std::string& shaderSource,
653 const std::unordered_set<Cogs::Core::StringRef>& identifiersSeen,
654 const std::vector<MaterialTextureDefinition>& definition)
655 {
656 for (auto& texture : engineTextures) {
657 if (identifiersSeen.contains(texture.name)) {
658 shaderSource.append("#define COGS_");
659 shaderSource.append(Strings::get(texture.defineName).to_string_view());
660 shaderSource.append("_REFERENCED 1\n");
661 }
662 }
663 for (auto& texture : definition) {
664 if (identifiersSeen.contains(Strings::add(texture.name))) {
665 addTextureDeclaration(shaderSource, texture.name, texture.dimensions, texture.format, texture.precision);
666 }
667 }
668 shaderSource.append("\n");
669 }
670}
671
672bool addPermutationFragmentOutput(std::string& source, const std::vector<EffectOutputMemberDefinition> outputDefinition, const std::vector<std::pair<std::string, std::string>>& definitions, bool depthOnly) {
673 bool outputDepth = false;
674 bool hasColorAttachments = !outputDefinition.empty() && !depthOnly;
675
676 std::string targetString = "COGS_CUSTOM_DEPTH_WRITE";
677 auto it = std::find_if(definitions.begin(), definitions.end(),
678 [&targetString](const std::pair<std::string, std::string>& p) {
679 return p.first == targetString;
680 });
681
682 if (it != definitions.end()) {
683 outputDepth = it->second != "0";
684 }
685
686 if (!outputDepth && !hasColorAttachments) {
687 return false;
688 }
689
690 std::string outVariables;
691
692 source.append("struct FragmentOut {\n");
693 if (hasColorAttachments) {
694 for (auto member : outputDefinition) {
695 outVariables.append("layout(location = " + std::to_string(member.target) + ") out " + std::string(attributeVaryingType(member.dataType)) + "po_" + member.name + ";\n");
696
697 source.append(" " + std::string(attributeVaryingType(member.dataType)) + " " + member.name + ";\n");
698 }
699 }
700 if (outputDepth) {
701 source.append(" float fragDepth;\n");
702 }
703 source.append("};\n");
704 source.append(outVariables);
705 return true;
706}
707
708void addMainFunction(std::string& source, const std::vector<EffectOutputMemberDefinition> outputDefinition, const std::vector<std::pair<std::string, std::string>>& definitions, bool depthOnly, bool hasOutput) {
709 if (!hasOutput) {
710 source.append(R"(
711void main() {
712 VertexIn In = importIn();
713 permutationMain(In);
714}
715)");
716 return;
717 }
718
719 bool outputDepth = false;
720 std::string targetString = "COGS_CUSTOM_DEPTH_WRITE";
721 auto it = std::find_if(definitions.begin(), definitions.end(),
722 [&targetString](const std::pair<std::string, std::string>& p) {
723 return p.first == targetString;
724 });
725
726 if (it != definitions.end()) {
727 outputDepth = it->second != "0";
728 }
729
730 source.append("void exportOut(FragmentOut Out) {\n");
731 if (!depthOnly) {
732 for (auto member : outputDefinition) {
733 source.append(" po_");
734 source.append(member.name);
735 source.append(" = Out.");
736 source.append(member.name);
737 source.append(";\n");
738 }
739 }
740 if (outputDepth) {
741 source.append(" gl_FragDepth = Out.fragDepth;\n");
742 }
743 source.append("}\n");
744 source.append(R"(
745void main() {
746 VertexIn In = importIn();
747 FragmentOut Out = permutationMain(In);
748 exportOut(Out);
749}
750)");
751}
752
753void concatUniqueMembers(const ShaderInterfaceDefinition& a, const ShaderInterfaceDefinition& b, ShaderInterfaceDefinition& out) {
754 out.members.reserve(a.members.size() + b.members.size());
755 out = a;
756 for (auto member : b.members) {
757 bool found = false;
758 for (auto existingMember : a.members) {
759 if (member.name == existingMember.name) {
760 found = true;
761 break;
762 }
763 }
764 if (!found) {
765 out.members.push_back(member);
766 }
767 }
768}
769
770bool Cogs::Core::buildEffectES3(Context* context,
771 std::vector<std::pair<std::string, std::string>>& definitions,
772 MaterialDefinition& materialDefinition,
773 const EnginePermutation& permutation,
774 uint32_t multiViewCount)
775{
776 if (!materialDefinition.effect.geometryShader.entryPoint.empty()) {
777 LOG_ERROR(logger, "%s: Geometry shader not allowed in GLES3.", materialDefinition.name.c_str());
778 return false;
779 }
780 if (!materialDefinition.effect.hullShader.entryPoint.empty()) {
781 LOG_ERROR(logger, "%s: Hull shader not allowed in GLES3.", materialDefinition.name.c_str());
782 return false;
783 }
784 if (!materialDefinition.effect.domainShader.entryPoint.empty()) {
785 LOG_ERROR(logger, "%s: Domain shader not allowed in GLES3.", materialDefinition.name.c_str());
786 return false;
787 }
788 if (!materialDefinition.effect.computeShader.entryPoint.empty()) {
789 LOG_ERROR(logger, "%s: Compute shader not allowed in GLES3.", materialDefinition.name.c_str());
790 return false;
791 }
792
793 changeSuffix(materialDefinition.effect.vertexShader.customSourcePath, ".hlsl", ".es30.glsl");
794 changeSuffix(materialDefinition.effect.pixelShader.customSourcePath, ".hlsl", ".es30.glsl");
795 std::string_view prefix = materialDefinition.name;
796
797 ShaderInterfaceDefinition vertexInterface;
798 concatUniqueMembers(materialDefinition.effect.vertexShader.shaderInterface, permutation.getDefinition()->vertexInterface, vertexInterface);
799 ShaderInterfaceDefinition surfaceInterface;
800 concatUniqueMembers(materialDefinition.effect.pixelShader.shaderInterface, permutation.getDefinition()->surfaceInterface, surfaceInterface);
801
802 { // Vertex shader
803
805 pp.processed.reserve(::InitialBufferCapasity);
806
807 std::string vsheader;
808 vsheader.reserve(::InitialBufferCapasity);
809 addEffectDefinesAndStuff(vsheader, definitions, multiViewCount, true);
810 vsheader.append("#define COGS_VERTEX_SHADER 1\n");
811 if (!pp.process(context, vsheader)) return false;
812 vsheader.swap(pp.processed);
813 pp.processed.clear();
814
815 std::string vsbody;
816 vsbody.reserve(::InitialBufferCapasity);
817 addAttributes(vsbody, vertexInterface);
818 addInterfaceStruct(vsbody, "VertexIn", vertexInterface);
819 addAttributeImportFunc(vsbody, "VertexIn", vertexInterface);
820 addInOutVariables(vsbody, "vsfs_",surfaceInterface, false);
821 addInterfaceStruct(vsbody, "VertexOut",surfaceInterface);
822 addInterfaceConstructor(vsbody, "VertexOut",surfaceInterface);
823 addExportOutFunc(vsbody, "VertexOut",surfaceInterface);
824 addTransferFunc(vsbody, materialDefinition.effect.vertexShader, materialDefinition.effect.pixelShader);
825 addInclude(vsbody, Cogs::StringView(), materialDefinition.effect.vertexShader.customSourcePath);
826
827 vsbody.append("#define MATERIAL_VERTEX_FUNCTION ");
828 vsbody.append(materialDefinition.effect.vertexShader.entryPoint.empty() ? "vertexFunction" : materialDefinition.effect.vertexShader.entryPoint);
829 vsbody.append("\n");
830 addInclude(vsbody, Cogs::StringView(), "Engine/EngineVS.es30.glsl");
831
832 vsbody.append("#define VERTEX_FUNCTION invokeMaterial\n");
833 std::string permutationVS = permutation.getDefinition()->vertexShader;
834 changeSuffix(permutationVS, ".hlsl", ".es30.glsl");
835 addInclude(vsbody, nullptr, permutationVS);
836
837 if (!pp.process(context, vsbody)) return false;
838 vsbody.swap(pp.processed);
839 pp.processed.clear();
840
841 std::string vsuniforms;
842 vsuniforms.reserve(::InitialBufferCapasity);
843 addEngineUniformBuffer(vsuniforms, pp.identifiersSeen);
844 for (const ConstantBufferDefinition& buffer : permutation.getDefinition()->properties.buffers) {
845 addUniformBuffer(vsuniforms, pp.identifiersSeen, buffer);
846 }
847 for (const ConstantBufferDefinition& buffer : materialDefinition.properties.buffers) {
848 addUniformBuffer(vsuniforms, pp.identifiersSeen, buffer);
849 }
850 addTextures(vsuniforms, pp.identifiersSeen, materialDefinition.properties.textures);
851 addInclude(vsuniforms, Cogs::StringView(), "Engine/Common.es30.glsl");
852
853 if (!pp.process(context, vsuniforms)) return false;
854 vsuniforms.swap(pp.processed);
855 pp.processed.clear();
856
857 const std::string shaderName = Cogs::stringConcatenate({ prefix, "VertexShader", permutation.getDefinition()->name, ".es30.glsl" });
858 const std::string all = Cogs::stringConcatenate({ vsheader, vsuniforms, vsbody });
859
860#if 0
861#ifndef EMSCRIPTEN
862 FILE* f = fopen(shaderName.c_str(), "wb");
863 fwrite(all.c_str(), all.length(), 1, f);
864 fclose(f);
865 LOG_DEBUG(logger, "Generated %s", shaderName.c_str());
866#endif
867#endif
868
869 std::string vspath = "Shaders/" + shaderName;
870 context->resourceStore->addResource(vspath, all);
871 }
872
873 { // Fragment shader
875 pp.processed.reserve(::InitialBufferCapasity);
876
877 std::string fsheader;
878 fsheader.reserve(::InitialBufferCapasity);
879 addEffectDefinesAndStuff(fsheader, definitions, multiViewCount, false);
880 fsheader.append("#define COGS_FRAGMENT_SHADER 1\n");
881 if (!pp.process(context, fsheader)) return false;
882 fsheader.swap(pp.processed);
883 pp.processed.clear();
884
885 std::string fsbody;
886 fsbody.reserve(::InitialBufferCapasity);
887 addInOutVariables(fsbody, "vsfs_",surfaceInterface, true);
888 addInterfaceStruct(fsbody, "VertexIn",surfaceInterface);
889 addImportInFunc(fsbody, "VertexIn",surfaceInterface);
890 addInclude(fsbody, nullptr, materialDefinition.effect.pixelShader.customSourcePath);
891
892 fsbody.append("#define MATERIAL_SURFACE_FUNCTION ");
893 fsbody.append(materialDefinition.effect.pixelShader.entryPoint.empty() ? "surfaceFunction" : materialDefinition.effect.pixelShader.entryPoint);
894 fsbody.append("\n");
895 addInclude(fsbody, Cogs::StringView(), "Engine/EnginePS.es30.glsl");
896
897 bool hasOutputStruct = addPermutationFragmentOutput(fsbody, permutation.getDefinition()->outputs.members, definitions, permutation.isDepthOnly());
898
899 fsbody.append("#define SURFACE_FUNCTION invokeMaterial\n");
900 std::string permutationPS = permutation.getDefinition()->pixelShader;
901 changeSuffix(permutationPS, ".hlsl", ".es30.glsl");
902 addInclude(fsbody, nullptr, permutationPS);
903
904 if (!pp.process(context, fsbody)) return false;
905 fsbody.swap(pp.processed);
906 pp.processed.clear();
907
908 addMainFunction(fsbody, permutation.getDefinition()->outputs.members, definitions, permutation.isDepthOnly(), hasOutputStruct);
909
910 std::string fsuniforms;
911 fsuniforms.reserve(::InitialBufferCapasity);
912 addEngineUniformBuffer(fsuniforms, pp.identifiersSeen);
913 for (const ConstantBufferDefinition& buffer : permutation.getDefinition()->properties.buffers) {
914 addUniformBuffer(fsuniforms, pp.identifiersSeen, buffer);
915 }
916 for (const ConstantBufferDefinition& buffer : materialDefinition.properties.buffers) {
917 addUniformBuffer(fsuniforms, pp.identifiersSeen, buffer);
918 }
919 addTextures(fsuniforms, pp.identifiersSeen, materialDefinition.properties.textures);
920 addInclude(fsuniforms, nullptr, "Engine/Common.es30.glsl");
921
922 if (!pp.process(context, fsuniforms)) return false;
923 fsuniforms.swap(pp.processed);
924 pp.processed.clear();
925
926 const std::string shaderName = Cogs::stringConcatenate({ prefix, "PixelShader", permutation.getDefinition()->name, ".es30.glsl" });
927 const std::string all = Cogs::stringConcatenate({ fsheader, fsuniforms, fsbody });
928
929#if 0
930#ifndef EMSCRIPTEN
931 FILE* f = fopen(shaderName.c_str(), "wb");
932 fwrite(all.c_str(), all.length(), 1, f);
933 fclose(f);
934 LOG_DEBUG(logger, "Generated %s", shaderName.c_str());
935#endif
936#endif
937
938 std::string fspath = "Shaders/" + shaderName;
939 context->resourceStore->addResource(fspath, all);
940 }
941
942 definitions.clear();
943
944 return true;
945}
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:140
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:181
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