Shader: Merge AstScopedVisitor, AstValidator and TransformVisitor to SanitizeVisitor

This commit is contained in:
Jérôme Leclercq 2021-04-15 11:20:56 +02:00
parent 8515c9cea1
commit afe3a0ea93
19 changed files with 1027 additions and 1339 deletions

View File

@ -41,7 +41,6 @@
#include <Nazara/Shader/ShaderAstStatementVisitor.hpp>
#include <Nazara/Shader/ShaderAstStatementVisitorExcept.hpp>
#include <Nazara/Shader/ShaderAstUtils.hpp>
#include <Nazara/Shader/ShaderAstValidator.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderEnums.hpp>
#include <Nazara/Shader/ShaderLangLexer.hpp>

View File

@ -0,0 +1,112 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Shader generator"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_SHADERAST_TRANSFORMVISITOR_HPP
#define NAZARA_SHADERAST_TRANSFORMVISITOR_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Shader/Config.hpp>
#include <Nazara/Shader/ShaderAstCloner.hpp>
#include <vector>
namespace Nz::ShaderAst
{
class NAZARA_SHADER_API SanitizeVisitor final : AstCloner
{
public:
inline SanitizeVisitor();
SanitizeVisitor(const SanitizeVisitor&) = delete;
SanitizeVisitor(SanitizeVisitor&&) = delete;
~SanitizeVisitor() = default;
StatementPtr Sanitize(StatementPtr& statement, std::string* error = nullptr);
SanitizeVisitor& operator=(const SanitizeVisitor&) = delete;
SanitizeVisitor& operator=(SanitizeVisitor&&) = delete;
private:
struct Identifier;
const ExpressionType& CheckField(const ExpressionType& structType, const std::string* memberIdentifier, std::size_t remainingMembers, std::size_t* structIndices);
using AstCloner::CloneExpression;
ExpressionPtr Clone(AccessMemberIdentifierExpression& node) override;
ExpressionPtr Clone(AssignExpression& node) override;
ExpressionPtr Clone(BinaryExpression& node) override;
ExpressionPtr Clone(CastExpression& node) override;
ExpressionPtr Clone(ConditionalExpression& node) override;
ExpressionPtr Clone(ConstantExpression& node) override;
ExpressionPtr Clone(IdentifierExpression& node) override;
ExpressionPtr Clone(IntrinsicExpression& node) override;
ExpressionPtr Clone(SwizzleExpression& node) override;
StatementPtr Clone(BranchStatement& node) override;
StatementPtr Clone(ConditionalStatement& node) override;
StatementPtr Clone(DeclareExternalStatement& node) override;
StatementPtr Clone(DeclareFunctionStatement& node) override;
StatementPtr Clone(DeclareStructStatement& node) override;
StatementPtr Clone(DeclareVariableStatement& node) override;
StatementPtr Clone(ExpressionStatement& node) override;
StatementPtr Clone(MultiStatement& node) override;
inline const Identifier* FindIdentifier(const std::string_view& identifierName) const;
Expression& MandatoryExpr(ExpressionPtr& node);
Statement& MandatoryStatement(StatementPtr& node);
void TypeMustMatch(ExpressionPtr& left, ExpressionPtr& right);
void TypeMustMatch(const ExpressionType& left, const ExpressionType& right);
void PushScope();
void PopScope();
inline std::size_t RegisterFunction(std::string name);
inline std::size_t RegisterStruct(std::string name, StructDescription description);
inline std::size_t RegisterVariable(std::string name, ExpressionType type);
std::size_t ResolveStruct(const ExpressionType& exprType);
std::size_t ResolveStruct(const IdentifierType& identifierType);
std::size_t ResolveStruct(const StructType& structType);
std::size_t ResolveStruct(const UniformType& uniformType);
ExpressionType ResolveType(const ExpressionType& exprType);
struct Alias
{
std::variant<ExpressionType> value;
};
struct Struct
{
std::size_t structIndex;
};
struct Variable
{
std::size_t varIndex;
};
struct Identifier
{
std::string name;
std::variant<Alias, Struct, Variable> value;
};
std::size_t m_nextFuncIndex;
std::vector<Identifier> m_identifiersInScope;
std::vector<StructDescription> m_structs;
std::vector<ExpressionType> m_variables;
std::vector<std::size_t> m_scopeSizes;
struct Context;
Context* m_context;
};
inline StatementPtr Sanitize(StatementPtr& ast, std::string* error = nullptr);
}
#include <Nazara/Shader/Ast/SanitizeVisitor.inl>
#endif

View File

