Refactor material shaders generator to use modular features as extensions

This commit is contained in:
Wojtek Figat
2021-02-04 10:43:04 +01:00
parent a560b19cbc
commit 2a3b6edf50
29 changed files with 877 additions and 711 deletions

View File

@@ -163,27 +163,4 @@ void MaterialGenerator::linearizeSceneDepth(Node* caller, const Value& depth, Va
value = writeLocal(VariantType::Float, String::Format(TEXT("ViewInfo.w / ({0}.x - ViewInfo.z)"), depth.Value), caller);
}
byte MaterialGenerator::getStartSrvRegister(MaterialLayer* baseLayer)
{
// Note: this must match material templates
switch (baseLayer->Domain)
{
case MaterialDomain::Surface:
return baseLayer->BlendMode == MaterialBlendMode::Transparent ? 3 : 3;
case MaterialDomain::PostProcess:
return 0;
case MaterialDomain::Decal:
return 1;
case MaterialDomain::GUI:
return 0;
case MaterialDomain::Terrain:
return 6;
case MaterialDomain::Particle:
return 5;
default:
CRASH;
return 0;
}
}
#endif

View File

@@ -4,7 +4,9 @@
#include "MaterialGenerator.h"
#include "Engine/Visject/ShaderGraphUtilities.h"
#include "Engine/Platform/File.h"
#include "Engine/Graphics/Materials/MaterialShader.h"
#include "Engine/Graphics/Materials/MaterialShaderFeatures.h"
/// <summary>
/// Material shader source code template has special marks for generated code.
@@ -20,10 +22,86 @@ enum MaterialTemplateInputsMapping
In_GetMaterialVS = 5,
In_GetMaterialDS = 6,
In_Includes = 7,
In_Utilities = 8,
In_Shaders = 9,
In_MAX
};
/// <summary>
/// Material shader feature source code template has special marks for generated code. Each starts with '@' char and index of the mapped string.
/// </summary>
enum class FeatureTemplateInputsMapping
{
Defines = 0,
Includes = 1,
Constants = 2,
Resources = 3,
Utilities = 4,
Shaders = 5,
MAX
};
struct FeatureData
{
MaterialShaderFeature::GeneratorData Data;
String Inputs[(int32)FeatureTemplateInputsMapping::MAX];
bool Init();
};
namespace
{
// Loaded and parsed features data cache
Dictionary<StringAnsi, FeatureData> Features;
}
bool FeatureData::Init()
{
// Load template file
const String path = Globals::EngineContentFolder / TEXT("Editor/MaterialTemplates/Features/") + Data.Template;
String contents;
if (File::ReadAllText(path, contents))
{
LOG(Error, "Cannot open file {0}", path);
return true;
}
int32 i = 0;
const int32 length = contents.Length();
// Skip until input start
for (; i < length; i++)
{
if (contents[i] == '@')
break;
}
// Load all inputs
do
{
// Parse input type
i++;
const int32 inIndex = contents[i++] - '0';
ASSERT_LOW_LAYER(Math::IsInRange(inIndex, 0, (int32)FeatureTemplateInputsMapping::MAX - 1));
// Read until next input start
const Char* start = &contents[i];
for (; i < length; i++)
{
const auto c = contents[i];
if (c == '@')
break;
}
const Char* end = &contents[i];
// Set input
Inputs[inIndex].Set(start, (int32)(end - start));
} while (i < length);
return false;
}
MaterialValue MaterialGenerator::getUVs(VariantType::Vector2, TEXT("input.TexCoord"));
MaterialValue MaterialGenerator::getTime(VariantType::Float, TEXT("TimeParam"));
MaterialValue MaterialGenerator::getNormal(VariantType::Vector3, TEXT("input.TBN[2]"));
@@ -53,6 +131,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
ASSERT_LOW_LAYER(_layers.Count() > 0);
String inputs[In_MAX];
Array<StringAnsiView, FixedAllocation<8>> features;
// Setup and prepare layers
_writer.Clear();
@@ -87,6 +166,32 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
const MaterialGraphBox* layerInputBox = baseLayer->Root->GetBox(0);
const bool isLayered = layerInputBox->HasConnection();
// Initialize features
#define ADD_FEATURE(type) \
{ \
StringAnsiView typeName(#type, ARRAY_COUNT(#type) - 1); \
features.Add(typeName); \
if (!Features.ContainsKey(typeName)) \
{ \
auto& feature = Features[typeName]; \
type::Generate(feature.Data); \
if (feature.Init()) \
return true; \
} \
}
switch (baseLayer->Domain)
{
case MaterialDomain::Surface:
if (materialInfo.TessellationMode != TessellationMethod::None)
ADD_FEATURE(TessellationFeature);
if (materialInfo.BlendMode == MaterialBlendMode::Opaque)
ADD_FEATURE(LightmapFeature);
break;
default:
break;
}
#undef ADD_FEATURE
// Check if material is using special features and update the metadata flags
if (!isLayered)
{
@@ -240,11 +345,13 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
// Update material usage based on material generator outputs
materialInfo.UsageFlags = baseLayer->UsageFlags;
#define WRITE_FEATURES(input) for (auto f : features) _writer.Write(Features[f].Inputs[(int32)FeatureTemplateInputsMapping::input]);
// Defines
{
_writer.Write(TEXT("#define MATERIAL_MASK_THRESHOLD ({0})\n"), baseLayer->MaskThreshold);
_writer.Write(TEXT("#define CUSTOM_VERTEX_INTERPOLATORS_COUNT ({0})\n"), _vsToPsInterpolants.Count());
_writer.Write(TEXT("#define MATERIAL_OPACITY_THRESHOLD ({0})"), baseLayer->OpacityThreshold);
_writer.Write(TEXT("#define MATERIAL_OPACITY_THRESHOLD ({0})\n"), baseLayer->OpacityThreshold);
WRITE_FEATURES(Defines);
inputs[In_Defines] = _writer.ToString();
_writer.Clear();
}
@@ -252,31 +359,87 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
// Includes
{
for (auto& include : _includes)
{
_writer.Write(TEXT("#include \"{0}\"\n"), include.Item);
}
WRITE_FEATURES(Includes);
inputs[In_Includes] = _writer.ToString();
_writer.Clear();
}
// Check if material is using any parameters
if (_parameters.HasItems())
// Constants
{
ShaderGraphUtilities::GenerateShaderConstantBuffer(_writer, _parameters);
WRITE_FEATURES(Constants);
if (_parameters.HasItems())
ShaderGraphUtilities::GenerateShaderConstantBuffer(_writer, _parameters);
inputs[In_Constants] = _writer.ToString();
_writer.Clear();
}
const int32 startRegister = getStartSrvRegister(baseLayer);
const auto error = ShaderGraphUtilities::GenerateShaderResources(_writer, _parameters, startRegister);
if (error)
// Resources
{
int32 srv = 0;
switch (baseLayer->Domain)
{
OnError(nullptr, nullptr, error);
return true;
case MaterialDomain::Surface:
if (materialInfo.BlendMode != MaterialBlendMode::Opaque)
srv = 3; // Forward shading resources
break;
case MaterialDomain::Decal:
srv = 1;
break;
case MaterialDomain::Terrain:
srv = 6;
break;
case MaterialDomain::Particle:
srv = 5;
break;
}
for (auto f : features)
{
const auto& text = Features[f].Inputs[(int32)FeatureTemplateInputsMapping::Resources];
const Char* str = text.Get();
int32 prevIdx = 0, idx = 0;
while (true)
{
idx = text.Find(TEXT("__SRV__"), StringSearchCase::CaseSensitive, prevIdx);
if (idx == -1)
break;
int32 len = idx - prevIdx;
_writer.Write(StringView(str, len));
str += len;
_writer.Write(StringUtils::ToString(srv));
srv++;
str += ARRAY_COUNT("__SRV__") - 1;
prevIdx = idx + ARRAY_COUNT("__SRV__") - 1;
}
_writer.Write(StringView(str));
}
if (_parameters.HasItems())
{
const auto error = ShaderGraphUtilities::GenerateShaderResources(_writer, _parameters, srv);
if (error)
{
OnError(nullptr, nullptr, error);
return true;
}
}
inputs[In_ShaderResources] = _writer.ToString();
_writer.Clear();
}
// Utilities
{
WRITE_FEATURES(Utilities);
inputs[In_Utilities] = _writer.ToString();
_writer.Clear();
}
// Shaders
{
WRITE_FEATURES(Shaders);
inputs[In_Shaders] = _writer.ToString();
_writer.Clear();
}
// Save material parameters data
if (_parameters.HasItems())
MaterialParams::Save(parametersData, &_parameters);
@@ -315,16 +478,15 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
LOG(Warning, "Unknown material domain.");
return true;
}
auto file = FileReadStream::Open(path);
if (file == nullptr)
{
LOG(Warning, "Cannot load material base source code.");
LOG(Error, "Cannot open file {0}", path);
return true;
}
// Format template
uint32 length = file->GetLength();
const uint32 length = file->GetLength();
Array<char> tmp;
for (uint32 i = 0; i < length; i++)
{

View File

@@ -209,8 +209,6 @@ public:
static MaterialGraphBoxesMapping MaterialGraphBoxesMappings[];
static const MaterialGraphBoxesMapping& GetMaterialRootNodeBox(MaterialGraphBoxes box);
static byte getStartSrvRegister(MaterialLayer* baseLayer);
};
#endif