Shader/Parser: Fix handling of . in rvalues + add support for swizzling

For example: var.field was okay but texture.Sample(uv).x wasn't
This commit is contained in:
Jérôme Leclercq 2021-05-05 12:05:46 +02:00
parent eb67990b7b
commit 7d4a084a62
2 changed files with 95 additions and 43 deletions

View File

@ -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<VectorType>(exprType);
// Swizzle expression
auto swizzle = std::make_unique<SwizzleExpression>();
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<AccessMemberIndexExpression>();
@ -284,7 +339,7 @@ namespace Nz::ShaderAst
if (IsPrimitiveType(rightExprType))
{
TypeMustMatch(leftType.type, rightExprType);
clone->cachedExpressionType = rightExprType;
clone->cachedExpressionType = leftExprType;
}
else if (IsVectorType(rightExprType))
{

View File

@ -3,6 +3,7 @@
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <cassert>
#include <Nazara/Shader/Debug.hpp>
@ -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<ShaderAst::AccessMemberIdentifierExpression> accessMemberNode = std::make_unique<ShaderAst::AccessMemberIdentifierExpression>();
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<std::string>(identifierToken.data);
ShaderAst::ExpressionPtr identifierExpr = ShaderBuilder::Identifier(identifier);
if (Peek().type == TokenType::Dot)
{
std::unique_ptr<ShaderAst::AccessMemberIdentifierExpression> accessMemberNode = std::make_unique<ShaderAst::AccessMemberIdentifierExpression>();
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<ShaderAst::AccessMemberIdentifierExpression*>(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;
}
}