diff --git a/include/Nazara/Shader/Ast/ExpressionType.hpp b/include/Nazara/Shader/Ast/ExpressionType.hpp index 559602038..2b436c7aa 100644 --- a/include/Nazara/Shader/Ast/ExpressionType.hpp +++ b/include/Nazara/Shader/Ast/ExpressionType.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -81,11 +82,13 @@ namespace Nz::ShaderAst { struct StructMember { + std::optional builtin; + std::optional locationIndex; std::string name; - std::vector attributes; ExpressionType type; }; + std::optional layout; std::string name; std::vector members; }; diff --git a/include/Nazara/Shader/GlslWriter.hpp b/include/Nazara/Shader/GlslWriter.hpp index 83acdc4cf..9f228c51e 100644 --- a/include/Nazara/Shader/GlslWriter.hpp +++ b/include/Nazara/Shader/GlslWriter.hpp @@ -71,7 +71,7 @@ namespace Nz void HandleEntryPoint(ShaderAst::DeclareFunctionStatement& node); void HandleInOut(); - void RegisterStruct(std::size_t structIndex, bool isStd140, ShaderAst::StructDescription desc); + void RegisterStruct(std::size_t structIndex, ShaderAst::StructDescription desc); void RegisterVariable(std::size_t varIndex, std::string varName); void Visit(ShaderAst::ExpressionPtr& expr, bool encloseIfRequired = false); diff --git a/include/Nazara/Shader/ShaderAstSerializer.hpp b/include/Nazara/Shader/ShaderAstSerializer.hpp index 20ac281bf..bb7e5f28b 100644 --- a/include/Nazara/Shader/ShaderAstSerializer.hpp +++ b/include/Nazara/Shader/ShaderAstSerializer.hpp @@ -48,7 +48,6 @@ namespace Nz::ShaderAst void Serialize(ReturnStatement& node); protected: - void Attributes(std::vector& attributes); template void Container(T& container); template void Enum(T& enumVal); template void OptEnum(std::optional& optVal); diff --git a/include/Nazara/Shader/ShaderBuilder.hpp b/include/Nazara/Shader/ShaderBuilder.hpp index c9f0b50de..ed6b3f624 100644 --- a/include/Nazara/Shader/ShaderBuilder.hpp +++ b/include/Nazara/Shader/ShaderBuilder.hpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace Nz::ShaderBuilder { @@ -59,13 +60,12 @@ namespace Nz::ShaderBuilder struct DeclareFunction { inline std::unique_ptr operator()(std::string name, std::vector parameters, std::vector statements, ShaderAst::ExpressionType returnType = ShaderAst::NoType{}) const; - inline std::unique_ptr operator()(std::vector attributes, std::string name, std::vector parameters, std::vector statements, ShaderAst::ExpressionType returnType = ShaderAst::NoType{}) const; + inline std::unique_ptr operator()(std::optional entryStage, std::string name, std::vector parameters, std::vector statements, ShaderAst::ExpressionType returnType = ShaderAst::NoType{}) const; }; struct DeclareStruct { inline std::unique_ptr operator()(ShaderAst::StructDescription description) const; - inline std::unique_ptr operator()(std::vector attributes, ShaderAst::StructDescription description) const; }; struct DeclareVariable diff --git a/include/Nazara/Shader/ShaderBuilder.inl b/include/Nazara/Shader/ShaderBuilder.inl index ec83815a7..a034d35f9 100644 --- a/include/Nazara/Shader/ShaderBuilder.inl +++ b/include/Nazara/Shader/ShaderBuilder.inl @@ -108,10 +108,10 @@ namespace Nz::ShaderBuilder return declareFunctionNode; } - inline std::unique_ptr Impl::DeclareFunction::operator()(std::vector attributes, std::string name, std::vector parameters, std::vector statements, ShaderAst::ExpressionType returnType) const + inline std::unique_ptr Impl::DeclareFunction::operator()(std::optional entryStage, std::string name, std::vector parameters, std::vector statements, ShaderAst::ExpressionType returnType) const { auto declareFunctionNode = std::make_unique(); - declareFunctionNode->attributes = std::move(attributes); + declareFunctionNode->entryStage = entryStage; declareFunctionNode->name = std::move(name); declareFunctionNode->parameters = std::move(parameters); declareFunctionNode->returnType = std::move(returnType); @@ -128,15 +128,6 @@ namespace Nz::ShaderBuilder return declareStructNode; } - inline std::unique_ptr Impl::DeclareStruct::operator()(std::vector attributes, ShaderAst::StructDescription description) const - { - auto declareStructNode = std::make_unique(); - declareStructNode->attributes = std::move(attributes); - declareStructNode->description = std::move(description); - - return declareStructNode; - } - inline std::unique_ptr Nz::ShaderBuilder::Impl::DeclareVariable::operator()(std::string name, ShaderAst::ExpressionType type, ShaderAst::ExpressionPtr initialValue) const { auto declareVariableNode = std::make_unique(); diff --git a/include/Nazara/Shader/ShaderEnums.hpp b/include/Nazara/Shader/ShaderEnums.hpp index e56e71093..a25ef4caa 100644 --- a/include/Nazara/Shader/ShaderEnums.hpp +++ b/include/Nazara/Shader/ShaderEnums.hpp @@ -25,14 +25,6 @@ namespace Nz::ShaderAst Location //< Location (struct member only) - has argument index }; - enum class PrimitiveType - { - Boolean, //< bool - Float32, //< f32 - Int32, //< i32 - UInt32, //< ui32 - }; - enum class BinaryType { Add, //< + @@ -80,6 +72,14 @@ namespace Nz::ShaderAst #include }; + enum class PrimitiveType + { + Boolean, //< bool + Float32, //< f32 + Int32, //< i32 + UInt32, //< ui32 + }; + enum class SwizzleComponent { First, diff --git a/include/Nazara/Shader/ShaderLangParser.hpp b/include/Nazara/Shader/ShaderLangParser.hpp index 3c61ec888..1fa385c9f 100644 --- a/include/Nazara/Shader/ShaderLangParser.hpp +++ b/include/Nazara/Shader/ShaderLangParser.hpp @@ -14,6 +14,12 @@ namespace Nz::ShaderLang { + class AttributeError : public std::exception + { + public: + using exception::exception; + }; + class ExpectedToken : public std::exception { public: diff --git a/include/Nazara/Shader/ShaderNodes.hpp b/include/Nazara/Shader/ShaderNodes.hpp index 5413457f2..e5663c5d1 100644 --- a/include/Nazara/Shader/ShaderNodes.hpp +++ b/include/Nazara/Shader/ShaderNodes.hpp @@ -210,13 +210,12 @@ namespace Nz::ShaderAst struct ExternalVar { + std::optional bindingIndex; std::string name; - std::vector attributes; ExpressionType type; }; std::optional varIndex; - std::vector attributes; std::vector externalVars; }; @@ -231,10 +230,10 @@ namespace Nz::ShaderAst ExpressionType type; }; + std::optional entryStage; std::optional funcIndex; std::optional varIndex; std::string name; - std::vector attributes; std::vector parameters; std::vector statements; ExpressionType returnType; @@ -246,7 +245,6 @@ namespace Nz::ShaderAst void Visit(AstStatementVisitor& visitor) override; std::optional structIndex; - std::vector attributes; StructDescription description; }; diff --git a/src/Nazara/Shader/GlslWriter.cpp b/src/Nazara/Shader/GlslWriter.cpp index 6fcf520ed..08088d24f 100644 --- a/src/Nazara/Shader/GlslWriter.cpp +++ b/src/Nazara/Shader/GlslWriter.cpp @@ -24,12 +24,6 @@ namespace Nz static const char* s_outputPrefix = "_NzOut_"; static const char* s_outputVarName = "_nzOutput"; - //FIXME: Have this only once - std::unordered_map s_entryPoints = { - { "frag", ShaderStageType::Fragment }, - { "vert", ShaderStageType::Vertex }, - }; - template const T& Retrieve(const std::unordered_map& map, std::size_t id) { auto it = map.find(id); @@ -49,29 +43,19 @@ namespace Nz ShaderAst::DeclareFunctionStatement* func = static_cast(clone.get()); // Remove function if it's an entry point of another type than the one selected - bool isEntryPoint = false; - for (auto& attribute : func->attributes) + if (node.entryStage) { - if (attribute.type == ShaderAst::AttributeType::Entry) - { - auto it = s_entryPoints.find(std::get(attribute.args)); - assert(it != s_entryPoints.end()); + ShaderStageType stage = *node.entryStage; + if (stage != selectedStage) + return ShaderBuilder::NoOp(); - if (it->second != selectedEntryPoint) - return ShaderBuilder::NoOp(); - - isEntryPoint = true; - break; - } - } - - if (isEntryPoint) entryPoint = func; + } return clone; } - ShaderStageType selectedEntryPoint; + ShaderStageType selectedStage; ShaderAst::DeclareFunctionStatement* entryPoint = nullptr; }; @@ -81,8 +65,8 @@ namespace Nz ShaderStageTypeFlags stageFlags; }; - std::unordered_map builtinMapping = { - { "position", { "gl_Position", ShaderStageType::Vertex } } + std::unordered_map s_builtinMapping = { + { ShaderAst::BuiltinEntry::VertexPosition, { "gl_Position", ShaderStageType::Vertex } } }; } @@ -95,17 +79,11 @@ namespace Nz std::string targetName; }; - struct StructInfo - { - ShaderAst::StructDescription structDesc; - bool isStd140 = false; - }; - ShaderStageType stage; const States* states = nullptr; ShaderAst::DeclareFunctionStatement* entryFunc = nullptr; std::stringstream stream; - std::unordered_map structs; + std::unordered_map structs; std::unordered_map variableNames; std::vector inputFields; std::vector outputFields; @@ -138,7 +116,7 @@ namespace Nz ShaderAst::StatementPtr transformedShader = transformVisitor.Transform(shader); PreVisitor previsitor; - previsitor.selectedEntryPoint = shaderStage; + previsitor.selectedStage = shaderStage; ShaderAst::StatementPtr adaptedShader = previsitor.Clone(transformedShader); @@ -241,13 +219,13 @@ namespace Nz void GlslWriter::Append(const ShaderAst::StructType& structType) { - const auto& structDesc = Retrieve(m_currentState->structs, structType.structIndex).structDesc; + const auto& structDesc = Retrieve(m_currentState->structs, structType.structIndex); Append(structDesc.name); } void GlslWriter::Append(const ShaderAst::UniformType& uniformType) { - /* TODO */ + throw std::runtime_error("unexpected UniformType"); } void GlslWriter::Append(const ShaderAst::VectorType& vecType) @@ -305,7 +283,7 @@ namespace Nz void GlslWriter::AppendField(std::size_t structIndex, const std::size_t* memberIndices, std::size_t remainingMembers) { - const auto& structDesc = Retrieve(m_currentState->structs, structIndex).structDesc; + const auto& structDesc = Retrieve(m_currentState->structs, structIndex); const auto& member = structDesc.members[*memberIndices]; @@ -371,7 +349,7 @@ namespace Nz assert(IsStructType(parameter.type)); std::size_t structIndex = std::get(parameter.type).structIndex; - const ShaderAst::StructDescription& structDesc = Retrieve(m_currentState->structs, structIndex).structDesc; + const ShaderAst::StructDescription& structDesc = Retrieve(m_currentState->structs, structIndex); AppendLine(structDesc.name, " ", varName, ";"); for (const auto& [memberName, targetName] : m_currentState->inputFields) @@ -397,41 +375,24 @@ namespace Nz { for (const auto& member : structDesc.members) { - bool skip = false; - std::optional builtinName; - std::optional attributeLocation; - for (const auto& [attributeType, attributeParam] : member.attributes) + if (member.builtin) { - if (attributeType == ShaderAst::AttributeType::Builtin) - { - auto it = builtinMapping.find(std::get(attributeParam)); - if (it != builtinMapping.end()) - { - const Builtin& builtin = it->second; - if (!builtin.stageFlags.Test(m_currentState->stage)) - { - skip = true; - break; - } + auto it = s_builtinMapping.find(member.builtin.value()); + assert(it != s_builtinMapping.end()); - builtinName = builtin.identifier; - break; - } - } - else if (attributeType == ShaderAst::AttributeType::Location) - { - attributeLocation = std::get(attributeParam); - break; - } + const Builtin& builtin = it->second; + if (!builtin.stageFlags.Test(m_currentState->stage)) + continue; //< This builtin is not active in this stage, skip it + + fields.push_back({ + member.name, + builtin.identifier + }); } - - if (skip) - continue; - - if (attributeLocation) + else if (member.locationIndex) { Append("layout(location = "); - Append(*attributeLocation); + Append(*member.locationIndex); Append(") "); Append(keyword); Append(" "); @@ -446,13 +407,6 @@ namespace Nz targetPrefix + member.name }); } - else if (builtinName) - { - fields.push_back({ - member.name, - *builtinName - }); - } } AppendLine(); }; @@ -468,7 +422,7 @@ namespace Nz assert(std::holds_alternative(parameter.type)); std::size_t inputStructIndex = std::get(parameter.type).structIndex; - inputStruct = &Retrieve(m_currentState->structs, inputStructIndex).structDesc; + inputStruct = &Retrieve(m_currentState->structs, inputStructIndex); AppendCommentSection("Inputs"); AppendInOut(*inputStruct, m_currentState->inputFields, "in", s_inputPrefix); @@ -485,20 +439,17 @@ namespace Nz assert(std::holds_alternative(node.returnType)); std::size_t outputStructIndex = std::get(node.returnType).structIndex; - const ShaderAst::StructDescription& outputStruct = Retrieve(m_currentState->structs, outputStructIndex).structDesc; + const ShaderAst::StructDescription& outputStruct = Retrieve(m_currentState->structs, outputStructIndex); AppendCommentSection("Outputs"); AppendInOut(outputStruct, m_currentState->outputFields, "out", s_outputPrefix); } } - void GlslWriter::RegisterStruct(std::size_t structIndex, bool isStd140, ShaderAst::StructDescription desc) + void GlslWriter::RegisterStruct(std::size_t structIndex, ShaderAst::StructDescription desc) { assert(m_currentState->structs.find(structIndex) == m_currentState->structs.end()); - m_currentState->structs.emplace(structIndex, State::StructInfo{ - std::move(desc), - isStd140 - }); + m_currentState->structs.emplace(structIndex, std::move(desc)); } void GlslWriter::RegisterVariable(std::size_t varIndex, std::string varName) @@ -667,16 +618,6 @@ namespace Nz for (const auto& externalVar : node.externalVars) { - std::optional bindingIndex; - for (const auto& [attributeType, attributeParam] : externalVar.attributes) - { - if (attributeType == ShaderAst::AttributeType::Binding) - { - bindingIndex = std::get(attributeParam); - break; - } - } - bool isStd140 = false; if (IsUniformType(externalVar.type)) { @@ -685,13 +626,13 @@ namespace Nz std::size_t structIndex = std::get(uniform.containedType).structIndex; auto& structInfo = Retrieve(m_currentState->structs, structIndex); - isStd140 = structInfo.isStd140; + isStd140 = structInfo.layout == StructLayout_Std140; } - if (bindingIndex) + if (externalVar.bindingIndex) { Append("layout(binding = "); - Append(*bindingIndex); + Append(*externalVar.bindingIndex); if (isStd140) Append(", std140"); @@ -708,19 +649,19 @@ namespace Nz assert(std::holds_alternative(uniform.containedType)); std::size_t structIndex = std::get(uniform.containedType).structIndex; - auto& structInfo = Retrieve(m_currentState->structs, structIndex); + auto& structDesc = Retrieve(m_currentState->structs, structIndex); bool first = true; - for (const auto& [name, attribute, type] : structInfo.structDesc.members) + for (const auto& member : structDesc.members) { if (!first) AppendLine(); first = false; - Append(type); + Append(member.type); Append(" "); - Append(name); + Append(member.name); Append(";"); } } @@ -778,34 +719,24 @@ namespace Nz void GlslWriter::Visit(ShaderAst::DeclareStructStatement& node) { - bool isStd140 = false; - for (const auto& [attributeType, attributeParam] : node.attributes) - { - if (attributeType == ShaderAst::AttributeType::Layout && std::get(attributeParam) == "std140") - { - isStd140 = true; - break; - } - } - assert(node.structIndex); - RegisterStruct(*node.structIndex, isStd140, node.description); + RegisterStruct(*node.structIndex, node.description); Append("struct "); AppendLine(node.description.name); EnterScope(); { bool first = true; - for (const auto& [name, attribute, type] : node.description.members) + for (const auto& member : node.description.members) { if (!first) AppendLine(); first = false; - Append(type); + Append(member.type); Append(" "); - Append(name); + Append(member.name); Append(";"); } } @@ -897,7 +828,7 @@ namespace Nz const ShaderAst::ExpressionType& returnType = GetExpressionType(*node.returnExpr); assert(IsStructType(returnType)); std::size_t structIndex = std::get(returnType).structIndex; - const ShaderAst::StructDescription& structDesc = Retrieve(m_currentState->structs, structIndex).structDesc; + const ShaderAst::StructDescription& structDesc = Retrieve(m_currentState->structs, structIndex); std::string outputStructVarName; if (node.returnExpr->GetType() == ShaderAst::NodeType::VariableExpression) diff --git a/src/Nazara/Shader/ShaderAstCloner.cpp b/src/Nazara/Shader/ShaderAstCloner.cpp index 7e1b3256c..1a80e654f 100644 --- a/src/Nazara/Shader/ShaderAstCloner.cpp +++ b/src/Nazara/Shader/ShaderAstCloner.cpp @@ -45,7 +45,6 @@ namespace Nz::ShaderAst StatementPtr AstCloner::Clone(DeclareExternalStatement& node) { auto clone = std::make_unique(); - clone->attributes = node.attributes; clone->externalVars = node.externalVars; clone->varIndex = node.varIndex; @@ -55,7 +54,7 @@ namespace Nz::ShaderAst StatementPtr AstCloner::Clone(DeclareFunctionStatement& node) { auto clone = std::make_unique(); - clone->attributes = node.attributes; + clone->entryStage = node.entryStage; clone->funcIndex = node.funcIndex; clone->name = node.name; clone->parameters = node.parameters; diff --git a/src/Nazara/Shader/ShaderAstSerializer.cpp b/src/Nazara/Shader/ShaderAstSerializer.cpp index 7102e45fa..73ed6db60 100644 --- a/src/Nazara/Shader/ShaderAstSerializer.cpp +++ b/src/Nazara/Shader/ShaderAstSerializer.cpp @@ -115,6 +115,7 @@ namespace Nz::ShaderAst void AstSerializerBase::Serialize(DeclareVariableStatement& node) { + OptVal(node.varIndex); Value(node.varName); Type(node.varType); Node(node.initialExpression); @@ -168,14 +169,14 @@ namespace Nz::ShaderAst void AstSerializerBase::Serialize(DeclareExternalStatement& node) { - Attributes(node.attributes); + OptVal(node.varIndex); Container(node.externalVars); for (auto& extVar : node.externalVars) { - Attributes(extVar.attributes); Value(extVar.name); Type(extVar.type); + OptVal(extVar.bindingIndex); } } @@ -183,8 +184,9 @@ namespace Nz::ShaderAst { Value(node.name); Type(node.returnType); - - Attributes(node.attributes); + OptEnum(node.entryStage); + OptVal(node.funcIndex); + OptVal(node.varIndex); Container(node.parameters); for (auto& parameter : node.parameters) @@ -200,13 +202,18 @@ namespace Nz::ShaderAst void AstSerializerBase::Serialize(DeclareStructStatement& node) { + OptVal(node.structIndex); + Value(node.description.name); + OptEnum(node.description.layout); Container(node.description.members); for (auto& member : node.description.members) { Value(member.name); Type(member.type); + OptEnum(member.builtin); + OptVal(member.locationIndex); } } @@ -246,78 +253,6 @@ namespace Nz::ShaderAst m_stream.FlushBits(); } - void AstSerializerBase::Attributes(std::vector& attributes) - { - Container(attributes); - for (auto& attribute : attributes) - { - Enum(attribute.type); - - if (IsWriting()) - { - std::visit([&](auto&& arg) - { - using T = std::decay_t; - - if constexpr (std::is_same_v) - { - UInt8 typeId = 0; - Value(typeId); - } - else if constexpr (std::is_same_v) - { - UInt8 typeId = 1; - UInt64 v = UInt64(arg); - Value(typeId); - Value(v); - } - else if constexpr (std::is_same_v) - { - UInt8 typeId = 2; - Value(typeId); - Value(arg); - } - else - static_assert(AlwaysFalse::value, "non-exhaustive visitor"); - - }, attribute.args); - } - else - { - UInt8 typeId; - Value(typeId); - - switch (typeId) - { - case 0: - attribute.args.emplace(); - break; - - case 1: - { - UInt64 arg; - Value(arg); - - attribute.args = static_cast(arg); - break; - } - - case 2: - { - std::string arg; - Value(arg); - - attribute.args = std::move(arg); - break; - } - - default: - throw std::runtime_error("invalid attribute type id"); - } - } - } - } - bool ShaderAstSerializer::IsWriting() const { return true; diff --git a/src/Nazara/Shader/ShaderAstValidator.cpp b/src/Nazara/Shader/ShaderAstValidator.cpp index 98ecb65d8..ed7dcff50 100644 --- a/src/Nazara/Shader/ShaderAstValidator.cpp +++ b/src/Nazara/Shader/ShaderAstValidator.cpp @@ -11,14 +11,6 @@ namespace Nz::ShaderAst { - namespace - { - std::unordered_map entryPoints = { - { "frag", ShaderStageType::Fragment }, - { "vert", ShaderStageType::Vertex }, - }; - } - struct AstError { std::string errMsg; @@ -28,7 +20,7 @@ namespace Nz::ShaderAst { std::array entryFunctions = {}; std::unordered_set declaredExternalVar; - std::unordered_set usedBindingIndexes; + std::unordered_set usedBindingIndexes; }; bool AstValidator::Validate(StatementPtr& node, std::string* error) @@ -552,44 +544,15 @@ namespace Nz::ShaderAst void AstValidator::Visit(DeclareExternalStatement& node) { - if (!node.attributes.empty()) - throw AstError{ "unhandled attribute for external block" }; - - /*for (const auto& [attributeType, arg] : node.attributes) - { - switch (attributeType) - { - default: - throw AstError{ "unhandled attribute for external block" }; - } - }*/ - for (const auto& extVar : node.externalVars) { - bool hasBinding = false; - for (const auto& [attributeType, arg] : extVar.attributes) + if (extVar.bindingIndex) { - switch (attributeType) - { - case AttributeType::Binding: - { - if (hasBinding) - throw AstError{ "attribute binding must be present once" }; + 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" }; - if (!std::holds_alternative(arg)) - throw AstError{ "attribute binding requires a string parameter" }; - - long long bindingIndex = std::get(arg); - 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); - break; - } - - default: - throw AstError{ "unhandled attribute for external variable" }; - } + m_context->usedBindingIndexes.insert(bindingIndex); } if (m_context->declaredExternalVar.find(extVar.name) != m_context->declaredExternalVar.end()) @@ -603,42 +566,17 @@ namespace Nz::ShaderAst void AstValidator::Visit(DeclareFunctionStatement& node) { - bool hasEntry = false; - for (const auto& [attributeType, arg] : node.attributes) + if (node.entryStage) { - switch (attributeType) - { - case AttributeType::Entry: - { - if (hasEntry) - throw AstError{ "attribute entry must be present once" }; + ShaderStageType stageType = *node.entryStage; - if (!std::holds_alternative(arg)) - throw AstError{ "attribute entry requires a string parameter" }; + if (m_context->entryFunctions[UnderlyingCast(stageType)]) + throw AstError{ "the same entry type has been defined multiple times" }; - const std::string& argStr = std::get(arg); + m_context->entryFunctions[UnderlyingCast(stageType)] = &node; - auto it = entryPoints.find(argStr); - if (it == entryPoints.end()) - throw AstError{ "invalid parameter " + argStr + " for entry attribute" }; - - ShaderStageType stageType = it->second; - - if (m_context->entryFunctions[UnderlyingCast(stageType)]) - throw AstError{ "the same entry type has been defined multiple times" }; - - m_context->entryFunctions[UnderlyingCast(it->second)] = &node; - - if (node.parameters.size() > 1) - throw AstError{ "entry functions can either take one struct parameter or no parameter" }; - - hasEntry = true; - break; - } - - default: - throw AstError{ "unhandled attribute for function" }; - } + if (node.parameters.size() > 1) + throw AstError{ "entry functions can either take one struct parameter or no parameter" }; } for (auto& statement : node.statements) diff --git a/src/Nazara/Shader/ShaderLangParser.cpp b/src/Nazara/Shader/ShaderLangParser.cpp index b896055a0..b0636f7be 100644 --- a/src/Nazara/Shader/ShaderLangParser.cpp +++ b/src/Nazara/Shader/ShaderLangParser.cpp @@ -1,631 +1,737 @@ -// 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 -#include -#include -#include - -namespace Nz::ShaderLang -{ - namespace - { - std::unordered_map identifierToBasicType = { - { "bool", ShaderAst::PrimitiveType::Boolean }, - { "i32", ShaderAst::PrimitiveType::Int32 }, - { "f32", ShaderAst::PrimitiveType::Float32 }, - { "u32", ShaderAst::PrimitiveType::UInt32 } - }; +// 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 +#include +#include +#include + +namespace Nz::ShaderLang +{ + namespace + { + std::unordered_map identifierToBasicType = { + { "bool", ShaderAst::PrimitiveType::Boolean }, + { "i32", ShaderAst::PrimitiveType::Int32 }, + { "f32", ShaderAst::PrimitiveType::Float32 }, + { "u32", ShaderAst::PrimitiveType::UInt32 } + }; std::unordered_map identifierToIntrinsic = { { "cross", ShaderAst::IntrinsicType::CrossProduct }, { "dot", ShaderAst::IntrinsicType::DotProduct }, }; - - std::unordered_map identifierToAttributeType = { - { "binding", ShaderAst::AttributeType::Binding }, - { "builtin", ShaderAst::AttributeType::Builtin }, - { "entry", ShaderAst::AttributeType::Entry }, - { "layout", ShaderAst::AttributeType::Layout }, - { "location", ShaderAst::AttributeType::Location }, + + std::unordered_map identifierToAttributeType = { + { "binding", ShaderAst::AttributeType::Binding }, + { "builtin", ShaderAst::AttributeType::Builtin }, + { "entry", ShaderAst::AttributeType::Entry }, + { "layout", ShaderAst::AttributeType::Layout }, + { "location", ShaderAst::AttributeType::Location }, + }; + + std::unordered_map s_entryPoints = { + { "frag", ShaderStageType::Fragment }, + { "vert", ShaderStageType::Vertex }, }; - } - ShaderAst::StatementPtr Parser::Parse(const std::vector& tokens) - { - Context context; - context.tokenCount = tokens.size(); - context.tokens = tokens.data(); + std::unordered_map s_builtinMapping = { + { "position", ShaderAst::BuiltinEntry::VertexPosition } + }; - context.root = std::make_unique(); + template + std::optional BoundCast(U val) + { + if (val < std::numeric_limits::min() || val > std::numeric_limits::max()) + return std::nullopt; - m_context = &context; - - std::vector attributes; + return static_cast(val); + } + } + + ShaderAst::StatementPtr Parser::Parse(const std::vector& tokens) + { + Context context; + context.tokenCount = tokens.size(); + context.tokens = tokens.data(); + + context.root = std::make_unique(); + + m_context = &context; + + std::vector attributes; EnterScope(); - - bool reachedEndOfStream = false; - while (!reachedEndOfStream) - { - const Token& nextToken = Peek(); - switch (nextToken.type) - { - case TokenType::EndOfStream: - if (!attributes.empty()) - throw UnexpectedToken{}; - - reachedEndOfStream = true; - break; - - case TokenType::External: - context.root->statements.push_back(ParseExternalBlock(std::move(attributes))); - attributes.clear(); - break; - - case TokenType::OpenAttribute: - assert(attributes.empty()); - attributes = ParseAttributes(); - break; - - case TokenType::FunctionDeclaration: - context.root->statements.push_back(ParseFunctionDeclaration(std::move(attributes))); - attributes.clear(); - break; - - case TokenType::Struct: - context.root->statements.push_back(ParseStructDeclaration(std::move(attributes))); - attributes.clear(); - break; - - default: - throw UnexpectedToken{}; - } - } + + bool reachedEndOfStream = false; + while (!reachedEndOfStream) + { + const Token& nextToken = Peek(); + switch (nextToken.type) + { + case TokenType::EndOfStream: + if (!attributes.empty()) + throw UnexpectedToken{}; + + reachedEndOfStream = true; + break; + + case TokenType::External: + context.root->statements.push_back(ParseExternalBlock(std::move(attributes))); + attributes.clear(); + break; + + case TokenType::OpenAttribute: + assert(attributes.empty()); + attributes = ParseAttributes(); + break; + + case TokenType::FunctionDeclaration: + context.root->statements.push_back(ParseFunctionDeclaration(std::move(attributes))); + attributes.clear(); + break; + + case TokenType::Struct: + context.root->statements.push_back(ParseStructDeclaration(std::move(attributes))); + attributes.clear(); + break; + + default: + throw UnexpectedToken{}; + } + } LeaveScope(); - - return std::move(context.root); - } - - const Token& Parser::Advance() + + return std::move(context.root); + } + + const Token& Parser::Advance() + { + const Token& token = Peek(); + m_context->tokenIndex++; + + return token; + } + + void Parser::Consume(std::size_t count) + { + assert(m_context->tokenIndex + count < m_context->tokenCount); + m_context->tokenIndex += count; + } + + ShaderAst::ExpressionType Parser::DecodeType(const std::string& identifier) + { + if (auto it = identifierToBasicType.find(identifier); it != identifierToBasicType.end()) + return it->second; + + //FIXME: Handle this better + if (identifier == "mat4") + { + ShaderAst::MatrixType matrixType; + matrixType.columnCount = 4; + matrixType.rowCount = 4; + + Expect(Advance(), TokenType::LessThan); //< '<' + matrixType.type = ParsePrimitiveType(); + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return matrixType; + } + else if (identifier == "sampler2D") + { + ShaderAst::SamplerType samplerType; + samplerType.dim = ImageType_2D; + + Expect(Advance(), TokenType::LessThan); //< '<' + samplerType.sampledType = ParsePrimitiveType(); + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return samplerType; + } + else if (identifier == "uniform") + { + ShaderAst::UniformType uniformType; + + Expect(Advance(), TokenType::LessThan); //< '<' + uniformType.containedType = ShaderAst::IdentifierType{ ParseIdentifierAsName() }; + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return uniformType; + } + else if (identifier == "vec2") + { + ShaderAst::VectorType vectorType; + vectorType.componentCount = 2; + + Expect(Advance(), TokenType::LessThan); //< '<' + vectorType.type = ParsePrimitiveType(); + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return vectorType; + } + else if (identifier == "vec3") + { + ShaderAst::VectorType vectorType; + vectorType.componentCount = 3; + + Expect(Advance(), TokenType::LessThan); //< '<' + vectorType.type = ParsePrimitiveType(); + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return vectorType; + } + else if (identifier == "vec4") + { + ShaderAst::VectorType vectorType; + vectorType.componentCount = 4; + + Expect(Advance(), TokenType::LessThan); //< '<' + vectorType.type = ParsePrimitiveType(); + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return vectorType; + } + else + { + ShaderAst::IdentifierType identifierType; + identifierType.name = identifier; + + return identifierType; + } + } + + void Parser::EnterScope() + { + m_context->scopeSizes.push_back(m_context->identifiersInScope.size()); + } + + const Token& Parser::Expect(const Token& token, TokenType type) + { + if (token.type != type) + throw ExpectedToken{}; + + return token; + } + + const Token& Parser::ExpectNot(const Token& token, TokenType type) + { + if (token.type == type) + throw ExpectedToken{}; + + return token; + } + + const Token& Parser::Expect(TokenType type) + { + const Token& token = Peek(); + Expect(token, type); + + return token; + } + + void Parser::LeaveScope() + { + assert(!m_context->scopeSizes.empty()); + m_context->identifiersInScope.resize(m_context->scopeSizes.back()); + m_context->scopeSizes.pop_back(); + } + + bool Parser::IsVariableInScope(const std::string_view& identifier) const { - const Token& token = Peek(); - m_context->tokenIndex++; - - return token; + return std::find(m_context->identifiersInScope.rbegin(), m_context->identifiersInScope.rend(), identifier) != m_context->identifiersInScope.rend(); } - - void Parser::Consume(std::size_t count) - { - assert(m_context->tokenIndex + count < m_context->tokenCount); - m_context->tokenIndex += count; - } - - ShaderAst::ExpressionType Parser::DecodeType(const std::string& identifier) - { - if (auto it = identifierToBasicType.find(identifier); it != identifierToBasicType.end()) - return it->second; - - //FIXME: Handle this better - if (identifier == "mat4") - { - ShaderAst::MatrixType matrixType; - matrixType.columnCount = 4; - matrixType.rowCount = 4; - - Expect(Advance(), TokenType::LessThan); //< '<' - matrixType.type = ParsePrimitiveType(); - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return matrixType; - } - else if (identifier == "sampler2D") - { - ShaderAst::SamplerType samplerType; - samplerType.dim = ImageType_2D; - - Expect(Advance(), TokenType::LessThan); //< '<' - samplerType.sampledType = ParsePrimitiveType(); - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return samplerType; - } - else if (identifier == "uniform") - { - ShaderAst::UniformType uniformType; - - Expect(Advance(), TokenType::LessThan); //< '<' - uniformType.containedType = ShaderAst::IdentifierType{ ParseIdentifierAsName() }; - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return uniformType; - } - else if (identifier == "vec2") - { - ShaderAst::VectorType vectorType; - vectorType.componentCount = 2; - - Expect(Advance(), TokenType::LessThan); //< '<' - vectorType.type = ParsePrimitiveType(); - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return vectorType; - } - else if (identifier == "vec3") - { - ShaderAst::VectorType vectorType; - vectorType.componentCount = 3; - - Expect(Advance(), TokenType::LessThan); //< '<' - vectorType.type = ParsePrimitiveType(); - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return vectorType; - } - else if (identifier == "vec4") - { - ShaderAst::VectorType vectorType; - vectorType.componentCount = 4; - - Expect(Advance(), TokenType::LessThan); //< '<' - vectorType.type = ParsePrimitiveType(); - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return vectorType; - } - else - { - ShaderAst::IdentifierType identifierType; - identifierType.name = identifier; - - return identifierType; - } - } - - void Parser::EnterScope() - { - m_context->scopeSizes.push_back(m_context->identifiersInScope.size()); - } - - const Token& Parser::Expect(const Token& token, TokenType type) - { - if (token.type != type) - throw ExpectedToken{}; - - return token; - } - - const Token& Parser::ExpectNot(const Token& token, TokenType type) - { - if (token.type == type) - throw ExpectedToken{}; - - return token; - } - - const Token& Parser::Expect(TokenType type) - { - const Token& token = Peek(); - Expect(token, type); - - return token; - } - - void Parser::LeaveScope() - { - assert(!m_context->scopeSizes.empty()); - m_context->identifiersInScope.resize(m_context->scopeSizes.back()); - m_context->scopeSizes.pop_back(); - } - - bool Parser::IsVariableInScope(const std::string_view& identifier) const - { - return std::find(m_context->identifiersInScope.rbegin(), m_context->identifiersInScope.rend(), identifier) != m_context->identifiersInScope.rend(); - } - - void Parser::RegisterVariable(std::string identifier) + + void Parser::RegisterVariable(std::string identifier) { if (IsVariableInScope(identifier)) throw DuplicateIdentifier{ ("identifier name " + identifier + " is already taken").c_str() }; assert(!m_context->scopeSizes.empty()); - m_context->identifiersInScope.push_back(std::move(identifier)); - } - - const Token& Parser::Peek(std::size_t advance) + m_context->identifiersInScope.push_back(std::move(identifier)); + } + + const Token& Parser::Peek(std::size_t advance) + { + assert(m_context->tokenIndex + advance < m_context->tokenCount); + return m_context->tokens[m_context->tokenIndex + advance]; + } + + std::vector Parser::ParseAttributes() + { + std::vector attributes; + + Expect(Advance(), TokenType::OpenAttribute); + + bool expectComma = false; + for (;;) + { + const Token& t = Peek(); + ExpectNot(t, TokenType::EndOfStream); + + if (t.type == TokenType::ClosingAttribute) + { + // Parse [[attribute1]] [[attribute2]] the same as [[attribute1, attribute2]] + if (Peek(1).type == TokenType::OpenAttribute) + { + Consume(2); + expectComma = false; + continue; + } + + break; + } + + if (expectComma) + Expect(Advance(), TokenType::Comma); + + ShaderAst::AttributeType attributeType = ParseIdentifierAsAttributeType(); + + ShaderAst::Attribute::Param arg; + if (Peek().type == TokenType::OpenParenthesis) + { + Consume(); + + const Token& n = Peek(); + if (n.type == TokenType::Identifier) + { + arg = std::get(n.data); + Consume(); + } + else if (n.type == TokenType::IntegerValue) + { + arg = std::get(n.data); + Consume(); + } + + Expect(Advance(), TokenType::ClosingParenthesis); + } + + expectComma = true; + + attributes.push_back({ + attributeType, + std::move(arg) + }); + } + + Expect(Advance(), TokenType::ClosingAttribute); + + return attributes; + } + + ShaderAst::StatementPtr Parser::ParseExternalBlock(std::vector attributes) { - assert(m_context->tokenIndex + advance < m_context->tokenCount); - return m_context->tokens[m_context->tokenIndex + advance]; - } - - std::vector Parser::ParseAttributes() - { - std::vector attributes; - - Expect(Advance(), TokenType::OpenAttribute); - - bool expectComma = false; - for (;;) - { - const Token& t = Peek(); - ExpectNot(t, TokenType::EndOfStream); - - if (t.type == TokenType::ClosingAttribute) - { - // Parse [[attribute1]] [[attribute2]] the same as [[attribute1, attribute2]] - if (Peek(1).type == TokenType::OpenAttribute) - { - Consume(2); - expectComma = false; - continue; - } - - break; - } - - if (expectComma) - Expect(Advance(), TokenType::Comma); - - ShaderAst::AttributeType attributeType = ParseIdentifierAsAttributeType(); - - ShaderAst::Attribute::Param arg; - if (Peek().type == TokenType::OpenParenthesis) - { - Consume(); - - const Token& n = Peek(); - if (n.type == TokenType::Identifier) - { - arg = std::get(n.data); - Consume(); - } - else if (n.type == TokenType::IntegerValue) - { - arg = std::get(n.data); - Consume(); - } - - Expect(Advance(), TokenType::ClosingParenthesis); - } - - expectComma = true; - - attributes.push_back({ - attributeType, - std::move(arg) - }); - } - - Expect(Advance(), TokenType::ClosingAttribute); - - return attributes; - } - - ShaderAst::StatementPtr Parser::ParseExternalBlock(std::vector attributes) - { - Expect(Advance(), TokenType::External); - Expect(Advance(), TokenType::OpenCurlyBracket); - - std::unique_ptr externalStatement = std::make_unique(); - externalStatement->attributes = std::move(attributes); - - bool first = true; - - for (;;) - { - if (!first) - { - const Token& nextToken = Peek(); - if (nextToken.type == TokenType::Comma) - Consume(); - else - { - Expect(nextToken, TokenType::ClosingCurlyBracket); - break; - } - } - - first = false; - - const Token& token = Peek(); - if (token.type == TokenType::ClosingCurlyBracket) - break; - - auto& extVar = externalStatement->externalVars.emplace_back(); - + if (!attributes.empty()) + throw AttributeError{ "unhandled attribute for external block" }; + + Expect(Advance(), TokenType::External); + Expect(Advance(), TokenType::OpenCurlyBracket); + + std::unique_ptr externalStatement = std::make_unique(); + + bool first = true; + for (;;) + { + if (!first) + { + const Token& nextToken = Peek(); + if (nextToken.type == TokenType::Comma) + Consume(); + else + { + Expect(nextToken, TokenType::ClosingCurlyBracket); + break; + } + } + + first = false; + + const Token& token = Peek(); + if (token.type == TokenType::ClosingCurlyBracket) + break; + + auto& extVar = externalStatement->externalVars.emplace_back(); + if (token.type == TokenType::OpenAttribute) - extVar.attributes = ParseAttributes(); + { + for (const auto& [attributeType, arg] : ParseAttributes()) + { + switch (attributeType) + { + case ShaderAst::AttributeType::Binding: + { + if (extVar.bindingIndex) + throw AttributeError{ "attribute binding must be present once" }; - extVar.name = ParseIdentifierAsName(); - Expect(Advance(), TokenType::Colon); + if (!std::holds_alternative(arg)) + throw AttributeError{ "attribute binding requires a string parameter" }; + + std::optional bindingIndex = BoundCast(std::get(arg)); + if (!bindingIndex) + throw AttributeError{ "invalid binding index" }; + + extVar.bindingIndex = bindingIndex.value(); + break; + } + + default: + throw AttributeError{ "unhandled attribute for external variable" }; + } + } + } + + extVar.name = ParseIdentifierAsName(); + Expect(Advance(), TokenType::Colon); extVar.type = ParseType(); - RegisterVariable(extVar.name); - } + RegisterVariable(extVar.name); + } + + Expect(Advance(), TokenType::ClosingCurlyBracket); + + return externalStatement; + } + + std::vector Parser::ParseFunctionBody() + { + return ParseStatementList(); + } + + ShaderAst::StatementPtr Parser::ParseFunctionDeclaration(std::vector attributes) + { + Expect(Advance(), TokenType::FunctionDeclaration); + + std::string functionName = ParseIdentifierAsName(); + + Expect(Advance(), TokenType::OpenParenthesis); + + std::vector parameters; + + bool firstParameter = true; + for (;;) + { + const Token& t = Peek(); + ExpectNot(t, TokenType::EndOfStream); + + if (t.type == TokenType::ClosingParenthesis) + break; + + if (!firstParameter) + Expect(Advance(), TokenType::Comma); + + parameters.push_back(ParseFunctionParameter()); + firstParameter = false; + } + + Expect(Advance(), TokenType::ClosingParenthesis); + + ShaderAst::ExpressionType returnType; + if (Peek().type == TokenType::FunctionReturn) + { + Consume(); + returnType = ParseType(); + } + + Expect(Advance(), TokenType::OpenCurlyBracket); - Expect(Advance(), TokenType::ClosingCurlyBracket); - - return externalStatement; - } - - std::vector Parser::ParseFunctionBody() - { - return ParseStatementList(); - } - - ShaderAst::StatementPtr Parser::ParseFunctionDeclaration(std::vector attributes) - { - Expect(Advance(), TokenType::FunctionDeclaration); - - std::string functionName = ParseIdentifierAsName(); - - Expect(Advance(), TokenType::OpenParenthesis); - - std::vector parameters; - - bool firstParameter = true; - for (;;) - { - const Token& t = Peek(); - ExpectNot(t, TokenType::EndOfStream); - - if (t.type == TokenType::ClosingParenthesis) - break; - - if (!firstParameter) - Expect(Advance(), TokenType::Comma); - - parameters.push_back(ParseFunctionParameter()); - firstParameter = false; - } - - Expect(Advance(), TokenType::ClosingParenthesis); - - ShaderAst::ExpressionType returnType; - if (Peek().type == TokenType::FunctionReturn) - { - Consume(); - returnType = ParseType(); - } - - Expect(Advance(), TokenType::OpenCurlyBracket); - - EnterScope(); + EnterScope(); for (const auto& parameter : parameters) RegisterVariable(parameter.name); + + std::vector functionBody = ParseFunctionBody(); - std::vector functionBody = ParseFunctionBody(); - - LeaveScope(); - - Expect(Advance(), TokenType::ClosingCurlyBracket); - - return ShaderBuilder::DeclareFunction(std::move(attributes), std::move(functionName), std::move(parameters), std::move(functionBody), std::move(returnType)); - } - - ShaderAst::DeclareFunctionStatement::Parameter Parser::ParseFunctionParameter() - { - std::string parameterName = ParseIdentifierAsName(); - - Expect(Advance(), TokenType::Colon); - - ShaderAst::ExpressionType parameterType = ParseType(); - - return { parameterName, parameterType }; - } - - ShaderAst::StatementPtr Parser::ParseStructDeclaration(std::vector attributes) - { - Expect(Advance(), TokenType::Struct); - - ShaderAst::StructDescription description; - description.name = ParseIdentifierAsName(); - - Expect(Advance(), TokenType::OpenCurlyBracket); - - bool first = true; - - for (;;) - { - if (!first) - { - const Token& nextToken = Peek(); - if (nextToken.type == TokenType::Comma) - Consume(); - else - { - Expect(nextToken, TokenType::ClosingCurlyBracket); - break; - } - } - - first = false; - - const Token& token = Peek(); - if (token.type == TokenType::ClosingCurlyBracket) - break; - - auto& structField = description.members.emplace_back(); + LeaveScope(); + + Expect(Advance(), TokenType::ClosingCurlyBracket); + std::optional entryPoint; + for (const auto& [attributeType, arg] : attributes) + { + switch (attributeType) + { + case ShaderAst::AttributeType::Entry: + { + if (entryPoint) + throw AttributeError{ "attribute entry must be present once" }; + + if (!std::holds_alternative(arg)) + throw AttributeError{ "attribute entry requires a string parameter" }; + + const std::string& argStr = std::get(arg); + + auto it = s_entryPoints.find(argStr); + if (it == s_entryPoints.end()) + throw AttributeError{ ("invalid parameter " + argStr + " for entry attribute").c_str() }; + + entryPoint = it->second; + break; + } + + default: + throw AttributeError{ "unhandled attribute for function" }; + } + } + + return ShaderBuilder::DeclareFunction(entryPoint, std::move(functionName), std::move(parameters), std::move(functionBody), std::move(returnType)); + } + + ShaderAst::DeclareFunctionStatement::Parameter Parser::ParseFunctionParameter() + { + std::string parameterName = ParseIdentifierAsName(); + + Expect(Advance(), TokenType::Colon); + + ShaderAst::ExpressionType parameterType = ParseType(); + + return { parameterName, parameterType }; + } + + ShaderAst::StatementPtr Parser::ParseStructDeclaration(std::vector attributes) + { + Expect(Advance(), TokenType::Struct); + + ShaderAst::StructDescription description; + description.name = ParseIdentifierAsName(); + + Expect(Advance(), TokenType::OpenCurlyBracket); + + bool first = true; + + for (;;) + { + if (!first) + { + const Token& nextToken = Peek(); + if (nextToken.type == TokenType::Comma) + Consume(); + else + { + Expect(nextToken, TokenType::ClosingCurlyBracket); + break; + } + } + + first = false; + + const Token& token = Peek(); + if (token.type == TokenType::ClosingCurlyBracket) + break; + + auto& structField = description.members.emplace_back(); + if (token.type == TokenType::OpenAttribute) - structField.attributes = ParseAttributes(); - - structField.name = ParseIdentifierAsName(); - - Expect(Advance(), TokenType::Colon); - - structField.type = ParseType(); - } - - Expect(Advance(), TokenType::ClosingCurlyBracket); - - return ShaderBuilder::DeclareStruct(std::move(attributes), std::move(description)); - } - - ShaderAst::StatementPtr Parser::ParseReturnStatement() - { - Expect(Advance(), TokenType::Return); - - ShaderAst::ExpressionPtr expr; - if (Peek().type != TokenType::Semicolon) - expr = ParseExpression(); - - return ShaderBuilder::Return(std::move(expr)); - } - - ShaderAst::StatementPtr Parser::ParseStatement() - { - const Token& token = Peek(); - - ShaderAst::StatementPtr statement; - switch (token.type) - { - case TokenType::Let: - statement = ParseVariableDeclaration(); - break; - - case TokenType::Identifier: - statement = ShaderBuilder::ExpressionStatement(ParseVariableAssignation()); - break; - - case TokenType::Return: - statement = ParseReturnStatement(); - break; - - default: - break; - } - - Expect(Advance(), TokenType::Semicolon); - - return statement; - } - - std::vector Parser::ParseStatementList() - { - std::vector statements; - while (Peek().type != TokenType::ClosingCurlyBracket) - { - ExpectNot(Peek(), TokenType::EndOfStream); - statements.push_back(ParseStatement()); - } - - return statements; - } - - ShaderAst::ExpressionPtr Parser::ParseVariableAssignation() - { - ShaderAst::ExpressionPtr left = ParseIdentifier(); - Expect(Advance(), TokenType::Assign); - - ShaderAst::ExpressionPtr right = ParseExpression(); - - return ShaderBuilder::Assign(ShaderAst::AssignType::Simple, std::move(left), std::move(right)); - } - - ShaderAst::StatementPtr Parser::ParseVariableDeclaration() - { - Expect(Advance(), TokenType::Let); - - std::string variableName = ParseIdentifierAsName(); - RegisterVariable(variableName); - - Expect(Advance(), TokenType::Colon); - - ShaderAst::ExpressionType variableType = ParseType(); - - ShaderAst::ExpressionPtr expression; - if (Peek().type == TokenType::Assign) - { - Consume(); - expression = ParseExpression(); - } - - return ShaderBuilder::DeclareVariable(std::move(variableName), std::move(variableType), std::move(expression)); - } - - ShaderAst::ExpressionPtr Parser::ParseBinOpRhs(int exprPrecedence, ShaderAst::ExpressionPtr lhs) - { - for (;;) - { - const Token& currentOp = Peek(); - ExpectNot(currentOp, TokenType::EndOfStream); - - int tokenPrecedence = GetTokenPrecedence(currentOp.type); - if (tokenPrecedence < exprPrecedence) - return lhs; - - Consume(); - ShaderAst::ExpressionPtr rhs = ParsePrimaryExpression(); - - const Token& nextOp = Peek(); - - int nextTokenPrecedence = GetTokenPrecedence(nextOp.type); - if (tokenPrecedence < nextTokenPrecedence) - rhs = ParseBinOpRhs(tokenPrecedence + 1, std::move(rhs)); - - ShaderAst::BinaryType binaryType; { - switch (currentOp.type) + for (const auto& [attributeType, attributeParam] : ParseAttributes()) { - case TokenType::Plus: binaryType = ShaderAst::BinaryType::Add; break; - case TokenType::Minus: binaryType = ShaderAst::BinaryType::Subtract; break; - case TokenType::Multiply: binaryType = ShaderAst::BinaryType::Multiply; break; - case TokenType::Divide: binaryType = ShaderAst::BinaryType::Divide; break; - default: throw UnexpectedToken{}; + switch (attributeType) + { + case ShaderAst::AttributeType::Builtin: + { + if (structField.builtin) + throw AttributeError{ "attribute builtin must be present once" }; + + auto it = s_builtinMapping.find(std::get(attributeParam)); + if (it == s_builtinMapping.end()) + throw AttributeError{ "unknown builtin" }; + + structField.builtin = it->second; + break; + } + + case ShaderAst::AttributeType::Location: + { + if (structField.locationIndex) + throw AttributeError{ "attribute location must be present once" }; + + structField.locationIndex = BoundCast(std::get(attributeParam)); + if (!structField.locationIndex) + throw AttributeError{ "invalid location index" }; + + break; + } + } } - } - lhs = ShaderBuilder::Binary(binaryType, std::move(lhs), std::move(rhs)); - } - } - - ShaderAst::ExpressionPtr Parser::ParseExpression() - { - return ParseBinOpRhs(0, ParsePrimaryExpression()); - } - - ShaderAst::ExpressionPtr Parser::ParseFloatingPointExpression(bool minus) - { - const Token& floatingPointToken = Expect(Advance(), TokenType::FloatingPointValue); - return ShaderBuilder::Constant(((minus) ? -1.f : 1.f) * float(std::get(floatingPointToken.data))); //< FIXME - } - - ShaderAst::ExpressionPtr Parser::ParseIdentifier() - { - const Token& identifierToken = Expect(Advance(), TokenType::Identifier); - const std::string& identifier = std::get(identifierToken.data); - - ShaderAst::ExpressionPtr identifierExpr = ShaderBuilder::Identifier(identifier); - - if (Peek().type == TokenType::Dot) - { - std::unique_ptr accessMemberNode = std::make_unique(); - accessMemberNode->structExpr = std::move(identifierExpr); - - do - { - Consume(); - - accessMemberNode->memberIdentifiers.push_back(ParseIdentifierAsName()); - } while (Peek().type == TokenType::Dot); - - identifierExpr = std::move(accessMemberNode); - } - - return identifierExpr; - } - - ShaderAst::ExpressionPtr Parser::ParseIntegerExpression(bool minus) - { - const Token& integerToken = Expect(Advance(), TokenType::IntegerValue); - return ShaderBuilder::Constant(((minus) ? -1 : 1) * static_cast(std::get(integerToken.data))); - } - - std::vector Parser::ParseParameters() - { + if (structField.builtin && structField.locationIndex) + throw AttributeError{ "A struct field cannot have both builtin and location attributes" }; + } + + structField.name = ParseIdentifierAsName(); + + Expect(Advance(), TokenType::Colon); + + structField.type = ParseType(); + } + + Expect(Advance(), TokenType::ClosingCurlyBracket); + + return ShaderBuilder::DeclareStruct(std::move(description)); + } + + ShaderAst::StatementPtr Parser::ParseReturnStatement() + { + Expect(Advance(), TokenType::Return); + + ShaderAst::ExpressionPtr expr; + if (Peek().type != TokenType::Semicolon) + expr = ParseExpression(); + + return ShaderBuilder::Return(std::move(expr)); + } + + ShaderAst::StatementPtr Parser::ParseStatement() + { + const Token& token = Peek(); + + ShaderAst::StatementPtr statement; + switch (token.type) + { + case TokenType::Let: + statement = ParseVariableDeclaration(); + break; + + case TokenType::Identifier: + statement = ShaderBuilder::ExpressionStatement(ParseVariableAssignation()); + break; + + case TokenType::Return: + statement = ParseReturnStatement(); + break; + + default: + break; + } + + Expect(Advance(), TokenType::Semicolon); + + return statement; + } + + std::vector Parser::ParseStatementList() + { + std::vector statements; + while (Peek().type != TokenType::ClosingCurlyBracket) + { + ExpectNot(Peek(), TokenType::EndOfStream); + statements.push_back(ParseStatement()); + } + + return statements; + } + + ShaderAst::ExpressionPtr Parser::ParseVariableAssignation() + { + ShaderAst::ExpressionPtr left = ParseIdentifier(); + Expect(Advance(), TokenType::Assign); + + ShaderAst::ExpressionPtr right = ParseExpression(); + + return ShaderBuilder::Assign(ShaderAst::AssignType::Simple, std::move(left), std::move(right)); + } + + ShaderAst::StatementPtr Parser::ParseVariableDeclaration() + { + Expect(Advance(), TokenType::Let); + + std::string variableName = ParseIdentifierAsName(); + RegisterVariable(variableName); + + Expect(Advance(), TokenType::Colon); + + ShaderAst::ExpressionType variableType = ParseType(); + + ShaderAst::ExpressionPtr expression; + if (Peek().type == TokenType::Assign) + { + Consume(); + expression = ParseExpression(); + } + + return ShaderBuilder::DeclareVariable(std::move(variableName), std::move(variableType), std::move(expression)); + } + + ShaderAst::ExpressionPtr Parser::ParseBinOpRhs(int exprPrecedence, ShaderAst::ExpressionPtr lhs) + { + for (;;) + { + const Token& currentOp = Peek(); + ExpectNot(currentOp, TokenType::EndOfStream); + + int tokenPrecedence = GetTokenPrecedence(currentOp.type); + if (tokenPrecedence < exprPrecedence) + return lhs; + + Consume(); + ShaderAst::ExpressionPtr rhs = ParsePrimaryExpression(); + + const Token& nextOp = Peek(); + + int nextTokenPrecedence = GetTokenPrecedence(nextOp.type); + if (tokenPrecedence < nextTokenPrecedence) + rhs = ParseBinOpRhs(tokenPrecedence + 1, std::move(rhs)); + + ShaderAst::BinaryType binaryType; + { + switch (currentOp.type) + { + case TokenType::Plus: binaryType = ShaderAst::BinaryType::Add; break; + case TokenType::Minus: binaryType = ShaderAst::BinaryType::Subtract; break; + case TokenType::Multiply: binaryType = ShaderAst::BinaryType::Multiply; break; + case TokenType::Divide: binaryType = ShaderAst::BinaryType::Divide; break; + default: throw UnexpectedToken{}; + } + } + + lhs = ShaderBuilder::Binary(binaryType, std::move(lhs), std::move(rhs)); + } + } + + ShaderAst::ExpressionPtr Parser::ParseExpression() + { + return ParseBinOpRhs(0, ParsePrimaryExpression()); + } + + ShaderAst::ExpressionPtr Parser::ParseFloatingPointExpression(bool minus) + { + const Token& floatingPointToken = Expect(Advance(), TokenType::FloatingPointValue); + return ShaderBuilder::Constant(((minus) ? -1.f : 1.f) * float(std::get(floatingPointToken.data))); //< FIXME + } + + ShaderAst::ExpressionPtr Parser::ParseIdentifier() + { + const Token& identifierToken = Expect(Advance(), TokenType::Identifier); + const std::string& identifier = std::get(identifierToken.data); + + ShaderAst::ExpressionPtr identifierExpr = ShaderBuilder::Identifier(identifier); + + if (Peek().type == TokenType::Dot) + { + std::unique_ptr accessMemberNode = std::make_unique(); + accessMemberNode->structExpr = std::move(identifierExpr); + + do + { + Consume(); + + accessMemberNode->memberIdentifiers.push_back(ParseIdentifierAsName()); + } while (Peek().type == TokenType::Dot); + + identifierExpr = std::move(accessMemberNode); + } + + return identifierExpr; + } + + ShaderAst::ExpressionPtr Parser::ParseIntegerExpression(bool minus) + { + const Token& integerToken = Expect(Advance(), TokenType::IntegerValue); + return ShaderBuilder::Constant(((minus) ? -1 : 1) * static_cast(std::get(integerToken.data))); + } + + std::vector Parser::ParseParameters() + { Expect(Advance(), TokenType::OpenParenthesis); std::vector parameters; @@ -638,37 +744,37 @@ namespace Nz::ShaderLang first = false; parameters.push_back(ParseExpression()); } - + Expect(Advance(), TokenType::ClosingParenthesis); return parameters; - } - - ShaderAst::ExpressionPtr Parser::ParseParenthesisExpression() - { - Expect(Advance(), TokenType::OpenParenthesis); - ShaderAst::ExpressionPtr expression = ParseExpression(); - Expect(Advance(), TokenType::ClosingParenthesis); - - return expression; - } - - ShaderAst::ExpressionPtr Parser::ParsePrimaryExpression() - { - const Token& token = Peek(); - switch (token.type) - { - case TokenType::BoolFalse: - Consume(); - return ShaderBuilder::Constant(false); - - case TokenType::BoolTrue: - Consume(); - return ShaderBuilder::Constant(true); - - case TokenType::FloatingPointValue: - return ParseFloatingPointExpression(); - + } + + ShaderAst::ExpressionPtr Parser::ParseParenthesisExpression() + { + Expect(Advance(), TokenType::OpenParenthesis); + ShaderAst::ExpressionPtr expression = ParseExpression(); + Expect(Advance(), TokenType::ClosingParenthesis); + + return expression; + } + + ShaderAst::ExpressionPtr Parser::ParsePrimaryExpression() + { + const Token& token = Peek(); + switch (token.type) + { + case TokenType::BoolFalse: + Consume(); + return ShaderBuilder::Constant(false); + + case TokenType::BoolTrue: + Consume(); + return ShaderBuilder::Constant(true); + + case TokenType::FloatingPointValue: + return ParseFloatingPointExpression(); + case TokenType::Identifier: { const std::string& identifier = std::get(token.data); @@ -682,7 +788,7 @@ namespace Nz::ShaderLang } } - if (IsVariableInScope(identifier)) + if (IsVariableInScope(identifier)) { auto node = ParseIdentifier(); if (node->GetType() == ShaderAst::NodeType::AccessMemberIdentifierExpression) @@ -707,11 +813,11 @@ namespace Nz::ShaderLang ShaderAst::ExpressionType exprType = DecodeType(identifier); - return ShaderBuilder::Cast(std::move(exprType), ParseParameters()); - } - - case TokenType::IntegerValue: - return ParseIntegerExpression(); + return ShaderBuilder::Cast(std::move(exprType), ParseParameters()); + } + + case TokenType::IntegerValue: + return ParseIntegerExpression(); case TokenType::Minus: //< FIXME: Handle this with an unary node @@ -729,78 +835,78 @@ namespace Nz::ShaderLang throw UnexpectedToken{}; break; + + case TokenType::OpenParenthesis: + return ParseParenthesisExpression(); + + default: + throw UnexpectedToken{}; + } + } + + ShaderAst::AttributeType Parser::ParseIdentifierAsAttributeType() + { + const Token& identifierToken = Expect(Advance(), TokenType::Identifier); + const std::string& identifier = std::get(identifierToken.data); + + auto it = identifierToAttributeType.find(identifier); + if (it == identifierToAttributeType.end()) + throw UnknownAttribute{}; + + return it->second; + } + + const std::string& Parser::ParseIdentifierAsName() + { + const Token& identifierToken = Expect(Advance(), TokenType::Identifier); + const std::string& identifier = std::get(identifierToken.data); + + auto it = identifierToBasicType.find(identifier); + if (it != identifierToBasicType.end()) + throw ReservedKeyword{}; + + return identifier; + } + + ShaderAst::PrimitiveType Parser::ParsePrimitiveType() + { + const Token& identifierToken = Expect(Advance(), TokenType::Identifier); + const std::string& identifier = std::get(identifierToken.data); + + auto it = identifierToBasicType.find(identifier); + if (it == identifierToBasicType.end()) + throw UnknownType{}; + + return it->second; + } + + ShaderAst::ExpressionType Parser::ParseType() + { + // Handle () as no type + if (Peek().type == TokenType::OpenParenthesis) + { + Consume(); + Expect(Advance(), TokenType::ClosingParenthesis); + + return ShaderAst::NoType{}; + } + + const Token& identifierToken = Expect(Advance(), TokenType::Identifier); + const std::string& identifier = std::get(identifierToken.data); - case TokenType::OpenParenthesis: - return ParseParenthesisExpression(); - - default: - throw UnexpectedToken{}; - } - } - - ShaderAst::AttributeType Parser::ParseIdentifierAsAttributeType() - { - const Token& identifierToken = Expect(Advance(), TokenType::Identifier); - const std::string& identifier = std::get(identifierToken.data); - - auto it = identifierToAttributeType.find(identifier); - if (it == identifierToAttributeType.end()) - throw UnknownAttribute{}; - - return it->second; - } - - const std::string& Parser::ParseIdentifierAsName() - { - const Token& identifierToken = Expect(Advance(), TokenType::Identifier); - const std::string& identifier = std::get(identifierToken.data); - - auto it = identifierToBasicType.find(identifier); - if (it != identifierToBasicType.end()) - throw ReservedKeyword{}; - - return identifier; - } - - ShaderAst::PrimitiveType Parser::ParsePrimitiveType() - { - const Token& identifierToken = Expect(Advance(), TokenType::Identifier); - const std::string& identifier = std::get(identifierToken.data); - - auto it = identifierToBasicType.find(identifier); - if (it == identifierToBasicType.end()) - throw UnknownType{}; - - return it->second; - } - - ShaderAst::ExpressionType Parser::ParseType() - { - // Handle () as no type - if (Peek().type == TokenType::OpenParenthesis) - { - Consume(); - Expect(Advance(), TokenType::ClosingParenthesis); - - return ShaderAst::NoType{}; - } - - const Token& identifierToken = Expect(Advance(), TokenType::Identifier); - const std::string& identifier = std::get(identifierToken.data); - - return DecodeType(identifier); - } - - int Parser::GetTokenPrecedence(TokenType token) - { - switch (token) - { - case TokenType::Plus: return 20; - case TokenType::Divide: return 40; - case TokenType::Multiply: return 40; - case TokenType::Minus: return 20; - default: return -1; - } - } - -} + return DecodeType(identifier); + } + + int Parser::GetTokenPrecedence(TokenType token) + { + switch (token) + { + case TokenType::Plus: return 20; + case TokenType::Divide: return 40; + case TokenType::Multiply: return 40; + case TokenType::Minus: return 20; + default: return -1; + } + } + +} diff --git a/src/Nazara/Shader/SpirvWriter.cpp b/src/Nazara/Shader/SpirvWriter.cpp index 82a1d360c..af994659a 100644 --- a/src/Nazara/Shader/SpirvWriter.cpp +++ b/src/Nazara/Shader/SpirvWriter.cpp @@ -27,12 +27,6 @@ namespace Nz { namespace { - //FIXME: Have this only once - std::unordered_map s_entryPoints = { - { "frag", ShaderStageType::Fragment }, - { "vert", ShaderStageType::Vertex }, - }; - struct Builtin { const char* debugName; @@ -40,8 +34,8 @@ namespace Nz SpirvBuiltIn decoration; }; - std::unordered_map s_builtinMapping = { - { "position", { "VertexPosition", ShaderStageType::Vertex, SpirvBuiltIn::Position } } + std::unordered_map s_builtinMapping = { + { ShaderAst::BuiltinEntry::VertexPosition, { "VertexPosition", ShaderStageType::Vertex, SpirvBuiltIn::Position } } }; class PreVisitor : public ShaderAst::AstScopedVisitor @@ -129,32 +123,13 @@ namespace Nz UniformVar& uniformVar = extVars[varIndex++]; uniformVar.pointerId = m_constantCache.Register(variable); - - for (const auto& [attributeType, attributeParam] : extVar.attributes) - { - if (attributeType == ShaderAst::AttributeType::Binding) - { - uniformVar.bindingIndex = std::get(attributeParam); - break; - } - } + uniformVar.bindingIndex = extVar.bindingIndex; } } void Visit(ShaderAst::DeclareFunctionStatement& node) override { - std::optional entryPointType; - for (auto& attribute : node.attributes) - { - if (attribute.type == ShaderAst::AttributeType::Entry) - { - auto it = s_entryPoints.find(std::get(attribute.args)); - assert(it != s_entryPoints.end()); - - entryPointType = it->second; - break; - } - } + std::optional entryPointType = node.entryStage; assert(node.funcIndex); std::size_t funcIndex = *node.funcIndex; @@ -325,29 +300,12 @@ namespace Nz UInt32 HandleEntryInOutType(ShaderStageType entryPointType, std::size_t funcIndex, const ShaderAst::StructDescription::StructMember& member, SpirvStorageClass storageClass) { - std::optional> builtinOpt; - std::optional attributeLocation; - for (const auto& [attributeType, attributeParam] : member.attributes) + if (member.builtin) { - if (attributeType == ShaderAst::AttributeType::Builtin) - { - auto it = s_builtinMapping.find(std::get(attributeParam)); - if (it != s_builtinMapping.end()) - { - builtinOpt = it->second; - break; - } - } - else if (attributeType == ShaderAst::AttributeType::Location) - { - attributeLocation = std::get(attributeParam); - break; - } - } + auto it = s_builtinMapping.find(*member.builtin); + assert(it != s_builtinMapping.end()); - if (builtinOpt) - { - Builtin& builtin = *builtinOpt; + Builtin& builtin = it->second; if ((builtin.compatibleStages & entryPointType) == 0) return 0; @@ -364,7 +322,7 @@ namespace Nz return varId; } - else if (attributeLocation) + else if (member.locationIndex) { SpirvConstantCache::Variable variable; variable.debugName = member.name; @@ -373,7 +331,7 @@ namespace Nz variable.type = m_constantCache.BuildPointerType(member.type, storageClass); UInt32 varId = m_constantCache.Register(variable); - locationDecorations[varId] = *attributeLocation; + locationDecorations[varId] = *member.locationIndex; return varId; }