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;
30 const size_t spaceIndex = valueString.
find(
" ");
35 value.type = MaterialDataType::Unknown;
39 switch (typeString.
hash()) {
64 for (
size_t i = 0; i + 1 < typeString.
size(); i++) {
65 if ((typeString[i] ==
'[') && (
'0' <= typeString[i + 1]) && (typeString[i + 1] <=
'9')) {
67 size_t dimension = typeString[i + 1] -
'0';
68 for (
size_t j = i + 2; j < typeString.
size(); j++) {
69 if (
'0' <= typeString[j] && typeString[j] <=
'9') {
70 dimension = 10 * dimension + (typeString[j] -
'0');
72 else if ((typeString[j] ==
']') && (j + 1 == typeString.
size())) {
77 value.dimension = dimension;
78 value.dimensionString = typeString.
substr(i + 1, j - i - 1).
to_string();
83 value.dimension = dimension;
84 value.dimensionString = typeString.
substr(i + 1, j - i - 1).
to_string();
98 LOG_ERROR(logger,
"Failed to parse datatype '%.*s'", StringViewFormat(valueString));
107 if (!jsonObject.IsString()) {
108 LOG_ERROR(logger,
"Interpolation modifier is not a JSON string");
113 switch (value.
hash()) {
130 LOG_ERROR(logger,
"Unrecognized interpolation modifier '%.*s'", StringViewFormat(value));
139 if (!jsonObject.IsObject()) {
140 LOG_ERROR(logger,
"Shader interface is not an JSON object");
144 for (
auto& m : jsonObject.GetObject()) {
151 auto arr = obj.GetArray();
155 if (!parseType(toKey(arr[0]), value, allowFormat))
return false;
157 if (value.dimension ==
size_t(-1)) {
158 if (arr[1].IsString()) {
159 value.dimensionString = toString(arr[1]);
162 value.dimension = arr[1].GetInt();
166 if (arr.Size() >= 2) {
167 if (arr[1].IsString()) {
168 value.semantic.name = ShaderInterfaceMemberDefinition::SemanticName::None;
169 value.semantic.slot = 0;
170 Cogs::StringView semanticName(arr[1].GetString(), arr[1].GetStringLength());
173 for (
size_t i = 0; i < semanticName.size(); i++) {
174 if (
'0' <= semanticName[i] && semanticName[i] <=
'9') {
175 value.semantic.slot = semanticName[i] -
'0';
177 while ((k < semanticName.size()) &&
178 (
'0' <= semanticName[k]) &&
179 (semanticName[k] <=
'9'))
181 value.semantic.slot = uint8_t(10u * value.semantic.slot + (semanticName[k] -
'0'));
183 semanticName = semanticName.substr(0, i);
187 switch (semanticName.hashLowercase()) {
202 LOG_ERROR(logger,
"Unrecognized semantic '%.*s'", StringViewFormat(semanticName));
207 LOG_ERROR(logger,
"Semantic is not a string");
212 if (arr.Size() >= 3) {
213 auto& modifiers = arr[2];
214 if (modifiers.IsString()) {
215 if (!parseInterpolationModifier(modifiers, value))
return false;
217 else if (modifiers.IsArray()) {
218 for (
auto& modifier : modifiers.GetArray()) {
219 if (!parseInterpolationModifier(modifier, value))
return false;
225 else if (obj.IsString()) {
226 if (!parseType(toKey(obj), value, allowFormat))
return false;
230 value.inheritanceLevel = inheritanceLevel;
232 interfaceDefinition.members.push_back(value);
241 const size_t spaceIndex = valueString.
find(
" ");
244 switch (typeString.
hash()) {
258 LOG_ERROR(logger,
"Failed to parse effect ouput datatype '%.*s'", StringViewFormat(valueString));
267 if (!jsonObject.IsObject()) {
268 LOG_ERROR(logger,
"Effect output is not an JSON object");
272 for (
auto& m : jsonObject.GetObject()) {
276 outputMember.name = name;
277 outputMember.target =
static_cast<uint8_t
>(outputDefinition.members.size());
278 outputMember.dataType = MaterialDataType::Float4;
280 if (!obj.IsObject()) {
281 LOG_ERROR(logger,
"Effect output parameter description %s is not a json object.", outputMember.name.c_str());
284 for (
auto& p : obj.GetObject()) {
286 auto& parameter = p.value;
290 if (!parameter.IsString()) {
291 LOG_ERROR(logger,
"Datatype of effect output parameter %s for %s is not a string.", parameterName.
to_string().c_str(), outputMember.name.c_str());
294 std::string s = parameter.GetString();
295 if (!parseOutputType(s, outputMember.dataType))
return false;
299 if (!parameter.IsInt()) {
300 LOG_ERROR(logger,
"Target of effect output parameter %s for %s is not a number.", parameterName.
to_string().c_str(), outputMember.name.c_str());
304 outputMember.target =
static_cast<uint8_t
>(parameter.GetInt());
308 LOG_ERROR(logger,
"Unknown effect output parameter for %s.", parameterName.
to_string().c_str());
312 outputDefinition.members.push_back(outputMember);
318Cogs::Core::EnginePermutations::EnginePermutations(
Context * context) :
321 enginePermutations.reserve(kMaxEnginePermutations);
324Cogs::Core::EnginePermutations::~EnginePermutations()
329 permutationDefinitions.destroy(def);
335void Cogs::Core::EnginePermutations::initialize(
Context * context)
337 load(
"Default.permutations");
340 assert(get(0) == get(
"Forward") &&
"Invalid default permutation order.");
341 assert(get(1) == get(
"Deferred") &&
"Invalid default permutation order.");
342 assert(get(2) == get(
"Shadow") &&
"Invalid default permutation order.");
343 assert(get(3) == get(
"Transparent") &&
"Invalid default permutation order.");
345 for (
auto & e : enginePermutations) {
346 auto & definitions = e.getDefinition()->definitions;
348 definitions.push_back({
"COGS_MAX_LIGHTS", std::to_string(context->
renderer->
getMaxLights()) });
350 if (context->
variables->get(
"renderer.samples", 1) > 1) {
351 definitions.push_back({
"COGS_MSAA",
"1" });
359 rv->next = firstDefinition;
360 firstDefinition = rv;
367 auto definition = createDefinition();
370 return create(definition, base);
375 assert(enginePermutations.size() < kMaxEnginePermutations &&
"Permutation limit reached.");
378 auto nameHash = name.
hash();
380 for (
auto & e : enginePermutations) {
381 if (e.getNameHash() == nameHash) {
382 LOG_WARNING(logger,
"Overwriting engine permutation %.*s.", StringViewFormat(name));
387 enginePermutations.emplace_back();
388 auto enginePermutation = &enginePermutations.back();
390 enginePermutation->index = enginePermutations.size() - 1;
391 enginePermutation->permutationMask = 1u << enginePermutation->index;
392 enginePermutation->definition = definition;
395 assert(definition->inherits.empty());
396 bool ret = inherit(enginePermutation, base);
397 if (!ret) LOG_WARNING(logger,
"EnginePermutation %s inherit %s failed", definition->name.c_str(), base->getName().c_str());
398 }
else if (definition->inherits.size()) {
400 bool ret = inherit(enginePermutation, inherits);
401 if (!ret) LOG_WARNING(logger,
"EnginePermutation %s inherit %s failed", definition->name.c_str(), inherits->getName().c_str());
404 enginePermutation->initialize(mContext, definition);
406 return enginePermutation;
411 enginePermutation->permutationMask = base->permutationMask;
413 auto inheritIfNotSet = [](std::string & a,
const std::string & b) {
414 if (a.empty()) a = b;
417 inheritIfNotSet(enginePermutation->definition->vertexShader, base->definition->vertexShader);
418 inheritIfNotSet(enginePermutation->definition->geometryShader, base->definition->geometryShader);
419 inheritIfNotSet(enginePermutation->definition->pixelShader, base->definition->pixelShader);
420 if (enginePermutation->definition->outputs.members.empty()) {
421 enginePermutation->definition->outputs = base->definition->outputs;
424 enginePermutation->definition->flags |= base->definition->flags;
425 enginePermutation->definition->requiredFlags |= base->definition->requiredFlags;
427 for (
auto & d : base->definition->definitions) {
428 enginePermutation->definition->definitions.emplace_back(d);
431 return inheritMaterialVariants(enginePermutation->definition->variants, base->definition->variants,
432 enginePermutation->definition->name, base->definition->name,
438 if (index ==
static_cast<size_t>(-1) || index >= enginePermutations.size())
return &enginePermutations[0];
440 return &enginePermutations[index];
445 return get(getIndex(name));
450 if (index ==
static_cast<size_t>(-1) || index >= enginePermutations.size())
return &enginePermutations[0];
452 return &enginePermutations[index];
457 return get(getIndex(name));
462 return &enginePermutations[index];
467 return getInternal(getIndex(name));
470bool Cogs::Core::EnginePermutations::exists(
const StringView & name)
const
472 const size_t nameHash = name.
hash();
474 for (
auto & ep : enginePermutations) {
475 if (nameHash == ep.nameHash) {
483size_t Cogs::Core::EnginePermutations::getIndex(
const StringView & name)
const
485 const size_t nameHash = name.
hash();
487 for (
auto & ep : enginePermutations) {
488 if (nameHash == ep.nameHash) {
493 LOG_ERROR(logger,
"Cannot retrieve invalid permutation index for %.*s.", StringViewFormat(name));
498bool Cogs::Core::EnginePermutations::load(
const StringView & resource,
bool isContent)
500 const auto document = isContent ? parseJson(resource, JsonParseFlags::None) : parseJson(mContext, resource);
502 if (!document.IsObject()) {
504 LOG_ERROR(logger,
"Could not read engine permutations from %.*s.", StringViewFormat(resource));
510 for (
auto & m : document.GetObject()) {
511 auto section = toKey(m.name);
513 if (section ==
"permutations") {
514 for (
auto & p : m.value.GetObject()) {
515 auto pKey = toKey(p.name);
517 auto definition = createDefinition();
518 definition->name = toString(p.name);
520 for (
auto & v : p.value.GetObject()) {
521 auto vKey = toKey(v.name);
523 if (vKey ==
"vertexShader") {
524 definition->vertexShader = v.value.GetString();
525 }
else if (vKey ==
"geometryShader") {
526 definition->geometryShader = v.value.GetString();
527 }
else if (vKey ==
"pixelShader") {
528 definition->pixelShader = v.value.GetString();
529 }
else if (vKey ==
"definitions") {
530 if (!v.value.IsObject()) {
531 LOG_ERROR(logger,
"Cannot read definitions from %.*s.", StringViewFormat(pKey));
535 for (
auto & d : v.value.GetObject()) {
536 definition->definitions.push_back({ toString(d.name), toString(d.value) });
538 }
else if (vKey ==
"requiredFlags") {
539 if (v.value.IsArray()) {
540 auto arr = v.value.GetArray();
542 for (
auto & element : arr) {
543 if (!element.IsString()) {
544 LOG_ERROR(logger,
"Invalid flag type.");
548 auto key = toKey(element);
551 }
else if (v.value.IsString()) {
554 LOG_ERROR(logger,
"Invalid type for requiredFlags.");
557 }
else if (vKey ==
"inherits") {
558 definition->inherits = toString(v.value);
559 }
else if (vKey ==
"flags") {
560 if (v.value.IsString()) {
561 definition->flags |= getFlag(toKey(v.value));
563 auto arr = v.value.GetArray();
565 for (
const auto & element : arr) {
566 definition->flags |= getFlag(toKey(element));
569 }
else if (vKey ==
"variants") {
570 if (!readMaterialVariants(mContext, v.value, definition->variants))
return false;
571 }
else if (vKey ==
"properties") {
572 if (!readMaterialProperties(mContext, v.value, definition->properties,
false))
return false;
574 else if (vKey ==
"vertexInterface") {
575 if (!parseShaderInterface(v.value, definition->vertexInterface, 0,
false))
return false;
577 else if (vKey ==
"surfaceInterface") {
578 if (!parseShaderInterface(v.value, definition->surfaceInterface, 0,
false))
return false;
579 }
else if (vKey ==
"output") {
580 if (!parseEffectOutput(v.value, definition->outputs))
return false;
594Cogs::Core::EnginePermutationFlags Cogs::Core::EnginePermutations::getFlag(
const StringView & name)
596 if (name ==
"DepthOnly")
597 return EnginePermutationFlags::DepthOnly;
598 else if (name ==
"NoShading")
599 return EnginePermutationFlags::NoShading;
600 else if (name ==
"ReverseDepth")
601 return EnginePermutationFlags::ReverseDepth;
602 else if (name ==
"HasBuiltins")
603 return EnginePermutationFlags::HasBuiltins;
604 else if (name !=
"None") {
605 LOG_WARNING(logger,
"EnginePermutationFlags mismatch %.*s", StringViewFormat(name));
607 return EnginePermutationFlags::None;
612 definition = definition_in;
614 nameHash =
hash(definition->name);
616 selectors.resize(definition->variants.size());
618 for (
const auto & v : definition->variants) {
619 selectors[v.index].index = v.index;
620 selectors[v.index].value = v.defaultValue;
623 applyConstantBuffers(context, definition->properties, constantBuffers);
626 requiredFlags |= RenderItemFlags::CastShadows;
635 void applySelectorValue(
bool & dirty,
size_t & selector,
size_t value)
637 if (value != selector) {
644void Cogs::Core::EnginePermutation::updateFlags()
646 flags = definition->flags;
647 for (
auto & v : definition->variants) {
648 if (selectors[v.index].value != 0) {
649 flags = flags | (EnginePermutationFlags)v.flags;
655 assert(selectors.size() == definition->variants.size() &&
"Variant selectors invalid.");
657 for (
auto & v : definition->variants) {
659 if (v.type == ShaderVariantType::Bool) {
660 auto result = parseBool(value, v.defaultValue != 0);
662 applySelectorValue(dirty, selectors[v.index].value, result ? 1 : 0);
663 }
else if (v.type == ShaderVariantType::Int) {
664 auto result = parseInt(value, v.defaultValue);
666 applySelectorValue(dirty, selectors[v.index].value, result);
667 }
else if (v.type == ShaderVariantType::Enum) {
668 for (
auto & e : v.values) {
669 if (value == e.key) {
670 applySelectorValue(dirty, selectors[v.index].value, e.index);
674 LOG_ERROR(logger,
"Variant type %d not recognized.",
static_cast<int>(v.type));
683void Cogs::Core::EnginePermutation::setVariant(
const StringView & key,
bool value)
685 for (
auto & v : definition->variants) {
687 if (v.type != ShaderVariantType::Bool) {
688 LOG_WARNING(logger,
"Variant type mismatch for %.*s (%d not Bool(%d)).", StringViewFormat(key),
static_cast<int>(v.type),
static_cast<int>(ShaderVariantType::Bool));
690 applySelectorValue(dirty, selectors[v.index].value,
static_cast<size_t>(value));
696 LOG_WARNING(logger,
"No matching boolean variant found for key %.*s.", StringViewFormat(key));
699void Cogs::Core::EnginePermutation::setVariant(
const StringView & key,
int value)
701 for (
auto & v : definition->variants) {
703 if (v.type != ShaderVariantType::Int && v.type != ShaderVariantType::Enum) {
704 LOG_WARNING(logger,
"Variant type mismatch for %.*s (%d not Int/Enum).", StringViewFormat(key),
static_cast<int>(v.type));
706 applySelectorValue(dirty, selectors[v.index].value,
static_cast<size_t>(value));
712 LOG_WARNING(logger,
"No matching integer variant found for key %.*s.", StringViewFormat(key));
715bool Cogs::Core::EnginePermutation::hasVariant(
const StringView & key)
717 for (
auto & v : definition->variants)
718 if (key == v.name)
return true;
722void Cogs::Core::EnginePermutation::setProperty(
const StringView & name,
const void * value,
int valueSize)
724 auto key = constantBuffers.getPropertyKey(name);
726 if (key == NoProperty) {
727 LOG_WARNING(logger,
"Could not find property %.*s in engine permutation %s.", StringViewFormat(name), definition->name.c_str());
731 auto & variable = constantBuffers.variables[key];
734 enforcePropertyFlags(variable, variable.flags, &value);
737 constantBuffers.buffers[variable.buffer].setValue(variable.descriptor.offset, valueSize,
static_cast<const uint8_t *
>(value));
741size_t Cogs::Core::EnginePermutation::getCode()
const
744 code =
hash(index, nameHash);
746 for (
auto & d : definition->definitions) {
747 code =
hash(d.second, code);
749 for (
auto & s : selectors) {
750 code =
hash(s.value, code);
A Context instance contains all the services, systems and runtime components needed to use Cogs.
class IRenderer * renderer
Renderer.
std::unique_ptr< class Variables > variables
Variables service instance.
virtual unsigned getMaxLights() const =0
Get the maximum number of lights.
Log implementation class.
Provides a weakly referenced view over the contents of a string.
constexpr size_t size() const noexcept
Get the size of the string.
constexpr StringView substr(size_t offset, size_t count=NoPosition) const noexcept
Get the given sub string.
static constexpr size_t NoPosition
No position.
size_t find(const StringView &other, size_t offset=0) const noexcept
Find the given string segment inside the string.
std::string to_string() const
String conversion method.
constexpr size_t hash() const noexcept
Get the hash code of the string.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
@ Normal
Regular shaded rendering.
MaterialDataType
Defines available data types for material properties.
@ 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.
COGSFOUNDATION_API size_t hashLowercase(std::string_view str, size_t hashValue=Cogs::hash()) noexcept
Get the hash code of the string converted to lowercase.