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