@ -2,18 +2,17 @@
// This file is part of the "Nazara Engine - Shader generator"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Shader/Ast/TransformVisitor.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <Nazara/Shader/Debug.hpp>
namespace Nz::ShaderAst
{
inline TransformVisitor::TransformVisitor() :
m_nextFuncIndex(0),
m_nextVarIndex(0)
inline SanitizeVisitor::SanitizeVisitor() :
m_nextFuncIndex(0)
{
}
inline auto TransformVisitor::FindIdentifier(const std::string_view& identifierName) const -> const Identifier*
inline auto SanitizeVisitor::FindIdentifier(const std::string_view& identifierName) const -> const Identifier*
{
auto it = std::find_if(m_identifiersInScope.rbegin(), m_identifiersInScope.rend(), [&](const Identifier& identifier) { return identifier.name == identifierName; });
if (it == m_identifiersInScope.rend())
@ -22,14 +21,14 @@ namespace Nz::ShaderAst
return &*it;
}
inline std::size_t TransformVisitor::RegisterFunction(std::string name)
inline std::size_t SanitizeVisitor::RegisterFunction(std::string name)
{
std::size_t funcIndex = m_nextFuncIndex++;
return funcIndex;
}
inline std::size_t TransformVisitor::RegisterStruct(std::string name, StructDescription description)
inline std::size_t SanitizeVisitor::RegisterStruct(std::string name, StructDescription description)
{
std::size_t structIndex = m_structs.size();
m_structs.emplace_back(std::move(description));
@ -44,9 +43,10 @@ namespace Nz::ShaderAst
return structIndex;
}
inline std::size_t TransformVisitor::RegisterVariable(std::string name)
inline std::size_t SanitizeVisitor::RegisterVariable(std::string name, ExpressionType type)
{
std::size_t varIndex = m_nextVarIndex++;
std::size_t varIndex = m_variables.size();
m_variables.emplace_back(std::move(type));
m_identifiersInScope.push_back({
std::move(name),
@ -57,6 +57,12 @@ namespace Nz::ShaderAst
return varIndex;
}
StatementPtr Sanitize(StatementPtr& ast, std::string* error)
{
SanitizeVisitor sanitizer;
return sanitizer.Sanitize(ast, error);
}
}
#include <Nazara/Shader/DebugOff.hpp>

View File

@ -1,90 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Shader generator"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_SHADERAST_TRANSFORMVISITOR_HPP
#define NAZARA_SHADERAST_TRANSFORMVISITOR_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Shader/Config.hpp>
#include <Nazara/Shader/ShaderAstCloner.hpp>
#include <vector>
namespace Nz::ShaderAst
{
class NAZARA_SHADER_API TransformVisitor : AstCloner
{
public:
inline TransformVisitor();
TransformVisitor(const TransformVisitor&) = delete;
TransformVisitor(TransformVisitor&&) = delete;
~TransformVisitor() = default;
StatementPtr Transform(StatementPtr& statement);
TransformVisitor& operator=(const TransformVisitor&) = delete;
TransformVisitor& operator=(TransformVisitor&&) = delete;
private:
struct Identifier;
ExpressionPtr Clone(AccessMemberIdentifierExpression& node) override;
ExpressionPtr Clone(CastExpression& node) override;
ExpressionPtr Clone(IdentifierExpression& node) override;
ExpressionPtr CloneExpression(ExpressionPtr& expr) override;
inline const Identifier* FindIdentifier(const std::string_view& identifierName) const;
void PushScope();
void PopScope();
inline std::size_t RegisterFunction(std::string name);
inline std::size_t RegisterStruct(std::string name, StructDescription description);
inline std::size_t RegisterVariable(std::string name);
ExpressionType ResolveType(const ExpressionType& exprType);
using AstCloner::Visit;
void Visit(BranchStatement& node) override;
void Visit(ConditionalStatement& node) override;
void Visit(DeclareExternalStatement& node) override;
void Visit(DeclareFunctionStatement& node) override;
void Visit(DeclareStructStatement& node) override;
void Visit(DeclareVariableStatement& node) override;
void Visit(MultiStatement& node) override;
struct Alias
{
std::variant<ExpressionType> value;
};
struct Struct
{
std::size_t structIndex;
};
struct Variable
{
std::size_t varIndex;
};
struct Identifier
{
std::string name;
std::variant<Alias, Struct, Variable> value;
};
private:
std::size_t m_nextFuncIndex;
std::size_t m_nextVarIndex;
std::vector<Identifier> m_identifiersInScope;
std::vector<StructDescription> m_structs;
std::vector<std::size_t> m_scopeSizes;
};
}
#include <Nazara/Shader/Ast/TransformVisitor.inl>
#endif

View File

@ -24,12 +24,12 @@ namespace Nz
struct Environment;
using ExtSupportCallback = std::function<bool(const std::string_view& name)>;
GlslWriter();
inline GlslWriter();
GlslWriter(const GlslWriter&) = delete;
GlslWriter(GlslWriter&&) = delete;
~GlslWriter() = default;
std::string Generate(ShaderAst::StatementPtr& shader, const States& conditions = {});
inline std::string Generate(ShaderAst::StatementPtr& shader, const States& conditions = {});
std::string Generate(std::optional<ShaderStageType> shaderStage, ShaderAst::StatementPtr& shader, const States& conditions = {});
void SetEnv(Environment environment);

View File

@ -7,6 +7,15 @@
namespace Nz
{
inline GlslWriter::GlslWriter() :
m_currentState(nullptr)
{
}
inline std::string GlslWriter::Generate(ShaderAst::StatementPtr& shader, const States& conditions)
{
return Generate(std::nullopt, shader, conditions);
}
}
#include <Nazara/Shader/DebugOff.hpp>

View File

@ -30,9 +30,11 @@ namespace Nz::ShaderAst
AstCloner& operator=(AstCloner&&) = delete;
protected:
virtual ExpressionPtr CloneExpression(ExpressionPtr& expr);
virtual StatementPtr CloneStatement(StatementPtr& statement);
inline ExpressionPtr CloneExpression(ExpressionPtr& expr);
inline StatementPtr CloneStatement(StatementPtr& statement);
virtual ExpressionPtr CloneExpression(Expression& expr);
virtual StatementPtr CloneStatement(Statement& statement);
virtual ExpressionPtr Clone(AccessMemberIdentifierExpression& node);
virtual ExpressionPtr Clone(AccessMemberIndexExpression& node);

View File

@ -7,6 +7,22 @@
namespace Nz::ShaderAst
{
ExpressionPtr AstCloner::CloneExpression(ExpressionPtr& expr)
{
if (!expr)
return nullptr;
return CloneExpression(*expr);
}
StatementPtr AstCloner::CloneStatement(StatementPtr& statement)
{
if (!statement)
return nullptr;
return CloneStatement(*statement);
}
inline ExpressionPtr Clone(ExpressionPtr& node)
{
AstCloner cloner;

View File

@ -1,68 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Shader generator"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_SHADER_SCOPED_VISITOR_HPP
#define NAZARA_SHADER_SCOPED_VISITOR_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Shader/Config.hpp>
#include <Nazara/Shader/ShaderAstRecursiveVisitor.hpp>
namespace Nz::ShaderAst
{
class NAZARA_SHADER_API AstScopedVisitor : public AstRecursiveVisitor
{
public:
struct Identifier;
AstScopedVisitor() = default;
~AstScopedVisitor() = default;
inline const Identifier* FindIdentifier(const std::string_view& identifierName) const;
void ScopedVisit(StatementPtr& nodePtr);
using AstRecursiveVisitor::Visit;
void Visit(BranchStatement& node) override;
void Visit(ConditionalStatement& node) override;
void Visit(DeclareExternalStatement& node) override;
void Visit(DeclareFunctionStatement& node) override;
void Visit(DeclareStructStatement& node) override;
void Visit(DeclareVariableStatement& node) override;
void Visit(MultiStatement& node) override;
struct Alias
{
std::variant<ExpressionType> value;
};
struct Variable
{
ExpressionType type;
};
struct Identifier
{
std::string name;
std::variant<Alias, Variable, StructDescription> value;
};
protected:
void PushScope();
void PopScope();
inline void RegisterStruct(StructDescription structDesc);
inline void RegisterVariable(std::string name, ExpressionType type);
private:
std::vector<Identifier> m_identifiersInScope;
std::vector<std::size_t> m_scopeSizes;
};
}
#include <Nazara/Shader/ShaderAstScopedVisitor.inl>
#endif

View File

@ -1,38 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Shader generator"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Shader/ShaderAstScopedVisitor.hpp>
#include <Nazara/Shader/Debug.hpp>
namespace Nz::ShaderAst
{
inline auto AstScopedVisitor::FindIdentifier(const std::string_view& identifierName) const -> const Identifier*
{
auto it = std::find_if(m_identifiersInScope.rbegin(), m_identifiersInScope.rend(), [&](const Identifier& identifier) { return identifier.name == identifierName; });
if (it == m_identifiersInScope.rend())
return nullptr;
return &*it;
}
inline void AstScopedVisitor::RegisterStruct(StructDescription structDesc)
{
std::string name = structDesc.name;
m_identifiersInScope.push_back({
std::move(name),
std::move(structDesc)
});
}
inline void AstScopedVisitor::RegisterVariable(std::string name, ExpressionType type)
{
m_identifiersInScope.push_back({
std::move(name),
Variable { std::move(type) }
});
}
}
#include <Nazara/Shader/DebugOff.hpp>

View File

@ -1,66 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Shader generator"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_SHADERVALIDATOR_HPP
#define NAZARA_SHADERVALIDATOR_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Shader/Config.hpp>
#include <Nazara/Shader/ShaderAstScopedVisitor.hpp>
#include <Nazara/Utility/Enums.hpp>
namespace Nz::ShaderAst
{
class NAZARA_SHADER_API AstValidator final : public AstScopedVisitor
{
public:
inline AstValidator();
AstValidator(const AstValidator&) = delete;
AstValidator(AstValidator&&) = delete;
~AstValidator() = default;
bool Validate(StatementPtr& node, std::string* error = nullptr);
private:
const ExpressionType& GetExpressionType(Expression& expression);
Expression& MandatoryExpr(ExpressionPtr& node);
Statement& MandatoryStatement(StatementPtr& node);
void TypeMustMatch(ExpressionPtr& left, ExpressionPtr& right);
void TypeMustMatch(const ExpressionType& left, const ExpressionType& right);
ExpressionType CheckField(const std::string& structName, const std::string* memberIdentifier, std::size_t remainingMembers);
const ExpressionType& ResolveAlias(const ExpressionType& expressionType);
void Visit(AccessMemberIdentifierExpression& node) override;
void Visit(AssignExpression& node) override;
void Visit(BinaryExpression& node) override;
void Visit(CastExpression& node) override;
void Visit(ConstantExpression& node) override;
void Visit(ConditionalExpression& node) override;
void Visit(IdentifierExpression& node) override;
void Visit(IntrinsicExpression& node) override;
void Visit(SwizzleExpression& node) override;
void Visit(BranchStatement& node) override;
void Visit(ConditionalStatement& node) override;
void Visit(DeclareExternalStatement& node) override;
void Visit(DeclareFunctionStatement& node) override;
void Visit(DeclareStructStatement& node) override;
void Visit(DeclareVariableStatement& node) override;
void Visit(ExpressionStatement& node) override;
void Visit(MultiStatement& node) override;
struct Context;
Context* m_context;
};
NAZARA_SHADER_API bool ValidateAst(StatementPtr& node, std::string* error = nullptr);
}
#include <Nazara/Shader/ShaderAstValidator.inl>
#endif

View File

@ -1,16 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Shader generator"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Shader/ShaderAstValidator.hpp>
#include <Nazara/Shader/Debug.hpp>
namespace Nz::ShaderAst
{
AstValidator::AstValidator() :
m_context(nullptr)
{
}
}
#include <Nazara/Shader/DebugOff.hpp>

View File

@ -0,0 +1,832 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Shader generator"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/StackArray.hpp>
#include <Nazara/Shader/ShaderAstUtils.hpp>
#include <stdexcept>
#include <unordered_set>
#include <Nazara/Shader/Debug.hpp>
namespace Nz::ShaderAst
{
namespace
{
struct AstError
{
std::string errMsg;
};
template<typename T, typename U>
std::unique_ptr<T> static_unique_pointer_cast(std::unique_ptr<U>&& ptr)
{
return std::unique_ptr<T>(static_cast<T*>(ptr.release()));
}
}
struct SanitizeVisitor::Context
{
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 clone;
Context currentContext;
m_context = &currentContext;
CallOnExit resetContext([&] { m_context = nullptr; });
PushScope(); //< Global scope
{
try
{
clone = AstCloner::Clone(nodePtr);
}
catch (const AstError& err)
{
if (!error)
throw std::runtime_error(err.errMsg);
*error = err.errMsg;
}
}
PopScope();
return clone;
}
const ExpressionType& SanitizeVisitor::CheckField(const ExpressionType& structType, const std::string* memberIdentifier, std::size_t remainingMembers, std::size_t* structIndices)
{
std::size_t structIndex = ResolveStruct(structType);
*structIndices++ = structIndex;
assert(structIndex < m_structs.size());
const StructDescription& s = m_structs[structIndex];
auto memberIt = std::find_if(s.members.begin(), s.members.end(), [&](const auto& field) { return field.name == memberIdentifier[0]; });
if (memberIt == s.members.end())
throw AstError{ "unknown field " + memberIdentifier[0] };
const auto& member = *memberIt;
if (remainingMembers > 1)
return CheckField(member.type, memberIdentifier + 1, remainingMembers - 1, structIndices);
else
return member.type;
}
ExpressionPtr SanitizeVisitor::Clone(AccessMemberIdentifierExpression& node)
{
auto structExpr = CloneExpression(MandatoryExpr(node.structExpr));
const ExpressionType& exprType = GetExpressionType(*structExpr);
// Transform to AccessMemberIndexExpression
auto accessMemberIndex = std::make_unique<AccessMemberIndexExpression>();
accessMemberIndex->structExpr = std::move(structExpr);
StackArray<std::size_t> structIndices = NazaraStackArrayNoInit(std::size_t, node.memberIdentifiers.size());
accessMemberIndex->cachedExpressionType = ResolveType(CheckField(exprType, node.memberIdentifiers.data(), node.memberIdentifiers.size(), structIndices.data()));
accessMemberIndex->memberIndices.resize(node.memberIdentifiers.size());
for (std::size_t i = 0; i < node.memberIdentifiers.size(); ++i)
{
std::size_t structIndex = structIndices[i];
assert(structIndex < m_structs.size());
const StructDescription& structDesc = m_structs[structIndex];
auto it = std::find_if(structDesc.members.begin(), structDesc.members.end(), [&](const auto& member) { return member.name == node.memberIdentifiers[i]; });
assert(it != structDesc.members.end());
accessMemberIndex->memberIndices[i] = std::distance(structDesc.members.begin(), it);
}
return accessMemberIndex;
}
ExpressionPtr SanitizeVisitor::Clone(AssignExpression& node)
{
MandatoryExpr(node.left);
MandatoryExpr(node.right);
if (GetExpressionCategory(*node.left) != ExpressionCategory::LValue)
throw AstError{ "Assignation is only possible with a l-value" };
auto clone = static_unique_pointer_cast<AssignExpression>(AstCloner::Clone(node));
TypeMustMatch(clone->left, clone->right);
clone->cachedExpressionType = GetExpressionType(*clone->right);
return clone;
}
ExpressionPtr SanitizeVisitor::Clone(BinaryExpression& node)
{
auto clone = static_unique_pointer_cast<BinaryExpression>(AstCloner::Clone(node));
const ExpressionType& leftExprType = GetExpressionType(MandatoryExpr(clone->left));
if (!IsPrimitiveType(leftExprType) && !IsMatrixType(leftExprType) && !IsVectorType(leftExprType))
throw AstError{ "left expression type does not support binary operation" };
const ExpressionType& rightExprType = GetExpressionType(MandatoryExpr(clone->right));
if (!IsPrimitiveType(rightExprType) && !IsMatrixType(rightExprType) && !IsVectorType(rightExprType))
throw AstError{ "right expression type does not support binary operation" };
if (IsPrimitiveType(leftExprType))
{
PrimitiveType leftType = std::get<PrimitiveType>(leftExprType);
switch (clone->op)
{
case BinaryType::CompGe:
case BinaryType::CompGt:
case BinaryType::CompLe:
case BinaryType::CompLt:
if (leftType == PrimitiveType::Boolean)
throw AstError{ "this operation is not supported for booleans" };
TypeMustMatch(clone->left, clone->right);
clone->cachedExpressionType = PrimitiveType::Boolean;
break;
case BinaryType::Add:
case BinaryType::CompEq:
case BinaryType::CompNe:
case BinaryType::Subtract:
TypeMustMatch(clone->left, clone->right);
clone->cachedExpressionType = leftExprType;
break;
case BinaryType::Multiply:
case BinaryType::Divide:
{
switch (leftType)
{
case PrimitiveType::Float32:
case PrimitiveType::Int32:
case PrimitiveType::UInt32:
{
if (IsMatrixType(rightExprType))
{
TypeMustMatch(leftType, std::get<MatrixType>(rightExprType).type);
clone->cachedExpressionType = rightExprType;
}
else if (IsPrimitiveType(rightExprType))
{
TypeMustMatch(leftType, rightExprType);
clone->cachedExpressionType = leftExprType;
}
else if (IsVectorType(rightExprType))
{
TypeMustMatch(leftType, std::get<VectorType>(rightExprType).type);
clone->cachedExpressionType = rightExprType;
}
else
throw AstError{ "incompatible types" };
break;
}
case PrimitiveType::Boolean:
throw AstError{ "this operation is not supported for booleans" };
default:
throw AstError{ "incompatible types" };
}
}
}
}
else if (IsMatrixType(leftExprType))
{
const MatrixType& leftType = std::get<MatrixType>(leftExprType);
switch (clone->op)
{
case BinaryType::CompGe:
case BinaryType::CompGt:
case BinaryType::CompLe:
case BinaryType::CompLt:
case BinaryType::CompEq:
case BinaryType::CompNe:
TypeMustMatch(clone->left, clone->right);
clone->cachedExpressionType = PrimitiveType::Boolean;
break;
case BinaryType::Add:
case BinaryType::Subtract:
TypeMustMatch(clone->left, clone->right);
clone->cachedExpressionType = leftExprType;
break;
case BinaryType::Multiply:
case BinaryType::Divide:
{
if (IsMatrixType(rightExprType))
{
TypeMustMatch(leftExprType, rightExprType);
clone->cachedExpressionType = leftExprType; //< FIXME
}
else if (IsPrimitiveType(rightExprType))
{
TypeMustMatch(leftType.type, rightExprType);
clone->cachedExpressionType = leftExprType;
}
else if (IsVectorType(rightExprType))
{
const VectorType& rightType = std::get<VectorType>(rightExprType);
TypeMustMatch(leftType.type, rightType.type);
if (leftType.columnCount != rightType.componentCount)
throw AstError{ "incompatible types" };
clone->cachedExpressionType = rightExprType;
}
else
throw AstError{ "incompatible types" };
}
}
}
else if (IsVectorType(leftExprType))
{
const VectorType& leftType = std::get<VectorType>(leftExprType);
switch (clone->op)
{
case BinaryType::CompGe:
case BinaryType::CompGt:
case BinaryType::CompLe:
case BinaryType::CompLt:
case BinaryType::CompEq:
case BinaryType::CompNe:
TypeMustMatch(clone->left, clone->right);
clone->cachedExpressionType = PrimitiveType::Boolean;
break;
case BinaryType::Add:
case BinaryType::Subtract:
TypeMustMatch(clone->left, clone->right);
clone->cachedExpressionType = leftExprType;
break;
case BinaryType::Multiply:
case BinaryType::Divide:
{
if (IsPrimitiveType(rightExprType))
{
TypeMustMatch(leftType.type, rightExprType);
clone->cachedExpressionType = rightExprType;
}
else if (IsVectorType(rightExprType))
{
TypeMustMatch(leftType, rightExprType);
clone->cachedExpressionType = rightExprType;
}
else
throw AstError{ "incompatible types" };
}
}
}
return clone;
}
ExpressionPtr SanitizeVisitor::Clone(CastExpression& node)
{
auto clone = static_unique_pointer_cast<CastExpression>(AstCloner::Clone(node));
auto GetComponentCount = [](const ExpressionType& exprType) -> std::size_t
{
if (IsVectorType(exprType))
return std::get<VectorType>(exprType).componentCount;
else
{
assert(IsPrimitiveType(exprType));
return 1;
}
};
std::size_t componentCount = 0;
std::size_t requiredComponents = GetComponentCount(clone->targetType);
for (auto& exprPtr : clone->expressions)
{
if (!exprPtr)
break;
const ExpressionType& exprType = GetExpressionType(*exprPtr);
if (!IsPrimitiveType(exprType) && !IsVectorType(exprType))
throw AstError{ "incompatible type" };
componentCount += GetComponentCount(exprType);
}
if (componentCount != requiredComponents)
throw AstError{ "component count doesn't match required component count" };
clone->targetType = ResolveType(clone->targetType);
clone->cachedExpressionType = clone->targetType;
return clone;
}
ExpressionPtr SanitizeVisitor::Clone(ConditionalExpression& node)
{
MandatoryExpr(node.truePath);
MandatoryExpr(node.falsePath);
auto clone = static_unique_pointer_cast<ConditionalExpression>(AstCloner::Clone(node));
const ExpressionType& leftExprType = GetExpressionType(*clone->truePath);
if (leftExprType != GetExpressionType(*clone->falsePath))
throw AstError{ "true path type must match false path type" };
clone->cachedExpressionType = leftExprType;
return clone;
}
ExpressionPtr SanitizeVisitor::Clone(ConstantExpression& node)
{
auto clone = static_unique_pointer_cast<ConstantExpression>(AstCloner::Clone(node));
clone->cachedExpressionType = 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");
}, clone->value);
return clone;
}
ExpressionPtr SanitizeVisitor::Clone(IdentifierExpression& node)
{
assert(m_context);
const Identifier* identifier = FindIdentifier(node.identifier);
if (!identifier)
throw AstError{ "unknown identifier " + node.identifier };
if (!std::holds_alternative<Variable>(identifier->value))
throw AstError{ "expected variable identifier" };
const Variable& variable = std::get<Variable>(identifier->value);
// Replace IdentifierExpression by VariableExpression
auto varExpr = std::make_unique<VariableExpression>();
varExpr->cachedExpressionType = m_variables[variable.varIndex];
varExpr->variableId = variable.varIndex;
return varExpr;
}
ExpressionPtr SanitizeVisitor::Clone(IntrinsicExpression& node)
{
auto clone = static_unique_pointer_cast<IntrinsicExpression>(AstCloner::Clone(node));
// Parameter validation
switch (clone->intrinsic)
{
case IntrinsicType::CrossProduct:
case IntrinsicType::DotProduct:
{
if (clone->parameters.size() != 2)
throw AstError { "Expected two parameters" };
for (auto& param : clone->parameters)
MandatoryExpr(param);
const ExpressionType& type = GetExpressionType(*clone->parameters.front());
for (std::size_t i = 1; i < clone->parameters.size(); ++i)
{
if (type != GetExpressionType(*clone->parameters[i]))
throw AstError{ "All type must match" };
}
break;
}
case IntrinsicType::SampleTexture:
{
if (clone->parameters.size() != 2)
throw AstError{ "Expected two parameters" };
for (auto& param : clone->parameters)
MandatoryExpr(param);
if (!IsSamplerType(GetExpressionType(*clone->parameters[0])))
throw AstError{ "First parameter must be a sampler" };
if (!IsVectorType(GetExpressionType(*clone->parameters[1])))
throw AstError{ "Second parameter must be a vector" };
}
}
// Return type attribution
switch (clone->intrinsic)
{
case IntrinsicType::CrossProduct:
{
const ExpressionType& type = GetExpressionType(*clone->parameters.front());
if (type != ExpressionType{ VectorType{ 3, PrimitiveType::Float32 } })
throw AstError{ "CrossProduct only works with vec3<f32> expressions" };
clone->cachedExpressionType = type;
break;
}
case IntrinsicType::DotProduct:
{
ExpressionType type = GetExpressionType(*clone->parameters.front());
if (!IsVectorType(type))
throw AstError{ "DotProduct expects vector types" };
clone->cachedExpressionType = std::get<VectorType>(type).type;
break;
}
case IntrinsicType::SampleTexture:
{
clone->cachedExpressionType = VectorType{ 4, std::get<SamplerType>(GetExpressionType(*clone->parameters.front())).sampledType };
break;
}
}
return clone;
}
ExpressionPtr SanitizeVisitor::Clone(SwizzleExpression& node)
{
if (node.componentCount > 4)
throw AstError{ "Cannot swizzle more than four elements" };
MandatoryExpr(node.expression);
auto clone = static_unique_pointer_cast<SwizzleExpression>(AstCloner::Clone(node));
const ExpressionType& exprType = GetExpressionType(*clone->expression);
if (!IsPrimitiveType(exprType) && !IsVectorType(exprType))
throw AstError{ "Cannot swizzle this type" };
PrimitiveType baseType;
if (IsPrimitiveType(exprType))
baseType = std::get<PrimitiveType>(exprType);
else
baseType = std::get<VectorType>(exprType).type;
if (clone->componentCount > 1)
{
clone->cachedExpressionType = VectorType{
clone->componentCount,
baseType
};
}
else
clone->cachedExpressionType = baseType;
return clone;
}
StatementPtr SanitizeVisitor::Clone(BranchStatement& node)
{
auto clone = std::make_unique<BranchStatement>();
clone->condStatements.reserve(node.condStatements.size());
for (auto& cond : node.condStatements)
{
PushScope();
auto& condStatement = clone->condStatements.emplace_back();
condStatement.condition = CloneExpression(MandatoryExpr(cond.condition));
const ExpressionType& condType = GetExpressionType(*condStatement.condition);
if (!IsPrimitiveType(condType) || std::get<PrimitiveType>(condType) != PrimitiveType::Boolean)
throw AstError{ "branch expressions must resolve to boolean type" };
condStatement.statement = CloneStatement(MandatoryStatement(cond.statement));
PopScope();
}
if (node.elseStatement)
{
PushScope();
clone->elseStatement = CloneStatement(node.elseStatement);
PopScope();
}
return clone;
}
StatementPtr SanitizeVisitor::Clone(ConditionalStatement& node)
{
MandatoryStatement(node.statement);
PushScope();
auto clone = static_unique_pointer_cast<ConditionalStatement>(AstCloner::Clone(node));
PopScope();
return clone;
}
StatementPtr SanitizeVisitor::Clone(DeclareExternalStatement& node)
{
assert(m_context);
for (const auto& extVar : node.externalVars)
{
if (extVar.bindingIndex)
{
unsigned int bindingIndex = extVar.bindingIndex.value();
if (m_context->usedBindingIndexes.find(bindingIndex) != m_context->usedBindingIndexes.end())
throw AstError{ "Binding #" + std::to_string(bindingIndex) + " is already in use" };
m_context->usedBindingIndexes.insert(bindingIndex);
}
if (m_context->declaredExternalVar.find(extVar.name) != m_context->declaredExternalVar.end())
throw AstError{ "External variable " + extVar.name + " is already declared" };
m_context->declaredExternalVar.insert(extVar.name);
}
auto clone = static_unique_pointer_cast<DeclareExternalStatement>(AstCloner::Clone(node));
for (auto& extVar : clone->externalVars)
{
extVar.type = ResolveType(extVar.type);
ExpressionType varType = extVar.type;
if (IsUniformType(extVar.type))
varType = std::get<StructType>(std::get<UniformType>(varType).containedType);
std::size_t varIndex = RegisterVariable(extVar.name, std::move(varType));
if (!clone->varIndex)
clone->varIndex = varIndex; //< First external variable index is node variable index
}
return clone;
}
StatementPtr SanitizeVisitor::Clone(DeclareFunctionStatement& node)
{
if (node.entryStage)
{
ShaderStageType stageType = *node.entryStage;
if (m_context->entryFunctions[UnderlyingCast(stageType)])
throw AstError{ "the same entry type has been defined multiple times" };
m_context->entryFunctions[UnderlyingCast(stageType)] = &node;
if (node.parameters.size() > 1)
throw AstError{ "entry functions can either take one struct parameter or no parameter" };
}
auto clone = std::make_unique<DeclareFunctionStatement>();
clone->entryStage = node.entryStage;
clone->name = node.name;
clone->funcIndex = m_nextFuncIndex++;
clone->parameters = node.parameters;
clone->returnType = ResolveType(node.returnType);
PushScope();
{
for (auto& parameter : clone->parameters)
{
parameter.type = ResolveType(parameter.type);
std::size_t varIndex = RegisterVariable(parameter.name, parameter.type);
if (!clone->varIndex)
clone->varIndex = varIndex; //< First parameter variable index is node variable index
}
clone->statements.reserve(node.statements.size());
for (auto& statement : node.statements)
clone->statements.push_back(CloneStatement(MandatoryStatement(statement)));
}
PopScope();
return clone;
}
StatementPtr SanitizeVisitor::Clone(DeclareStructStatement& node)
{
std::unordered_set<std::string> declaredMembers;
for (auto& member : node.description.members)
{
if (declaredMembers.find(member.name) != declaredMembers.end())
throw AstError{ "struct member " + member.name + " found multiple time" };
declaredMembers.insert(member.name);
}
auto clone = static_unique_pointer_cast<DeclareStructStatement>(AstCloner::Clone(node));
for (auto& member : clone->description.members)
member.type = ResolveType(member.type);
clone->structIndex = RegisterStruct(clone->description.name, clone->description);
return clone;
}
StatementPtr SanitizeVisitor::Clone(DeclareVariableStatement& node)
{
auto clone = static_unique_pointer_cast<DeclareVariableStatement>(AstCloner::Clone(node));
if (IsNoType(clone->varType))
{
if (!clone->initialExpression)
throw AstError{ "variable must either have a type or an initial value" };
clone->varType = ResolveType(GetExpressionType(*clone->initialExpression));
}
else
clone->varType = ResolveType(clone->varType);
clone->varIndex = RegisterVariable(clone->varName, clone->varType);
return clone;
}
StatementPtr SanitizeVisitor::Clone(ExpressionStatement& node)
{
MandatoryExpr(node.expression);
return AstCloner::Clone(node);
}
StatementPtr SanitizeVisitor::Clone(MultiStatement& node)
{
for (auto& statement : node.statements)
MandatoryStatement(statement);
PushScope();
auto clone = static_unique_pointer_cast<MultiStatement>(AstCloner::Clone(node));
PopScope();
return clone;
}
Expression& SanitizeVisitor::MandatoryExpr(ExpressionPtr& node)
{
if (!node)
throw AstError{ "Invalid expression" };
return *node;
}
Statement& SanitizeVisitor::MandatoryStatement(StatementPtr& node)
{
if (!node)
throw AstError{ "Invalid statement" };
return *node;
}
void SanitizeVisitor::PushScope()
{
m_scopeSizes.push_back(m_identifiersInScope.size());
}
void SanitizeVisitor::PopScope()
{
assert(!m_scopeSizes.empty());
m_identifiersInScope.resize(m_scopeSizes.back());
m_scopeSizes.pop_back();
}
std::size_t SanitizeVisitor::ResolveStruct(const ExpressionType& exprType)
{
return std::visit([&](auto&& arg) -> std::size_t
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, IdentifierType> || std::is_same_v<T, StructType> || std::is_same_v<T, UniformType>)
return ResolveStruct(arg);
else if constexpr (std::is_same_v<T, NoType> ||
std::is_same_v<T, PrimitiveType> ||
std::is_same_v<T, MatrixType> ||
std::is_same_v<T, SamplerType> ||
std::is_same_v<T, VectorType>)
{
throw AstError{ "expression is not a structure" };
}
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
}, exprType);
}
std::size_t SanitizeVisitor::ResolveStruct(const IdentifierType& identifierType)
{
const Identifier* identifier = FindIdentifier(identifierType.name);
if (!identifier)
throw AstError{ "unknown identifier " + identifierType.name };
if (!std::holds_alternative<Struct>(identifier->value))
throw AstError{ identifierType.name + " is not a struct" };
return std::get<Struct>(identifier->value).structIndex;
}
std::size_t SanitizeVisitor::ResolveStruct(const StructType& structType)
{
return structType.structIndex;
}
std::size_t SanitizeVisitor::ResolveStruct(const UniformType& uniformType)
{
return std::visit([&](auto&& arg) -> std::size_t
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, IdentifierType> || std::is_same_v<T, StructType>)
return ResolveStruct(arg);
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
}, uniformType.containedType);
}
ExpressionType SanitizeVisitor::ResolveType(const ExpressionType& exprType)
{
return std::visit([&](auto&& arg) -> ExpressionType
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, NoType> ||
std::is_same_v<T, PrimitiveType> ||
std::is_same_v<T, MatrixType> ||
std::is_same_v<T, SamplerType> ||
std::is_same_v<T, StructType> ||
std::is_same_v<T, VectorType>)
{
return exprType;
}
else if constexpr (std::is_same_v<T, IdentifierType>)
{
const Identifier* identifier = FindIdentifier(arg.name);
if (!identifier)
throw AstError{ "unknown identifier " + arg.name };
if (!std::holds_alternative<Struct>(identifier->value))
throw AstError{ "expected type identifier" };
return StructType{ std::get<Struct>(identifier->value).structIndex };
}
else if constexpr (std::is_same_v<T, UniformType>)
{
return std::visit([&](auto&& containedArg)
{
ExpressionType resolvedType = ResolveType(containedArg);
assert(std::holds_alternative<StructType>(resolvedType));
return UniformType{ std::get<StructType>(resolvedType) };
}, arg.containedType);
}
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
}, exprType);
}
void SanitizeVisitor::TypeMustMatch(ExpressionPtr& left, ExpressionPtr& right)
{
return TypeMustMatch(GetExpressionType(*left), GetExpressionType(*right));
}
void SanitizeVisitor::TypeMustMatch(const ExpressionType& left, const ExpressionType& right)
{
if (left != right)
throw AstError{ "Left expression type must match right expression type" };
}
}

