You're breathtaking!

This commit is contained in:
Wojtek Figat
2020-12-07 23:40:54 +01:00
commit 6fb9eee74c
5143 changed files with 1153594 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "../Config.h"
#if COMPILE_WITH_SHADER_COMPILER
#include "Engine/Utilities/TextProcessing.h"
namespace ShaderProcessing
{
typedef TextProcessing Reader;
typedef Reader::Token Token;
typedef Reader::SeparatorData Separator;
}
#endif
// Don't count ending '\0' character
#define MACRO_LENGTH(macro) (ARRAY_COUNT(macro) - 1)

View File

@@ -0,0 +1,36 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "ITokenReader.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
/// <summary>
/// Interface for shader functions readers like Pixel Shader readers or Constant Buffer readers
/// </summary>
class IShaderFunctionReader : public ITokenReader
{
public:
/// <summary>
/// Virtual destructor
/// </summary>
virtual ~IShaderFunctionReader()
{
}
public:
/// <summary>
/// Collects shader function reader results to the final Shader Meta
/// </summary>
/// <param name="parser">Parser object</param>
/// <param name="result">Parsing result</param>
virtual void CollectResults(IShaderParser* parser, ShaderMeta* result) = 0;
};
}
#endif

View File

@@ -0,0 +1,90 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Config.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
struct ParserMacros
{
const Array<ShaderMacro>* Data;
ParserMacros(const Array<ShaderMacro>& data)
{
Data = &data;
}
Token GetValue(Token& token) const
{
for (int32 i = 0; i < Data->Count(); i++)
{
if (token == Data->At(i).Name)
{
// Use macro value
return Token(Data->At(i).Definition);
}
}
// Fallback to token
return token;
}
};
/// <summary>
/// Interface describing shader source code parser
/// </summary>
class IShaderParser
{
public:
/// <summary>
/// Virtual destructor
/// </summary>
virtual ~IShaderParser()
{
}
public:
/// <summary>
/// Gets the parser feature level of the target platform graphics backend.
/// </summary>
/// <returns>The graphics feature level</returns>
virtual FeatureLevel GetFeatureLevel() const = 0;
/// <summary>
/// Gets the parser macros.
/// </summary>
/// <returns>The macros</returns>
virtual ParserMacros GetMacros() const = 0;
/// <summary>
/// Gets value indicating that shader processing operation failed
/// </summary>
/// <returns>True if shader processing failed, otherwise false</returns>
virtual bool Failed() const = 0;
/// <summary>
/// Gets source code reader
/// </summary>
/// <returns>Source code reader</returns>
virtual Reader& GetReader() = 0;
/// <summary>
/// Event send to the parser on reading shader source code error
/// </summary>
/// <param name="message">Message to send</param>
virtual void OnError(const String& message) = 0;
/// <summary>
/// Event send to the parser on reading shader source code warning
/// </summary>
/// <param name="message">Message to send</param>
virtual void OnWarning(const String& message) = 0;
};
}
#endif

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Config.h"
#include "IShaderParser.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
/// <summary>
/// Interface for objects that can read shader source code tokens
/// </summary>
class ITokenReader
{
public:
/// <summary>
/// Virtual destructor
/// </summary>
virtual ~ITokenReader()
{
}
public:
/// <summary>
/// Checks if given token can be processed by this reader
/// </summary>
/// <param name="token">Starting token to check</param>
/// <returns>True if given token is valid starting token, otherwise false</returns>
virtual bool CheckStartToken(const Token& token) = 0;
/// <summary>
/// Start processing source after reading start token
/// </summary>
/// <param name="parser">Parser object</param>
/// <param name="text">Source code reader</param>
virtual void Process(IShaderParser* parser, Reader& text) = 0;
};
}
#endif

View File

@@ -0,0 +1,74 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Collections/Array.h"
#include "ITokenReader.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
/// <summary>
/// Interface for objects that can have child token readers
/// </summary>
template<typename Type>
class ITokenReadersContainerBase
{
protected:
Array<Type*> _childReaders;
public:
/// <summary>
/// Virtual destructor
/// </summary>
virtual ~ITokenReadersContainerBase()
{
// Cleanup
_childReaders.ClearDelete();
}
protected:
/// <summary>
/// Try to process given token by any child reader
/// </summary>
/// <param name="token">Starting token to check</param>
/// <param name="parser">Parser object</param>
/// <returns>True if no token processing has been done, otherwise false</returns>
virtual bool ProcessChildren(const Token& token, IShaderParser* parser)
{
for (int32 i = 0; i < _childReaders.Count(); i++)
{
if (_childReaders[i]->CheckStartToken(token))
{
_childReaders[i]->Process(parser, parser->GetReader());
return false;
}
}
return true;
}
};
/// <summary>
/// Interface for objects that can have child ITokenReader objects
/// </summary>
class ITokenReadersContainer : public ITokenReadersContainerBase<ITokenReader>
{
public:
/// <summary>
/// Virtual destructor
/// </summary>
virtual ~ITokenReadersContainer()
{
// Cleanup
_childReaders.ClearDelete();
}
};
}
#endif

