Shader: Merge AstScopedVisitor, AstValidator and TransformVisitor to SanitizeVisitor
This commit is contained in:
parent
8515c9cea1
commit
afe3a0ea93
|
|
@ -41,7 +41,6 @@
|
||||||
#include <Nazara/Shader/ShaderAstStatementVisitor.hpp>
|
#include <Nazara/Shader/ShaderAstStatementVisitor.hpp>
|
||||||
#include <Nazara/Shader/ShaderAstStatementVisitorExcept.hpp>
|
#include <Nazara/Shader/ShaderAstStatementVisitorExcept.hpp>
|
||||||
#include <Nazara/Shader/ShaderAstUtils.hpp>
|
#include <Nazara/Shader/ShaderAstUtils.hpp>
|
||||||
#include <Nazara/Shader/ShaderAstValidator.hpp>
|
|
||||||
#include <Nazara/Shader/ShaderBuilder.hpp>
|
#include <Nazara/Shader/ShaderBuilder.hpp>
|
||||||
#include <Nazara/Shader/ShaderEnums.hpp>
|
#include <Nazara/Shader/ShaderEnums.hpp>
|
||||||
#include <Nazara/Shader/ShaderLangLexer.hpp>
|
#include <Nazara/Shader/ShaderLangLexer.hpp>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -2,18 +2,17 @@
|
||||||
// This file is part of the "Nazara Engine - Shader generator"
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
// 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>
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
namespace Nz::ShaderAst
|
namespace Nz::ShaderAst
|
||||||
{
|
{
|
||||||
inline TransformVisitor::TransformVisitor() :
|
inline SanitizeVisitor::SanitizeVisitor() :
|
||||||
m_nextFuncIndex(0),
|
m_nextFuncIndex(0)
|
||||||
m_nextVarIndex(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; });
|
auto it = std::find_if(m_identifiersInScope.rbegin(), m_identifiersInScope.rend(), [&](const Identifier& identifier) { return identifier.name == identifierName; });
|
||||||
if (it == m_identifiersInScope.rend())
|
if (it == m_identifiersInScope.rend())
|
||||||
|
|
@ -22,14 +21,14 @@ namespace Nz::ShaderAst
|
||||||
return &*it;
|
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++;
|
std::size_t funcIndex = m_nextFuncIndex++;
|
||||||
return funcIndex;
|
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();
|
std::size_t structIndex = m_structs.size();
|
||||||
m_structs.emplace_back(std::move(description));
|
m_structs.emplace_back(std::move(description));
|
||||||
|
|
@ -44,9 +43,10 @@ namespace Nz::ShaderAst
|
||||||
return structIndex;
|
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({
|
m_identifiersInScope.push_back({
|
||||||
std::move(name),
|
std::move(name),
|
||||||
|
|
@ -57,6 +57,12 @@ namespace Nz::ShaderAst
|
||||||
|
|
||||||
return varIndex;
|
return varIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StatementPtr Sanitize(StatementPtr& ast, std::string* error)
|
||||||
|
{
|
||||||
|
SanitizeVisitor sanitizer;
|
||||||
|
return sanitizer.Sanitize(ast, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <Nazara/Shader/DebugOff.hpp>
|
#include <Nazara/Shader/DebugOff.hpp>
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -24,12 +24,12 @@ namespace Nz
|
||||||
struct Environment;
|
struct Environment;
|
||||||
using ExtSupportCallback = std::function<bool(const std::string_view& name)>;
|
using ExtSupportCallback = std::function<bool(const std::string_view& name)>;
|
||||||
|
|
||||||
GlslWriter();
|
inline GlslWriter();
|
||||||
GlslWriter(const GlslWriter&) = delete;
|
GlslWriter(const GlslWriter&) = delete;
|
||||||
GlslWriter(GlslWriter&&) = delete;
|
GlslWriter(GlslWriter&&) = delete;
|
||||||
~GlslWriter() = default;
|
~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 = {});
|
std::string Generate(std::optional<ShaderStageType> shaderStage, ShaderAst::StatementPtr& shader, const States& conditions = {});
|
||||||
|
|
||||||
void SetEnv(Environment environment);
|
void SetEnv(Environment environment);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,15 @@
|
||||||
|
|
||||||
namespace Nz
|
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>
|
#include <Nazara/Shader/DebugOff.hpp>
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,11 @@ namespace Nz::ShaderAst
|
||||||
AstCloner& operator=(AstCloner&&) = delete;
|
AstCloner& operator=(AstCloner&&) = delete;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ExpressionPtr CloneExpression(ExpressionPtr& expr);
|
inline ExpressionPtr CloneExpression(ExpressionPtr& expr);
|
||||||
virtual StatementPtr CloneStatement(StatementPtr& statement);
|
inline StatementPtr CloneStatement(StatementPtr& statement);
|
||||||
|
|
||||||
|
virtual ExpressionPtr CloneExpression(Expression& expr);
|
||||||
|
virtual StatementPtr CloneStatement(Statement& statement);
|
||||||
|
|
||||||
virtual ExpressionPtr Clone(AccessMemberIdentifierExpression& node);
|
virtual ExpressionPtr Clone(AccessMemberIdentifierExpression& node);
|
||||||
virtual ExpressionPtr Clone(AccessMemberIndexExpression& node);
|
virtual ExpressionPtr Clone(AccessMemberIndexExpression& node);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,22 @@
|
||||||
|
|
||||||
namespace Nz::ShaderAst
|
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)
|
inline ExpressionPtr Clone(ExpressionPtr& node)
|
||||||
{
|
{
|
||||||
AstCloner cloner;
|
AstCloner cloner;
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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 = ¤tContext;
|
||||||
|
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" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,9 +8,9 @@
|
||||||
#include <Nazara/Math/Algorithm.hpp>
|
#include <Nazara/Math/Algorithm.hpp>
|
||||||
#include <Nazara/Shader/ShaderBuilder.hpp>
|
#include <Nazara/Shader/ShaderBuilder.hpp>
|
||||||
#include <Nazara/Shader/ShaderAstCloner.hpp>
|
#include <Nazara/Shader/ShaderAstCloner.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderAstRecursiveVisitor.hpp>
|
||||||
#include <Nazara/Shader/ShaderAstUtils.hpp>
|
#include <Nazara/Shader/ShaderAstUtils.hpp>
|
||||||
#include <Nazara/Shader/ShaderAstValidator.hpp>
|
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
|
||||||
#include <Nazara/Shader/Ast/TransformVisitor.hpp>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <Nazara/Shader/Debug.hpp>
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
@ -31,36 +31,29 @@ namespace Nz
|
||||||
return it->second;
|
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);
|
// Dismiss function if it's an entry point of another type than the one selected
|
||||||
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
|
|
||||||
if (selectedStage)
|
if (selectedStage)
|
||||||
{
|
{
|
||||||
if (node.entryStage)
|
if (node.entryStage)
|
||||||
{
|
{
|
||||||
ShaderStageType stage = *node.entryStage;
|
ShaderStageType stage = *node.entryStage;
|
||||||
if (stage != *selectedStage)
|
if (stage != *selectedStage)
|
||||||
return ShaderBuilder::NoOp();
|
return;
|
||||||
|
|
||||||
entryPoint = func;
|
entryPoint = &node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
assert(!entryPoint);
|
assert(!entryPoint);
|
||||||
entryPoint = func;
|
entryPoint = &node;
|
||||||
}
|
}
|
||||||
|
|
||||||
return clone;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ShaderStageType> selectedStage;
|
std::optional<ShaderStageType> selectedStage;
|
||||||
|
|
@ -99,17 +92,6 @@ namespace Nz
|
||||||
unsigned int indentLevel = 0;
|
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)
|
std::string GlslWriter::Generate(std::optional<ShaderStageType> shaderStage, ShaderAst::StatementPtr& shader, const States& conditions)
|
||||||
{
|
{
|
||||||
State state;
|
State state;
|
||||||
|
|
@ -121,17 +103,11 @@ namespace Nz
|
||||||
m_currentState = nullptr;
|
m_currentState = nullptr;
|
||||||
});
|
});
|
||||||
|
|
||||||
std::string error;
|
ShaderAst::StatementPtr sanitizedAst = ShaderAst::Sanitize(shader);
|
||||||
if (!ShaderAst::ValidateAst(shader, &error))
|
|
||||||
throw std::runtime_error("Invalid shader AST: " + error);
|
|
||||||
|
|
||||||
ShaderAst::TransformVisitor transformVisitor;
|
|
||||||
ShaderAst::StatementPtr transformedShader = transformVisitor.Transform(shader);
|
|
||||||
|
|
||||||
PreVisitor previsitor;
|
PreVisitor previsitor;
|
||||||
previsitor.selectedStage = shaderStage;
|
previsitor.selectedStage = shaderStage;
|
||||||
|
sanitizedAst->Visit(previsitor);
|
||||||
ShaderAst::StatementPtr adaptedShader = previsitor.Clone(transformedShader);
|
|
||||||
|
|
||||||
if (!previsitor.entryPoint)
|
if (!previsitor.entryPoint)
|
||||||
throw std::runtime_error("missing entry point");
|
throw std::runtime_error("missing entry point");
|
||||||
|
|
@ -140,7 +116,7 @@ namespace Nz
|
||||||
|
|
||||||
AppendHeader();
|
AppendHeader();
|
||||||
|
|
||||||
adaptedShader->Visit(*this);
|
sanitizedAst->Visit(*this);
|
||||||
|
|
||||||
return state.stream.str();
|
return state.stream.str();
|
||||||
}
|
}
|
||||||
|
|
@ -361,6 +337,9 @@ namespace Nz
|
||||||
|
|
||||||
void GlslWriter::HandleEntryPoint(ShaderAst::DeclareFunctionStatement& node)
|
void GlslWriter::HandleEntryPoint(ShaderAst::DeclareFunctionStatement& node)
|
||||||
{
|
{
|
||||||
|
if (m_currentState->entryFunc != &node)
|
||||||
|
return; //< Ignore other entry points
|
||||||
|
|
||||||
HandleInOut();
|
HandleInOut();
|
||||||
AppendLine("void main()");
|
AppendLine("void main()");
|
||||||
EnterScope();
|
EnterScope();
|
||||||
|
|
@ -712,11 +691,10 @@ namespace Nz
|
||||||
{
|
{
|
||||||
NazaraAssert(m_currentState, "This function should only be called while processing an AST");
|
NazaraAssert(m_currentState, "This function should only be called while processing an AST");
|
||||||
|
|
||||||
if (m_currentState->entryFunc == &node)
|
if (node.entryStage)
|
||||||
return HandleEntryPoint(node);
|
return HandleEntryPoint(node);
|
||||||
|
|
||||||
assert(node.varIndex);
|
std::optional<std::size_t> varIndexOpt = node.varIndex;
|
||||||
std::size_t varIndex = *node.varIndex;
|
|
||||||
|
|
||||||
Append(node.returnType);
|
Append(node.returnType);
|
||||||
Append(" ");
|
Append(" ");
|
||||||
|
|
@ -731,6 +709,8 @@ namespace Nz
|
||||||
Append(" ");
|
Append(" ");
|
||||||
Append(node.parameters[i].name);
|
Append(node.parameters[i].name);
|
||||||
|
|
||||||
|
assert(varIndexOpt);
|
||||||
|
std::size_t& varIndex = *varIndexOpt;
|
||||||
RegisterVariable(varIndex++, node.parameters[i].name);
|
RegisterVariable(varIndex++, node.parameters[i].name);
|
||||||
}
|
}
|
||||||
Append(")\n");
|
Append(")\n");
|
||||||
|
|
|
||||||
|
|
@ -24,21 +24,15 @@ namespace Nz::ShaderAst
|
||||||
return PopStatement();
|
return PopStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpressionPtr AstCloner::CloneExpression(ExpressionPtr& expr)
|
ExpressionPtr AstCloner::CloneExpression(Expression& expr)
|
||||||
{
|
{
|
||||||
if (!expr)
|
expr.Visit(*this);
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
expr->Visit(*this);
|
|
||||||
return PopExpression();
|
return PopExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
StatementPtr AstCloner::CloneStatement(StatementPtr& statement)
|
StatementPtr AstCloner::CloneStatement(Statement& statement)
|
||||||
{
|
{
|
||||||
if (!statement)
|
statement.Visit(*this);
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
statement->Visit(*this);
|
|
||||||
return PopStatement();
|
return PopStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 = ¤tContext;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,13 +6,13 @@
|
||||||
#include <Nazara/Core/CallOnExit.hpp>
|
#include <Nazara/Core/CallOnExit.hpp>
|
||||||
#include <Nazara/Core/StackVector.hpp>
|
#include <Nazara/Core/StackVector.hpp>
|
||||||
#include <Nazara/Shader/ShaderAstCloner.hpp>
|
#include <Nazara/Shader/ShaderAstCloner.hpp>
|
||||||
#include <Nazara/Shader/ShaderAstValidator.hpp>
|
#include <Nazara/Shader/ShaderAstRecursiveVisitor.hpp>
|
||||||
#include <Nazara/Shader/SpirvAstVisitor.hpp>
|
#include <Nazara/Shader/SpirvAstVisitor.hpp>
|
||||||
#include <Nazara/Shader/SpirvBlock.hpp>
|
#include <Nazara/Shader/SpirvBlock.hpp>
|
||||||
#include <Nazara/Shader/SpirvConstantCache.hpp>
|
#include <Nazara/Shader/SpirvConstantCache.hpp>
|
||||||
#include <Nazara/Shader/SpirvData.hpp>
|
#include <Nazara/Shader/SpirvData.hpp>
|
||||||
#include <Nazara/Shader/SpirvSection.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_map.h>
|
||||||
#include <tsl/ordered_set.h>
|
#include <tsl/ordered_set.h>
|
||||||
#include <SpirV/GLSL.std.450.h>
|
#include <SpirV/GLSL.std.450.h>
|
||||||
|
|
@ -38,7 +38,7 @@ namespace Nz
|
||||||
{ ShaderAst::BuiltinEntry::VertexPosition, { "VertexPosition", ShaderStageType::Vertex, SpirvBuiltIn::Position } }
|
{ ShaderAst::BuiltinEntry::VertexPosition, { "VertexPosition", ShaderStageType::Vertex, SpirvBuiltIn::Position } }
|
||||||
};
|
};
|
||||||
|
|
||||||
class PreVisitor : public ShaderAst::AstScopedVisitor
|
class PreVisitor : public ShaderAst::AstRecursiveVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct UniformVar
|
struct UniformVar
|
||||||
|
|
@ -107,7 +107,7 @@ namespace Nz
|
||||||
m_constantCache.Register(*m_constantCache.BuildConstant(arg));
|
m_constantCache.Register(*m_constantCache.BuildConstant(arg));
|
||||||
}, node.value);
|
}, node.value);
|
||||||
|
|
||||||
AstScopedVisitor::Visit(node);
|
AstRecursiveVisitor::Visit(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Visit(ShaderAst::DeclareExternalStatement& node) override
|
void Visit(ShaderAst::DeclareExternalStatement& node) override
|
||||||
|
|
@ -233,13 +233,13 @@ namespace Nz
|
||||||
}
|
}
|
||||||
|
|
||||||
m_funcIndex = funcIndex;
|
m_funcIndex = funcIndex;
|
||||||
AstScopedVisitor::Visit(node);
|
AstRecursiveVisitor::Visit(node);
|
||||||
m_funcIndex.reset();
|
m_funcIndex.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Visit(ShaderAst::DeclareStructStatement& node) override
|
void Visit(ShaderAst::DeclareStructStatement& node) override
|
||||||
{
|
{
|
||||||
AstScopedVisitor::Visit(node);
|
AstRecursiveVisitor::Visit(node);
|
||||||
|
|
||||||
assert(node.structIndex);
|
assert(node.structIndex);
|
||||||
std::size_t structIndex = *node.structIndex;
|
std::size_t structIndex = *node.structIndex;
|
||||||
|
|
@ -253,7 +253,7 @@ namespace Nz
|
||||||
|
|
||||||
void Visit(ShaderAst::DeclareVariableStatement& node) override
|
void Visit(ShaderAst::DeclareVariableStatement& node) override
|
||||||
{
|
{
|
||||||
AstScopedVisitor::Visit(node);
|
AstRecursiveVisitor::Visit(node);
|
||||||
|
|
||||||
assert(m_funcIndex);
|
assert(m_funcIndex);
|
||||||
auto& func = m_funcs[*m_funcIndex];
|
auto& func = m_funcs[*m_funcIndex];
|
||||||
|
|
@ -269,12 +269,12 @@ namespace Nz
|
||||||
{
|
{
|
||||||
m_constantCache.Register(*m_constantCache.BuildType(node.cachedExpressionType.value()));
|
m_constantCache.Register(*m_constantCache.BuildType(node.cachedExpressionType.value()));
|
||||||
|
|
||||||
AstScopedVisitor::Visit(node);
|
AstRecursiveVisitor::Visit(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Visit(ShaderAst::IntrinsicExpression& node) override
|
void Visit(ShaderAst::IntrinsicExpression& node) override
|
||||||
{
|
{
|
||||||
AstScopedVisitor::Visit(node);
|
AstRecursiveVisitor::Visit(node);
|
||||||
|
|
||||||
switch (node.intrinsic)
|
switch (node.intrinsic)
|
||||||
{
|
{
|
||||||
|
|
@ -285,6 +285,7 @@ namespace Nz
|
||||||
|
|
||||||
// Part of SPIR-V core
|
// Part of SPIR-V core
|
||||||
case ShaderAst::IntrinsicType::DotProduct:
|
case ShaderAst::IntrinsicType::DotProduct:
|
||||||
|
case ShaderAst::IntrinsicType::SampleTexture:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,7 +294,7 @@ namespace Nz
|
||||||
|
|
||||||
void Visit(ShaderAst::SwizzleExpression& node) override
|
void Visit(ShaderAst::SwizzleExpression& node) override
|
||||||
{
|
{
|
||||||
AstScopedVisitor::Visit(node);
|
AstRecursiveVisitor::Visit(node);
|
||||||
|
|
||||||
m_constantCache.Register(*m_constantCache.BuildType(node.cachedExpressionType.value()));
|
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::vector<UInt32> SpirvWriter::Generate(ShaderAst::StatementPtr& shader, const States& conditions)
|
||||||
{
|
{
|
||||||
std::string error;
|
ShaderAst::StatementPtr sanitizedAst = ShaderAst::Sanitize(shader);
|
||||||
if (!ShaderAst::ValidateAst(shader, &error))
|
|
||||||
throw std::runtime_error("Invalid shader AST: " + error);
|
|
||||||
|
|
||||||
ShaderAst::TransformVisitor transformVisitor;
|
|
||||||
ShaderAst::StatementPtr transformedShader = transformVisitor.Transform(shader);
|
|
||||||
|
|
||||||
m_context.states = &conditions;
|
m_context.states = &conditions;
|
||||||
|
|
||||||
|
|
@ -409,7 +405,7 @@ namespace Nz
|
||||||
|
|
||||||
// Register all extended instruction sets
|
// Register all extended instruction sets
|
||||||
PreVisitor preVisitor(conditions, state.constantTypeCache, state.funcs);
|
PreVisitor preVisitor(conditions, state.constantTypeCache, state.funcs);
|
||||||
transformedShader->Visit(preVisitor);
|
sanitizedAst->Visit(preVisitor);
|
||||||
|
|
||||||
m_currentState->preVisitor = &preVisitor;
|
m_currentState->preVisitor = &preVisitor;
|
||||||
|
|
||||||
|
|
@ -417,7 +413,7 @@ namespace Nz
|
||||||
state.extensionInstructions[extInst] = AllocateResultId();
|
state.extensionInstructions[extInst] = AllocateResultId();
|
||||||
|
|
||||||
SpirvAstVisitor visitor(*this, state.instructions, state.funcs);
|
SpirvAstVisitor visitor(*this, state.instructions, state.funcs);
|
||||||
transformedShader->Visit(visitor);
|
sanitizedAst->Visit(visitor);
|
||||||
|
|
||||||
AppendHeader();
|
AppendHeader();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue