Shader: Add initial support for options (WIP)
This commit is contained in:
@@ -56,7 +56,7 @@ namespace Nz::ShaderAst
|
||||
StatementPtr AstCloner::Clone(ConditionalStatement& node)
|
||||
{
|
||||
auto clone = std::make_unique<ConditionalStatement>();
|
||||
clone->conditionName = node.conditionName;
|
||||
clone->optionIndex = node.optionIndex;
|
||||
clone->statement = CloneStatement(node.statement);
|
||||
|
||||
return clone;
|
||||
@@ -77,6 +77,7 @@ namespace Nz::ShaderAst
|
||||
clone->entryStage = node.entryStage;
|
||||
clone->funcIndex = node.funcIndex;
|
||||
clone->name = node.name;
|
||||
clone->optionName = node.optionName;
|
||||
clone->parameters = node.parameters;
|
||||
clone->returnType = node.returnType;
|
||||
clone->varIndex = node.varIndex;
|
||||
@@ -88,6 +89,17 @@ namespace Nz::ShaderAst
|
||||
return clone;
|
||||
}
|
||||
|
||||
StatementPtr AstCloner::Clone(DeclareOptionStatement& node)
|
||||
{
|
||||
auto clone = std::make_unique<DeclareOptionStatement>();
|
||||
clone->initialValue = CloneExpression(node.initialValue);
|
||||
clone->optIndex = node.optIndex;
|
||||
clone->optName = node.optName;
|
||||
clone->optType = node.optType;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
StatementPtr AstCloner::Clone(DeclareStructStatement& node)
|
||||
{
|
||||
auto clone = std::make_unique<DeclareStructStatement>();
|
||||
@@ -212,7 +224,7 @@ namespace Nz::ShaderAst
|
||||
ExpressionPtr AstCloner::Clone(ConditionalExpression& node)
|
||||
{
|
||||
auto clone = std::make_unique<ConditionalExpression>();
|
||||
clone->conditionName = node.conditionName;
|
||||
clone->optionIndex = node.optionIndex;
|
||||
clone->falsePath = CloneExpression(node.falsePath);
|
||||
clone->truePath = CloneExpression(node.truePath);
|
||||
|
||||
@@ -255,6 +267,18 @@ namespace Nz::ShaderAst
|
||||
return clone;
|
||||
}
|
||||
|
||||
ExpressionPtr AstCloner::Clone(SelectOptionExpression& node)
|
||||
{
|
||||
auto clone = std::make_unique<SelectOptionExpression>();
|
||||
clone->optionName = node.optionName;
|
||||
clone->falsePath = CloneExpression(node.falsePath);
|
||||
clone->truePath = CloneExpression(node.truePath);
|
||||
|
||||
clone->cachedExpressionType = node.cachedExpressionType;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
ExpressionPtr AstCloner::Clone(SwizzleExpression& node)
|
||||
{
|
||||
auto clone = std::make_unique<SwizzleExpression>();
|
||||
|
||||
@@ -62,6 +62,12 @@ namespace Nz::ShaderAst
|
||||
param->Visit(*this);
|
||||
}
|
||||
|
||||
void AstRecursiveVisitor::Visit(SelectOptionExpression& node)
|
||||
{
|
||||
node.truePath->Visit(*this);
|
||||
node.falsePath->Visit(*this);
|
||||
}
|
||||
|
||||
void AstRecursiveVisitor::Visit(SwizzleExpression& node)
|
||||
{
|
||||
node.expression->Visit(*this);
|
||||
@@ -100,6 +106,12 @@ namespace Nz::ShaderAst
|
||||
statement->Visit(*this);
|
||||
}
|
||||
|
||||
void AstRecursiveVisitor::Visit(DeclareOptionStatement& node)
|
||||
{
|
||||
if (node.initialValue)
|
||||
node.initialValue->Visit(*this);
|
||||
}
|
||||
|
||||
void AstRecursiveVisitor::Visit(DeclareStructStatement& /*node*/)
|
||||
{
|
||||
/* Nothing to do */
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Nz::ShaderAst
|
||||
|
||||
void AstSerializerBase::Serialize(ConditionalExpression& node)
|
||||
{
|
||||
Value(node.conditionName);
|
||||
SizeT(node.optionIndex);
|
||||
Node(node.truePath);
|
||||
Node(node.falsePath);
|
||||
}
|
||||
@@ -113,14 +113,6 @@ namespace Nz::ShaderAst
|
||||
}
|
||||
}
|
||||
|
||||
void AstSerializerBase::Serialize(DeclareVariableStatement& node)
|
||||
{
|
||||
OptVal(node.varIndex);
|
||||
Value(node.varName);
|
||||
Type(node.varType);
|
||||
Node(node.initialExpression);
|
||||
}
|
||||
|
||||
void AstSerializerBase::Serialize(IdentifierExpression& node)
|
||||
{
|
||||
Value(node.identifier);
|
||||
@@ -134,6 +126,13 @@ namespace Nz::ShaderAst
|
||||
Node(param);
|
||||
}
|
||||
|
||||
void AstSerializerBase::Serialize(SelectOptionExpression& node)
|
||||
{
|
||||
Value(node.optionName);
|
||||
Node(node.truePath);
|
||||
Node(node.falsePath);
|
||||
}
|
||||
|
||||
void AstSerializerBase::Serialize(SwizzleExpression& node)
|
||||
{
|
||||
SizeT(node.componentCount);
|
||||
@@ -163,7 +162,7 @@ namespace Nz::ShaderAst
|
||||
|
||||
void AstSerializerBase::Serialize(ConditionalStatement& node)
|
||||
{
|
||||
Value(node.conditionName);
|
||||
SizeT(node.optionIndex);
|
||||
Node(node.statement);
|
||||
}
|
||||
|
||||
@@ -186,6 +185,7 @@ namespace Nz::ShaderAst
|
||||
Type(node.returnType);
|
||||
OptEnum(node.entryStage);
|
||||
OptVal(node.funcIndex);
|
||||
Value(node.optionName);
|
||||
OptVal(node.varIndex);
|
||||
|
||||
Container(node.parameters);
|
||||
@@ -200,6 +200,14 @@ namespace Nz::ShaderAst
|
||||
Node(statement);
|
||||
}
|
||||
|
||||
void AstSerializerBase::Serialize(DeclareOptionStatement& node)
|
||||
{
|
||||
OptVal(node.optIndex);
|
||||
Value(node.optName);
|
||||
Type(node.optType);
|
||||
Node(node.initialValue);
|
||||
}
|
||||
|
||||
void AstSerializerBase::Serialize(DeclareStructStatement& node)
|
||||
{
|
||||
OptVal(node.structIndex);
|
||||
@@ -216,6 +224,14 @@ namespace Nz::ShaderAst
|
||||
OptVal(member.locationIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void AstSerializerBase::Serialize(DeclareVariableStatement& node)
|
||||
{
|
||||
OptVal(node.varIndex);
|
||||
Value(node.varName);
|
||||
Type(node.varType);
|
||||
Node(node.initialExpression);
|
||||
}
|
||||
|
||||
void AstSerializerBase::Serialize(DiscardStatement& /*node*/)
|
||||
{
|
||||
|
||||
@@ -67,6 +67,20 @@ namespace Nz::ShaderAst
|
||||
m_expressionCategory = ExpressionCategory::RValue;
|
||||
}
|
||||
|
||||
void ShaderAstValueCategory::Visit(SelectOptionExpression& node)
|
||||
{
|
||||
node.truePath->Visit(*this);
|
||||
ExpressionCategory trueExprCategory = m_expressionCategory;
|
||||
|
||||
node.falsePath->Visit(*this);
|
||||
ExpressionCategory falseExprCategory = m_expressionCategory;
|
||||
|
||||
if (trueExprCategory == ExpressionCategory::RValue || falseExprCategory == ExpressionCategory::RValue)
|
||||
m_expressionCategory = ExpressionCategory::RValue;
|
||||
else
|
||||
m_expressionCategory = ExpressionCategory::LValue;
|
||||
}
|
||||
|
||||
void ShaderAstValueCategory::Visit(SwizzleExpression& node)
|
||||
{
|
||||
node.expression->Visit(*this);
|
||||
|
||||
@@ -29,4 +29,35 @@ namespace Nz::ShaderAst
|
||||
}
|
||||
|
||||
#include <Nazara/Shader/Ast/AstNodeList.hpp>
|
||||
|
||||
ExpressionType ConstantExpression::GetExpressionType() const
|
||||
{
|
||||
return std::visit([&](auto&& arg) -> ShaderAst::ExpressionType
|
||||
{
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, bool>)
|
||||
return PrimitiveType::Boolean;
|
||||
else if constexpr (std::is_same_v<T, float>)
|
||||
return PrimitiveType::Float32;
|
||||
else if constexpr (std::is_same_v<T, Int32>)
|
||||
return PrimitiveType::Int32;
|
||||
else if constexpr (std::is_same_v<T, UInt32>)
|
||||
return PrimitiveType::UInt32;
|
||||
else if constexpr (std::is_same_v<T, Vector2f>)
|
||||
return VectorType{ 2, PrimitiveType::Float32 };
|
||||
else if constexpr (std::is_same_v<T, Vector3f>)
|
||||
return VectorType{ 3, PrimitiveType::Float32 };
|
||||
else if constexpr (std::is_same_v<T, Vector4f>)
|
||||
return VectorType{ 4, PrimitiveType::Float32 };
|
||||
else if constexpr (std::is_same_v<T, Vector2i32>)
|
||||
return VectorType{ 2, PrimitiveType::Int32 };
|
||||
else if constexpr (std::is_same_v<T, Vector3i32>)
|
||||
return VectorType{ 3, PrimitiveType::Int32 };
|
||||
else if constexpr (std::is_same_v<T, Vector4i32>)
|
||||
return VectorType{ 4, PrimitiveType::Int32 };
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
|
||||
}, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
|
||||
#include <Nazara/Core/CallOnExit.hpp>
|
||||
#include <Nazara/Core/StackArray.hpp>
|
||||
#include <Nazara/Shader/ShaderBuilder.hpp>
|
||||
#include <Nazara/Shader/Ast/AstUtils.hpp>
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
@@ -28,16 +29,18 @@ namespace Nz::ShaderAst
|
||||
|
||||
struct SanitizeVisitor::Context
|
||||
{
|
||||
Options options;
|
||||
std::array<DeclareFunctionStatement*, ShaderStageTypeCount> entryFunctions = {};
|
||||
std::unordered_set<std::string> declaredExternalVar;
|
||||
std::unordered_set<unsigned int> usedBindingIndexes;
|
||||
};
|
||||
|
||||
StatementPtr SanitizeVisitor::Sanitize(StatementPtr& nodePtr, std::string* error)
|
||||
StatementPtr SanitizeVisitor::Sanitize(StatementPtr& nodePtr, const Options& options, std::string* error)
|
||||
{
|
||||
StatementPtr clone;
|
||||
|
||||
Context currentContext;
|
||||
currentContext.options = options;
|
||||
|
||||
m_context = ¤tContext;
|
||||
CallOnExit resetContext([&] { m_context = nullptr; });
|
||||
@@ -483,6 +486,33 @@ namespace Nz::ShaderAst
|
||||
return clone;
|
||||
}
|
||||
|
||||
ExpressionPtr SanitizeVisitor::Clone(SelectOptionExpression& node)
|
||||
{
|
||||
MandatoryExpr(node.truePath);
|
||||
MandatoryExpr(node.falsePath);
|
||||
|
||||
auto condExpr = std::make_unique<ConditionalExpression>();
|
||||
condExpr->truePath = CloneExpression(node.truePath);
|
||||
condExpr->falsePath = CloneExpression(node.falsePath);
|
||||
|
||||
const Identifier* identifier = FindIdentifier(node.optionName);
|
||||
if (!identifier)
|
||||
throw AstError{ "unknown option " + node.optionName };
|
||||
|
||||
if (!std::holds_alternative<Option>(identifier->value))
|
||||
throw AstError{ "expected option identifier" };
|
||||
|
||||
condExpr->optionIndex = std::get<Option>(identifier->value).optionIndex;
|
||||
|
||||
const ExpressionType& leftExprType = GetExpressionType(*condExpr->truePath);
|
||||
if (leftExprType != GetExpressionType(*condExpr->falsePath))
|
||||
throw AstError{ "true path type must match false path type" };
|
||||
|
||||
condExpr->cachedExpressionType = leftExprType;
|
||||
|
||||
return condExpr;
|
||||
}
|
||||
|
||||
ExpressionPtr SanitizeVisitor::Clone(SwizzleExpression& node)
|
||||
{
|
||||
if (node.componentCount > 4)
|
||||
@@ -585,9 +615,13 @@ namespace Nz::ShaderAst
|
||||
{
|
||||
extVar.type = ResolveType(extVar.type);
|
||||
|
||||
ExpressionType varType = extVar.type;
|
||||
ExpressionType varType;
|
||||
if (IsUniformType(extVar.type))
|
||||
varType = std::get<StructType>(std::get<UniformType>(varType).containedType);
|
||||
varType = std::get<StructType>(std::get<UniformType>(extVar.type).containedType);
|
||||
else if (IsSamplerType(extVar.type))
|
||||
varType = extVar.type;
|
||||
else
|
||||
throw AstError{ "External variable " + extVar.name + " is of wrong type: only uniform and sampler are allowed in external blocks" };
|
||||
|
||||
std::size_t varIndex = RegisterVariable(extVar.name, std::move(varType));
|
||||
if (!clone->varIndex)
|
||||
@@ -616,6 +650,7 @@ namespace Nz::ShaderAst
|
||||
clone->entryStage = node.entryStage;
|
||||
clone->name = node.name;
|
||||
clone->funcIndex = m_nextFuncIndex++;
|
||||
clone->optionName = node.optionName;
|
||||
clone->parameters = node.parameters;
|
||||
clone->returnType = ResolveType(node.returnType);
|
||||
|
||||
@@ -635,6 +670,36 @@ namespace Nz::ShaderAst
|
||||
}
|
||||
PopScope();
|
||||
|
||||
if (!clone->optionName.empty())
|
||||
{
|
||||
const Identifier* identifier = FindIdentifier(node.optionName);
|
||||
if (!identifier)
|
||||
throw AstError{ "unknown option " + node.optionName };
|
||||
|
||||
if (!std::holds_alternative<Option>(identifier->value))
|
||||
throw AstError{ "expected option identifier" };
|
||||
|
||||
std::size_t optionIndex = std::get<Option>(identifier->value).optionIndex;
|
||||
|
||||
return ShaderBuilder::ConditionalStatement(optionIndex, std::move(clone));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
StatementPtr SanitizeVisitor::Clone(DeclareOptionStatement& node)
|
||||
{
|
||||
auto clone = static_unique_pointer_cast<DeclareOptionStatement>(AstCloner::Clone(node));
|
||||
clone->optType = ResolveType(clone->optType);
|
||||
|
||||
if (clone->initialValue && clone->optType != GetExpressionType(*clone->initialValue))
|
||||
throw AstError{ "option " + clone->optName + " initial expression must be of the same type than the option" };
|
||||
|
||||
clone->optIndex = RegisterOption(clone->optName, clone->optType);
|
||||
|
||||
if (m_context->options.removeOptionDeclaration)
|
||||
return ShaderBuilder::NoOp();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user