View File

@@ -0,0 +1,131 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "ShaderFunctionReader.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
/// <summary>
/// Constant Buffers reader
/// </summary>
class ConstantBufferReader : public ShaderMetaReader<ConstantBufferMeta>
{
private:
Token _endToken;
DECLARE_SHADER_META_READER_HEADER("META_CB_BEGIN", CB);
ConstantBufferReader()
: _endToken("META_CB_END")
{
}
~ConstantBufferReader()
{
}
// [ShaderMetaReader]
void OnParseBefore(IShaderParser* parser, Reader& text) override
{
Token token;
// Clear current meta
_current.Name.Clear();
// Here we read '(x)\n' where 'x' is a shader function slot
text.ReadToken(&token);
if (StringUtils::Parse(token.Start, token.Length, &_current.Slot))
{
parser->OnError(TEXT("Invalid constant buffer slot index."));
return;
}
// Read buffer 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 constant buffer \'{0}\'. Buffer with that name already exists."), String(_current.Name)));
return;
}
}
// Read rest of the line
text.ReadLine();
}
void OnParse(IShaderParser* parser, Reader& text) override
{
Token token;
// Read function properties
bool foundEnd = false;
while (text.CanRead())
{
text.ReadToken(&token);
// Try to find the ending
if (token == _endToken)
{
foundEnd = true;
break;
}
}
// Check if end has not been found
if (!foundEnd)
{
parser->OnError(String::Format(TEXT("Missing constant buffer \'{0}\' ending."), String(_current.Name)));
return;
}
}
void OnParseAfter(IShaderParser* parser, Reader& text) override
{
// Cache buffer
_cache.Add(_current);
}
void CollectResults(IShaderParser* parser, ShaderMeta* result) override
{
// Validate constant buffer slots overlapping
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.Slot == second.Slot)
{
parser->OnError(TEXT("Constant buffers slots are overlapping."));
return;
}
}
}
// Validate amount of used constant buffers
for (int32 i = 0; i < _cache.Count(); i++)
{
auto& f = _cache[i];
if (f.Slot >= MAX_CONSTANT_BUFFER_SLOTS)
{
parser->OnError(String::Format(TEXT("Constant buffer {0} is using invalid slot {1}. Maximum supported slot is {2}."), String(f.Name), f.Slot, MAX_CONSTANT_BUFFER_SLOTS - 1));
return;
}
}
// Base
ShaderMetaReader::CollectResults(parser, result);
}
};
}
#endif

View File

@@ -0,0 +1,29 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "ShaderFunctionReader.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
/// <summary>
/// Compute Shaders reader
/// </summary>
class ComputeShaderFunctionReader : public ShaderFunctionReader<ComputeShaderMeta>
{
DECLARE_SHADER_META_READER_HEADER("META_CS", CS);
ComputeShaderFunctionReader()
{
_childReaders.Add(New<StripLineReader>("numthreads"));
}
~ComputeShaderFunctionReader()
{
}
};
}
#endif

View File

@@ -0,0 +1,29 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "ShaderFunctionReader.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
/// <summary>
/// Domain Shaders reader
/// </summary>
class DomainShaderFunctionReader : public ShaderFunctionReader<DomainShaderMeta>
{
DECLARE_SHADER_META_READER_HEADER("META_DS", DS);
DomainShaderFunctionReader()
{
_childReaders.Add(New<StripLineReader>("domain"));
}
~DomainShaderFunctionReader()
{
}
};
}
#endif

View File

@@ -0,0 +1,57 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "ShaderFunctionReader.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
/// <summary>
/// Geometry Shaders reader
/// </summary>
class GeometryShaderFunctionReader : public ShaderFunctionReader<GeometryShaderMeta>
{
class MaxVertexCountReader : public ITokenReader
{
private:
Token _startToken;
public:
MaxVertexCountReader()
: _startToken("maxvertexcount")
{
}
public:
// [ITokenReader]
bool CheckStartToken(const Token& token) override
{
return token == _startToken;
}
void Process(IShaderParser* parser, Reader& text) override
{
// Read line to end
text.ReadLine();
}
};
DECLARE_SHADER_META_READER_HEADER("META_GS", GS);
GeometryShaderFunctionReader()
{
_childReaders.Add(New<MaxVertexCountReader>());
}
~GeometryShaderFunctionReader()
{
}
};
}
#endif

View File