View File

@ -1,228 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Shader generator"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Shader/Ast/TransformVisitor.hpp>
#include <stdexcept>
#include <Nazara/Shader/Debug.hpp>
namespace Nz::ShaderAst
{
StatementPtr TransformVisitor::Transform(StatementPtr& nodePtr)
{
StatementPtr clone;
PushScope(); //< Global scope
{
clone = AstCloner::Clone(nodePtr);
}
PopScope();
return clone;
}
void TransformVisitor::Visit(BranchStatement& node)
{
for (auto& cond : node.condStatements)
{
PushScope();
{
cond.condition->Visit(*this);
cond.statement->Visit(*this);
}
PopScope();
}
if (node.elseStatement)
{
PushScope();
{
node.elseStatement->Visit(*this);
}
PopScope();
}
}
void TransformVisitor::Visit(ConditionalStatement& node)
{
PushScope();
{
AstCloner::Visit(node);
}
PopScope();
}
ExpressionType TransformVisitor::ResolveType(const ExpressionType& exprType)
{
return std::visit([&](auto&& arg) -> ExpressionType
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, NoType> ||
std::is_same_v<T, PrimitiveType> ||
std::is_same_v<T, MatrixType> ||
std::is_same_v<T, SamplerType> ||
std::is_same_v<T, StructType> ||
std::is_same_v<T, VectorType>)
{
return exprType;
}
else if constexpr (std::is_same_v<T, IdentifierType>)
{
const Identifier* identifier = FindIdentifier(arg.name);
assert(identifier);
assert(std::holds_alternative<Struct>(identifier->value));
return StructType{ std::get<Struct>(identifier->value).structIndex };
}
else if constexpr (std::is_same_v<T, UniformType>)
{
return std::visit([&](auto&& containedArg)
{
ExpressionType resolvedType = ResolveType(containedArg);
assert(std::holds_alternative<StructType>(resolvedType));
return UniformType{ std::get<StructType>(resolvedType) };
}, arg.containedType);
}
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
}, exprType);
}
void TransformVisitor::Visit(DeclareExternalStatement& node)
{
for (auto& extVar : node.externalVars)
{
extVar.type = ResolveType(extVar.type);
std::size_t varIndex = RegisterVariable(extVar.name);
if (!node.varIndex)
node.varIndex = varIndex;
}
AstCloner::Visit(node);
}
void TransformVisitor::Visit(DeclareFunctionStatement& node)
{
node.funcIndex = m_nextFuncIndex++;
node.returnType = ResolveType(node.returnType);
for (auto& parameter : node.parameters)
parameter.type = ResolveType(parameter.type);
PushScope();
{
for (auto& parameter : node.parameters)
{
std::size_t varIndex = RegisterVariable(parameter.name);
if (!node.varIndex)
node.varIndex = varIndex;
}
AstCloner::Visit(node);
}
PopScope();
}
void TransformVisitor::Visit(DeclareStructStatement& node)
{
for (auto& member : node.description.members)
member.type = ResolveType(member.type);
node.structIndex = RegisterStruct(node.description.name, node.description);
AstCloner::Visit(node);
}
void TransformVisitor::Visit(DeclareVariableStatement& node)
{
node.varType = ResolveType(node.varType);
node.varIndex = RegisterVariable(node.varName);
AstCloner::Visit(node);
}
void TransformVisitor::Visit(MultiStatement& node)
{
PushScope();
{
AstCloner::Visit(node);
}
PopScope();
}
ExpressionPtr TransformVisitor::Clone(AccessMemberIdentifierExpression& node)
{
auto accessMemberIndex = std::make_unique<AccessMemberIndexExpression>();
accessMemberIndex->structExpr = CloneExpression(node.structExpr);
accessMemberIndex->cachedExpressionType = node.cachedExpressionType;
accessMemberIndex->memberIndices.resize(node.memberIdentifiers.size());
ExpressionType exprType = GetExpressionType(*node.structExpr);
for (std::size_t i = 0; i < node.memberIdentifiers.size(); ++i)
{
exprType = ResolveType(exprType);
assert(std::holds_alternative<StructType>(exprType));
std::size_t structIndex = std::get<StructType>(exprType).structIndex;
assert(structIndex < m_structs.size());
const StructDescription& structDesc = m_structs[structIndex];
auto it = std::find_if(structDesc.members.begin(), structDesc.members.end(), [&](const auto& member) { return member.name == node.memberIdentifiers[i]; });
assert(it != structDesc.members.end());
accessMemberIndex->memberIndices[i] = std::distance(structDesc.members.begin(), it);
exprType = it->type;
}
return accessMemberIndex;
}
ExpressionPtr TransformVisitor::Clone(CastExpression& node)
{
ExpressionPtr expr = AstCloner::Clone(node);
CastExpression* castExpr = static_cast<CastExpression*>(expr.get());
castExpr->targetType = ResolveType(castExpr->targetType);
return expr;
}
ExpressionPtr TransformVisitor::Clone(IdentifierExpression& node)
{
const Identifier* identifier = FindIdentifier(node.identifier);
assert(identifier);
assert(std::holds_alternative<Variable>(identifier->value));
auto varExpr = std::make_unique<VariableExpression>();
varExpr->cachedExpressionType = node.cachedExpressionType;
varExpr->variableId = std::get<Variable>(identifier->value).varIndex;
return varExpr;
}
ExpressionPtr TransformVisitor::CloneExpression(ExpressionPtr& expr)
{
ExpressionPtr exprPtr = AstCloner::CloneExpression(expr);
if (exprPtr)
{
assert(exprPtr->cachedExpressionType);
*exprPtr->cachedExpressionType = ResolveType(*exprPtr->cachedExpressionType);
}
return exprPtr;
}
void TransformVisitor::PushScope()
{
m_scopeSizes.push_back(m_identifiersInScope.size());
}
void TransformVisitor::PopScope()
{
assert(!m_scopeSizes.empty());
m_identifiersInScope.resize(m_scopeSizes.back());
m_scopeSizes.pop_back();
}
}

