1#include "MaterialManager.h"
8#include "MaterialBuilder.h"
9#include "ResourceStore.h"
10#include "ShaderBuilder.h"
12#include "Components/Core/ClipShapeComponent.h"
13#include "Renderer/IRenderer.h"
14#include "Serialization/MaterialReader.h"
15#include "Systems/Core/CameraSystem.h"
17#include "TextureManager.h"
18#include "EffectManager.h"
19#include "DefaultMaterial.h"
20#include "MaterialDefinition.h"
22#include "Foundation/Logging/Logger.h"
23#include "Foundation/Platform/FileSystemWatcher.h"
24#include "Foundation/Platform/IO.h"
25#include "Foundation/Platform/Timer.h"
27#include "Rendering/IGraphicsDevice.h"
49 const static char * ShaderNames[ShaderTypes::ShaderTypeCount] = {
63 reportLeaks(
"Material");
75 context->resourceStore->addSearchPath(
"../Data/Materials/");
76 context->resourceStore->addSearchPath(
"Materials/");
78 materialBase = loadMaterial(
"MaterialBase.material");
80 defaultResource = loadMaterial(
"DefaultMaterial.material");
82 static_cast<void>(loadMaterial(
"LineMaterial.material"));
83 static_cast<void>(loadMaterial(
"PointMaterial.material"));
84 static_cast<void>(loadMaterial(
"StandardMaterial.material"));
88 defaultResource->setTextureProperty(DefaultMaterial::DiffuseMap, context->textureManager->white);
93 return defaultResource;
98 auto found = materials.find(name.
to_string());
100 if (found != materials.end())
return found->second;
107 auto existing = getMaterial(fileName);
116 auto loaded = getAllocatedResources();
118 for (
auto & m : loaded) {
119 if (fileName == m->getSource()) {
127 loadInfo.materialLoadFlags = materialLoadFlags;
129 if (context->variables->get(
"resources.materials.autoReload",
false)) {
130 loadInfo.
loadFlags |= ResourceLoadFlags::AutoReload;
133 return loadResource(&loadInfo);
138 auto material = get(loadInfo->
handle);
141 if (!parseMaterial(context, loadInfo->
resourcePath, material->definition)) {
142 loadInfo->
handle->setFailedLoad();
143 setProcessed(loadInfo);
148 if (!setupMaterial(material)) {
149 LOG_WARNING(logger,
"Failed set up material %.*s", StringViewFormat(material->getName()));
150 loadInfo->
handle->setFailedLoad();
151 setProcessed(loadInfo);
155 auto found = materials.find(material->getName().to_string());
157 if (found != materials.end()) {
158 LOG_WARNING(logger,
"Overwriting registered material with name %s. Existing instances will not be affected, but retrieving by name will return new material.", material->getName().data());
161 materials[material->getName().to_string()] = loadInfo->
handle;
163 setProcessed(loadInfo);
170 auto loadInfo = createLoadInfo();
171 loadInfo->resourceId = material->getId();
172 loadInfo->resourcePath = material->getSource().to_string();
173 loadInfo->resourceName = material->getName().to_string();
175 loadInfo->handle = material;
177 static_cast<void>(loadResource(loadInfo));
180bool Cogs::Core::MaterialManager::setupMaterial(
Material * material)
182 auto & definition = material->definition;
184 if (!definition.isTemplate()) {
185 if (!material->constantBuffers.
buffers.size()) {
186 if (!applyMaterialDefinition(context, material->definition, *material))
return false;
188 material->constantBuffers.
buffers.clear();
190 if (!applyMaterialDefinition(context, definition_, *material))
return false;
191 material->definition = definition_;
196 if (definition.isTemplate()) {
197 material->setResident();
201 material->permutationKeys.resize(definition.permutations.size());
203 for (
auto & materialPermutation : definition.permutations) {
204 material->permutationKeys[materialPermutation.permutationIndex] = materialPermutation.permutationName;
218 case Cogs::Format::R8_UNORM:
219 case Cogs::Format::R16_UNORM:
220 case Cogs::Format::R8_SNORM:
221 case Cogs::Format::R16_SNORM:
222 case Cogs::Format::R16_FLOAT:
223 case Cogs::Format::R32_FLOAT:
224 return MaterialDataType::Float;
225 case Cogs::Format::R8G8_UNORM:
226 case Cogs::Format::R16G16_UNORM:
227 case Cogs::Format::R8G8_SNORM:
228 case Cogs::Format::R16G16_SNORM:
229 case Cogs::Format::R16G16_FLOAT:
230 case Cogs::Format::R32G32_FLOAT:
231 return MaterialDataType::Float2;
232 case Cogs::Format::R8G8B8_UNORM:
233 case Cogs::Format::R16G16B16_UNORM:
234 case Cogs::Format::R8G8B8_SNORM:
235 case Cogs::Format::R16G16B16_SNORM:
236 case Cogs::Format::R16G16B16_FLOAT:
237 case Cogs::Format::R32G32B32_FLOAT:
238 case Cogs::Format::R8G8B8_UNORM_SRGB:
239 case Cogs::Format::R11G11B10_FLOAT:
240 case Cogs::Format::R5G6B5_UNORM:
241 return MaterialDataType::Float3;
242 case Cogs::Format::R8G8B8A8_UNORM:
243 case Cogs::Format::R16G16B16A16_UNORM:
244 case Cogs::Format::R8G8B8A8_SNORM:
245 case Cogs::Format::R16G16B16A16_SNORM:
246 case Cogs::Format::R16G16B16A16_FLOAT:
247 case Cogs::Format::R32G32B32A32_FLOAT:
248 case Cogs::Format::R8G8B8A8_UNORM_SRGB:
249 case Cogs::Format::R10G10B10A2_UNORM:
250 case Cogs::Format::R5G5B5A1_UNORM:
251 case Cogs::Format::R4G4B4A4_UNORM:
252 case Cogs::Format::R9G9B9E5_FLOAT:
253 return MaterialDataType::Float4;
254 case Cogs::Format::R8_UINT:
255 case Cogs::Format::R16_UINT:
256 case Cogs::Format::R32_UINT:
257 return MaterialDataType::UInt;
258 case Cogs::Format::R8G8_UINT:
259 case Cogs::Format::R16G16_UINT:
260 case Cogs::Format::R32G32_UINT:
261 return MaterialDataType::UInt2;
262 case Cogs::Format::R8G8B8_UINT:
263 case Cogs::Format::R16G16B16_UINT:
264 case Cogs::Format::R32G32B32_UINT:
265 return MaterialDataType::UInt3;
266 case Cogs::Format::R8G8B8A8_UINT:
267 case Cogs::Format::R16G16B16A16_UINT:
268 case Cogs::Format::R32G32B32A32_UINT:
269 case Cogs::Format::R10G10B10A2_UINT:
270 return MaterialDataType::UInt4;
271 case Cogs::Format::R8_SINT:
272 case Cogs::Format::R16_SINT:
273 case Cogs::Format::R32_SINT:
274 return MaterialDataType::Int;
275 case Cogs::Format::R8G8_SINT:
276 case Cogs::Format::R16G16_SINT:
277 case Cogs::Format::R32G32_SINT:
278 return MaterialDataType::Int2;
279 case Cogs::Format::R8G8B8_SINT:
280 case Cogs::Format::R16G16B16_SINT:
281 case Cogs::Format::R32G32B32_SINT:
282 return MaterialDataType::Int3;
283 case Cogs::Format::R8G8B8A8_SINT:
284 case Cogs::Format::R16G16B16A16_SINT:
285 case Cogs::Format::R32G32B32A32_SINT:
286 return MaterialDataType::Int4;
287 case Cogs::Format::MAT4X4_FLOAT:
288 return MaterialDataType::Float4x4;
290 LOG_ERROR(logger,
"Illegal format %u",
unsigned(format));
291 return MaterialDataType::Unknown;
295 const char* formatShaderTypeString(Cogs::Format format)
297 switch (formatShaderType(format)) {
298 case MaterialDataType::Float:
return "float";
299 case MaterialDataType::Float2:
return "float2";
300 case MaterialDataType::Float3:
return "float3";
301 case MaterialDataType::Float4:
return "float4";
302 case MaterialDataType::UInt:
return "uint";
303 case MaterialDataType::UInt2:
return "uint2";
304 case MaterialDataType::UInt3:
return "uint3";
305 case MaterialDataType::UInt4:
return "uint4";
306 case MaterialDataType::Int:
return "int";
307 case MaterialDataType::Int2:
return "int2";
308 case MaterialDataType::Int3:
return "int3";
309 case MaterialDataType::Int4:
return "int4";
310 case MaterialDataType::Float4x4:
return "float4x4";
317 void moveSystemGeneratedParametersLast(std::vector<ShaderInterfaceMemberDefinition>& parameters)
319 std::sort(parameters.begin(), parameters.end(),
322 int aValue = (a.type == MaterialDataType::SV_IsFrontFace || a.type == MaterialDataType::VFACE || a.type == MaterialDataType::SV_InstanceID) ? 1 : 0;
323 int bValue = (b.type == MaterialDataType::SV_IsFrontFace || b.type == MaterialDataType::VFACE || b.type == MaterialDataType::SV_InstanceID) ? 1 : 0;
324 return aValue < bValue;
328 void addShaderStageInterfaceMembers(
const std::string& materialName,
329 std::vector<ShaderInterfaceMemberDefinition>& existingMembers,
330 std::span<const ShaderInterfaceMemberDefinition> newMembers)
339 if ((member.semantic.name != ShaderInterfaceMemberDefinition::SemanticName::None) &&
340 (member.semantic.name == existing.semantic.name) &&
341 (member.semantic.slot == existing.semantic.slot))
345 if (member.modifiers == existing.modifiers &&
346 member.name == existing.name &&
347 member.type == existing.type &&
348 member.dimension == existing.dimension &&
349 member.dimensionString == existing.dimensionString)
353 existing.inheritanceLevel = std::min(existing.inheritanceLevel, member.inheritanceLevel);
355 else if (member.inheritanceLevel < existing.inheritanceLevel) {
356 LOG_DEBUG(logger,
"%s: Replacing '%s' (level=%d) with '%s' (level=%d) (semantic=%d:%d)",
357 materialName.c_str(),
358 existing.name.c_str(),
int(existing.inheritanceLevel),
359 member.name.c_str(),
int(member.inheritanceLevel),
360 int(existing.semantic.name),
int(existing.semantic.slot));
361 existing.name = member.name;
362 existing.modifiers = member.modifiers;
363 existing.type = member.type;
364 existing.dimension = member.dimension;
365 existing.dimensionString = member.dimensionString;
367 else if (existing.inheritanceLevel < member.inheritanceLevel) {
368 LOG_DEBUG(logger,
"%s: Keeping '%s' (level=%d) over '%s' (level=%d) (semantic=%d:%d)",
369 materialName.c_str(),
370 existing.name.c_str(),
int(existing.inheritanceLevel),
371 member.name.c_str(),
int(member.inheritanceLevel),
372 int(existing.semantic.name),
int(existing.semantic.slot));
375 LOG_ERROR(logger,
"%s: Conflicting inheritance levels: '%s' (level=%d) over '%s' (level=%d) (semantic=%d:%d)",
376 materialName.c_str(),
377 existing.name.c_str(),
int(existing.inheritanceLevel),
378 member.name.c_str(),
int(member.inheritanceLevel),
379 int(existing.semantic.name),
int(existing.semantic.slot));
384 if (member.name == existing.name) {
391 existingMembers.emplace_back(member);
400 std::vector<ShaderInterfaceMemberDefinition>& vertexMembers = materialPermutation.effect.vertexShader.shaderInterface.members;
401 std::vector<ShaderInterfaceMemberDefinition>& geometryMembers = materialPermutation.effect.geometryShader.shaderInterface.members;
402 std::vector<ShaderInterfaceMemberDefinition>& surfaceMembers = materialPermutation.effect.pixelShader.shaderInterface.members;
404 std::vector<ShaderInterfaceMemberDefinition> tmp;
405 addShaderStageInterfaceMembers(materialPermutation.name, tmp, vertexMembers);
406 vertexMembers.swap(tmp);
409 addShaderStageInterfaceMembers(materialPermutation.name, tmp, geometryMembers);
410 geometryMembers.swap(tmp);
413 addShaderStageInterfaceMembers(materialPermutation.name, tmp, surfaceMembers);
414 surfaceMembers.swap(tmp);
419 std::span<const ShaderVariantSelector> selectors,
420 std::span<const ShaderVariantDefinition> variants)
422 std::vector<ShaderInterfaceMemberDefinition>& vertexMembers = materialPermutation.effect.vertexShader.shaderInterface.members;
423 std::vector<ShaderInterfaceMemberDefinition>& geometryMembers = materialPermutation.effect.geometryShader.shaderInterface.members;
424 std::vector<ShaderInterfaceMemberDefinition>& surfaceMembers = materialPermutation.effect.pixelShader.shaderInterface.members;
428 if ((variant.type == ShaderVariantType::Bool && v.value == 1) ||
429 (variant.type == ShaderVariantType::Format && v.value != 0))
431 size_t a = vertexMembers.size();
432 addShaderStageInterfaceMembers(materialPermutation.name, vertexMembers, variant.vertexInterface.members);
433 size_t b = vertexMembers.size();
436 if (variant.type == ShaderVariantType::Format) {
437 for (
size_t i = a; i < b; i++) {
438 if (vertexMembers[i].type == MaterialDataType::Unknown) {
439 vertexMembers[i].type = formatShaderType(Cogs::Format(selectors[variant.index].value));
443 addShaderStageInterfaceMembers(materialPermutation.name, geometryMembers, variant.geometryInterface.members);
444 addShaderStageInterfaceMembers(materialPermutation.name, surfaceMembers, variant.surfaceInterface.members);
447 moveSystemGeneratedParametersLast(materialPermutation.effect.pixelShader.shaderInterface.members);
452 void addDefinesFromVariants(std::vector<std::pair<std::string, std::string>>& definitions,
454 std::span<const ShaderVariantSelector> selectors,
455 std::span<const ShaderVariantDefinition> variants)
460 if (variant.type == ShaderVariantType::Bool && selector.value == 1) {
461 definitions.emplace_back(variant.value,
"1");
464 else if (variant.type == ShaderVariantType::Int) {
465 definitions.emplace_back(variant.value, std::to_string(selector.value));
468 else if (variant.type == ShaderVariantType::Enum) {
469 for (
auto & enumerator : variant.values) {
470 if (enumerator.index == selector.value) {
471 definitions.emplace_back(enumerator.value,
"1");
477 else if (variant.type == ShaderVariantType::Format && selector.value != 0) {
479 const char* value =
nullptr;
480 switch (formatShaderType(Cogs::Format(selector.value))) {
481 case MaterialDataType::Float: value =
"COGS_FLOAT";
break;
482 case MaterialDataType::Float2: value =
"COGS_FLOAT2";
break;
483 case MaterialDataType::Float3: value =
"COGS_FLOAT3";
break;
484 case MaterialDataType::Float4: value =
"COGS_FLOAT4";
break;
485 case MaterialDataType::UInt: value =
"COGS_UINT";
break;
486 case MaterialDataType::UInt2: value =
"COGS_UINT2";
break;
487 case MaterialDataType::UInt3: value =
"COGS_UINT3";
break;
488 case MaterialDataType::UInt4: value =
"COGS_UINT4";
break;
489 case MaterialDataType::Int: value =
"COGS_INT";
break;
490 case MaterialDataType::Int2: value =
"COGS_INT2";
break;
491 case MaterialDataType::Int3: value =
"COGS_INT3";
break;
492 case MaterialDataType::Int4: value =
"COGS_INT4";
break;
493 case MaterialDataType::Float4x4: value =
"COGS_FLOAT4X4";
break;
495 LOG_ERROR(logger,
"Variant selector value is not a valid vertex format");
499 definitions.emplace_back(variant.value, value);
502 else if (variant.type == ShaderVariantType::String) {
503 if (selector.value ==
size_t(-1))
continue;
505 assert(selector.value <= materialInstance->
variantStrings.size() &&
"Variant selector string index out of range.");
506 definitions.emplace_back(variant.value, materialInstance->
variantStrings[selector.value]);
511 bool sanityCheckVertexElements(std::span<const Cogs::VertexElement> vertexElements)
513 for (
size_t i = 1; i < vertexElements.size(); i++) {
514 for (
size_t j = i; j < vertexElements.size(); j++) {
516 if ((vertexElements[i - 1].semantic == vertexElements[j].semantic) &&
517 (vertexElements[i - 1].semanticIndex == vertexElements[j].semanticIndex))
519 LOG_ERROR(logger,
"Invalid streams layout, vertex element semantic %.*s%u is specified more than once",
520 StringViewFormat(Cogs::getElementSemanticName(vertexElements[j].semantic)),
521 unsigned(vertexElements[j].semanticIndex));
530 void adjustVariantsUsingVertexElements(ShaderVariantSelectors& variantSelectors,
532 std::span<const Cogs::VertexElement> vertexElements)
534 const size_t indexLimit = variantSelectors.size();
535 const ShaderVariants& variantDefinitions = materialInstance->
material->definition.variants;
536 assert(variantDefinitions.size() == indexLimit);
538 std::string variantKeyBase;
541 switch (element.semantic) {
550 assert(
false &&
"Illegal semantic");
554 assert(element.semanticIndex < 10);
555 variantKeyBase.push_back(
char(
'0' + element.semanticIndex));
556 if (
size_t index = materialInstance->
material->getVariantIndex(variantKeyBase); index != Material::NoVariantIndex) {
557 assert(index < indexLimit);
558 assert(variantSelectors[index].index == index);
559 assert(variantDefinitions[index].type == ShaderVariantType::Format &&
"Error in MaterialBase.material");
560 variantSelectors[index].value = size_t(element.format);
566 [[nodiscard]]
bool checkSingleRequirement(
const ShaderVariantSelectors& variantSelectors,
570 if (requirement.variant.empty()) {
571 LOG_ERROR(logger,
"Empty variant requirement");
576 if (requirement.value.empty()) {
577 const char* a = requirement.variant.c_str();
581 while (*b !=
'\0' && *b !=
'|') { b++; }
584 if (
size_t ix = materialInstance->
material->getVariantIndex(key); ix != Material::NoVariantIndex) {
586 switch (target.type) {
587 case ShaderVariantType::Bool:
588 case ShaderVariantType::Int:
589 case ShaderVariantType::Format:
590 if (variantSelectors[ix].value != 0)
return true;
593 case ShaderVariantType::Enum:
594 case ShaderVariantType::String:
595 LOG_ERROR(logger,
"Requirement '%.*s' references variant with unsupported type", StringViewFormat(key));
602 LOG_ERROR(logger,
"Variant requirement '%.*s' refers non-existing variant", StringViewFormat(key));
615 size_t ix = materialInstance->
material->getVariantIndex(requirement.variant);
617 if (ix == Material::NoVariantIndex) {
618 LOG_ERROR(logger,
"Variant requirement %s=%s refers non-existing variant", requirement.variant.c_str(), requirement.value.c_str());
622 assert(ix < materialInstance->material->definition.variants.size());
626 bool anySuccess =
false;
636 switch (target.type) {
637 case ShaderVariantType::Format:
639 if (variantSelectors[ix].value && value == formatShaderTypeString(Cogs::Format(variantSelectors[ix].value))) {
642 else if (
const Cogs::FormatInfo* info = Cogs::getFormatInfo(Cogs::Format(variantSelectors[ix].value)); info) {
643 if (value == info->vName || value == info->name) {
649 case ShaderVariantType::Bool:
650 case ShaderVariantType::Int:
651 case ShaderVariantType::Enum:
652 case ShaderVariantType::String:
653 LOG_ERROR(logger,
"Requirement '%s' references variant with type with unimplemented handling", requirement.variant.c_str());
661 }
while (!anySuccess);
666 void checkVariantTriggers(ShaderVariantSelectors& variantSelectors,
669 std::vector<size_t> unsatisifiedSet;
672 if ( variant.type == ShaderVariantType::Bool && !variant.triggers.empty()) {
673 unsatisifiedSet.push_back(selector.index);
677 bool anyChange =
true;
678 std::vector<size_t> nextSet;
679 while (!unsatisifiedSet.empty() && anyChange) {
683 while (!unsatisifiedSet.empty()) {
684 size_t unsatisfiedIx = unsatisifiedSet.back();
685 unsatisifiedSet.pop_back();
689 bool allSatisfied =
true;
691 allSatisfied = checkSingleRequirement(variantSelectors, materialInstance, requirement);
692 if (!allSatisfied)
break;
696 variantSelectors[unsatisfiedIx].value = 1;
700 nextSet.push_back(unsatisfiedIx);
703 unsatisifiedSet.swap(nextSet);
707 [[nodiscard]]
bool checkMaterialRequirements(
const ShaderVariantSelectors& variantSelectors,
712 if (!checkSingleRequirement(variantSelectors, materialInstance, requirement)) {
713 LOG_ERROR(logger,
"Material requirement %s=%s not met", requirement.variant.c_str(), requirement.value.c_str());
720 [[nodiscard]]
bool checkVariantRequirements(
const ShaderVariantSelectors& variantSelectors,
726 if (selector.value && variant.type == ShaderVariantType::Bool && !variant.requirements.empty()) {
730 if (!checkSingleRequirement(variantSelectors, materialInstance, requirement)) {
731 LOG_ERROR(logger,
"Material '%.*s' instance '%.*s' variant requirement %s=%s not met",
733 StringViewFormat(materialInstance->
getName()),
734 requirement.variant.c_str(),
735 requirement.value.c_str());
747 bool matchInterfaceMemberToVertexElement(std::span<size_t> memberElementIndex,
748 std::span<const ShaderInterfaceMemberDefinition> interfaceMembers,
749 std::span<const Cogs::VertexElement> vertexElements)
752 assert(memberElementIndex.size() == interfaceMembers.size());
754 for (
size_t j = 0; j < memberElementIndex.size(); j++) {
759 switch (member.semantic.name) {
762 case ShaderInterfaceMemberDefinition::SemanticName::None:
763 LOG_ERROR(logger,
"Vertex shader input '%s' has no associated semantic", member.name.c_str());
776 assert(
size_t(ShaderInterfaceMemberDefinition::SemanticName::FirstSystemValueSemantic) <=
size_t(member.semantic.name));
777 memberElementIndex[j] = ~size_t(0);
782 for (
size_t i = 0; i < vertexElements.size(); i++) {
788 memberElementIndex[j] = i;
794 LOG_ERROR(logger,
"Failed to match vertex shader input '%s' with semantic %.*s:%u to a vertex stream semantic",
796 StringViewFormat(ShaderInterfaceMemberDefinition::semanticNameString(member.semantic.name)),
797 unsigned(member.semantic.slot));
807 if (
size_t index = materialInstance->
material->getVariantIndex(key); index != Material::NoVariantIndex) {
808 assert(index < variantSelectors.size());
809 assert(variantSelectors[index].index == index);
810 const ShaderVariants& variantDefinitions = materialInstance->
material->definition.variants;
811 assert(variantDefinitions[index].type == ShaderVariantType::Enum &&
"Error in MaterialBase.material");
813 if (e.key == value) {
814 variantSelectors[index].value = e.index;
821 if (
size_t index = materialInstance->
material->getVariantIndex(key); index != Material::NoVariantIndex) {
822 assert(index < variantSelectors.size());
823 assert(variantSelectors[index].index == index);
824 const ShaderVariants& variantDefinitions = materialInstance->
material->definition.variants;
825 assert(variantDefinitions[index].type == ShaderVariantType::Bool &&
"Error in MaterialBase.material");
826 variantSelectors[index].value = value ? 1 : 0;
839 assert(materialInstance);
840 assert(streamsLayout);
846 if (material->definition.permutations.empty() || materialInstance->
permutationIndex > (material->definition.permutations.size() - 1)) {
847 LOG_ERROR(logger,
"Permutation out of range.");
852 std::vector<VertexElement> vertexElements;
853 for (
size_t i = 0; i < streamsLayout->
numStreams; i++) {
855 vertexElements.insert(vertexElements.end(),
859 if (!sanityCheckVertexElements(vertexElements)) {
863 ShaderVariantSelectors variantSelectors = materialInstance->
variantSelectors;
864 adjustVariantsUsingVertexElements(variantSelectors, materialInstance, vertexElements);
874 setEnumVariant(variantSelectors, materialInstance,
"ClipShape",
"Cube");
877 setEnumVariant(variantSelectors, materialInstance,
"ClipShape",
"InvertedCube");
878 clipInPixelShader =
true;
881 assert(
false &&
"Invalid enum");
884 if (clipInPixelShader) {
885 setBoolVariant(variantSelectors, materialInstance,
"ClipInPixelShader",
true);
888 setBoolVariant(variantSelectors, materialInstance,
"ClipInVertexShader",
true);
893 checkVariantTriggers(variantSelectors, materialInstance);
896 if (!checkMaterialRequirements(variantSelectors, materialInstance)
897 || !checkVariantRequirements(variantSelectors, materialInstance))
907 materialPermutation.name += std::to_string(
id++);
911 assert(variantSelectors.size() == material->definition.variants.size());
913 if (definition.isShared) {
914 variantSelectors[definition.index].value = definition.defaultValue;
919 resolveShaderInterfaceMembers(materialPermutation);
922 addShaderInterfaceMembersFromVariants(materialPermutation, variantSelectors, materialPermutation.variants);
923 addShaderInterfaceMembersFromVariants(materialPermutation, permutation->getSelectors(), permutation->getVariants());
926 std::vector<size_t> memberElementIndex(materialPermutation.effect.vertexShader.shaderInterface.members.size());
927 if (!matchInterfaceMemberToVertexElement(memberElementIndex,
928 materialPermutation.effect.vertexShader.shaderInterface.members,
934 const std::string& permutationName = permutation->getDefinition()->name;
937 materialPermutation.effect.
name = materialPermutation.name + permutationName;
938 materialPermutation.effect.vertexShader.loadPath = materialPermutation.name + ShaderNames[ShaderTypes::Vertex] + permutationName;
939 if (materialPermutation.effect.hullShader.customSourcePath.size()) {
940 materialPermutation.effect.hullShader.loadPath = materialPermutation.name + ShaderNames[ShaderTypes::Hull] + permutationName;
941 materialPermutation.effect.domainShader.loadPath = materialPermutation.name + ShaderNames[ShaderTypes::Domain] + permutationName;
943 if (materialPermutation.effect.geometryShader.customSourcePath.size()) {
944 materialPermutation.effect.geometryShader.loadPath = materialPermutation.name + ShaderNames[ShaderTypes::Geometry] + permutationName;
946 materialPermutation.effect.pixelShader.loadPath = materialPermutation.name + ShaderNames[ShaderTypes::Pixel] + permutationName;
949 std::vector<std::pair<std::string, std::string>> definitions{
952 {
"COGS_FLOAT",
"1" },
953 {
"COGS_FLOAT2",
"2" },
954 {
"COGS_FLOAT3",
"3" },
955 {
"COGS_FLOAT4",
"4" },
956 {
"COGS_UINT",
"5" },
957 {
"COGS_UINT2",
"6" },
958 {
"COGS_UINT3",
"7" },
959 {
"COGS_UINT4",
"8" },
961 {
"COGS_INT2",
"10" },
962 {
"COGS_INT3",
"11" },
963 {
"COGS_INT4",
"12" },
964 {
"COGS_FLOAT4X4",
"13" }
968 for (
auto & d : materialPermutation.effect.
definitions) {
969 definitions.emplace_back(d.first, d.second);
971 for (
auto & d : permutation->getDefinition()->definitions) {
972 definitions.emplace_back(d.first, d.second);
974 addDefinesFromVariants(definitions, materialInstance, permutation->getSelectors(), permutation->getVariants());
975 addDefinesFromVariants(definitions, materialInstance, variantSelectors, materialPermutation.variants);
979 switch (graphicsDeviceType)
982 success = buildEffectES3(context, definitions, materialPermutation, *permutation, passOptions.multiViews);
985 success = buildEffectWebGPU(context, definitions, materialPermutation, *permutation, passOptions.multiViews);
988 success = buildEffect(context, definitions, materialPermutation, *permutation);
992 LOG_ERROR(logger,
"Failed to build material shader source");
998 materialPermutation.effect.
definitions.reserve(definitions.size());
999 for (
const std::pair<std::string,std::string>& d : definitions) {
1003 EffectHandle effect = context->effectManager->loadEffect(materialPermutation.effect);
1007 if (context->watcher && context->variables->get(
"resources.effects.autoReload",
false)) {
1008 auto watchShader = [&](
const StringView & fileName) {
1009 auto path = context->resourceStore->getResourcePath(fileName);
1012 context->resourceStore->purge(path);
1017 auto absolute = IO::absolute(path);
1019 context->watcher->watchFile(absolute, callback);
1022 watchShader(materialPermutation.effect.vertexShader.customSourcePath);
1023 watchShader(materialPermutation.effect.pixelShader.customSourcePath);
1025 watchShader(permutation->getDefinition()->vertexShader);
1026 watchShader(permutation->getDefinition()->pixelShader);
1034 return context->renderer->getResources()->updateResource(handle);
1039 context->renderer->getResources()->releaseResource(resource);
1044 reportLeaks(
"MaterialInstance");
1049 ResourceManager::initialize();
1051 resources.resize(16384);
1054void Cogs::Core::MaterialInstanceManager::initializeDefaultMaterialInstance()
1056 defaultResource = createMaterialInstance(context->materialManager->getDefaultMaterial());
1061 auto material = materialHandle.
resolve();
1063 auto handle = create();
1064 auto instance = handle.resolve();
1066 instance->setupInstance(material);
1070 bool useArrayObjectBuffer =
true;
1071 const StringView arrayObjectBufferKey =
"renderer.arrayObjectBuffer";
1072 if (
Variable* var = context->variables->get(arrayObjectBufferKey); !var->isEmpty()) {
1073 useArrayObjectBuffer = var->getBool();
1076 context->variables->set(arrayObjectBufferKey, useArrayObjectBuffer);
1078 if(useArrayObjectBuffer){
1079 size_t index = instance->material->getVariantIndex(
"UseObjectArray");
1080 if (index != Material::NoVariantIndex) {
1081 instance->setVariant(index,
true);
1086 instance->setLoaded();
1087 instance->setChanged();
1094 return getByName(name);
1099 return context->renderer->getResources()->updateResource(handle);
1104 context->renderer->getResources()->releaseResource(resource);
1107int Cogs::Core::MaterialInstanceManager::getUpdateQuota()
const
1109 return context->variables->get(
"resources.materialInstances.frameUpdateQuota", 0);
void initialize() override
Initialize the MaterialInstanceManager.
~MaterialInstanceManager()
Destructs the MaterialInstanceManager.
MaterialInstanceHandle createMaterialInstance(const MaterialHandle &material)
Create a new MaterialInstance from the Material given held by material.
void handleDeletion(MaterialInstance *resource) override
Overridden to handle material instance deletion, removing the resource from the renderer.
ActivationResult handleActivation(MaterialInstanceHandle handle, MaterialInstance *resource) override
Overridden to handle activation of MaterialInstances, updating the resource in the renderer.
MaterialHandle loadMaterial(const StringView &fileName, MaterialLoadFlags materialLoadFlags=MaterialLoadFlags::None, ResourceId resourceId=NoResourceId)
Loads a Material from the given fileName.
void handleDeletion(Material *resource) override
Overridden to handle material deletion, removing the resource from the renderer.
ActivationResult handleActivation(MaterialHandle handle, Material *resource) override
Overridden to handle activation of Materials, updating the resource in the renderer.
MaterialHandle getDefaultMaterial()
Get the default material.
void releaseAll()
Clear out all materials, done as part of shutdown process.
EffectHandle loadMaterialVariant(Material *material, const MaterialInstance *materialInstance, const MeshStreamsLayout *streamsLayout, const EnginePermutation *permutation, const RenderPassOptions &passOptions, const ClipShapeType clipShape)
void handleLoad(MaterialLoadInfo *loadInfo) override
Overridden to handle loading of Material resources.
void initializeDefaultMaterial()
Initializes the MaterialManager, setting up the default material.
~MaterialManager()
Destructs the MaterialManager.
void clear() override
Clear the resource manager, cleaning up resources held by member handles.
Log implementation class.
Provides a weakly referenced view over the contents of a string.
size_t find_first_of(char character, size_t pos=0) const noexcept
Find the first occurance of the given character from the specified starting position.
constexpr StringView substr(size_t offset, size_t count=NoPosition) const noexcept
Get the given sub string.
static constexpr size_t NoPosition
No position.
std::string to_string() const
String conversion method.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
ClipShapeType
Specifices what kind of shape a clip shape has.
@ InvertedCube
Clip the inside of a cube.
@ None
No clipping at all.
@ Cube
Clip the outside of a cube,.
ActivationResult
Defines results for resource activation.
MaterialDataType
Defines available data types for material properties.
MaterialLoadFlags
Material loading flags.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
GraphicsDeviceType
Contains types of graphics devices that may be supported.
@ OpenGLES30
Graphics device using the OpenGLES 3.0 API.
@ WebGPU
Graphics device using the WebGPU API Backend.
std::pair< std::string, std::string > PreprocessorDefinition
Preprocessor definition.
ElementSemantic
Element semantics used to map data to the shader stage.
@ Position
Position semantic.
@ Tangent
Tangent semantic.
@ InstanceMatrix
Instance matrix semantic.
@ InstanceVector
Instance vector semantic.
@ TextureCoordinate
Texture coordinate semantic.
std::vector< MaterialPropertyBuffer > buffers
Constant buffer instances.
uint16_t buffersGeneration
If the constant buffer bindings need updates.
static void initialize(MaterialManager *materialManager)
Initialize the default material, updating all material property keys.
std::string name
Name of the effect.
MeshStreamsLayout streamsLayout
The vertex layout this effect expects.
PreprocessorDefinitions definitions
Preprocessor definitions.
struct Material * material
Owning material resource.
Material instances represent a specialized Material combined with state for all its buffers and prope...
std::vector< std::string > variantStrings
String storage for string variants.
size_t permutationIndex
Index of material permutation to use.
Material * material
Material resource this MaterialInstance is created from.
ShaderVariantSelectors variantSelectors
Variant selectors.
Defines loading information for Material resources.
Material resources define the how of geometry rendering (the what is defined by Mesh and Texture reso...
VertexFormatHandle vertexFormats[maxStreams]
StringView getName() const
Get the name of the resource.
ResourceTypes getType() const
Gets the type enumeration of the resource.
Resource handle base class handling reference counting of resources derived from ResourceBase.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.
std::string resourcePath
Resource path. Used to locate resource.
ResourceId resourceId
Unique resource identifier. Must be unique among resources of the same kind.
ResourceHandleBase handle
Handle to resource structure for holding actual resource data.
ResourceLoadFlags loadFlags
Desired loading flags. Used to specify how the resource will be loaded.
Runtime control variable.
Vertex element structure used to describe a single data element in a vertex for the input assembler.
uint16_t semanticIndex
Index for the semantic mapping.
ElementSemantic semantic
Semantic mapping of the element (position, normal, etc...).