From 7d4a084a62f010d3d1048d3c22ad763ce4156776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Wed, 5 May 2021 12:05:46 +0200 Subject: [PATCH] Shader/Parser: Fix handling of . in rvalues + add support for swizzling For example: var.field was okay but texture.Sample(uv).x wasn't --- src/Nazara/Shader/Ast/SanitizeVisitor.cpp | 57 +++++++++++++++- src/Nazara/Shader/ShaderLangParser.cpp | 81 +++++++++++------------ 2 files changed, 95 insertions(+), 43 deletions(-) diff --git a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp index 759a724ed..24dced757 100644 --- a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp +++ b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp @@ -90,6 +90,61 @@ namespace Nz::ShaderAst auto structExpr = CloneExpression(MandatoryExpr(node.structExpr)); const ExpressionType& exprType = GetExpressionType(*structExpr); + if (IsVectorType(exprType)) + { + const VectorType& swizzledVec = std::get(exprType); + + // 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) + { + switch (swizzleStr[i]) + { + case 'r': + case 'x': + case 's': + swizzle->components[i] = SwizzleComponent::First; + break; + + 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; + } + } + + return swizzle; + } // Transform to AccessMemberIndexExpression auto accessMemberIndex = std::make_unique(); @@ -284,7 +339,7 @@ namespace Nz::ShaderAst if (IsPrimitiveType(rightExprType)) { TypeMustMatch(leftType.type, rightExprType); - clone->cachedExpressionType = rightExprType; + clone->cachedExpressionType = leftExprType; } else if (IsVectorType(rightExprType)) { diff --git a/src/Nazara/Shader/ShaderLangParser.cpp b/src/Nazara/Shader/ShaderLangParser.cpp index 0cb31927b..6ace15958 100644 --- a/src/Nazara/Shader/ShaderLangParser.cpp +++ b/src/Nazara/Shader/ShaderLangParser.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -696,7 +697,7 @@ namespace Nz::ShaderLang ShaderAst::ExpressionPtr Parser::ParseVariableAssignation() { - ShaderAst::ExpressionPtr left = ParseIdentifier(); + ShaderAst::ExpressionPtr left = ParseExpression(); Expect(Advance(), TokenType::Assign); ShaderAst::ExpressionPtr right = ParseExpression(); @@ -740,6 +741,36 @@ namespace Nz::ShaderLang if (tokenPrecedence < exprPrecedence) return lhs; + if (currentOp.type == TokenType::Dot) + { + std::unique_ptr accessMemberNode = std::make_unique(); + accessMemberNode->structExpr = std::move(lhs); + + do + { + Consume(); + + accessMemberNode->memberIdentifiers.push_back(ParseIdentifierAsName()); + } + while (Peek().type == TokenType::Dot); + + // FIXME + if (!accessMemberNode->memberIdentifiers.empty() && accessMemberNode->memberIdentifiers.front() == "Sample") + { + if (Peek().type == TokenType::OpenParenthesis) + { + auto parameters = ParseParameters(); + parameters.insert(parameters.begin(), std::move(accessMemberNode->structExpr)); + + lhs = ShaderBuilder::Intrinsic(ShaderAst::IntrinsicType::SampleTexture, std::move(parameters)); + continue; + } + } + + lhs = std::move(accessMemberNode); + continue; + } + Consume(); ShaderAst::ExpressionPtr rhs = ParsePrimaryExpression(); @@ -780,25 +811,8 @@ namespace Nz::ShaderLang { 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; + + return ShaderBuilder::Identifier(identifier); } ShaderAst::ExpressionPtr Parser::ParseIntegerExpression(bool minus) @@ -866,25 +880,7 @@ namespace Nz::ShaderLang } if (IsVariableInScope(identifier)) - { - auto node = ParseIdentifier(); - if (node->GetType() == ShaderAst::NodeType::AccessMemberIdentifierExpression) - { - ShaderAst::AccessMemberIdentifierExpression* memberExpr = static_cast(node.get()); - if (!memberExpr->memberIdentifiers.empty() && memberExpr->memberIdentifiers.front() == "Sample") - { - if (Peek().type == TokenType::OpenParenthesis) - { - auto parameters = ParseParameters(); - parameters.insert(parameters.begin(), std::move(memberExpr->structExpr)); - - return ShaderBuilder::Intrinsic(ShaderAst::IntrinsicType::SampleTexture, std::move(parameters)); - } - } - } - - return node; - } + return ParseIdentifier(); Consume(); @@ -1001,10 +997,11 @@ namespace Nz::ShaderLang { switch (token) { - case TokenType::Plus: return 20; - case TokenType::Divide: return 40; + case TokenType::Plus: return 20; + case TokenType::Divide: return 40; case TokenType::Multiply: return 40; - case TokenType::Minus: return 20; + case TokenType::Minus: return 20; + case TokenType::Dot: return 50; default: return -1; } }