View File

@ -8,9 +8,9 @@
#include <Nazara/Math/Algorithm.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderAstCloner.hpp>
#include <Nazara/Shader/ShaderAstRecursiveVisitor.hpp>
#include <Nazara/Shader/ShaderAstUtils.hpp>
#include <Nazara/Shader/ShaderAstValidator.hpp>
#include <Nazara/Shader/Ast/TransformVisitor.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <optional>
#include <stdexcept>
#include <Nazara/Shader/Debug.hpp>
@ -31,36 +31,29 @@ namespace Nz
return it->second;
}
struct PreVisitor : ShaderAst::AstCloner
struct PreVisitor : ShaderAst::AstRecursiveVisitor
{
using AstCloner::Clone;
using AstRecursiveVisitor::Visit;
ShaderAst::StatementPtr Clone(ShaderAst::DeclareFunctionStatement& node) override
void Visit(ShaderAst::DeclareFunctionStatement& node) override
{
auto clone = AstCloner::Clone(node);
assert(clone->GetType() == ShaderAst::NodeType::DeclareFunctionStatement);
ShaderAst::DeclareFunctionStatement* func = static_cast<ShaderAst::DeclareFunctionStatement*>(clone.get());
// Remove function if it's an entry point of another type than the one selected
// Dismiss function if it's an entry point of another type than the one selected
if (selectedStage)
{
if (node.entryStage)
{
ShaderStageType stage = *node.entryStage;
if (stage != *selectedStage)
return ShaderBuilder::NoOp();
return;
entryPoint = func;
entryPoint = &node;
}
}
else
{
assert(!entryPoint);
entryPoint = func;
entryPoint = &node;
}
return clone;
}
std::optional<ShaderStageType> selectedStage;
@ -99,17 +92,6 @@ namespace Nz
unsigned int indentLevel = 0;
};
GlslWriter::GlslWriter() :
m_currentState(nullptr)
{
}
std::string GlslWriter::Generate(ShaderAst::StatementPtr& shader, const States& conditions)
{
return Generate(std::nullopt, shader, conditions);
}
std::string GlslWriter::Generate(std::optional<ShaderStageType> shaderStage, ShaderAst::StatementPtr& shader, const States& conditions)
{
State state;
@ -121,17 +103,11 @@ namespace Nz
m_currentState = nullptr;
});
std::string error;
if (!ShaderAst::ValidateAst(shader, &error))
throw std::runtime_error("Invalid shader AST: " + error);
ShaderAst::TransformVisitor transformVisitor;
ShaderAst::StatementPtr transformedShader = transformVisitor.Transform(shader);
ShaderAst::StatementPtr sanitizedAst = ShaderAst::Sanitize(shader);
PreVisitor previsitor;
previsitor.selectedStage = shaderStage;
ShaderAst::StatementPtr adaptedShader = previsitor.Clone(transformedShader);
sanitizedAst->Visit(previsitor);
if (!previsitor.entryPoint)
throw std::runtime_error("missing entry point");
@ -140,7 +116,7 @@ namespace Nz
AppendHeader();
adaptedShader->Visit(*this);
sanitizedAst->Visit(*this);
return state.stream.str();
}
@ -361,6 +337,9 @@ namespace Nz
void GlslWriter::HandleEntryPoint(ShaderAst::DeclareFunctionStatement& node)
{
if (m_currentState->entryFunc != &node)
return; //< Ignore other entry points
HandleInOut();
AppendLine("void main()");
EnterScope();
@ -712,11 +691,10 @@ namespace Nz
{
NazaraAssert(m_currentState, "This function should only be called while processing an AST");
if (m_currentState->entryFunc == &node)
if (node.entryStage)
return HandleEntryPoint(node);
assert(node.varIndex);
std::size_t varIndex = *node.varIndex;
std::optional<std::size_t> varIndexOpt = node.varIndex;
Append(node.returnType);
Append(" ");
@ -731,6 +709,8 @@ namespace Nz
Append(" ");
Append(node.parameters[i].name);
assert(varIndexOpt);
std::size_t& varIndex = *varIndexOpt;
RegisterVariable(varIndex++, node.parameters[i].name);
}
Append(")\n");

View File

@ -24,21 +24,15 @@ namespace Nz::ShaderAst
return PopStatement();
}
ExpressionPtr AstCloner::CloneExpression(ExpressionPtr& expr)
ExpressionPtr AstCloner::CloneExpression(Expression& expr)
{
if (!expr)
return nullptr;
expr->Visit(*this);
expr.Visit(*this);
return PopExpression();
}
StatementPtr AstCloner::CloneStatement(StatementPtr& statement)
StatementPtr AstCloner::CloneStatement(Statement& statement)
{
if (!statement)
return nullptr;
statement->Visit(*this);
statement.Visit(*this);
return PopStatement();
}

