Cogs.Core
EnginePermutations.cpp
1#include "EnginePermutations.h"
2
3#include "Context.h"
4
5#include "ParameterBuffers.h"
6
7#include "Resources/Material.h"
8#include "Resources/MaterialBuilder.h"
9
10#include "Serialization/JsonParser.h"
11#include "Serialization/MaterialReader.h"
12
13#include "Utilities/Parsing.h"
14#include "Components/Core/LightComponent.h"
15#include "Services/Variables.h"
16#include "RenderList.h"
17
18#include "Foundation/HashSequence.h"
19#include "Foundation/Logging/Logger.h"
20
21namespace
22{
23 Cogs::Logging::Log logger = Cogs::Logging::getLogger("EnginePermutations");
24
25 const size_t kMaxEnginePermutations = 64;
26}
27
28Cogs::Core::EnginePermutations::EnginePermutations(Context * context) :
29 permutationDefinitions(MemBlockType::RenderResourceStorage), mContext(context)
30{
31 enginePermutations.reserve(kMaxEnginePermutations);
32}
33
34Cogs::Core::EnginePermutations::~EnginePermutations()
35{
36 EnginePermutationDefinition* def = firstDefinition;
37 while (def) {
38 EnginePermutationDefinition* n = def->next;
39 permutationDefinitions.destroy(def);
40 def = n;
41 }
42}
43
44
45void Cogs::Core::EnginePermutations::initialize(Context * context)
46{
47 load("Default.permutations");
48
49 //FIXME: When order-dependence for permutation masking is fixed, this section may be removed.
50 assert(get(0) == get("Forward") && "Invalid default permutation order.");
51 assert(get(1) == get("Deferred") && "Invalid default permutation order.");
52 assert(get(2) == get("Shadow") && "Invalid default permutation order.");
53 assert(get(3) == get("Transparent") && "Invalid default permutation order.");
54
55 for (auto & e : enginePermutations) {
56 auto & definitions = e.getDefinition()->definitions;
57
58 definitions.push_back({ "COGS_MAX_LIGHTS", std::to_string(context->renderer->getMaxLights()) });
59
60 if (context->variables->get("renderer.samples", 1) > 1) {
61 definitions.push_back({ "COGS_MSAA", "1" });
62 }
63 }
64}
65
66Cogs::Core::EnginePermutationDefinition* Cogs::Core::EnginePermutations::createDefinition()
67{
68 EnginePermutationDefinition* rv = permutationDefinitions.create();
69 rv->next = firstDefinition;
70 firstDefinition = rv;
71 return rv;
72}
73
74
75Cogs::Core::EnginePermutation * Cogs::Core::EnginePermutations::create(const StringView & name, EnginePermutation * base)
76{
77 auto definition = createDefinition();
78 definition->name = name.to_string();
79
80 return create(definition, base);
81}
82
83Cogs::Core::EnginePermutation * Cogs::Core::EnginePermutations::create(EnginePermutationDefinition * definition, EnginePermutation * base)
84{
85 assert(enginePermutations.size() < kMaxEnginePermutations && "Permutation limit reached.");
86
87 auto name = StringView(definition->name);
88 auto nameHash = name.hash();
89
90 for (auto & e : enginePermutations) {
91 if (e.getNameHash() == nameHash) {
92 LOG_WARNING(logger, "Overwriting engine permutation %.*s.", StringViewFormat(name));
93 return &e;
94 }
95 }
96
97 enginePermutations.emplace_back();
98 auto enginePermutation = &enginePermutations.back();
99
100 enginePermutation->index = enginePermutations.size() - 1;
101 enginePermutation->permutationMask = 1u << enginePermutation->index;
102 enginePermutation->definition = definition;
103
104 if (base) {
105 assert(definition->inherits.empty());
106 bool ret = inherit(enginePermutation, base);
107 if (!ret) LOG_WARNING(logger, "EnginePermutation %s inherit %s failed", definition->name.c_str(), base->getName().c_str());
108 } else if (definition->inherits.size()) {
109 EnginePermutation* inherits = get(definition->inherits);
110 bool ret = inherit(enginePermutation, inherits);
111 if (!ret) LOG_WARNING(logger, "EnginePermutation %s inherit %s failed", definition->name.c_str(), inherits->getName().c_str());
112 }
113
114 enginePermutation->initialize(mContext, definition);
115
116 return enginePermutation;
117}
118
119bool Cogs::Core::EnginePermutations::inherit(EnginePermutation * enginePermutation, const EnginePermutation * base)
120{
121 enginePermutation->permutationMask = base->permutationMask;
122
123 auto inheritIfNotSet = [](std::string & a, const std::string & b) {
124 if (a.empty()) a = b;
125 };
126
127 inheritIfNotSet(enginePermutation->definition->vertexShader, base->definition->vertexShader);
128 inheritIfNotSet(enginePermutation->definition->geometryShader, base->definition->geometryShader);
129 inheritIfNotSet(enginePermutation->definition->pixelShader, base->definition->pixelShader);
130
131 enginePermutation->definition->flags |= base->definition->flags;
132 enginePermutation->definition->requiredFlags |= base->definition->requiredFlags;
133
134 for (auto & d : base->definition->definitions) {
135 enginePermutation->definition->definitions.emplace_back(d);
136 }
137
138 return inheritMaterialVariants(enginePermutation->definition->variants, base->definition->variants,
139 enginePermutation->definition->name, base->definition->name,
140 true);
141}
142
143const Cogs::Core::EnginePermutation * Cogs::Core::EnginePermutations::get(size_t index) const
144{
145 if (index == static_cast<size_t>(-1) || index >= enginePermutations.size()) return &enginePermutations[0];
146
147 return &enginePermutations[index];
148}
149
150const Cogs::Core::EnginePermutation * Cogs::Core::EnginePermutations::get(const StringView & name) const
151{
152 return get(getIndex(name));
153}
154
155Cogs::Core::EnginePermutation * Cogs::Core::EnginePermutations::get(size_t index)
156{
157 if (index == static_cast<size_t>(-1) || index >= enginePermutations.size()) return &enginePermutations[0];
158
159 return &enginePermutations[index];
160}
161
162Cogs::Core::EnginePermutation * Cogs::Core::EnginePermutations::get(const StringView & name)
163{
164 return get(getIndex(name));
165}
166
167Cogs::Core::EnginePermutation * Cogs::Core::EnginePermutations::getInternal(size_t index)
168{
169 return &enginePermutations[index];
170}
171
172Cogs::Core::EnginePermutation * Cogs::Core::EnginePermutations::getInternal(const StringView & name)
173{
174 return getInternal(getIndex(name));
175}
176
177bool Cogs::Core::EnginePermutations::exists(const StringView & name) const
178{
179 const size_t nameHash = name.hash();
180
181 for (auto & ep : enginePermutations) {
182 if (nameHash == ep.nameHash) {
183 return true;
184 }
185 }
186
187 return false;
188}
189
190size_t Cogs::Core::EnginePermutations::getIndex(const StringView & name) const
191{
192 const size_t nameHash = name.hash();
193
194 for (auto & ep : enginePermutations) {
195 if (nameHash == ep.nameHash) {
196 return ep.index;
197 }
198 }
199
200 LOG_ERROR(logger, "Cannot retrieve invalid permutation index for %.*s.", StringViewFormat(name));
201
202 return (size_t)-1;
203}
204
205bool Cogs::Core::EnginePermutations::load(const StringView & resource, bool isContent)
206{
207 const auto document = isContent ? parseJson(resource, JsonParseFlags::None) : parseJson(mContext, resource);
208
209 if (!document.IsObject()) {
210 if (!isContent) {
211 LOG_ERROR(logger, "Could not read engine permutations from %.*s.", StringViewFormat(resource));
212 }
213
214 return false;
215 }
216
217 for (auto & m : document.GetObject()) {
218 auto section = toKey(m.name);
219
220 if (section == "permutations") {
221 for (auto & p : m.value.GetObject()) {
222 auto pKey = toKey(p.name);
223
224 auto definition = createDefinition();
225 definition->name = toString(p.name);
226
227 for (auto & v : p.value.GetObject()) {
228 auto vKey = toKey(v.name);
229
230 if (vKey == "vertexShader") {
231 definition->vertexShader = v.value.GetString();
232 } else if (vKey == "geometryShader") {
233 definition->geometryShader = v.value.GetString();
234 } else if (vKey == "pixelShader") {
235 definition->pixelShader = v.value.GetString();
236 } else if (vKey == "definitions") {
237 if (!v.value.IsObject()) {
238 LOG_ERROR(logger, "Cannot read definitions from %.*s.", StringViewFormat(pKey));
239 continue;
240 }
241
242 for (auto & d : v.value.GetObject()) {
243 definition->definitions.push_back({ toString(d.name), toString(d.value) });
244 }
245 } else if (vKey == "requiredFlags") {
246 if (v.value.IsArray()) {
247 auto arr = v.value.GetArray();
248
249 for (auto & element : arr) {
250 if (!element.IsString()) {
251 LOG_ERROR(logger, "Invalid flag type.");
252 continue;
253 }
254
255 auto key = toKey(element);
256 definition->requiredFlags |= parseEnum(key, RenderFlags::None);
257 }
258 } else if (v.value.IsString()) {
259 definition->requiredFlags |= parseEnumFlags(toKey(v.value), RenderFlags::None);
260 } else {
261 LOG_ERROR(logger, "Invalid type for requiredFlags.");
262 continue;
263 }
264 } else if (vKey == "inherits") {
265 definition->inherits = toString(v.value);
266 } else if (vKey == "flags") {
267 if (v.value.IsString()) {
268 definition->flags |= getFlag(toKey(v.value));
269 } else {
270 auto arr = v.value.GetArray();
271
272 for (const auto & element : arr) {
273 definition->flags |= getFlag(toKey(element));
274 }
275 }
276 } else if (vKey == "variants") {
277 if (!readMaterialVariants(mContext, v.value, definition->variants)) return false;
278 } else if (vKey == "properties") {
279 if (!readMaterialProperties(mContext, v.value, definition->properties, false)) return false;
280 }
281 }
282
283 create(definition);
284 }
285 }
286 }
287
288 return true;
289}
290
291Cogs::Core::EnginePermutationFlags Cogs::Core::EnginePermutations::getFlag(const StringView & name)
292{
293 if (name == "DepthOnly")
294 return EnginePermutationFlags::DepthOnly;
295 else if (name == "NoShading")
296 return EnginePermutationFlags::NoShading;
297 else if (name == "ReverseDepth")
298 return EnginePermutationFlags::ReverseDepth;
299 else if (name == "HasBuiltins")
300 return EnginePermutationFlags::HasBuiltins;
301 else if (name != "None") {
302 LOG_WARNING(logger, "EnginePermutationFlags mismatch %.*s", StringViewFormat(name));
303 }
304 return EnginePermutationFlags::None;
305}
306
307void Cogs::Core::EnginePermutation::initialize(Context * context, EnginePermutationDefinition * definition_in)
308{
309 definition = definition_in;
310
311 nameHash = hash(definition->name);
312
313 selectors.resize(definition->variants.size());
314
315 for (const auto & v : definition->variants) {
316 selectors[v.index].index = v.index;
317 selectors[v.index].value = v.defaultValue;
318 }
319
320 applyConstantBuffers(context, definition->properties, constantBuffers);
321
322 if ((definition->requiredFlags & RenderFlags::CastShadows) != 0) {
323 requiredFlags |= RenderItemFlags::CastShadows;
324 }
325 updateFlags();
326
327 dirty = true;
328}
329
330namespace
331{
332 void applySelectorValue(bool & dirty, size_t & selector, size_t value)
333 {
334 if (value != selector) {
335 selector = value;
336 dirty = true;
337 }
338 }
339}
340
341void Cogs::Core::EnginePermutation::updateFlags()
342{
343 flags = definition->flags;
344 for (auto & v : definition->variants) {
345 if (selectors[v.index].value != 0) {
346 flags = flags | (EnginePermutationFlags)v.flags;
347 }
348 }
349}
350void Cogs::Core::EnginePermutation::setVariant(const StringView & key, const StringView & value)
351{
352 assert(selectors.size() == definition->variants.size() && "Variant selectors invalid.");
353
354 for (auto & v : definition->variants) {
355 if (key == v.name) {
356 if (v.type == ShaderVariantType::Bool) {
357 auto result = parseBool(value, v.defaultValue != 0);
358
359 applySelectorValue(dirty, selectors[v.index].value, result ? 1 : 0);
360 } else if (v.type == ShaderVariantType::Int) {
361 auto result = parseInt(value, v.defaultValue);
362
363 applySelectorValue(dirty, selectors[v.index].value, result);
364 } else if (v.type == ShaderVariantType::Enum) {
365 for (auto & e : v.values) {
366 if (value == e.key) {
367 applySelectorValue(dirty, selectors[v.index].value, e.index);
368 }
369 }
370 } else {
371 LOG_ERROR(logger, "Variant type %d not recognized.", static_cast<int>(v.type));
372 }
373
374 break;
375 }
376 }
377 updateFlags();
378}
379
380void Cogs::Core::EnginePermutation::setVariant(const StringView & key, bool value)
381{
382 for (auto & v : definition->variants) {
383 if (key == v.name) {
384 if (v.type != ShaderVariantType::Bool) {
385 LOG_WARNING(logger, "Variant type mismatch for %.*s (%d not Bool(%d)).", StringViewFormat(key), static_cast<int>(v.type), static_cast<int>(ShaderVariantType::Bool));
386 }
387 applySelectorValue(dirty, selectors[v.index].value, static_cast<size_t>(value));
388 updateFlags();
389 return;
390 }
391 }
392
393 LOG_WARNING(logger, "No matching boolean variant found for key %.*s.", StringViewFormat(key));
394}
395
396void Cogs::Core::EnginePermutation::setVariant(const StringView & key, int value)
397{
398 for (auto & v : definition->variants) {
399 if (key == v.name) {
400 if (v.type != ShaderVariantType::Int && v.type != ShaderVariantType::Enum) {
401 LOG_WARNING(logger, "Variant type mismatch for %.*s (%d not Int/Enum).", StringViewFormat(key), static_cast<int>(v.type));
402 }
403 applySelectorValue(dirty, selectors[v.index].value, static_cast<size_t>(value));
404 updateFlags();
405 return;
406 }
407 }
408
409 LOG_WARNING(logger, "No matching integer variant found for key %.*s.", StringViewFormat(key));
410}
411
412bool Cogs::Core::EnginePermutation::hasVariant(const StringView & key)
413{
414 for (auto & v : definition->variants)
415 if (key == v.name) return true;
416 return false;
417}
418
419void Cogs::Core::EnginePermutation::setProperty(const StringView & name, const void * value, int valueSize)
420{
421 auto key = constantBuffers.getPropertyKey(name);
422
423 if (key == NoProperty) {
424 LOG_WARNING(logger, "Could not find property %.*s in engine permutation %s.", StringViewFormat(name), definition->name.c_str());
425 return;
426 }
427
428 auto & variable = constantBuffers.variables[key];
429
430 if (variable.flags != MaterialPropertyFlags::None) {
431 enforcePropertyFlags(variable, variable.flags, &value);
432 }
433
434 constantBuffers.buffers[variable.buffer].setValue(variable.descriptor.offset, valueSize, static_cast<const uint8_t *>(value));
435 updateFlags();
436}
437
438size_t Cogs::Core::EnginePermutation::getCode() const
439{
440 if (dirty) {
441 code = hash(index, nameHash);
442
443 for (auto & d : definition->definitions) {
444 code = hash(d.second, code);
445 }
446 for (auto & s : selectors) {
447 code = hash(s.value, code);
448 }
449 dirty = false;
450 }
451 return code;
452}
453
Log implementation class.
Definition: LogManager.h:139
@ CastShadows
Casts shadows.
@ None
No render flags set.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62