// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "IShaderFunctionReader.h"
#include "ShaderMeta.h"
#include "Config.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
///
/// Implementation of shader meta functions reader
///
template
class ShaderMetaReader : public IShaderFunctionReader, public ITokenReadersContainer
{
protected:
Array _cache;
MetaType _current;
ShaderMetaReader()
{
}
~ShaderMetaReader()
{
}
protected:
///
/// Event called before parsing shader function
///
/// Parser object
/// Source code reader
virtual void OnParseBefore(IShaderParser* parser, Reader& text) = 0;
///
/// Event called for parsing shader function
///
/// Parser object
/// Source code reader
virtual void OnParse(IShaderParser* parser, Reader& text)
{
Token token;
// Read function properties
while (text.CanRead())
{
text.ReadToken(&token);
// Call children
if (ProcessChildren(token, parser))
break;
}
// Token should contain function output type, now read function name
text.ReadToken(&token);
_current.Name = token.ToString();
// Check if name is unique
for (int32 i = 0; i < _cache.Count(); i++)
{
if (_cache[i].Name == _current.Name)
{
parser->OnError(String::Format(TEXT("Duplicated function \'{0}\'. Function with that name already exists."), String(_current.Name)));
return;
}
}
}
///
/// Event called after parsing shader function
///
/// Parser object
/// Source code reader
virtual void OnParseAfter(IShaderParser* parser, Reader& text) = 0;
///
/// Process final results and send them to the Shader Meta object
///
/// Parser object
/// Output shader meta
virtual void FlushCache(IShaderParser* parser, ShaderMeta* result) = 0;
public:
// [IShaderFunctionReader]
void Process(IShaderParser* parser, Reader& text) override
{
OnParseBefore(parser, text);
if (parser->Failed())
return;
OnParse(parser, text);
if (parser->Failed())
return;
OnParseAfter(parser, text);
}
void CollectResults(IShaderParser* parser, ShaderMeta* result) override
{
// Validate function names
for (int32 i = 0; i < _cache.Count(); i++)
{
auto& first = _cache[i];
for (int32 j = i + 1; j < _cache.Count(); j++)
{
auto& second = _cache[j];
if (first.Name == second.Name)
{
parser->OnError(TEXT("Duplicated shader function names."));
return;
}
}
}
FlushCache(parser, result);
}
};
///
/// Implementation of shader functions reader
///
template
class ShaderFunctionReader : public ShaderMetaReader
{
public:
typedef ShaderMetaReader ShaderMetaReaderType;
protected:
class StripLineReader : public ITokenReader
{
private:
Token _startToken;
public:
explicit StripLineReader(const char* token)
: _startToken(token)
{
}
public:
// [ITokenReader]
bool CheckStartToken(const Token& token) override
{
return token == _startToken;
}
void Process(IShaderParser* parser, Reader& text) override
{
// Read line to end
text.ReadLine();
}
};
public:
///
/// Shader function permutations reader
///
class PermutationReader : public ITokenReader
{
protected:
ShaderFunctionReader* _parent;
int32 _startTokenPermutationSize;
const char* PermutationTokens[SHADER_PERMUTATIONS_MAX_PARAMS_COUNT] =
{
"META_PERMUTATION_1",
"META_PERMUTATION_2",
"META_PERMUTATION_3",
"META_PERMUTATION_4",
};
static_assert(SHADER_PERMUTATIONS_MAX_PARAMS_COUNT == 4, "Invalid maximum amount of shader permutations.");
public:
///
/// Init
///
/// Parent shader function reader object
PermutationReader(ShaderFunctionReader* parent)
: _parent(parent)
, _startTokenPermutationSize(0)
{
}
public:
///
/// Clear cache
///
void Clear()
{
_startTokenPermutationSize = 0;
}
public:
// [ITokenReader]
bool CheckStartToken(const Token& token) override
{
for (int32 i = 0; i < ARRAY_COUNT(PermutationTokens); i++)
{
if (token == PermutationTokens[i])
{
_startTokenPermutationSize = i + 1;
return true;
}
}
return false;
}
void Process(IShaderParser* parser, Reader& text) override
{
Token token;
auto& current = _parent->_current;
// Add permutation
int32 permutationIndex = current.Permutations.Count();
auto& permutation = current.Permutations.AddOne();
// Read all parameters for the permutation
ASSERT(_startTokenPermutationSize > 0);
for (int32 paramIndex = 0; paramIndex < _startTokenPermutationSize; paramIndex++)
{
// Check for missing end
if (!text.CanRead())
{
parser->OnError(TEXT("Missing ending of shader function permutation."));
return;
}
// Read definition name
text.ReadToken(&token);
if (token.Length == 0)
{
parser->OnError(TEXT("Incorrect shader permutation. Definition name is empty."));
return;
}
StringAnsi name = token.ToString();
// Read '=' character
if (token.Separator != Separator('='))
{
if (token.Separator.IsWhiteSpace())
text.EatWhiteSpaces();
if (text.PeekChar() != '=')
{
parser->OnError(TEXT("Incorrect shader permutation. Missing \'='\' character for definition value."));
return;
}
}
// Read definition value
text.ReadToken(&token);
if (token.Length == 0)
{
parser->OnError(TEXT("Incorrect shader permutation. Definition value is empty."));
return;
}
StringAnsi value = token.ToString();
// Read ',' or ')' character (depends on parameter index)
char checkChar = (paramIndex == _startTokenPermutationSize - 1) ? ')' : ',';
if (token.Separator != Separator(checkChar))
{
parser->OnError(TEXT("Incorrect shader permutation declaration."));
return;
}
// Check if hasn't been already defined in that permutation
if (current.HasDefinition(permutationIndex, name))
{
parser->OnError(String::Format(TEXT("Incorrect shader function permutation definition. Already defined \'{0}\'."), String(name)));
return;
}
// Add entry to the meta
permutation.Entries.Add({ name, value });
}
}
};
///
/// Shader function flag reader
///
class FlagReader : public ITokenReader
{
protected:
ShaderFunctionReader* _parent;
Token _startToken;
public:
FlagReader(ShaderFunctionReader* parent)
: _parent(parent)
, _startToken("META_FLAG")
{
}
public:
// [ITokenReader]
bool CheckStartToken(const Token& token) override
{
return token == _startToken;
}
void Process(IShaderParser* parser, Reader& text) override
{
Token token;
auto& current = _parent->_current;
// Shader Flag type
text.ReadToken(&token);
current.Flags |= ParseShaderFlags(token);
}
};
protected:
PermutationReader* _permutationReader;
ShaderFunctionReader()
{
_childReaders.Add(_permutationReader = New(this));
_childReaders.Add(New(this));
}
~ShaderFunctionReader()
{
}
protected:
// [ShaderMetaReader]
void OnParseBefore(IShaderParser* parser, Reader& text) override
{
Token token;
// Clear current meta
_current.Name.Clear();
_current.Permutations.Clear();
_current.Flags = ShaderFlags::Default;
_current.MinFeatureLevel = FeatureLevel::ES2;
_permutationReader->Clear();
// Here we read '(x, y)\n' where 'x' is a shader function 'visible' flag, and 'y' is mini feature level
text.ReadToken(&token);
token = parser->GetMacros().GetValue(token);
if (token == "true" || token == "1")
{
// Visible shader
}
else if (token == "false" || token == "0")
{
// Hidden shader
_current.Flags = ShaderFlags::Hidden;
}
else
{
parser->OnError(TEXT("Invalid shader function \'isVisible\' option value."));
return;
}
text.ReadToken(&token);
token = parser->GetMacros().GetValue(token);
struct MinFeatureLevel
{
FeatureLevel Level;
const char* Token;
};
MinFeatureLevel levels[] =
{
{ FeatureLevel::ES2, "FEATURE_LEVEL_ES2" },
{ FeatureLevel::ES3, "FEATURE_LEVEL_ES3" },
{ FeatureLevel::ES3_1, "FEATURE_LEVEL_ES3_1" },
{ FeatureLevel::SM4, "FEATURE_LEVEL_SM4" },
{ FeatureLevel::SM5, "FEATURE_LEVEL_SM5" },
};
bool missing = true;
for (int32 i = 0; i < ARRAY_COUNT(levels); i++)
{
if (token == levels[i].Token)
{
_current.MinFeatureLevel = levels[i].Level;
missing = false;
break;
}
}
if (missing)
{
parser->OnError(TEXT("Invalid shader function \'minFeatureLevel\' option value."));
return;
}
// Read rest of the line
text.ReadLine();
}
void OnParseAfter(IShaderParser* parser, Reader& text) override
{
// Validate amount of permutations
if (_current.Permutations.Count() > SHADER_PERMUTATIONS_MAX_COUNT)
{
parser->OnError(String::Format(TEXT("Function \'{0}\' uses too many permutations. Maximum allowed amount is {1}."), String(_current.Name), SHADER_PERMUTATIONS_MAX_COUNT));
return;
}
// Check if shader has no permutations
if (_current.Permutations.IsEmpty())
{
// Just add blank permutation (rest of the code will work without any hacks for empty permutations list)
_current.Permutations.Add(ShaderPermutation());
}
// Check if use this shader program
if ((_current.Flags & ShaderFlags::Hidden) == false && _current.MinFeatureLevel <= parser->GetFeatureLevel())
{
// Cache read function
_cache.Add(_current);
}
}
};
}
#define DECLARE_SHADER_META_READER_HEADER(tokenName, shaderMetaMemberCollection) public: \
bool CheckStartToken(const Token& token) override \
{ return token == tokenName; } \
void FlushCache(IShaderParser* parser, ShaderMeta* result) \
{ result->shaderMetaMemberCollection.Add(_cache); }
#endif