View File

@ -1,110 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Shader generator"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Shader/ShaderAstScopedVisitor.hpp>
#include <Nazara/Shader/Debug.hpp>
namespace Nz::ShaderAst
{
void AstScopedVisitor::ScopedVisit(StatementPtr& nodePtr)
{
PushScope(); //< Global scope
{
nodePtr->Visit(*this);
}
PopScope();
}
void AstScopedVisitor::Visit(BranchStatement& node)
{
for (auto& cond : node.condStatements)
{
PushScope();
{
cond.condition->Visit(*this);
cond.statement->Visit(*this);
}
PopScope();
}
if (node.elseStatement)
{
PushScope();
{
node.elseStatement->Visit(*this);
}
PopScope();
}
}
void AstScopedVisitor::Visit(ConditionalStatement& node)
{
PushScope();
{
AstRecursiveVisitor::Visit(node);
}
PopScope();
}
void AstScopedVisitor::Visit(DeclareExternalStatement& node)
{
for (auto& extVar : node.externalVars)
{
ExpressionType subType = extVar.type;
if (IsUniformType(subType))
subType = std::get<IdentifierType>(std::get<UniformType>(subType).containedType);
RegisterVariable(extVar.name, std::move(subType));
}
AstRecursiveVisitor::Visit(node);
}
void AstScopedVisitor::Visit(DeclareFunctionStatement& node)
{
PushScope();
{
for (auto& parameter : node.parameters)
RegisterVariable(parameter.name, parameter.type);
AstRecursiveVisitor::Visit(node);
}
PopScope();
}
void AstScopedVisitor::Visit(DeclareStructStatement& node)
{
RegisterStruct(node.description);
AstRecursiveVisitor::Visit(node);
}
void AstScopedVisitor::Visit(DeclareVariableStatement& node)
{
RegisterVariable(node.varName, node.varType);
AstRecursiveVisitor::Visit(node);
}
void AstScopedVisitor::Visit(MultiStatement& node)
{
PushScope();
{
AstRecursiveVisitor::Visit(node);
}
PopScope();
}
void AstScopedVisitor::PushScope()
{
m_scopeSizes.push_back(m_identifiersInScope.size());
}
void AstScopedVisitor::PopScope()
{
assert(!m_scopeSizes.empty());
m_identifiersInScope.resize(m_scopeSizes.back());
m_scopeSizes.pop_back();
}
}

