1#include "EnginePermutations.h"
5#include "ParameterBuffers.h"
7#include "Resources/Material.h"
8#include "Resources/MaterialBuilder.h"
10#include "Serialization/JsonParser.h"
11#include "Serialization/MaterialReader.h"
13#include "Utilities/Parsing.h"
14#include "Components/Core/LightComponent.h"
15#include "Services/Variables.h"
16#include "RenderList.h"
18#include "Foundation/HashSequence.h"
19#include "Foundation/Logging/Logger.h"
25 const size_t kMaxEnginePermutations = 64;
28Cogs::Core::EnginePermutations::EnginePermutations(Context * context) :
29 permutationDefinitions(MemBlockType::RenderResourceStorage), mContext(context)
31 enginePermutations.reserve(kMaxEnginePermutations);
34Cogs::Core::EnginePermutations::~EnginePermutations()
36 EnginePermutationDefinition* def = firstDefinition;
38 EnginePermutationDefinition* n = def->next;
39 permutationDefinitions.destroy(def);
45void Cogs::Core::EnginePermutations::initialize(Context * context)
47 load(
"Default.permutations");
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.");
55 for (
auto & e : enginePermutations) {
56 auto & definitions = e.getDefinition()->definitions;
58 definitions.push_back({
"COGS_MAX_LIGHTS", std::to_string(context->renderer->getMaxLights()) });
60 if (context->variables->get(
"renderer.samples", 1) > 1) {
61 definitions.push_back({
"COGS_MSAA",
"1" });
68 EnginePermutationDefinition* rv = permutationDefinitions.create();
69 rv->next = firstDefinition;
77 auto definition = createDefinition();
78 definition->name = name.to_string();
80 return create(definition, base);
85 assert(enginePermutations.size() < kMaxEnginePermutations &&
"Permutation limit reached.");
87 auto name = StringView(definition->name);
88 auto nameHash = name.hash();
90 for (
auto & e : enginePermutations) {
91 if (e.getNameHash() == nameHash) {
92 LOG_WARNING(logger,
"Overwriting engine permutation %.*s.", StringViewFormat(name));
97 enginePermutations.emplace_back();
98 auto enginePermutation = &enginePermutations.back();
100 enginePermutation->index = enginePermutations.size() - 1;
101 enginePermutation->permutationMask = 1u << enginePermutation->index;
102 enginePermutation->definition = definition;
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());
114 enginePermutation->initialize(mContext, definition);
116 return enginePermutation;
119bool Cogs::Core::EnginePermutations::inherit(EnginePermutation * enginePermutation,
const EnginePermutation * base)
121 enginePermutation->permutationMask = base->permutationMask;
123 auto inheritIfNotSet = [](std::string & a,
const std::string & b) {
124 if (a.empty()) a = b;
127 inheritIfNotSet(enginePermutation->definition->vertexShader, base->definition->vertexShader);
128 inheritIfNotSet(enginePermutation->definition->geometryShader, base->definition->geometryShader);
129 inheritIfNotSet(enginePermutation->definition->pixelShader, base->definition->pixelShader);
131 enginePermutation->definition->flags |= base->definition->flags;
132 enginePermutation->definition->requiredFlags |= base->definition->requiredFlags;
134 for (
auto & d : base->definition->definitions) {
135 enginePermutation->definition->definitions.emplace_back(d);
138 return inheritMaterialVariants(enginePermutation->definition->variants, base->definition->variants,
139 enginePermutation->definition->name, base->definition->name,
145 if (index ==
static_cast<size_t>(-1) || index >= enginePermutations.size())
return &enginePermutations[0];
147 return &enginePermutations[index];
152 return get(getIndex(name));
157 if (index ==
static_cast<size_t>(-1) || index >= enginePermutations.size())
return &enginePermutations[0];
159 return &enginePermutations[index];
164 return get(getIndex(name));
169 return &enginePermutations[index];
174 return getInternal(getIndex(name));
177bool Cogs::Core::EnginePermutations::exists(
const StringView & name)
const
179 const size_t nameHash = name.hash();
181 for (
auto & ep : enginePermutations) {
182 if (nameHash == ep.nameHash) {
190size_t Cogs::Core::EnginePermutations::getIndex(
const StringView & name)
const
192 const size_t nameHash = name.hash();
194 for (
auto & ep : enginePermutations) {
195 if (nameHash == ep.nameHash) {
200 LOG_ERROR(logger,
"Cannot retrieve invalid permutation index for %.*s.", StringViewFormat(name));
205bool Cogs::Core::EnginePermutations::load(
const StringView & resource,
bool isContent)
207 const auto document = isContent ? parseJson(resource, JsonParseFlags::None) : parseJson(mContext, resource);
209 if (!document.IsObject()) {
211 LOG_ERROR(logger,
"Could not read engine permutations from %.*s.", StringViewFormat(resource));
217 for (
auto & m : document.GetObject()) {
218 auto section = toKey(m.name);
220 if (section ==
"permutations") {
221 for (
auto & p : m.value.GetObject()) {
222 auto pKey = toKey(p.name);
224 auto definition = createDefinition();
225 definition->name = toString(p.name);
227 for (
auto & v : p.value.GetObject()) {
228 auto vKey = toKey(v.name);
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));
242 for (
auto & d : v.value.GetObject()) {
243 definition->definitions.push_back({ toString(d.name), toString(d.value) });
245 }
else if (vKey ==
"requiredFlags") {
246 if (v.value.IsArray()) {
247 auto arr = v.value.GetArray();
249 for (
auto & element : arr) {
250 if (!element.IsString()) {
251 LOG_ERROR(logger,
"Invalid flag type.");
255 auto key = toKey(element);
258 }
else if (v.value.IsString()) {
261 LOG_ERROR(logger,
"Invalid type for requiredFlags.");
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));
270 auto arr = v.value.GetArray();
272 for (
const auto & element : arr) {
273 definition->flags |= getFlag(toKey(element));
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;
291Cogs::Core::EnginePermutationFlags Cogs::Core::EnginePermutations::getFlag(
const StringView & name)
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));
304 return EnginePermutationFlags::None;
307void Cogs::Core::EnginePermutation::initialize(Context * context, EnginePermutationDefinition * definition_in)
309 definition = definition_in;
311 nameHash =
hash(definition->name);
313 selectors.resize(definition->variants.size());
315 for (
const auto & v : definition->variants) {
316 selectors[v.index].index = v.index;
317 selectors[v.index].value = v.defaultValue;
320 applyConstantBuffers(context, definition->properties, constantBuffers);
323 requiredFlags |= RenderItemFlags::CastShadows;
332 void applySelectorValue(
bool & dirty,
size_t & selector,
size_t value)
334 if (value != selector) {
341void Cogs::Core::EnginePermutation::updateFlags()
343 flags = definition->flags;
344 for (
auto & v : definition->variants) {
345 if (selectors[v.index].value != 0) {
346 flags = flags | (EnginePermutationFlags)v.flags;
350void Cogs::Core::EnginePermutation::setVariant(
const StringView & key,
const StringView & value)
352 assert(selectors.size() == definition->variants.size() &&
"Variant selectors invalid.");
354 for (
auto & v : definition->variants) {
356 if (v.type == ShaderVariantType::Bool) {
357 auto result = parseBool(value, v.defaultValue != 0);
359 applySelectorValue(dirty, selectors[v.index].value, result ? 1 : 0);
360 }
else if (v.type == ShaderVariantType::Int) {
361 auto result = parseInt(value, v.defaultValue);
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);
371 LOG_ERROR(logger,
"Variant type %d not recognized.",
static_cast<int>(v.type));
380void Cogs::Core::EnginePermutation::setVariant(
const StringView & key,
bool value)
382 for (
auto & v : definition->variants) {
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));
387 applySelectorValue(dirty, selectors[v.index].value,
static_cast<size_t>(value));
393 LOG_WARNING(logger,
"No matching boolean variant found for key %.*s.", StringViewFormat(key));
396void Cogs::Core::EnginePermutation::setVariant(
const StringView & key,
int value)
398 for (
auto & v : definition->variants) {
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));
403 applySelectorValue(dirty, selectors[v.index].value,
static_cast<size_t>(value));
409 LOG_WARNING(logger,
"No matching integer variant found for key %.*s.", StringViewFormat(key));
412bool Cogs::Core::EnginePermutation::hasVariant(
const StringView & key)
414 for (
auto & v : definition->variants)
415 if (key == v.name)
return true;
419void Cogs::Core::EnginePermutation::setProperty(
const StringView & name,
const void * value,
int valueSize)
421 auto key = constantBuffers.getPropertyKey(name);
423 if (key == NoProperty) {
424 LOG_WARNING(logger,
"Could not find property %.*s in engine permutation %s.", StringViewFormat(name), definition->name.c_str());
428 auto & variable = constantBuffers.variables[key];
431 enforcePropertyFlags(variable, variable.flags, &value);
434 constantBuffers.buffers[variable.buffer].setValue(variable.descriptor.offset, valueSize,
static_cast<const uint8_t *
>(value));
438size_t Cogs::Core::EnginePermutation::getCode()
const
441 code =
hash(index, nameHash);
443 for (
auto & d : definition->definitions) {
444 code =
hash(d.second, code);
446 for (
auto & s : selectors) {
447 code =
hash(s.value, code);
Log implementation class.
@ CastShadows
Casts shadows.
@ None
No render flags set.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.