@@ -0,0 +1,104 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "ShaderFunctionReader.h"
#include "Engine/Core/Math/Math.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
/// <summary>
/// Hull Shaders reader
/// </summary>
class HullShaderFunctionReader : public ShaderFunctionReader<HullShaderMeta>
{
class PatchSizeReader : public ITokenReader
{
protected:
HullShaderFunctionReader* _parent;
Token _startToken;
public:
PatchSizeReader(HullShaderFunctionReader* parent)
: _parent(parent)
, _startToken("META_HS_PATCH")
{
}
public:
// [ITokenReader]
bool CheckStartToken(const Token& token) override
{
return token == _startToken;
}
void Process(IShaderParser* parser, Reader& text) override
{
Token token;
auto& current = _parent->_current;
// Input control points count
text.ReadToken(&token);
token = parser->GetMacros().GetValue(token);
int32 controlPointsCount;
if (StringUtils::Parse(token.Start, token.Length, &controlPointsCount))
{
parser->OnError(TEXT("Cannot parse Hull shader input control points count."));
return;
}
if (Math::IsNotInRange(controlPointsCount, 1, 32))
{
parser->OnError(TEXT("Invalid amount of control points. Valid range is [1-32]."));
return;
}
current.ControlPointsCount = controlPointsCount;
}
};
DECLARE_SHADER_META_READER_HEADER("META_HS", HS);
HullShaderFunctionReader()
{
_childReaders.Add(New<PatchSizeReader>(this));
_childReaders.Add(New<StripLineReader>("domain"));
_childReaders.Add(New<StripLineReader>("partitioning"));
_childReaders.Add(New<StripLineReader>("outputtopology"));
_childReaders.Add(New<StripLineReader>("maxtessfactor"));
_childReaders.Add(New<StripLineReader>("outputcontrolpoints"));
_childReaders.Add(New<StripLineReader>("patchconstantfunc"));
}
~HullShaderFunctionReader()
{
}
void OnParseBefore(IShaderParser* parser, Reader& text) override
{
// Clear current meta
_current.ControlPointsCount = 0;
// Base
ShaderFunctionReader::OnParseBefore(parser, text);
}
void OnParseAfter(IShaderParser* parser, Reader& text) override
{
// Check if errors in parsed data
if (_current.ControlPointsCount == 0)
{
parser->OnError(String::Format(TEXT("Hull Shader \'{0}\' has missing META_HS_PATCH macro that defines the amount of the input control points from the Input Assembler."), String(_current.Name)));
return;
}
// Base
ShaderFunctionReader::OnParseAfter(parser, text);
}
};
}
#endif

View File

@@ -0,0 +1,28 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "ShaderFunctionReader.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
/// <summary>
/// Pixel Shaders reader
/// </summary>
class PixelShaderFunctionReader : public ShaderFunctionReader<PixelShaderMeta>
{
DECLARE_SHADER_META_READER_HEADER("META_PS", PS);
PixelShaderFunctionReader()
{
}
~PixelShaderFunctionReader()
{
}
};
}
#endif

View File

@@ -0,0 +1,155 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "ShaderFunctionReader.h"
#include "ShaderProcessing.h"
#include "Engine/Graphics/Shaders/Config.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
/// <summary>
/// Vertex Shaders reader
/// </summary>
class VertexShaderFunctionReader : public ShaderFunctionReader<VertexShaderMeta>
{
class InputLayoutReader : public ITokenReader
{
protected:
VertexShaderFunctionReader* _parent;
Token _startToken;
public:
InputLayoutReader(VertexShaderFunctionReader* parent)
: _parent(parent)
, _startToken("META_VS_IN_ELEMENT")
{
}
public:
// [ITokenReader]
bool CheckStartToken(const Token& token) override
{
return token == _startToken;
}
void Process(IShaderParser* parser, Reader& text) override
{
VertexShaderMeta::InputElement element;
Token token;
auto& current = _parent->_current;
// Semantic type
text.ReadToken(&token);
element.Type = ParseInputType(token);
// Semantic index
text.ReadToken(&token);
if (StringUtils::Parse(token.Start, token.Length, &element.Index))
{
parser->OnError(TEXT("Cannot parse token."));
return;
}
// Element format
text.ReadToken(&token);
element.Format = ParsePixelFormat(token);
if (element.Format == PixelFormat::Unknown)
{
parser->OnError(String::Format(TEXT("Unknown input data format \'{0}\' for the Vertex Shader."), String(token.ToString())));
return;
}
// Input slot
text.ReadToken(&token);
if (StringUtils::Parse(token.Start, token.Length, &element.InputSlot))
{
parser->OnError(TEXT("Cannot parse token."));
return;
}
// Aligned byte offset
text.ReadToken(&token);
if (token == "ALIGN")
{
element.AlignedByteOffset = INPUT_LAYOUT_ELEMENT_ALIGN;
}
else if (StringUtils::Parse(token.Start, token.Length, &element.AlignedByteOffset))
{
parser->OnError(TEXT("Cannot parse token."));
return;
}
// Input slot class
text.ReadToken(&token);
if (token == "PER_VERTEX")
{
element.InputSlotClass = INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA;
}
else if (token == "PER_INSTANCE")
{
element.InputSlotClass = INPUT_LAYOUT_ELEMENT_PER_INSTANCE_DATA;
}
else
{
parser->OnError(String::Format(TEXT("Invalid input slot class type \'{0}\'."), String(token.ToString())));
return;
}
// Instance data step rate
text.ReadToken(&token);
if (StringUtils::Parse(token.Start, token.Length, &element.InstanceDataStepRate))
{
parser->OnError(TEXT("Cannot parse token."));
return;
}
// Visible state
text.ReadToken(&token);
element.VisibleFlag = token.ToString();
current.InputLayout.Add(element);
}
};
DECLARE_SHADER_META_READER_HEADER("META_VS", VS);
VertexShaderFunctionReader()
{
_childReaders.Add(New<InputLayoutReader>(this));
}
~VertexShaderFunctionReader()
{
}
void OnParseBefore(IShaderParser* parser, Reader& text) override
{
// Clear current meta
_current.InputLayout.Clear();
// Base
ShaderFunctionReader::OnParseBefore(parser, text);
}
void OnParseAfter(IShaderParser* parser, Reader& text) override
{
// Check if errors in specified input layout
if (_current.InputLayout.Count() > VERTEX_SHADER_MAX_INPUT_ELEMENTS)
{
parser->OnError(String::Format(TEXT("Vertex Shader \'{0}\' has too many input layout elements specified. Maximum allowed amount is {1}."), String(_current.Name), VERTEX_SHADER_MAX_INPUT_ELEMENTS));
return;
}
// Base
ShaderFunctionReader::OnParseAfter(parser, text);
}
};
}
#endif