View File

@ -1,642 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Shader generator"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Shader/ShaderAstValidator.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Shader/ShaderAstUtils.hpp>
#include <unordered_set>
#include <vector>
#include <Nazara/Shader/Debug.hpp>
namespace Nz::ShaderAst
{
struct AstError
{
std::string errMsg;
};
struct AstValidator::Context
{
std::array<DeclareFunctionStatement*, ShaderStageTypeCount> entryFunctions = {};
std::unordered_set<std::string> declaredExternalVar;
std::unordered_set<unsigned int> usedBindingIndexes;
};
bool AstValidator::Validate(StatementPtr& node, std::string* error)
{
try
{
Context currentContext;
m_context = &currentContext;
CallOnExit resetContext([&] { m_context = nullptr; });
ScopedVisit(node);
return true;
}
catch (const AstError& e)
{
if (error)
*error = e.errMsg;
return false;
}
}
const ExpressionType& AstValidator::GetExpressionType(Expression& expression)
{
assert(expression.cachedExpressionType);
return ResolveAlias(expression.cachedExpressionType.value());
}
Expression& AstValidator::MandatoryExpr(ExpressionPtr& node)
{
if (!node)
throw AstError{ "Invalid expression" };
return *node;
}
Statement& AstValidator::MandatoryStatement(StatementPtr& node)
{
if (!node)
throw AstError{ "Invalid statement" };
return *node;
}
void AstValidator::TypeMustMatch(ExpressionPtr& left, ExpressionPtr& right)
{
return TypeMustMatch(GetExpressionType(*left), GetExpressionType(*right));
}
void AstValidator::TypeMustMatch(const ExpressionType& left, const ExpressionType& right)
{
if (left != right)
throw AstError{ "Left expression type must match right expression type" };
}
ExpressionType AstValidator::CheckField(const std::string& structName, const std::string* memberIdentifier, std::size_t remainingMembers)
{
const Identifier* identifier = FindIdentifier(structName);
if (!identifier)
throw AstError{ "unknown identifier " + structName };
if (!std::holds_alternative<StructDescription>(identifier->value))
throw AstError{ "identifier is not a struct" };
const StructDescription& s = std::get<StructDescription>(identifier->value);
auto memberIt = std::find_if(s.members.begin(), s.members.end(), [&](const auto& field) { return field.name == memberIdentifier[0]; });
if (memberIt == s.members.end())
throw AstError{ "unknown field " + memberIdentifier[0]};
const auto& member = *memberIt;
if (remainingMembers > 1)
return CheckField(std::get<IdentifierType>(member.type).name, memberIdentifier + 1, remainingMembers - 1);
else
return member.type;
}
const ExpressionType& AstValidator::ResolveAlias(const ExpressionType& expressionType)
{
if (!IsIdentifierType(expressionType))
return expressionType;
const Identifier* identifier = FindIdentifier(std::get<IdentifierType>(expressionType).name);
if (identifier && std::holds_alternative<Alias>(identifier->value))
{
const Alias& alias = std::get<Alias>(identifier->value);
return std::visit([&](auto&& arg) -> const ShaderAst::ExpressionType&
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, ExpressionType>)
return arg;
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
}, alias.value);
}
return expressionType;
}
void AstValidator::Visit(AccessMemberIdentifierExpression& node)
{
// Register expressions types
AstScopedVisitor::Visit(node);
ExpressionType exprType = GetExpressionType(MandatoryExpr(node.structExpr));
if (!IsIdentifierType(exprType))
throw AstError{ "expression is not a structure" };
const std::string& structName = std::get<IdentifierType>(exprType).name;
node.cachedExpressionType = CheckField(structName, node.memberIdentifiers.data(), node.memberIdentifiers.size());
}
void AstValidator::Visit(AssignExpression& node)
{
MandatoryExpr(node.left);
MandatoryExpr(node.right);
// Register expressions types
AstScopedVisitor::Visit(node);
TypeMustMatch(node.left, node.right);
if (GetExpressionCategory(*node.left) != ExpressionCategory::LValue)
throw AstError { "Assignation is only possible with a l-value" };
node.cachedExpressionType = GetExpressionType(*node.right);
}
void AstValidator::Visit(BinaryExpression& node)
{
// Register expression type
AstScopedVisitor::Visit(node);
ExpressionType leftExprType = GetExpressionType(MandatoryExpr(node.left));
if (!IsPrimitiveType(leftExprType) && !IsMatrixType(leftExprType) && !IsVectorType(leftExprType))
throw AstError{ "left expression type does not support binary operation" };
ExpressionType rightExprType = GetExpressionType(MandatoryExpr(node.right));
if (!IsPrimitiveType(rightExprType) && !IsMatrixType(rightExprType) && !IsVectorType(rightExprType))
throw AstError{ "right expression type does not support binary operation" };
if (IsPrimitiveType(leftExprType))
{
PrimitiveType leftType = std::get<PrimitiveType>(leftExprType);
switch (node.op)
{
case BinaryType::CompGe:
case BinaryType::CompGt:
case BinaryType::CompLe:
case BinaryType::CompLt:
if (leftType == PrimitiveType::Boolean)
throw AstError{ "this operation is not supported for booleans" };
TypeMustMatch(node.left, node.right);
node.cachedExpressionType = PrimitiveType::Boolean;
break;
case BinaryType::Add:
case BinaryType::CompEq:
case BinaryType::CompNe:
case BinaryType::Subtract:
TypeMustMatch(node.left, node.right);
node.cachedExpressionType = leftExprType;
break;
case BinaryType::Multiply:
case BinaryType::Divide:
{
switch (leftType)
{
case PrimitiveType::Float32:
case PrimitiveType::Int32:
case PrimitiveType::UInt32:
{
if (IsMatrixType(rightExprType))
{
TypeMustMatch(leftType, std::get<MatrixType>(rightExprType).type);
node.cachedExpressionType = rightExprType;
}
else if (IsPrimitiveType(rightExprType))
{
TypeMustMatch(leftType, rightExprType);
node.cachedExpressionType = leftExprType;
}
else if (IsVectorType(rightExprType))
{
TypeMustMatch(leftType, std::get<VectorType>(rightExprType).type);
node.cachedExpressionType = rightExprType;
}
else
throw AstError{ "incompatible types" };
break;
}
case PrimitiveType::Boolean:
throw AstError{ "this operation is not supported for booleans" };
default:
throw AstError{ "incompatible types" };
}
}
}
}
else if (IsMatrixType(leftExprType))
{
const MatrixType& leftType = std::get<MatrixType>(leftExprType);
switch (node.op)
{
case BinaryType::CompGe:
case BinaryType::CompGt:
case BinaryType::CompLe:
case BinaryType::CompLt:
case BinaryType::CompEq:
case BinaryType::CompNe:
TypeMustMatch(node.left, node.right);
node.cachedExpressionType = PrimitiveType::Boolean;
break;
case BinaryType::Add:
case BinaryType::Subtract:
TypeMustMatch(node.left, node.right);
node.cachedExpressionType = leftExprType;
break;
case BinaryType::Multiply:
case BinaryType::Divide:
{
if (IsMatrixType(rightExprType))
{
TypeMustMatch(leftExprType, rightExprType);
node.cachedExpressionType = leftExprType; //< FIXME
}
else if (IsPrimitiveType(rightExprType))
{
TypeMustMatch(leftType.type, rightExprType);
node.cachedExpressionType = leftExprType;
}
else if (IsVectorType(rightExprType))
{
const VectorType& rightType = std::get<VectorType>(rightExprType);
TypeMustMatch(leftType.type, rightType.type);
if (leftType.columnCount != rightType.componentCount)
throw AstError{ "incompatible types" };
node.cachedExpressionType = rightExprType;
}
else
throw AstError{ "incompatible types" };
}
}
}
else if (IsVectorType(leftExprType))
{
const VectorType& leftType = std::get<VectorType>(leftExprType);
switch (node.op)
{
case BinaryType::CompGe:
case BinaryType::CompGt:
case BinaryType::CompLe:
case BinaryType::CompLt:
case BinaryType::CompEq:
case BinaryType::CompNe:
TypeMustMatch(node.left, node.right);
node.cachedExpressionType = PrimitiveType::Boolean;
break;
case BinaryType::Add:
case BinaryType::Subtract:
TypeMustMatch(node.left, node.right);
node.cachedExpressionType = leftExprType;
break;
case BinaryType::Multiply:
case BinaryType::Divide:
{
if (IsPrimitiveType(rightExprType))
{
TypeMustMatch(leftType.type, rightExprType);
node.cachedExpressionType = rightExprType;
}
else if (IsVectorType(rightExprType))
{
TypeMustMatch(leftType, rightExprType);
node.cachedExpressionType = rightExprType;
}
else
throw AstError{ "incompatible types" };
}
}
}
}
void AstValidator::Visit(CastExpression& node)
{
AstScopedVisitor::Visit(node);
auto GetComponentCount = [](const ExpressionType& exprType) -> std::size_t
{
if (IsPrimitiveType(exprType))
return 1;
else if (IsVectorType(exprType))
return std::get<VectorType>(exprType).componentCount;
else
throw AstError{ "wut" };
};
std::size_t componentCount = 0;
std::size_t requiredComponents = GetComponentCount(node.targetType);
for (auto& exprPtr : node.expressions)
{
if (!exprPtr)
break;
const ExpressionType& exprType = GetExpressionType(*exprPtr);
if (!IsPrimitiveType(exprType) && !IsVectorType(exprType))
throw AstError{ "incompatible type" };
componentCount += GetComponentCount(exprType);
}
if (componentCount != requiredComponents)
throw AstError{ "component count doesn't match required component count" };
node.cachedExpressionType = node.targetType;
}
void AstValidator::Visit(ConstantExpression& node)
{
node.cachedExpressionType = 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");
}, node.value);
}
void AstValidator::Visit(ConditionalExpression& node)
{
MandatoryExpr(node.truePath);
MandatoryExpr(node.falsePath);
AstScopedVisitor::Visit(node);
ExpressionType leftExprType = GetExpressionType(*node.truePath);
if (leftExprType != GetExpressionType(*node.falsePath))
throw AstError{ "true path type must match false path type" };
node.cachedExpressionType = leftExprType;
//if (m_shader.FindConditionByName(node.conditionName) == ShaderAst::InvalidCondition)
// throw AstError{ "condition not found" };
}
void AstValidator::Visit(IdentifierExpression& node)
{
assert(m_context);
const Identifier* identifier = FindIdentifier(node.identifier);
if (!identifier)
throw AstError{ "Unknown identifier " + node.identifier };
node.cachedExpressionType = ResolveAlias(std::get<Variable>(identifier->value).type);
}
void AstValidator::Visit(IntrinsicExpression& node)
{
AstScopedVisitor::Visit(node);
switch (node.intrinsic)
{
case IntrinsicType::CrossProduct:
case IntrinsicType::DotProduct:
{
if (node.parameters.size() != 2)
throw AstError { "Expected two parameters" };
for (auto& param : node.parameters)
MandatoryExpr(param);
ExpressionType type = GetExpressionType(*node.parameters.front());
for (std::size_t i = 1; i < node.parameters.size(); ++i)
{
if (type != GetExpressionType(MandatoryExpr(node.parameters[i])))
throw AstError{ "All type must match" };
}
break;
}
case IntrinsicType::SampleTexture:
{
if (node.parameters.size() != 2)
throw AstError{ "Expected two parameters" };
for (auto& param : node.parameters)
MandatoryExpr(param);
if (!IsSamplerType(GetExpressionType(*node.parameters[0])))
throw AstError{ "First parameter must be a sampler" };
if (!IsVectorType(GetExpressionType(*node.parameters[1])))
throw AstError{ "Second parameter must be a vector" };
}
}
switch (node.intrinsic)
{
case IntrinsicType::CrossProduct:
{
ExpressionType type = GetExpressionType(*node.parameters.front());
if (type != ExpressionType{ VectorType{ 3, PrimitiveType::Float32 } })
throw AstError{ "CrossProduct only works with vec3<f32> expressions" };
node.cachedExpressionType = std::move(type);
break;
}
case IntrinsicType::DotProduct:
{
ExpressionType type = GetExpressionType(*node.parameters.front());
if (!IsVectorType(type))
throw AstError{ "DotProduct expects vector types" };
node.cachedExpressionType = std::get<VectorType>(type).type;
break;
}
case IntrinsicType::SampleTexture:
{
node.cachedExpressionType = VectorType{ 4, std::get<SamplerType>(GetExpressionType(*node.parameters.front())).sampledType };
break;
}
}
}
void AstValidator::Visit(SwizzleExpression& node)
{
if (node.componentCount > 4)
throw AstError{ "Cannot swizzle more than four elements" };
MandatoryExpr(node.expression);
AstScopedVisitor::Visit(node);
ExpressionType exprType = GetExpressionType(*node.expression);
if (IsPrimitiveType(exprType) || IsVectorType(exprType))
{
PrimitiveType baseType;
if (IsPrimitiveType(exprType))
baseType = std::get<PrimitiveType>(exprType);
else
baseType = std::get<VectorType>(exprType).type;
if (node.componentCount > 1)
{
node.cachedExpressionType = VectorType{
node.componentCount,
baseType
};
}
else
node.cachedExpressionType = baseType;
}
else
throw AstError{ "Cannot swizzle this type" };
}
void AstValidator::Visit(BranchStatement& node)
{
for (auto& condStatement : node.condStatements)
{
ExpressionType condType = GetExpressionType(MandatoryExpr(condStatement.condition));
if (!IsPrimitiveType(condType) || std::get<PrimitiveType>(condType) != PrimitiveType::Boolean)
throw AstError{ "if expression must resolve to boolean type" };
MandatoryStatement(condStatement.statement);
}
AstScopedVisitor::Visit(node);
}
void AstValidator::Visit(ConditionalStatement& node)
{
MandatoryStatement(node.statement);
AstScopedVisitor::Visit(node);
//if (m_shader.FindConditionByName(node.conditionName) == ShaderAst::InvalidCondition)
// throw AstError{ "condition not found" };
}
void AstValidator::Visit(DeclareExternalStatement& node)
{
for (const auto& extVar : node.externalVars)
{
if (extVar.bindingIndex)
{
unsigned int bindingIndex = extVar.bindingIndex.value();
if (m_context->usedBindingIndexes.find(bindingIndex) != m_context->usedBindingIndexes.end())
throw AstError{ "Binding #" + std::to_string(bindingIndex) + " is already in use" };
m_context->usedBindingIndexes.insert(bindingIndex);
}
if (m_context->declaredExternalVar.find(extVar.name) != m_context->declaredExternalVar.end())
throw AstError{ "External variable " + extVar.name + " is already declared" };
m_context->declaredExternalVar.insert(extVar.name);
}
AstScopedVisitor::Visit(node);
}
void AstValidator::Visit(DeclareFunctionStatement& node)
{
if (node.entryStage)
{
ShaderStageType stageType = *node.entryStage;
if (m_context->entryFunctions[UnderlyingCast(stageType)])
throw AstError{ "the same entry type has been defined multiple times" };
m_context->entryFunctions[UnderlyingCast(stageType)] = &node;
if (node.parameters.size() > 1)
throw AstError{ "entry functions can either take one struct parameter or no parameter" };
}
for (auto& statement : node.statements)
MandatoryStatement(statement);
AstScopedVisitor::Visit(node);
}
void AstValidator::Visit(DeclareStructStatement& node)
{
assert(m_context);
std::unordered_set<std::string> declaredMembers;
for (auto& member : node.description.members)
{
if (declaredMembers.find(member.name) != declaredMembers.end())
throw AstError{ "struct member " + member.name + " found multiple time" };
declaredMembers.insert(member.name);
}
AstScopedVisitor::Visit(node);
}
void AstValidator::Visit(DeclareVariableStatement& node)
{
if (IsNoType(node.varType))
{
if (!node.initialExpression)
throw AstError{ "variable must either have a type or an initial value" };
node.initialExpression->Visit(*this);
node.varType = GetExpressionType(*node.initialExpression);
}
AstScopedVisitor::Visit(node);
}
void AstValidator::Visit(ExpressionStatement& node)
{
MandatoryExpr(node.expression);
AstScopedVisitor::Visit(node);
}
void AstValidator::Visit(MultiStatement& node)
{
assert(m_context);
for (auto& statement : node.statements)
MandatoryStatement(statement);
AstScopedVisitor::Visit(node);
}
bool ValidateAst(StatementPtr& node, std::string* error)
{
AstValidator validator;
return validator.Validate(node, error);
}
}

