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