From 4465e230af6db416bd8c5b40bcba4a0574f69f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Tue, 1 Jun 2021 16:22:41 +0200 Subject: [PATCH] Shader/NZSL: Add support for array indexing --- bin/resources/lighting.nzsl | 2 +- include/Nazara/Shader/Ast/Nodes.hpp | 4 +- include/Nazara/Shader/Ast/SanitizeVisitor.hpp | 4 +- include/Nazara/Shader/GlslWriter.hpp | 2 +- include/Nazara/Shader/LangWriter.hpp | 2 +- include/Nazara/Shader/ShaderBuilder.hpp | 3 +- include/Nazara/Shader/ShaderBuilder.inl | 18 +- src/Nazara/Shader/Ast/AstCloner.cpp | 7 +- src/Nazara/Shader/Ast/AstRecursiveVisitor.cpp | 2 + src/Nazara/Shader/Ast/AstSerializer.cpp | 10 +- src/Nazara/Shader/Ast/SanitizeVisitor.cpp | 235 ++- src/Nazara/Shader/GlslWriter.cpp | 23 +- src/Nazara/Shader/LangWriter.cpp | 23 +- src/Nazara/Shader/ShaderLangParser.cpp | 1751 +++++++++-------- src/Nazara/Shader/SpirvExpressionLoad.cpp | 17 +- src/Nazara/Shader/SpirvExpressionStore.cpp | 9 +- src/Nazara/Shader/SpirvWriter.cpp | 3 - 17 files changed, 1139 insertions(+), 976 deletions(-) diff --git a/bin/resources/lighting.nzsl b/bin/resources/lighting.nzsl index b74d428ea..c3366facb 100644 --- a/bin/resources/lighting.nzsl +++ b/bin/resources/lighting.nzsl @@ -70,7 +70,7 @@ struct VertOut [entry(frag)] fn main(input: FragIn) -> FragOut { - let fragcoord = (input.fragcoord).xy * viewerData.invRenderTargetSize; + let fragcoord = input.fragcoord.xy * viewerData.invRenderTargetSize; let normal = normalTexture.Sample(fragcoord).xyz * 2.0 - vec3(1.0, 1.0, 1.0); let position = positionTexture.Sample(fragcoord).xyz; diff --git a/include/Nazara/Shader/Ast/Nodes.hpp b/include/Nazara/Shader/Ast/Nodes.hpp index 5d06cd862..9a9974a0b 100644 --- a/include/Nazara/Shader/Ast/Nodes.hpp +++ b/include/Nazara/Shader/Ast/Nodes.hpp @@ -70,7 +70,7 @@ namespace Nz::ShaderAst void Visit(AstExpressionVisitor& visitor) override; ExpressionPtr expr; - std::vector memberIdentifiers; + std::vector identifiers; }; struct NAZARA_SHADER_API AccessIndexExpression : public Expression @@ -79,7 +79,7 @@ namespace Nz::ShaderAst void Visit(AstExpressionVisitor& visitor) override; ExpressionPtr expr; - std::vector memberIndices; + std::vector indices; }; struct NAZARA_SHADER_API AssignExpression : public Expression diff --git a/include/Nazara/Shader/Ast/SanitizeVisitor.hpp b/include/Nazara/Shader/Ast/SanitizeVisitor.hpp index 54428455c..8c3afb17b 100644 --- a/include/Nazara/Shader/Ast/SanitizeVisitor.hpp +++ b/include/Nazara/Shader/Ast/SanitizeVisitor.hpp @@ -43,11 +43,10 @@ namespace Nz::ShaderAst struct FunctionData; 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(AccessIdentifierExpression& node) override; + ExpressionPtr Clone(AccessIndexExpression& node) override; ExpressionPtr Clone(AssignExpression& node) override; ExpressionPtr Clone(BinaryExpression& node) override; ExpressionPtr Clone(CallFunctionExpression& node) override; @@ -101,6 +100,7 @@ namespace Nz::ShaderAst void SanitizeIdentifier(std::string& identifier); + void Validate(AccessIndexExpression& node); void Validate(CallFunctionExpression& node, const DeclareFunctionStatement* referenceDeclaration); void Validate(IntrinsicExpression& node); diff --git a/include/Nazara/Shader/GlslWriter.hpp b/include/Nazara/Shader/GlslWriter.hpp index cbe1e58c9..72cc2d768 100644 --- a/include/Nazara/Shader/GlslWriter.hpp +++ b/include/Nazara/Shader/GlslWriter.hpp @@ -62,7 +62,7 @@ namespace Nz template void Append(const T1& firstParam, const T2& secondParam, Args&&... params); void AppendCommentSection(const std::string& section); void AppendFunctionDeclaration(const ShaderAst::DeclareFunctionStatement& node, bool forward = false); - void AppendField(std::size_t structIndex, const std::size_t* memberIndices, std::size_t remainingMembers); + void AppendField(std::size_t structIndex, const ShaderAst::ExpressionPtr* memberIndices, std::size_t remainingMembers); void AppendHeader(); void AppendLine(const std::string& txt = {}); template void AppendLine(Args&&... params); diff --git a/include/Nazara/Shader/LangWriter.hpp b/include/Nazara/Shader/LangWriter.hpp index a3fc8f7f1..ae25399db 100644 --- a/include/Nazara/Shader/LangWriter.hpp +++ b/include/Nazara/Shader/LangWriter.hpp @@ -65,7 +65,7 @@ namespace Nz void AppendAttribute(LayoutAttribute layout); void AppendAttribute(LocationAttribute location); void AppendCommentSection(const std::string& section); - void AppendField(std::size_t structIndex, const std::size_t* memberIndices, std::size_t remainingMembers); + void AppendField(std::size_t structIndex, const ShaderAst::ExpressionPtr* memberIndices, std::size_t remainingMembers); void AppendHeader(); void AppendLine(const std::string& txt = {}); template void AppendLine(Args&&... params); diff --git a/include/Nazara/Shader/ShaderBuilder.hpp b/include/Nazara/Shader/ShaderBuilder.hpp index cacfec9ed..d4fd91862 100644 --- a/include/Nazara/Shader/ShaderBuilder.hpp +++ b/include/Nazara/Shader/ShaderBuilder.hpp @@ -19,7 +19,8 @@ namespace Nz::ShaderBuilder { struct AccessIndex { - inline std::unique_ptr operator()(ShaderAst::ExpressionPtr expr, std::vector memberIndices) const; + inline std::unique_ptr operator()(ShaderAst::ExpressionPtr expr, const std::vector& indexConstants) const; + inline std::unique_ptr operator()(ShaderAst::ExpressionPtr expr, std::vector indexExpressions) const; }; struct AccessMember diff --git a/include/Nazara/Shader/ShaderBuilder.inl b/include/Nazara/Shader/ShaderBuilder.inl index bbe87c6a0..9b9ecc510 100644 --- a/include/Nazara/Shader/ShaderBuilder.inl +++ b/include/Nazara/Shader/ShaderBuilder.inl @@ -11,16 +11,28 @@ namespace Nz::ShaderBuilder { auto accessMemberNode = std::make_unique(); accessMemberNode->expr = std::move(expr); - accessMemberNode->memberIdentifiers = std::move(memberIdentifiers); + accessMemberNode->identifiers = std::move(memberIdentifiers); return accessMemberNode; } - inline std::unique_ptr Impl::AccessIndex::operator()(ShaderAst::ExpressionPtr expr, std::vector memberIndices) const + inline std::unique_ptr Impl::AccessIndex::operator()(ShaderAst::ExpressionPtr expr, const std::vector& indexConstants) const { auto accessMemberNode = std::make_unique(); accessMemberNode->expr = std::move(expr); - accessMemberNode->memberIndices = std::move(memberIndices); + + accessMemberNode->indices.reserve(indexConstants.size()); + for (Int32 index : indexConstants) + accessMemberNode->indices.push_back(ShaderBuilder::Constant(index)); + + return accessMemberNode; + } + + inline std::unique_ptr Impl::AccessIndex::operator()(ShaderAst::ExpressionPtr expr, std::vector indexExpressions) const + { + auto accessMemberNode = std::make_unique(); + accessMemberNode->expr = std::move(expr); + accessMemberNode->indices = std::move(indexExpressions); return accessMemberNode; } diff --git a/src/Nazara/Shader/Ast/AstCloner.cpp b/src/Nazara/Shader/Ast/AstCloner.cpp index 262cac2de..4fdc9a05b 100644 --- a/src/Nazara/Shader/Ast/AstCloner.cpp +++ b/src/Nazara/Shader/Ast/AstCloner.cpp @@ -161,7 +161,7 @@ namespace Nz::ShaderAst ExpressionPtr AstCloner::Clone(AccessIdentifierExpression& node) { auto clone = std::make_unique(); - clone->memberIdentifiers = node.memberIdentifiers; + clone->identifiers = node.identifiers; clone->expr = CloneExpression(node.expr); clone->cachedExpressionType = node.cachedExpressionType; @@ -172,9 +172,12 @@ namespace Nz::ShaderAst ExpressionPtr AstCloner::Clone(AccessIndexExpression& node) { auto clone = std::make_unique(); - clone->memberIndices = node.memberIndices; clone->expr = CloneExpression(node.expr); + clone->indices.reserve(node.indices.size()); + for (auto& parameter : node.indices) + clone->indices.push_back(CloneExpression(parameter)); + clone->cachedExpressionType = node.cachedExpressionType; return clone; diff --git a/src/Nazara/Shader/Ast/AstRecursiveVisitor.cpp b/src/Nazara/Shader/Ast/AstRecursiveVisitor.cpp index a26867a2e..1268b8bf7 100644 --- a/src/Nazara/Shader/Ast/AstRecursiveVisitor.cpp +++ b/src/Nazara/Shader/Ast/AstRecursiveVisitor.cpp @@ -15,6 +15,8 @@ namespace Nz::ShaderAst void AstRecursiveVisitor::Visit(AccessIndexExpression& node) { node.expr->Visit(*this); + for (auto& index : node.indices) + index->Visit(*this); } void AstRecursiveVisitor::Visit(AssignExpression& node) diff --git a/src/Nazara/Shader/Ast/AstSerializer.cpp b/src/Nazara/Shader/Ast/AstSerializer.cpp index 113b0aec3..6c489bbce 100644 --- a/src/Nazara/Shader/Ast/AstSerializer.cpp +++ b/src/Nazara/Shader/Ast/AstSerializer.cpp @@ -37,8 +37,8 @@ namespace Nz::ShaderAst { Node(node.expr); - Container(node.memberIdentifiers); - for (std::string& identifier : node.memberIdentifiers) + Container(node.identifiers); + for (std::string& identifier : node.identifiers) Value(identifier); } @@ -46,9 +46,9 @@ namespace Nz::ShaderAst { Node(node.expr); - Container(node.memberIndices); - for (std::size_t& identifier : node.memberIndices) - SizeT(identifier); + Container(node.indices); + for (auto& identifier : node.indices) + Node(identifier); } void AstSerializerBase::Serialize(AssignExpression& node) diff --git a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp index 01716f1f5..68b9c8319 100644 --- a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp +++ b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp @@ -95,110 +95,114 @@ namespace Nz::ShaderAst 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(AccessIdentifierExpression& node) { - auto structExpr = CloneExpression(MandatoryExpr(node.expr)); + if (node.identifiers.empty()) + throw AstError{ "AccessIdentifierExpression must have at least one identifier" }; - const ExpressionType& exprType = GetExpressionType(*structExpr); - if (IsVectorType(exprType)) + ExpressionPtr indexedExpr = CloneExpression(MandatoryExpr(node.expr)); + for (std::size_t i = 0; i < node.identifiers.size(); ++i) { - const VectorType& swizzledVec = std::get(exprType); + const std::string& identifier = node.identifiers[i]; - // Swizzle expression - auto swizzle = std::make_unique(); - swizzle->expression = std::move(structExpr); - - // FIXME: Handle properly multiple identifiers (treat recursively) - if (node.memberIdentifiers.size() != 1) - throw AstError{ "invalid swizzle" }; - - const std::string& swizzleStr = node.memberIdentifiers.front(); - if (swizzleStr.empty() || swizzleStr.size() > swizzle->components.size()) - throw AstError{ "invalid swizzle" }; - - swizzle->componentCount = swizzleStr.size(); - - if (swizzle->componentCount > 1) - swizzle->cachedExpressionType = VectorType{ swizzle->componentCount, swizzledVec.type }; - else - swizzle->cachedExpressionType = swizzledVec.type; - - for (std::size_t i = 0; i < swizzle->componentCount; ++i) + const ExpressionType& exprType = GetExpressionType(*indexedExpr); + if (IsStructType(exprType)) { - switch (swizzleStr[i]) + // Transform to AccessIndexExpression + AccessIndexExpression* accessIndexPtr; + if (indexedExpr->GetType() != NodeType::AccessIndexExpression) { - case 'r': - case 'x': - case 's': - swizzle->components[i] = SwizzleComponent::First; - break; + std::unique_ptr accessIndex = std::make_unique(); + accessIndex->expr = std::move(indexedExpr); - case 'g': - case 'y': - case 't': - swizzle->components[i] = SwizzleComponent::Second; - break; - - case 'b': - case 'z': - case 'p': - swizzle->components[i] = SwizzleComponent::Third; - break; - - case 'a': - case 'w': - case 'q': - swizzle->components[i] = SwizzleComponent::Fourth; - break; + accessIndexPtr = accessIndex.get(); + indexedExpr = std::move(accessIndex); } + else + accessIndexPtr = static_cast(indexedExpr.get()); + + std::size_t structIndex = ResolveStruct(exprType); + assert(structIndex < m_structs.size()); + const StructDescription& s = m_structs[structIndex]; + + auto it = std::find_if(s.members.begin(), s.members.end(), [&](const auto& field) { return field.name == identifier; }); + if (it == s.members.end()) + throw AstError{ "unknown field " + identifier }; + + accessIndexPtr->indices.push_back(ShaderBuilder::Constant(Int32(std::distance(s.members.begin(), it)))); + accessIndexPtr->cachedExpressionType = ResolveType(it->type); } + else if (IsVectorType(exprType)) + { + // Swizzle expression + const VectorType& swizzledVec = std::get(exprType); - return swizzle; + auto swizzle = std::make_unique(); + swizzle->expression = std::move(indexedExpr); + + if (node.identifiers.size() - i != 1) + throw AstError{ "invalid swizzle" }; + + const std::string& swizzleStr = node.identifiers[i]; + if (swizzleStr.empty() || swizzleStr.size() > swizzle->components.size()) + throw AstError{ "invalid swizzle" }; + + swizzle->componentCount = swizzleStr.size(); + + if (swizzle->componentCount > 1) + swizzle->cachedExpressionType = VectorType{ swizzle->componentCount, swizzledVec.type }; + else + swizzle->cachedExpressionType = swizzledVec.type; + + for (std::size_t j = 0; j < swizzle->componentCount; ++j) + { + switch (swizzleStr[j]) + { + case 'r': + case 'x': + case 's': + swizzle->components[j] = SwizzleComponent::First; + break; + + case 'g': + case 'y': + case 't': + swizzle->components[j] = SwizzleComponent::Second; + break; + + case 'b': + case 'z': + case 'p': + swizzle->components[j] = SwizzleComponent::Third; + break; + + case 'a': + case 'w': + case 'q': + swizzle->components[j] = SwizzleComponent::Fourth; + break; + } + } + + indexedExpr = std::move(swizzle); + } + else + throw AstError{ "unexpected type (only struct and vectors can be indexed with identifiers)" }; //< TODO: Add support for arrays } - // Transform to AccessIndexExpression - auto accessMemberIndex = std::make_unique(); - accessMemberIndex->expr = std::move(structExpr); + return indexedExpr; + } - StackArray structIndices = NazaraStackArrayNoInit(std::size_t, node.memberIdentifiers.size()); + ExpressionPtr SanitizeVisitor::Clone(AccessIndexExpression& node) + { + MandatoryExpr(node.expr); + for (auto& index : node.indices) + MandatoryExpr(index); - accessMemberIndex->cachedExpressionType = ResolveType(CheckField(exprType, node.memberIdentifiers.data(), node.memberIdentifiers.size(), structIndices.data())); + auto clone = static_unique_pointer_cast(AstCloner::Clone(node)); + Validate(*clone); - 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; + return clone; } ExpressionPtr SanitizeVisitor::Clone(AssignExpression& node) @@ -1204,6 +1208,61 @@ namespace Nz::ShaderAst } } + void SanitizeVisitor::Validate(AccessIndexExpression& node) + { + if (node.indices.empty()) + throw AstError{ "AccessIndexExpression must have at least one index" }; + + for (auto& index : node.indices) + { + const ShaderAst::ExpressionType& indexType = GetExpressionType(*index); + if (!IsPrimitiveType(indexType) || std::get(indexType) != PrimitiveType::Int32) + throw AstError{ "AccessIndex expects Int32 indices" }; + } + + ExpressionType exprType = GetExpressionType(*node.expr); + for (std::size_t i = 0; i < node.indices.size(); ++i) + { + if (IsStructType(exprType)) + { + auto& indexExpr = node.indices[i]; + + const ShaderAst::ExpressionType& indexType = GetExpressionType(*indexExpr); + if (indexExpr->GetType() != NodeType::ConstantExpression) + throw AstError{ "struct can only be accessed with constant indices" }; + + ConstantExpression& constantExpr = static_cast(*indexExpr); + + Int32 index = std::get(constantExpr.value); + + std::size_t structIndex = ResolveStruct(exprType); + assert(structIndex < m_structs.size()); + const StructDescription& s = m_structs[structIndex]; + + exprType = ResolveType(s.members[index].type); + } + else if (IsMatrixType(exprType)) + { + // Matrix index (ex: mat[2]) + const MatrixType& matrixType = std::get(exprType); + + //TODO: Handle row-major matrices + exprType = VectorType{ matrixType.rowCount, matrixType.type }; + } + else if (IsVectorType(exprType)) + { + // Swizzle expression with one component (ex: vec[2]) + const VectorType& swizzledVec = std::get(exprType); + + exprType = swizzledVec.type; + } + else + throw AstError{ "unexpected type (only struct, vectors and matrices can be indexed)" }; //< TODO: Add support for arrays + } + + node.cachedExpressionType = std::move(exprType); + } + void SanitizeVisitor::Validate(CallFunctionExpression& node, const DeclareFunctionStatement* referenceDeclaration) { if (referenceDeclaration->entryStage) diff --git a/src/Nazara/Shader/GlslWriter.cpp b/src/Nazara/Shader/GlslWriter.cpp index 455b29a07..a0a58642f 100644 --- a/src/Nazara/Shader/GlslWriter.cpp +++ b/src/Nazara/Shader/GlslWriter.cpp @@ -355,11 +355,15 @@ namespace Nz AppendLine((forward) ? ");" : ")"); } - void GlslWriter::AppendField(std::size_t structIndex, const std::size_t* memberIndices, std::size_t remainingMembers) + void GlslWriter::AppendField(std::size_t structIndex, const ShaderAst::ExpressionPtr* memberIndices, std::size_t remainingMembers) { const auto& structDesc = Retrieve(m_currentState->structs, structIndex); - const auto& member = structDesc.members[*memberIndices]; + assert((*memberIndices)->GetType() == ShaderAst::NodeType::ConstantExpression); + auto& constantValue = static_cast(**memberIndices); + Int32 index = std::get(constantValue.value); + + const auto& member = structDesc.members[index]; Append("."); Append(member.name); @@ -655,9 +659,20 @@ namespace Nz Visit(node.expr, true); const ShaderAst::ExpressionType& exprType = GetExpressionType(*node.expr); - assert(IsStructType(exprType)); - AppendField(std::get(exprType).structIndex, node.memberIndices.data(), node.memberIndices.size()); + // For structs, convert indices to field names + if (IsStructType(exprType)) + AppendField(std::get(exprType).structIndex, node.indices.data(), node.indices.size()); + else + { + // Array access + for (ShaderAst::ExpressionPtr& expr : node.indices) + { + Append("["); + Visit(expr); + Append("]"); + } + } } void GlslWriter::Visit(ShaderAst::AssignExpression& node) diff --git a/src/Nazara/Shader/LangWriter.cpp b/src/Nazara/Shader/LangWriter.cpp index 2cb3459df..f6b061cea 100644 --- a/src/Nazara/Shader/LangWriter.cpp +++ b/src/Nazara/Shader/LangWriter.cpp @@ -342,11 +342,15 @@ namespace Nz AppendLine(); } - void LangWriter::AppendField(std::size_t structIndex, const std::size_t* memberIndices, std::size_t remainingMembers) + void LangWriter::AppendField(std::size_t structIndex, const ShaderAst::ExpressionPtr* memberIndices, std::size_t remainingMembers) { const auto& structDesc = Retrieve(m_currentState->structs, structIndex); - const auto& member = structDesc.members[*memberIndices]; + assert((*memberIndices)->GetType() == ShaderAst::NodeType::ConstantExpression); + auto& constantValue = static_cast(**memberIndices); + Int32 index = std::get(constantValue.value); + + const auto& member = structDesc.members[index]; Append("."); Append(member.name); @@ -443,9 +447,20 @@ namespace Nz Visit(node.expr, true); const ShaderAst::ExpressionType& exprType = GetExpressionType(*node.expr); - assert(IsStructType(exprType)); - AppendField(std::get(exprType).structIndex, node.memberIndices.data(), node.memberIndices.size()); + // For structs, convert indices to field names + if (IsStructType(exprType)) + AppendField(std::get(exprType).structIndex, node.indices.data(), node.indices.size()); + else + { + // Array access + for (ShaderAst::ExpressionPtr& expr : node.indices) + { + Append("["); + Visit(expr); + Append("]"); + } + } } void LangWriter::Visit(ShaderAst::AssignExpression& node) diff --git a/src/Nazara/Shader/ShaderLangParser.cpp b/src/Nazara/Shader/ShaderLangParser.cpp index 9d605f490..9fbd9e8f1 100644 --- a/src/Nazara/Shader/ShaderLangParser.cpp +++ b/src/Nazara/Shader/ShaderLangParser.cpp @@ -1,16 +1,16 @@ -// 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 -#include - -namespace Nz::ShaderLang -{ - namespace +// 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 +#include + +namespace Nz::ShaderLang +{ + namespace { std::unordered_map s_depthWriteModes = { { "greater", ShaderAst::DepthWriteMode::Greater }, @@ -18,35 +18,35 @@ namespace Nz::ShaderLang { "replace", ShaderAst::DepthWriteMode::Replace }, { "unchanged", ShaderAst::DepthWriteMode::Unchanged }, }; - - std::unordered_map s_identifierToBasicType = { - { "bool", ShaderAst::PrimitiveType::Boolean }, - { "i32", ShaderAst::PrimitiveType::Int32 }, - { "f32", ShaderAst::PrimitiveType::Float32 }, - { "u32", ShaderAst::PrimitiveType::UInt32 } - }; - - std::unordered_map s_identifierToAttributeType = { - { "binding", ShaderAst::AttributeType::Binding }, - { "builtin", ShaderAst::AttributeType::Builtin }, - { "depth_write", ShaderAst::AttributeType::DepthWrite }, - { "early_fragment_tests", ShaderAst::AttributeType::EarlyFragmentTests }, - { "entry", ShaderAst::AttributeType::Entry }, - { "layout", ShaderAst::AttributeType::Layout }, - { "location", ShaderAst::AttributeType::Location }, - { "opt", ShaderAst::AttributeType::Option }, - }; - - std::unordered_map s_entryPoints = { - { "frag", ShaderStageType::Fragment }, - { "vert", ShaderStageType::Vertex }, + + std::unordered_map s_identifierToBasicType = { + { "bool", ShaderAst::PrimitiveType::Boolean }, + { "i32", ShaderAst::PrimitiveType::Int32 }, + { "f32", ShaderAst::PrimitiveType::Float32 }, + { "u32", ShaderAst::PrimitiveType::UInt32 } }; - std::unordered_map s_builtinMapping = { - { "fragcoord", ShaderAst::BuiltinEntry::FragCoord }, - { "fragdepth", ShaderAst::BuiltinEntry::FragDepth }, - { "position", ShaderAst::BuiltinEntry::VertexPosition } - }; + std::unordered_map s_identifierToAttributeType = { + { "binding", ShaderAst::AttributeType::Binding }, + { "builtin", ShaderAst::AttributeType::Builtin }, + { "depth_write", ShaderAst::AttributeType::DepthWrite }, + { "early_fragment_tests", ShaderAst::AttributeType::EarlyFragmentTests }, + { "entry", ShaderAst::AttributeType::Entry }, + { "layout", ShaderAst::AttributeType::Layout }, + { "location", ShaderAst::AttributeType::Location }, + { "opt", ShaderAst::AttributeType::Option }, + }; + + std::unordered_map s_entryPoints = { + { "frag", ShaderStageType::Fragment }, + { "vert", ShaderStageType::Vertex }, + }; + + std::unordered_map s_builtinMapping = { + { "fragcoord", ShaderAst::BuiltinEntry::FragCoord }, + { "fragdepth", ShaderAst::BuiltinEntry::FragDepth }, + { "position", ShaderAst::BuiltinEntry::VertexPosition } + }; std::unordered_map s_layoutMapping = { { "std140", StructLayout::Std140 } @@ -58,349 +58,363 @@ namespace Nz::ShaderLang if (val < std::numeric_limits::min() || val > std::numeric_limits::max()) return std::nullopt; - 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; + 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::OpenSquareBracket: - assert(attributes.empty()); - attributes = ParseAttributes(); - break; - case TokenType::Option: - if (!attributes.empty()) - 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::OpenSquareBracket: + assert(attributes.empty()); + attributes = ParseAttributes(); + break; + + case TokenType::Option: + if (!attributes.empty()) + throw UnexpectedToken{}; context.root->statements.push_back(ParseOptionDeclaration()); 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{}; - } - } + + 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() - { - 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; - } - - std::optional Parser::DecodeType(const std::string& identifier) - { + + 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; + } + + std::optional Parser::DecodeType(const std::string& identifier) + { if (auto it = s_identifierToBasicType.find(identifier); it != s_identifierToBasicType.end()) - { - Consume(); - return it->second; - } - - //FIXME: Handle this better - if (identifier == "mat4") { Consume(); - - 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") + return it->second; + } + + //FIXME: Handle this better + if (identifier == "mat4") { Consume(); - - ShaderAst::SamplerType samplerType; - samplerType.dim = ImageType::E2D; - - Expect(Advance(), TokenType::LessThan); //< '<' - samplerType.sampledType = ParsePrimitiveType(); - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return samplerType; - } - else if (identifier == "samplerCube") + + 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 == "mat3") { Consume(); - - ShaderAst::SamplerType samplerType; - samplerType.dim = ImageType::Cubemap; - - Expect(Advance(), TokenType::LessThan); //< '<' - samplerType.sampledType = ParsePrimitiveType(); - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return samplerType; - } - else if (identifier == "uniform") + + ShaderAst::MatrixType matrixType; + matrixType.columnCount = 3; + matrixType.rowCount = 3; + + Expect(Advance(), TokenType::LessThan); //< '<' + matrixType.type = ParsePrimitiveType(); + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return matrixType; + } + else if (identifier == "sampler2D") { Consume(); - - ShaderAst::UniformType uniformType; - - Expect(Advance(), TokenType::LessThan); //< '<' - uniformType.containedType = ShaderAst::IdentifierType{ ParseIdentifierAsName() }; - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return uniformType; - } - else if (identifier == "vec2") + + ShaderAst::SamplerType samplerType; + samplerType.dim = ImageType::E2D; + + Expect(Advance(), TokenType::LessThan); //< '<' + samplerType.sampledType = ParsePrimitiveType(); + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return samplerType; + } + else if (identifier == "samplerCube") { Consume(); - - ShaderAst::VectorType vectorType; - vectorType.componentCount = 2; - - Expect(Advance(), TokenType::LessThan); //< '<' - vectorType.type = ParsePrimitiveType(); - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return vectorType; - } - else if (identifier == "vec3") + + ShaderAst::SamplerType samplerType; + samplerType.dim = ImageType::Cubemap; + + Expect(Advance(), TokenType::LessThan); //< '<' + samplerType.sampledType = ParsePrimitiveType(); + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return samplerType; + } + else if (identifier == "uniform") { Consume(); - - ShaderAst::VectorType vectorType; - vectorType.componentCount = 3; - - Expect(Advance(), TokenType::LessThan); //< '<' - vectorType.type = ParsePrimitiveType(); - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return vectorType; - } - else if (identifier == "vec4") + + ShaderAst::UniformType uniformType; + + Expect(Advance(), TokenType::LessThan); //< '<' + uniformType.containedType = ShaderAst::IdentifierType{ ParseIdentifierAsName() }; + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return uniformType; + } + else if (identifier == "vec2") { Consume(); - - ShaderAst::VectorType vectorType; - vectorType.componentCount = 4; - - Expect(Advance(), TokenType::LessThan); //< '<' - vectorType.type = ParsePrimitiveType(); - Expect(Advance(), TokenType::GreatherThan); //< '>' - - return vectorType; + + ShaderAst::VectorType vectorType; + vectorType.componentCount = 2; + + Expect(Advance(), TokenType::LessThan); //< '<' + vectorType.type = ParsePrimitiveType(); + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return vectorType; + } + else if (identifier == "vec3") + { + Consume(); + + ShaderAst::VectorType vectorType; + vectorType.componentCount = 3; + + Expect(Advance(), TokenType::LessThan); //< '<' + vectorType.type = ParsePrimitiveType(); + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return vectorType; + } + else if (identifier == "vec4") + { + Consume(); + + ShaderAst::VectorType vectorType; + vectorType.componentCount = 4; + + Expect(Advance(), TokenType::LessThan); //< '<' + vectorType.type = ParsePrimitiveType(); + Expect(Advance(), TokenType::GreatherThan); //< '>' + + return vectorType; } else - return std::nullopt; - } - - 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(); + return std::nullopt; } - - void Parser::RegisterVariable(std::string identifier) + + 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) { 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) - { - 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::OpenSquareBracket); - - bool expectComma = false; - for (;;) - { - const Token& t = Peek(); - ExpectNot(t, TokenType::EndOfStream); - - if (t.type == TokenType::ClosingSquareBracket) - { - // Parse [attribute1] [attribute2] the same as [attribute1, attribute2] - if (Peek(1).type == TokenType::OpenSquareBracket) - { - 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::ClosingSquareBracket); - - return attributes; - } - - ShaderAst::StatementPtr Parser::ParseExternalBlock(std::vector attributes) + m_context->identifiersInScope.push_back(std::move(identifier)); + } + + const Token& Parser::Peek(std::size_t advance) { - 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(); - + 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::OpenSquareBracket); + + bool expectComma = false; + for (;;) + { + const Token& t = Peek(); + ExpectNot(t, TokenType::EndOfStream); + + if (t.type == TokenType::ClosingSquareBracket) + { + // Parse [attribute1] [attribute2] the same as [attribute1, attribute2] + if (Peek(1).type == TokenType::OpenSquareBracket) + { + 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::ClosingSquareBracket); + + return attributes; + } + + ShaderAst::StatementPtr Parser::ParseExternalBlock(std::vector attributes) + { + 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::OpenSquareBracket) { - for (const auto& [attributeType, arg] : ParseAttributes()) - { - switch (attributeType) + for (const auto& [attributeType, arg] : ParseAttributes()) + { + switch (attributeType) { case ShaderAst::AttributeType::Binding: { @@ -417,96 +431,96 @@ namespace Nz::ShaderLang extVar.bindingIndex = bindingIndex.value(); break; } - - default: - throw AttributeError{ "unhandled attribute for external variable" }; - } - } - } - - extVar.name = ParseIdentifierAsName(); - Expect(Advance(), TokenType::Colon); + + default: + throw AttributeError{ "unhandled attribute for external variable" }; + } + } + } + + extVar.name = ParseIdentifierAsName(); + Expect(Advance(), TokenType::Colon); extVar.type = ParseType(); - 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); + RegisterVariable(extVar.name); + } - EnterScope(); + 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(); for (const auto& parameter : parameters) RegisterVariable(parameter.name); - - std::vector functionBody = ParseFunctionBody(); - LeaveScope(); - - Expect(Advance(), TokenType::ClosingCurlyBracket); + std::vector functionBody = ParseFunctionBody(); + + LeaveScope(); + + Expect(Advance(), TokenType::ClosingCurlyBracket); auto func = ShaderBuilder::DeclareFunction(std::move(functionName), std::move(parameters), std::move(functionBody), std::move(returnType)); - for (const auto& [attributeType, arg] : attributes) - { - switch (attributeType) + for (const auto& [attributeType, arg] : attributes) + { + switch (attributeType) { case ShaderAst::AttributeType::DepthWrite: { - if (func->depthWrite) - throw AttributeError{ "attribute depth_write can only be present once" }; + if (func->depthWrite) + throw AttributeError{ "attribute depth_write can only be present once" }; - if (!std::holds_alternative(arg)) - throw AttributeError{ "attribute entry requires a string parameter" }; + if (!std::holds_alternative(arg)) + throw AttributeError{ "attribute entry requires a string parameter" }; - const std::string& argStr = std::get(arg); + const std::string& argStr = std::get(arg); auto it = s_depthWriteModes.find(argStr); if (it == s_depthWriteModes.end()) - throw AttributeError{ ("invalid parameter " + argStr + " for depth_write attribute").c_str() }; + throw AttributeError{ ("invalid parameter " + argStr + " for depth_write attribute").c_str() }; func->depthWrite = it->second; break; @@ -514,12 +528,12 @@ namespace Nz::ShaderLang case ShaderAst::AttributeType::EarlyFragmentTests: { - if (func->earlyFragmentTests) - throw AttributeError{ "attribute early_fragment_tests can only be present once" }; + if (func->earlyFragmentTests) + throw AttributeError{ "attribute early_fragment_tests can only be present once" }; if (std::holds_alternative(arg)) { - const std::string& argStr = std::get(arg); + const std::string& argStr = std::get(arg); if (argStr == "true" || argStr == "on") func->earlyFragmentTests = true; else if (argStr == "false" || argStr == "off") @@ -537,23 +551,23 @@ namespace Nz::ShaderLang break; } - - case ShaderAst::AttributeType::Entry: - { - if (func->entryStage) - throw AttributeError{ "attribute entry can only 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() }; - - func->entryStage = it->second; - break; + + case ShaderAst::AttributeType::Entry: + { + if (func->entryStage) + throw AttributeError{ "attribute entry can only 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() }; + + func->entryStage = it->second; + break; } case ShaderAst::AttributeType::Option: @@ -561,41 +575,41 @@ namespace Nz::ShaderLang if (!func->optionName.empty()) throw AttributeError{ "attribute option must be present once" }; - if (!std::holds_alternative(arg)) - throw AttributeError{ "attribute option requires a string parameter" }; - - func->optionName = std::get(arg); - break; - } - - default: - throw AttributeError{ "unhandled attribute for function" }; - } + if (!std::holds_alternative(arg)) + throw AttributeError{ "attribute option requires a string parameter" }; + + func->optionName = std::get(arg); + break; + } + + default: + throw AttributeError{ "unhandled attribute for function" }; + } } - return func; - } - - ShaderAst::DeclareFunctionStatement::Parameter Parser::ParseFunctionParameter() - { - std::string parameterName = ParseIdentifierAsName(); - - Expect(Advance(), TokenType::Colon); - - ShaderAst::ExpressionType parameterType = ParseType(); - - return { parameterName, parameterType }; - } - - ShaderAst::StatementPtr Parser::ParseOptionDeclaration() + return func; + } + + ShaderAst::DeclareFunctionStatement::Parameter Parser::ParseFunctionParameter() + { + std::string parameterName = ParseIdentifierAsName(); + + Expect(Advance(), TokenType::Colon); + + ShaderAst::ExpressionType parameterType = ParseType(); + + return { parameterName, parameterType }; + } + + ShaderAst::StatementPtr Parser::ParseOptionDeclaration() { Expect(Advance(), TokenType::Option); std::string optionName = ParseIdentifierAsName(); - - Expect(Advance(), TokenType::Colon); - - ShaderAst::ExpressionType optionType = ParseType(); + + Expect(Advance(), TokenType::Colon); + + ShaderAst::ExpressionType optionType = ParseType(); ShaderAst::ExpressionPtr initialValue; if (Peek().type == TokenType::Assign) @@ -606,296 +620,326 @@ namespace Nz::ShaderLang } Expect(Advance(), TokenType::Semicolon); - - return ShaderBuilder::DeclareOption(std::move(optionName), std::move(optionType), std::move(initialValue)); - } - - ShaderAst::StatementPtr Parser::ParseStructDeclaration(std::vector attributes) - { - Expect(Advance(), TokenType::Struct); - - ShaderAst::StructDescription description; - description.name = ParseIdentifierAsName(); - for (const auto& [attributeType, attributeParam] : attributes) + return ShaderBuilder::DeclareOption(std::move(optionName), std::move(optionType), std::move(initialValue)); + } + + ShaderAst::StatementPtr Parser::ParseStructDeclaration(std::vector attributes) + { + Expect(Advance(), TokenType::Struct); + + ShaderAst::StructDescription description; + description.name = ParseIdentifierAsName(); + + for (const auto& [attributeType, attributeParam] : attributes) { switch (attributeType) - { + { case ShaderAst::AttributeType::Layout: - { - if (description.layout) - throw AttributeError{ "attribute layout must be present once" }; - - auto it = s_layoutMapping.find(std::get(attributeParam)); - if (it == s_layoutMapping.end()) - throw AttributeError{ "unknown layout" }; - - description.layout = it->second; - break; + { + if (description.layout) + throw AttributeError{ "attribute layout must be present once" }; + + auto it = s_layoutMapping.find(std::get(attributeParam)); + if (it == s_layoutMapping.end()) + throw AttributeError{ "unknown layout" }; + + description.layout = it->second; + break; } default: - throw AttributeError{ "unexpected attribute" }; - } + throw AttributeError{ "unexpected attribute" }; + } } - - 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(); - + + 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::OpenSquareBracket) { - for (const auto& [attributeType, attributeParam] : ParseAttributes()) + for (const auto& [attributeType, attributeParam] : ParseAttributes()) { 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; + { + 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) + { + 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; + break; } default: - throw AttributeError{ "unexpected attribute" }; - } + throw AttributeError{ "unexpected attribute" }; + } } 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 = ParseExpression(); - 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); - + 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 = ParseExpression(); + 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); + RegisterVariable(variableName); ShaderAst::ExpressionType variableType = ShaderAst::NoType{}; - if (Peek().type == TokenType::Colon) + if (Peek().type == TokenType::Colon) { - Expect(Advance(), TokenType::Colon); - + Expect(Advance(), TokenType::Colon); + variableType = ParseType(); - } - - ShaderAst::ExpressionPtr expression; - if (IsNoType(variableType) || Peek().type == TokenType::Assign) - { - Expect(Advance(), TokenType::Assign); - 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; - - if (currentOp.type == TokenType::Dot) - { - std::unique_ptr accessMemberNode = std::make_unique(); - accessMemberNode->expr = std::move(lhs); - - do - { - Consume(); - - accessMemberNode->memberIdentifiers.push_back(ParseIdentifierAsName()); - } - while (Peek().type == TokenType::Dot); + } - // FIXME - if (!accessMemberNode->memberIdentifiers.empty() && accessMemberNode->memberIdentifiers.front() == "Sample") + ShaderAst::ExpressionPtr expression; + if (IsNoType(variableType) || Peek().type == TokenType::Assign) + { + Expect(Advance(), TokenType::Assign); + expression = ParseExpression(); + } + + return ShaderBuilder::DeclareVariable(std::move(variableName), std::move(variableType), std::move(expression)); + } + + ShaderAst::ExpressionPtr Parser::ParseBinOpRhs(int exprPrecedence, ShaderAst::ExpressionPtr lhs) + { + for (;;) + { + TokenType currentTokenType = Peek().type; + if (currentTokenType == TokenType::EndOfStream) + throw UnexpectedToken{}; + + int tokenPrecedence = GetTokenPrecedence(currentTokenType); + if (tokenPrecedence < exprPrecedence) + return lhs; + + bool c = false; + while (currentTokenType == TokenType::Dot || currentTokenType == TokenType::OpenSquareBracket) + { + c = true; + + if (currentTokenType == TokenType::Dot) { - if (Peek().type == TokenType::OpenParenthesis) + std::unique_ptr accessMemberNode = std::make_unique(); + accessMemberNode->expr = std::move(lhs); + + do { - auto parameters = ParseParameters(); - parameters.insert(parameters.begin(), std::move(accessMemberNode->expr)); + Consume(); - lhs = ShaderBuilder::Intrinsic(ShaderAst::IntrinsicType::SampleTexture, std::move(parameters)); - continue; + accessMemberNode->identifiers.push_back(ParseIdentifierAsName()); + } while (Peek().type == TokenType::Dot); + + // FIXME + if (!accessMemberNode->identifiers.empty() && accessMemberNode->identifiers.front() == "Sample") + { + if (Peek().type == TokenType::OpenParenthesis) + { + auto parameters = ParseParameters(); + parameters.insert(parameters.begin(), std::move(accessMemberNode->expr)); + + lhs = ShaderBuilder::Intrinsic(ShaderAst::IntrinsicType::SampleTexture, std::move(parameters)); + break; + } } - } - - lhs = std::move(accessMemberNode); - continue; - } - - 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() - { - const Token& floatingPointToken = Expect(Advance(), TokenType::FloatingPointValue); - return ShaderBuilder::Constant(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); - return ShaderBuilder::Identifier(identifier); - } - - ShaderAst::ExpressionPtr Parser::ParseIntegerExpression() - { - const Token& integerToken = Expect(Advance(), TokenType::IntegerValue); - return ShaderBuilder::Constant(static_cast(std::get(integerToken.data))); //< FIXME - } - - std::vector Parser::ParseParameters() - { + lhs = std::move(accessMemberNode); + } + else + { + assert(currentTokenType == TokenType::OpenSquareBracket); + + std::unique_ptr indexNode = std::make_unique(); + indexNode->expr = std::move(lhs); + + do + { + Consume(); + + indexNode->indices.push_back(ParseExpression()); + + Expect(Advance(), TokenType::ClosingSquareBracket); + } + while (Peek().type == TokenType::OpenSquareBracket); + + lhs = std::move(indexNode); + } + + currentTokenType = Peek().type; + } + + if (c) + continue; + + 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 (currentTokenType) + { + 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() + { + const Token& floatingPointToken = Expect(Advance(), TokenType::FloatingPointValue); + return ShaderBuilder::Constant(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); + + return ShaderBuilder::Identifier(identifier); + } + + ShaderAst::ExpressionPtr Parser::ParseIntegerExpression() + { + const Token& integerToken = Expect(Advance(), TokenType::IntegerValue); + return ShaderBuilder::Constant(static_cast(std::get(integerToken.data))); //< FIXME + } + + std::vector Parser::ParseParameters() + { Expect(Advance(), TokenType::OpenParenthesis); std::vector parameters; @@ -908,37 +952,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); @@ -955,11 +999,11 @@ namespace Nz::ShaderLang return ShaderBuilder::CallFunction(identifier, ParseParameters()); } else - return ParseIdentifier(); - } - - case TokenType::IntegerValue: - return ParseIntegerExpression(); + return ParseIdentifier(); + } + + case TokenType::IntegerValue: + return ParseIntegerExpression(); case TokenType::Minus: { @@ -976,19 +1020,19 @@ namespace Nz::ShaderLang return ShaderBuilder::Unary(ShaderAst::UnaryType::Plus, std::move(expr)); } - - case TokenType::OpenParenthesis: - return ParseParenthesisExpression(); + + case TokenType::OpenParenthesis: + return ParseParenthesisExpression(); case TokenType::SelectOpt: return ParseSelectOptExpression(); - - default: - throw UnexpectedToken{}; - } - } - - ShaderAst::ExpressionPtr Parser::ParseSelectOptExpression() + + default: + throw UnexpectedToken{}; + } + } + + ShaderAst::ExpressionPtr Parser::ParseSelectOptExpression() { Expect(Advance(), TokenType::SelectOpt); Expect(Advance(), TokenType::OpenParenthesis); @@ -1004,101 +1048,102 @@ namespace Nz::ShaderLang ShaderAst::ExpressionPtr falseExpr = ParseExpression(); Expect(Advance(), TokenType::ClosingParenthesis); - - return ShaderBuilder::SelectOption(std::move(optionName), std::move(trueExpr), std::move(falseExpr)); - } - - ShaderAst::AttributeType Parser::ParseIdentifierAsAttributeType() - { - const Token& identifierToken = Expect(Advance(), TokenType::Identifier); - const std::string& identifier = std::get(identifierToken.data); - - auto it = s_identifierToAttributeType.find(identifier); - if (it == s_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 = s_identifierToBasicType.find(identifier); - if (it != s_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 = s_identifierToBasicType.find(identifier); - if (it == s_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(Peek(), TokenType::Identifier); - const std::string& identifier = std::get(identifierToken.data); + + return ShaderBuilder::SelectOption(std::move(optionName), std::move(trueExpr), std::move(falseExpr)); + } + + ShaderAst::AttributeType Parser::ParseIdentifierAsAttributeType() + { + const Token& identifierToken = Expect(Advance(), TokenType::Identifier); + const std::string& identifier = std::get(identifierToken.data); + + auto it = s_identifierToAttributeType.find(identifier); + if (it == s_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 = s_identifierToBasicType.find(identifier); + if (it != s_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 = s_identifierToBasicType.find(identifier); + if (it == s_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(Peek(), TokenType::Identifier); + const std::string& identifier = std::get(identifierToken.data); auto type = DecodeType(identifier); if (!type) { Consume(); - return ShaderAst::IdentifierType{ identifier }; + return ShaderAst::IdentifierType{ identifier }; } return *type; - } - - 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; - case TokenType::Dot: return 50; - default: return -1; - } - } + } - ShaderAst::StatementPtr Parse(const std::filesystem::path& sourcePath) - { - File file(sourcePath); - if (!file.Open(OpenMode::ReadOnly | OpenMode::Text)) - { - NazaraError("Failed to open \"" + sourcePath.generic_u8string() + '"'); - return {}; - } - - std::size_t length = static_cast(file.GetSize()); - - std::vector source(length); - if (file.Read(&source[0], length) != length) - { - NazaraError("Failed to read program file"); - return {}; - } + 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; + case TokenType::Dot: return 50; + case TokenType::OpenSquareBracket: return 50; + default: return -1; + } + } - return Parse(Tokenize(std::string_view(reinterpret_cast(source.data()), source.size()))); - } -} + ShaderAst::StatementPtr Parse(const std::filesystem::path& sourcePath) + { + File file(sourcePath); + if (!file.Open(OpenMode::ReadOnly | OpenMode::Text)) + { + NazaraError("Failed to open \"" + sourcePath.generic_u8string() + '"'); + return {}; + } + + std::size_t length = static_cast(file.GetSize()); + + std::vector source(length); + if (file.Read(&source[0], length) != length) + { + NazaraError("Failed to read program file"); + return {}; + } + + return Parse(Tokenize(std::string_view(reinterpret_cast(source.data()), source.size()))); + } +} diff --git a/src/Nazara/Shader/SpirvExpressionLoad.cpp b/src/Nazara/Shader/SpirvExpressionLoad.cpp index d2c59cebe..30ebadc2a 100644 --- a/src/Nazara/Shader/SpirvExpressionLoad.cpp +++ b/src/Nazara/Shader/SpirvExpressionLoad.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -55,28 +56,36 @@ namespace Nz { UInt32 pointerType = m_writer.RegisterPointerType(exprType, pointer.storage); //< FIXME + StackArray indexIds = NazaraStackArrayNoInit(UInt32, node.indices.size()); + for (std::size_t i = 0; i < node.indices.size(); ++i) + indexIds[i] = m_visitor.EvaluateExpression(node.indices[i]); + m_block.AppendVariadic(SpirvOp::OpAccessChain, [&](const auto& appender) { appender(pointerType); appender(resultId); appender(pointer.pointerId); - for (std::size_t index : node.memberIndices) - appender(m_writer.GetConstantId(Int32(index))); + for (UInt32 id : indexIds) + appender(id); }); m_value = Pointer { pointer.storage, resultId, typeId }; }, [&](const Value& value) { + StackArray indexIds = NazaraStackArrayNoInit(UInt32, node.indices.size()); + for (std::size_t i = 0; i < node.indices.size(); ++i) + indexIds[i] = m_visitor.EvaluateExpression(node.indices[i]); + m_block.AppendVariadic(SpirvOp::OpCompositeExtract, [&](const auto& appender) { appender(typeId); appender(resultId); appender(value.resultId); - for (std::size_t index : node.memberIndices) - appender(m_writer.GetConstantId(Int32(index))); + for (UInt32 id : indexIds) + appender(id); }); m_value = Value { resultId }; diff --git a/src/Nazara/Shader/SpirvExpressionStore.cpp b/src/Nazara/Shader/SpirvExpressionStore.cpp index 1298cae3c..47fba160c 100644 --- a/src/Nazara/Shader/SpirvExpressionStore.cpp +++ b/src/Nazara/Shader/SpirvExpressionStore.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -50,14 +51,18 @@ namespace Nz UInt32 resultId = m_visitor.AllocateResultId(); UInt32 pointerType = m_writer.RegisterPointerType(exprType, pointer.storage); //< FIXME + StackArray indexIds = NazaraStackArrayNoInit(UInt32, node.indices.size()); + for (std::size_t i = 0; i < node.indices.size(); ++i) + indexIds[i] = m_visitor.EvaluateExpression(node.indices[i]); + m_block.AppendVariadic(SpirvOp::OpAccessChain, [&](const auto& appender) { appender(pointerType); appender(resultId); appender(pointer.pointerId); - for (std::size_t index : node.memberIndices) - appender(m_writer.GetConstantId(Int32(index))); + for (UInt32 id : indexIds) + appender(id); }); m_value = Pointer { pointer.storage, resultId }; diff --git a/src/Nazara/Shader/SpirvWriter.cpp b/src/Nazara/Shader/SpirvWriter.cpp index c4182666a..824aa906d 100644 --- a/src/Nazara/Shader/SpirvWriter.cpp +++ b/src/Nazara/Shader/SpirvWriter.cpp @@ -75,9 +75,6 @@ namespace Nz { AstRecursiveVisitor::Visit(node); - for (std::size_t index : node.memberIndices) - m_constantCache.Register(*m_constantCache.BuildConstant(Int32(index))); - m_constantCache.Register(*m_constantCache.BuildType(node.cachedExpressionType.value())); }