View File

@ -6,13 +6,13 @@
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/StackVector.hpp>
#include <Nazara/Shader/ShaderAstCloner.hpp>
#include <Nazara/Shader/ShaderAstValidator.hpp>
#include <Nazara/Shader/ShaderAstRecursiveVisitor.hpp>
#include <Nazara/Shader/SpirvAstVisitor.hpp>
#include <Nazara/Shader/SpirvBlock.hpp>
#include <Nazara/Shader/SpirvConstantCache.hpp>
#include <Nazara/Shader/SpirvData.hpp>
#include <Nazara/Shader/SpirvSection.hpp>
#include <Nazara/Shader/Ast/TransformVisitor.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <tsl/ordered_map.h>
#include <tsl/ordered_set.h>
#include <SpirV/GLSL.std.450.h>
@ -38,7 +38,7 @@ namespace Nz
{ ShaderAst::BuiltinEntry::VertexPosition, { "VertexPosition", ShaderStageType::Vertex, SpirvBuiltIn::Position } }
};
class PreVisitor : public ShaderAst::AstScopedVisitor
class PreVisitor : public ShaderAst::AstRecursiveVisitor
{
public:
struct UniformVar
@ -107,7 +107,7 @@ namespace Nz
m_constantCache.Register(*m_constantCache.BuildConstant(arg));
}, node.value);
AstScopedVisitor::Visit(node);
AstRecursiveVisitor::Visit(node);
}
void Visit(ShaderAst::DeclareExternalStatement& node) override
@ -233,13 +233,13 @@ namespace Nz
}
m_funcIndex = funcIndex;
AstScopedVisitor::Visit(node);
AstRecursiveVisitor::Visit(node);
m_funcIndex.reset();
}
void Visit(ShaderAst::DeclareStructStatement& node) override
{
AstScopedVisitor::Visit(node);
AstRecursiveVisitor::Visit(node);
assert(node.structIndex);
std::size_t structIndex = *node.structIndex;
@ -253,7 +253,7 @@ namespace Nz
void Visit(ShaderAst::DeclareVariableStatement& node) override
{
AstScopedVisitor::Visit(node);
AstRecursiveVisitor::Visit(node);
assert(m_funcIndex);
auto& func = m_funcs[*m_funcIndex];
@ -269,12 +269,12 @@ namespace Nz
{
m_constantCache.Register(*m_constantCache.BuildType(node.cachedExpressionType.value()));
AstScopedVisitor::Visit(node);
AstRecursiveVisitor::Visit(node);
}
void Visit(ShaderAst::IntrinsicExpression& node) override
{
AstScopedVisitor::Visit(node);
AstRecursiveVisitor::Visit(node);
switch (node.intrinsic)
{
@ -285,6 +285,7 @@ namespace Nz
// Part of SPIR-V core
case ShaderAst::IntrinsicType::DotProduct:
case ShaderAst::IntrinsicType::SampleTexture:
break;
}
@ -293,7 +294,7 @@ namespace Nz
void Visit(ShaderAst::SwizzleExpression& node) override
{
AstScopedVisitor::Visit(node);
AstRecursiveVisitor::Visit(node);
m_constantCache.Register(*m_constantCache.BuildType(node.cachedExpressionType.value()));
}
@ -391,12 +392,7 @@ namespace Nz
std::vector<UInt32> SpirvWriter::Generate(ShaderAst::StatementPtr& shader, const States& conditions)
{
std::string error;
if (!ShaderAst::ValidateAst(shader, &error))
throw std::runtime_error("Invalid shader AST: " + error);
ShaderAst::TransformVisitor transformVisitor;
ShaderAst::StatementPtr transformedShader = transformVisitor.Transform(shader);
ShaderAst::StatementPtr sanitizedAst = ShaderAst::Sanitize(shader);
m_context.states = &conditions;
@ -409,7 +405,7 @@ namespace Nz
// Register all extended instruction sets
PreVisitor preVisitor(conditions, state.constantTypeCache, state.funcs);
transformedShader->Visit(preVisitor);
sanitizedAst->Visit(preVisitor);
m_currentState->preVisitor = &preVisitor;
@ -417,7 +413,7 @@ namespace Nz
state.extensionInstructions[extInst] = AllocateResultId();
SpirvAstVisitor visitor(*this, state.instructions, state.funcs);
transformedShader->Visit(visitor);
sanitizedAst->Visit(visitor);
AppendHeader();