View File

@@ -0,0 +1,446 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "IShaderFunctionReader.h"
#include "ShaderMeta.h"
#include "Config.h"
#if COMPILE_WITH_SHADER_COMPILER
namespace ShaderProcessing
{
/// <summary>
/// Implementation of shader meta functions reader
/// </summary>
template<typename MetaType>
class ShaderMetaReader : public IShaderFunctionReader, public ITokenReadersContainer
{
protected:
Array<MetaType> _cache;
MetaType _current;
ShaderMetaReader()
{
}
~ShaderMetaReader()
{
}
protected:
/// <summary>
/// Event called before parsing shader function
/// </summary>
/// <param name="parser">Parser object</param>
/// <param name="text">Source code reader</param>
virtual void OnParseBefore(IShaderParser* parser, Reader& text) = 0;
/// <summary>
/// Event called for parsing shader function
/// </summary>
/// <param name="parser">Parser object</param>
/// <param name="text">Source code reader</param>
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;
}
}
}
/// <summary>
/// Event called after parsing shader function
/// </summary>
/// <param name="parser">Parser object</param>
/// <param name="text">Source code reader</param>
virtual void OnParseAfter(IShaderParser* parser, Reader& text) = 0;
/// <summary>
/// Process final results and send them to the Shader Meta object
/// </summary>
/// <param name="parser">Parser object</param>
/// <param name="result">Output shader meta</param>
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);
}
};
/// <summary>
/// Implementation of shader functions reader
/// </summary>
template<typename MetaType>
class ShaderFunctionReader : public ShaderMetaReader<MetaType>
{
public:
typedef ShaderMetaReader<MetaType> 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:
/// <summary>
/// Shader function permutations reader
/// </summary>
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:
/// <summary>
/// Init
/// </summary>
/// <param name="parent">Parent shader function reader object</param>
PermutationReader(ShaderFunctionReader* parent)
: _parent(parent)
, _startTokenPermutationSize(0)
{
}
public:
/// <summary>
/// Clear cache
/// </summary>
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 });
}
}
};
/// <summary>
/// Shader function flag reader
/// </summary>
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<PermutationReader>(this));
_childReaders.Add(New<FlagReader>(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

View File

@@ -0,0 +1,377 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#if COMPILE_WITH_SHADER_COMPILER
#include "Engine/Core/Config.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Graphics/PixelFormat.h"
#include "Config.h"
struct ShaderPermutationEntry
{
StringAnsi Name;
StringAnsi Value;
};
struct ShaderPermutation
{
// TODO: maybe we could use macro SHADER_PERMUTATIONS_MAX_PARAMS_COUNT and reduce amount of dynamic allocations for permutations
Array<ShaderPermutationEntry> Entries;
Array<char> DebugData;
};
/// <summary>
/// Shader function metadata
/// </summary>
class ShaderFunctionMeta
{
public:
/// <summary>
/// Virtual destructor
/// </summary>
virtual ~ShaderFunctionMeta() = default;
public:
/// <summary>
/// Function name
/// </summary>
StringAnsi Name;
/// <summary>
/// Function flags.
/// </summary>
ShaderFlags Flags;
/// <summary>
/// The minimum graphics platform feature level to support this shader.
/// </summary>
FeatureLevel MinFeatureLevel;
/// <summary>
/// All possible values for the permutations and it's values to generate different permutation of this function
/// </summary>
Array<ShaderPermutation> Permutations;
public:
/// <summary>
/// Checks if definition name has been added to the given permutation
/// </summary>
/// <param name="permutationIndex">Shader permutation index</param>
/// <param name="defineName">Name of the definition to check</param>
/// <returns>True if definition of given name has been registered for the permutations, otherwise false</returns>
bool HasDefinition(int32 permutationIndex, const StringAnsi& defineName) const
{
ASSERT(Math::IsInRange(permutationIndex, 0, Permutations.Count() - 1));
auto& permutation = Permutations[permutationIndex];
for (int32 i = 0; i < permutation.Entries.Count(); i++)
{
if (permutation.Entries[i].Name == defineName)
return true;
}
return false;
}
/// <summary>
/// Checks if definition name has been added to the permutations collection
/// </summary>
/// <param name="defineName">Name of the definition to check</param>
/// <returns>True if definition of given name has been registered for the permutations, otherwise false</returns>
bool HasDefinition(const StringAnsi& defineName) const
{
for (int32 permutationIndex = 0; permutationIndex < Permutations.Count(); permutationIndex++)
{
if (HasDefinition(permutationIndex, defineName))
return true;
}
return false;
}
/// <summary>
/// Gets all macros for given shader permutation
/// </summary>
/// <param name="permutationIndex">Shader permutation index</param>
/// <param name="macros">Output array with permutation macros</param>
void GetDefinitionsForPermutation(int32 permutationIndex, Array<ShaderMacro>& macros) const
{
ASSERT(Math::IsInRange(permutationIndex, 0, Permutations.Count() - 1));
auto& permutation = Permutations[permutationIndex];
for (int32 i = 0; i < permutation.Entries.Count(); i++)
{
auto& e = permutation.Entries[i];
macros.Add({
e.Name.Get(),
e.Value.Get()
});
}
}
public:
/// <summary>
/// Gets shader function meta stage type
/// </summary>
/// <returns>Shader Stage type</returns>
virtual ShaderStage GetStage() const = 0;
};
/// <summary>
/// Vertex shader function meta
/// </summary>
class VertexShaderMeta : public ShaderFunctionMeta
{
public:
/// <summary>
/// Input element type
/// </summary>
enum class InputType : byte
{
Invalid = 0,
POSITION = 1,
COLOR = 2,
TEXCOORD = 3,
NORMAL = 4,
TANGENT = 5,
BITANGENT = 6,
ATTRIBUTE = 7,
BLENDINDICES = 8,
BLENDWEIGHT = 9,
};
/// <summary>
/// Input element
/// </summary>
struct InputElement
{
/// <summary>
/// Semantic type
/// </summary>
InputType Type;
/// <summary>
/// Semantic index
/// </summary>
byte Index;
/// <summary>
/// Element data format
/// </summary>
PixelFormat Format;
/// <summary>
/// An integer value that identifies the input-assembler
/// </summary>
byte InputSlot;
/// <summary>
/// Optional. Offset (in bytes) between each element. Use INPUT_LAYOUT_ELEMENT_ALIGN for convenience to define the current element directly after the previous one, including any packing if necessary
/// </summary>
uint32 AlignedByteOffset;
/// <summary>
/// Identifies the input data class for a single input slot. INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA or INPUT_LAYOUT_ELEMENT_PER_INSTANCE_DATA
/// </summary>
byte InputSlotClass;
/// <summary>
/// The number of instances to draw using the same per-instance data before advancing in the buffer by one element. This value must be 0 for an element that contains per-vertex data
/// </summary>
uint32 InstanceDataStepRate;
/// <summary>
/// The visible flag expression. Allows to show/hide element from the input layout based on input macros (also from permutation macros). use empty value to skip this feature.
/// </summary>
StringAnsi VisibleFlag;
};
public:
/// <summary>
/// Input layout description
/// </summary>
Array<InputElement> InputLayout;
public:
// [ShaderFunctionMeta]
ShaderStage GetStage() const override
{
return ShaderStage::Vertex;
}
};
/// <summary>
/// Hull (or tessellation control) shader function meta
/// </summary>
class HullShaderMeta : public ShaderFunctionMeta
{
public:
/// <summary>
/// The input control points count (valid range: 1-32).
/// </summary>
int32 ControlPointsCount;
public:
// [ShaderFunctionMeta]
ShaderStage GetStage() const override
{
return ShaderStage::Hull;
}
};
/// <summary>
/// Domain (or tessellation evaluation) shader function meta
/// </summary>
class DomainShaderMeta : public ShaderFunctionMeta
{
public:
// [ShaderFunctionMeta]
ShaderStage GetStage() const override
{
return ShaderStage::Domain;
}
};
/// <summary>
/// Geometry shader function meta
/// </summary>
class GeometryShaderMeta : public ShaderFunctionMeta
{
public:
// [ShaderFunctionMeta]
ShaderStage GetStage() const override
{
return ShaderStage::Geometry;
}
};
/// <summary>
/// Pixel shader function meta
/// </summary>
class PixelShaderMeta : public ShaderFunctionMeta
{
public:
// [ShaderFunctionMeta]
ShaderStage GetStage() const override
{
return ShaderStage::Pixel;
}
};
/// <summary>
/// Compute shader function meta
/// </summary>
class ComputeShaderMeta : public ShaderFunctionMeta
{
public:
// [ShaderFunctionMeta]
ShaderStage GetStage() const override
{
return ShaderStage::Compute;
}
};
/// <summary>
/// Constant buffer meta
/// </summary>
struct ConstantBufferMeta
{
/// <summary>
/// Slot index
/// </summary>
byte Slot;
/// <summary>
/// Buffer name
/// </summary>
StringAnsi Name;
};
/// <summary>
/// Shader source metadata
/// </summary>
class ShaderMeta
{
public:
/// <summary>
/// Vertex Shaders
/// </summary>
Array<VertexShaderMeta> VS;
/// <summary>
/// Hull Shaders
/// </summary>
Array<HullShaderMeta> HS;
/// <summary>
/// Domain Shaders
/// </summary>
Array<DomainShaderMeta> DS;
/// <summary>
/// Geometry Shaders
/// </summary>
Array<GeometryShaderMeta> GS;
/// <summary>
/// Pixel Shaders
/// </summary>
Array<PixelShaderMeta> PS;
/// <summary>
/// Compute Shaders
/// </summary>
Array<ComputeShaderMeta> CS;
/// <summary>
/// Constant Buffers
/// </summary>
Array<ConstantBufferMeta> CB;
public:
/// <summary>
/// Gets amount of shaders attached (not counting permutations)
/// </summary>
/// <returns>Amount of all shader programs</returns>
uint32 GetShadersCount() const
{
return VS.Count() + HS.Count() + DS.Count() + GS.Count() + PS.Count() + CS.Count();
}
/// <summary>
/// Gets all shader functions (all types)
/// </summary>
/// <param name="functions">Output collections of functions</param>
void GetShaders(Array<const ShaderFunctionMeta*>& functions) const
{
#define PEEK_SHADERS(collection) for (int32 i = 0; i < collection.Count(); i++) functions.Add(dynamic_cast<const ShaderFunctionMeta*>(&(collection[i])));
PEEK_SHADERS(VS);
PEEK_SHADERS(HS);
PEEK_SHADERS(DS);
PEEK_SHADERS(GS);
PEEK_SHADERS(PS);
PEEK_SHADERS(CS);
#undef PEEK_SHADERS
}
};
#endif

View File

@@ -0,0 +1,161 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "ShaderProcessing.h"
#if COMPILE_WITH_SHADER_COMPILER
#include "Engine/Core/Log.h"
VertexShaderMeta::InputType ShaderProcessing::ParseInputType(const Token& token)
{
struct InputDesc
{
VertexShaderMeta::InputType e;
const char* s;
};
#define _PARSE_ENTRY(x) { VertexShaderMeta::InputType::x, #x }
const InputDesc formats[] =
{
_PARSE_ENTRY(Invalid),
_PARSE_ENTRY(POSITION),
_PARSE_ENTRY(COLOR),
_PARSE_ENTRY(TEXCOORD),
_PARSE_ENTRY(NORMAL),
_PARSE_ENTRY(TANGENT),
_PARSE_ENTRY(BITANGENT),
_PARSE_ENTRY(ATTRIBUTE),
_PARSE_ENTRY(BLENDINDICES),
_PARSE_ENTRY(BLENDWEIGHT),
};
#undef _PARSE_ENTRY
for (int32 i = 0; i < ARRAY_COUNT(formats); i++)
{
if (token == formats[i].s)
return formats[i].e;
}
return VertexShaderMeta::InputType::Invalid;
}
PixelFormat ShaderProcessing::ParsePixelFormat(const Token& token)
{
struct DataDesc
{
PixelFormat e;
const char* s;
};
#define _PARSE_ENTRY(x) { PixelFormat::x, #x }
const DataDesc formats[] =
{
_PARSE_ENTRY(Unknown),
_PARSE_ENTRY(R32G32B32A32_Float),
_PARSE_ENTRY(R32G32B32A32_UInt),
_PARSE_ENTRY(R32G32B32A32_SInt),
_PARSE_ENTRY(R32G32B32_Float),
_PARSE_ENTRY(R32G32B32_UInt),
_PARSE_ENTRY(R32G32B32_SInt),
_PARSE_ENTRY(R16G16B16A16_Float),
_PARSE_ENTRY(R16G16B16A16_UNorm),
_PARSE_ENTRY(R16G16B16A16_UInt),
_PARSE_ENTRY(R16G16B16A16_SNorm),
_PARSE_ENTRY(R16G16B16A16_SInt),
_PARSE_ENTRY(R32G32_Float),
_PARSE_ENTRY(R32G32_UInt),
_PARSE_ENTRY(R32G32_SInt),
_PARSE_ENTRY(R10G10B10A2_UNorm),
_PARSE_ENTRY(R10G10B10A2_UInt),
_PARSE_ENTRY(R11G11B10_Float),
_PARSE_ENTRY(R8G8B8A8_UNorm),
_PARSE_ENTRY(R8G8B8A8_UNorm_sRGB),
_PARSE_ENTRY(R8G8B8A8_UInt),
_PARSE_ENTRY(R8G8B8A8_SNorm),
_PARSE_ENTRY(R8G8B8A8_SInt),
_PARSE_ENTRY(R16G16_Float),
_PARSE_ENTRY(R16G16_UNorm),
_PARSE_ENTRY(R16G16_UInt),
_PARSE_ENTRY(R16G16_SNorm),
_PARSE_ENTRY(R16G16_SInt),
_PARSE_ENTRY(R32_Float),
_PARSE_ENTRY(R32_UInt),
_PARSE_ENTRY(R32_SInt),
_PARSE_ENTRY(R8G8_UNorm),
_PARSE_ENTRY(R8G8_UInt),
_PARSE_ENTRY(R8G8_SNorm),
_PARSE_ENTRY(R8G8_SInt),
_PARSE_ENTRY(R16_Float),
_PARSE_ENTRY(R16_UNorm),
_PARSE_ENTRY(R16_UInt),
_PARSE_ENTRY(R16_SNorm),
_PARSE_ENTRY(R16_SInt),
_PARSE_ENTRY(R8_UNorm),
_PARSE_ENTRY(R8_UInt),
_PARSE_ENTRY(R8_SNorm),
_PARSE_ENTRY(R8_SInt),
_PARSE_ENTRY(A8_UNorm),
_PARSE_ENTRY(R1_UNorm),
_PARSE_ENTRY(R8G8_B8G8_UNorm),
_PARSE_ENTRY(G8R8_G8B8_UNorm),
_PARSE_ENTRY(BC1_UNorm),
_PARSE_ENTRY(BC1_UNorm_sRGB),
_PARSE_ENTRY(BC2_UNorm),
_PARSE_ENTRY(BC2_UNorm_sRGB),
_PARSE_ENTRY(BC3_UNorm),
_PARSE_ENTRY(BC3_UNorm_sRGB),
_PARSE_ENTRY(BC4_UNorm),
_PARSE_ENTRY(BC4_SNorm),
_PARSE_ENTRY(BC5_UNorm),
_PARSE_ENTRY(BC5_SNorm),
_PARSE_ENTRY(B5G6R5_UNorm),
_PARSE_ENTRY(B5G5R5A1_UNorm),
_PARSE_ENTRY(B8G8R8A8_UNorm),
_PARSE_ENTRY(B8G8R8X8_UNorm),
_PARSE_ENTRY(B8G8R8A8_UNorm_sRGB),
_PARSE_ENTRY(B8G8R8X8_UNorm_sRGB),
_PARSE_ENTRY(BC6H_Uf16),
_PARSE_ENTRY(BC6H_Sf16),
_PARSE_ENTRY(BC7_UNorm),
_PARSE_ENTRY(BC7_UNorm_sRGB),
};
#undef _PARSE_ENTRY
for (int32 i = 0; i < ARRAY_COUNT(formats); i++)
{
if (token.EqualsIgnoreCase(formats[i].s))
return formats[i].e;
}
return PixelFormat::Unknown;
}
ShaderFlags ShaderProcessing::ParseShaderFlags(const Token& token)
{
struct DataDesc
{
ShaderFlags e;
const char* s;
};
#define _PARSE_ENTRY(x) { ShaderFlags::x, #x }
const DataDesc data[] =
{
_PARSE_ENTRY(Default),
_PARSE_ENTRY(Hidden),
_PARSE_ENTRY(NoFastMath),
_PARSE_ENTRY(VertexToGeometryShader),
};
static_assert(ARRAY_COUNT(data) == 4, "Invalid amount of Shader Flag data entries.");
#undef _PARSE_ENTRY
for (int32 i = 0; i < ARRAY_COUNT(data); i++)
{
if (token.EqualsIgnoreCase(data[i].s))
return data[i].e;
}
return ShaderFlags::Default;
}
#endif

View File

@@ -0,0 +1,146 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "ShaderProcessing.h"
#if COMPILE_WITH_SHADER_COMPILER
#include "Engine/Core/Collections/Array.h"
#include "Engine/Utilities/TextProcessing.h"
#include "Engine/Core/Log.h"
#include "ShaderFunctionReader.CB.h"
#include "ShaderFunctionReader.VS.h"
#include "ShaderFunctionReader.HS.h"
#include "ShaderFunctionReader.DS.h"
#include "ShaderFunctionReader.GS.h"
#include "ShaderFunctionReader.PS.h"
#include "ShaderFunctionReader.CS.h"
#include "Config.h"
ShaderProcessing::Parser::Parser(const String& targetName, const char* source, int32 sourceLength, ParserMacros macros, FeatureLevel featureLevel)
: failed(false)
, targetName(targetName)
, text(source, sourceLength)
, _macros(macros)
, _featureLevel(featureLevel)
{
}
ShaderProcessing::Parser::~Parser()
{
}
bool ShaderProcessing::Parser::Process(const String& targetName, const char* source, int32 sourceLength, ParserMacros macros, FeatureLevel featureLevel, ShaderMeta* result)
{
Parser parser(targetName, source, sourceLength, macros, featureLevel);
parser.Process(result);
return parser.Failed();
}
void ShaderProcessing::Parser::Process(ShaderMeta* result)
{
init();
failed = process();
if (!failed)
failed = collectResults(result);
}
void ShaderProcessing::Parser::init()
{
// Init text processing tokens for hlsl language
text.Setup_HLSL();
// Init shader functions readers
_childReaders.Add(New<ConstantBufferReader>());
_childReaders.Add(New<VertexShaderFunctionReader>());
_childReaders.Add(New<HullShaderFunctionReader>());
_childReaders.Add(New<DomainShaderFunctionReader>());
_childReaders.Add(New<GeometryShaderFunctionReader>());
_childReaders.Add(New<PixelShaderFunctionReader>());
_childReaders.Add(New<ComputeShaderFunctionReader>());
}
bool ShaderProcessing::Parser::process()
{
const Token defineToken("#define");
const Separator singleLineCommentSeparator('/', '/');
const Separator multiLineCommentSeparator('/', '*');
// TODO: split parsing into two phrases: comments preprocessing and parsing
// Read whole source code
Token token;
while (text.CanRead())
{
text.ReadToken(&token);
// Single line comment
if (token.Separator == singleLineCommentSeparator)
{
// Read whole line
text.ReadLine();
}
// Multi line comment
else if (token.Separator == multiLineCommentSeparator)
{
// Read tokens until end sequence
char prev = ' ';
char c;
while (text.CanRead())
{
c = text.ReadChar();
if (prev == '*' && c == '/')
{
break;
}
prev = c;
}
// Check if comment is valid (has end before file end)
if (!text.CanRead())
{
OnWarning(TEXT("Missing multiline comment ending"));
}
}
// Preprocessor definition
else if (token == defineToken)
{
// Skip
text.ReadLine();
}
else
{
// Call children
ProcessChildren(token, this);
}
}
return false;
}
bool ShaderProcessing::Parser::collectResults(ShaderMeta* result)
{
// Collect results from all the readers
for (int32 i = 0; i < _childReaders.Count(); i++)
_childReaders[i]->CollectResults(this, result);
return false;
}
void ShaderProcessing::Parser::OnError(const String& message)
{
// Set flag
failed = true;
// Send event
LOG(Error, "Processing shader '{0}' error at line {1}. {2}", targetName, text.GetLine(), message);
}
void ShaderProcessing::Parser::OnWarning(const String& message)
{
// Send event
LOG(Warning, "Processing shader '{0}' warning at line {1}. {2}", targetName, text.GetLine(), message);
}
#endif

View File

@@ -0,0 +1,93 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "ShaderMeta.h"
#if COMPILE_WITH_SHADER_COMPILER
#include "Engine/Core/Types/String.h"
#include "IShaderFunctionReader.h"
#include "ITokenReadersContainer.h"
namespace ShaderProcessing
{
extern VertexShaderMeta::InputType ParseInputType(const Token& token);
extern PixelFormat ParsePixelFormat(const Token& token);
extern ShaderFlags ParseShaderFlags(const Token& token);
/// <summary>
/// Shader files meta data processing tool
/// </summary>
class Parser : public IShaderParser, public ITokenReadersContainerBase<IShaderFunctionReader>
{
private:
bool failed;
String targetName;
Reader text;
ParserMacros _macros;
FeatureLevel _featureLevel;
private:
Parser(const String& targetName, const char* source, int32 sourceLength, ParserMacros macros, FeatureLevel featureLevel);
~Parser();
public:
/// <summary>
/// Process shader source code and generate metadata
/// </summary>
/// <param name="targetName">Calling object name (used for warnings/errors logging)</param>
/// <param name="source">ANSI source code</param>
/// <param name="sourceLength">Amount of characters in the source code</param>
/// <param name="macros">The input macros.</param>
/// <param name="featureLevel">The target feature level.</param>
/// <param name="result">Output result with metadata</param>
/// <returns>True if cannot process the file (too many errors), otherwise false</returns>
static bool Process(const String& targetName, const char* source, int32 sourceLength, ParserMacros macros, FeatureLevel featureLevel, ShaderMeta* result);
public:
/// <summary>
/// Process shader source code and generate metadata
/// </summary>
/// <param name="result">Output result with metadata</param>
void Process(ShaderMeta* result);
private:
void init();
bool process();
bool collectResults(ShaderMeta* result);
public:
// [IShaderParser]
FeatureLevel GetFeatureLevel() const override
{
return _featureLevel;
}
bool Failed() const override
{
return failed;
}
Reader& GetReader() override
{
return text;
}
ParserMacros GetMacros() const override
{
return _macros;
}
void OnError(const String& message) override;
void OnWarning(const String& message) override;
};
}
#endif