diff --git a/include/Nazara/Shader/Ast/SanitizeVisitor.hpp b/include/Nazara/Shader/Ast/SanitizeVisitor.hpp index 7750e3eea..9c36388b6 100644 --- a/include/Nazara/Shader/Ast/SanitizeVisitor.hpp +++ b/include/Nazara/Shader/Ast/SanitizeVisitor.hpp @@ -33,6 +33,8 @@ namespace Nz::ShaderAst SanitizeVisitor& operator=(const SanitizeVisitor&) = delete; SanitizeVisitor& operator=(SanitizeVisitor&&) = delete; + static UInt32 ToSwizzleIndex(char c); + struct Options { std::unordered_set reservedIdentifiers; @@ -40,6 +42,7 @@ namespace Nz::ShaderAst bool makeVariableNameUnique = false; bool removeCompoundAssignments = false; bool removeOptionDeclaration = true; + bool removeScalarSwizzling = false; }; private: @@ -86,6 +89,8 @@ namespace Nz::ShaderAst void PushScope(); void PopScope(); + ExpressionPtr CacheResult(ExpressionPtr expression); + template const T& ComputeAttributeValue(AttributeValue& attribute); ConstantValue ComputeConstantValue(Expression& expr); template std::unique_ptr Optimize(T& node); @@ -112,7 +117,12 @@ namespace Nz::ShaderAst void Validate(AccessIndexExpression& node); void Validate(CallFunctionExpression& node, const DeclareFunctionStatement* referenceDeclaration); + void Validate(CastExpression& node); + void Validate(DeclareVariableStatement& node); void Validate(IntrinsicExpression& node); + void Validate(SwizzleExpression& node); + void Validate(UnaryExpression& node); + void Validate(VariableExpression& node); ExpressionType ValidateBinaryOp(BinaryType op, const ExpressionPtr& leftExpr, const ExpressionPtr& rightExpr); struct FunctionData diff --git a/src/Nazara/Shader/Ast/AstUtils.cpp b/src/Nazara/Shader/Ast/AstUtils.cpp index 19f12882a..cc627d7e8 100644 --- a/src/Nazara/Shader/Ast/AstUtils.cpp +++ b/src/Nazara/Shader/Ast/AstUtils.cpp @@ -84,7 +84,11 @@ namespace Nz::ShaderAst void ShaderAstValueCategory::Visit(SwizzleExpression& node) { - node.expression->Visit(*this); + // Swizzling more than a component on a primitive produces a rvalue (a.xxxx cannot be assigned) + if (IsPrimitiveType(GetExpressionType(node)) && node.componentCount > 1) + m_expressionCategory = ExpressionCategory::RValue; + else + node.expression->Visit(*this); } void ShaderAstValueCategory::Visit(VariableExpression& /*node*/) diff --git a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp index 0526f7842..af97bb65b 100644 --- a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp +++ b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -25,7 +26,7 @@ namespace Nz::ShaderAst template std::unique_ptr static_unique_pointer_cast(std::unique_ptr&& ptr) { - return std::unique_ptr(static_cast(ptr.release())); + return std::unique_ptr(SafeCast(ptr.release())); } } @@ -52,6 +53,7 @@ namespace Nz::ShaderAst std::vector variableTypes; std::vector scopeSizes; CurrentFunctionData* currentFunction = nullptr; + std::vector* currentStatementList = nullptr; }; StatementPtr SanitizeVisitor::Sanitize(Statement& statement, const Options& options, std::string* error) @@ -117,16 +119,46 @@ namespace Nz::ShaderAst return clone; } + + UInt32 SanitizeVisitor::ToSwizzleIndex(char c) + { + switch (c) + { + case 'r': + case 'x': + case 's': + return 0u; + + case 'g': + case 'y': + case 't': + return 1u; + + case 'b': + case 'z': + case 'p': + return 2u; + + case 'a': + case 'w': + case 'q': + return 3u; + + default: + throw AstError{ "unexpected character '" + std::string(&c, 1) + "' on swizzle " }; + } + } ExpressionPtr SanitizeVisitor::Clone(AccessIdentifierExpression& node) { if (node.identifiers.empty()) - throw AstError{ "AccessIdentifierExpression must have at least one identifier" }; + throw AstError{ "accessIdentifierExpression must have at least one identifier" }; ExpressionPtr indexedExpr = CloneExpression(MandatoryExpr(node.expr)); - for (std::size_t i = 0; i < node.identifiers.size(); ++i) + for (const std::string& identifier : node.identifiers) { - const std::string& identifier = node.identifiers[i]; + if (identifier.empty()) + throw AstError{ "empty identifier" }; const ExpressionType& exprType = GetExpressionType(*indexedExpr); if (IsStructType(exprType)) @@ -171,62 +203,64 @@ namespace Nz::ShaderAst accessIndexPtr->indices.push_back(ShaderBuilder::Constant(fieldIndex)); accessIndexPtr->cachedExpressionType = ResolveType(fieldPtr->type); } - else if (IsVectorType(exprType)) + else if (IsPrimitiveType(exprType) || IsVectorType(exprType)) { // Swizzle expression - const VectorType& swizzledVec = std::get(exprType); + PrimitiveType baseType; + std::size_t componentCount; - 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) + if (IsVectorType(exprType)) { - switch (swizzleStr[j]) - { - case 'r': - case 'x': - case 's': - swizzle->components[j] = 0u; - break; - - case 'g': - case 'y': - case 't': - swizzle->components[j] = 1u; - break; - - case 'b': - case 'z': - case 'p': - swizzle->components[j] = 2u; - break; - - case 'a': - case 'w': - case 'q': - swizzle->components[j] = 3u; - break; - - default: - throw AstError{ "unexpected character '" + std::string(swizzleStr) + "' on swizzle " }; - } + const VectorType& swizzledVec = std::get(exprType); + baseType = swizzledVec.type; + componentCount = swizzledVec.componentCount; + } + else + { + baseType = std::get(exprType); + componentCount = 1; } - indexedExpr = std::move(swizzle); + std::size_t swizzleComponentCount = identifier.size(); + if (swizzleComponentCount > 4) + throw AstError{ "cannot swizzle more than four elements" }; + + if (m_context->options.removeScalarSwizzling && IsPrimitiveType(exprType)) + { + for (std::size_t j = 0; j < swizzleComponentCount; ++j) + { + if (ToSwizzleIndex(identifier[j]) != 0) + throw AstError{ "invalid swizzle" }; + } + + if (swizzleComponentCount == 1) + continue; //< ignore this swizzle (a.x == a) + + // Use a Cast expression to replace swizzle + indexedExpr = CacheResult(std::move(indexedExpr)); //< Since we are going to use a value multiple times, cache it if required + + auto cast = std::make_unique(); + cast->targetType = VectorType{ swizzleComponentCount, baseType }; + for (std::size_t j = 0; j < swizzleComponentCount; ++j) + cast->expressions[j] = CloneExpression(indexedExpr); + + Validate(*cast); + + indexedExpr = std::move(cast); + } + else + { + auto swizzle = std::make_unique(); + swizzle->expression = std::move(indexedExpr); + + swizzle->componentCount = swizzleComponentCount; + for (std::size_t j = 0; j < swizzleComponentCount; ++j) + swizzle->components[j] = ToSwizzleIndex(identifier[j]); + + Validate(*swizzle); + + indexedExpr = std::move(swizzle); + } } else throw AstError{ "unexpected type (only struct and vectors can be indexed with identifiers)" }; //< TODO: Add support for arrays @@ -252,11 +286,11 @@ namespace Nz::ShaderAst MandatoryExpr(node.left); MandatoryExpr(node.right); - if (GetExpressionCategory(*node.left) != ExpressionCategory::LValue) - throw AstError{ "Assignation is only possible with a l-value" }; - auto clone = static_unique_pointer_cast(AstCloner::Clone(node)); + if (GetExpressionCategory(*clone->left) != ExpressionCategory::LValue) + throw AstError{ "Assignation is only possible with a l-value" }; + std::optional binaryType; switch (clone->op) { @@ -366,48 +400,7 @@ namespace Nz::ShaderAst ExpressionPtr SanitizeVisitor::Clone(CastExpression& node) { auto clone = static_unique_pointer_cast(AstCloner::Clone(node)); - - clone->cachedExpressionType = clone->targetType; - clone->targetType = ResolveType(clone->targetType); - - //FIXME: Make proper rules - if (IsMatrixType(clone->targetType) && clone->expressions.front()) - { - const ExpressionType& exprType = GetExpressionType(*clone->expressions.front()); - if (IsMatrixType(exprType) && !clone->expressions[1]) - { - return clone; - } - } - - auto GetComponentCount = [](const ExpressionType& exprType) -> std::size_t - { - if (IsVectorType(exprType)) - return std::get(exprType).componentCount; - else - { - assert(IsPrimitiveType(exprType)); - return 1; - } - }; - - std::size_t componentCount = 0; - std::size_t requiredComponents = GetComponentCount(clone->targetType); - - for (auto& exprPtr : clone->expressions) - { - if (!exprPtr) - break; - - const ExpressionType& exprType = GetExpressionType(*exprPtr); - if (!IsPrimitiveType(exprType) && !IsVectorType(exprType)) - throw AstError{ "incompatible type" }; - - componentCount += GetComponentCount(exprType); - } - - if (componentCount != requiredComponents) - throw AstError{ "component count doesn't match required component count" }; + Validate(*clone); return clone; } @@ -495,38 +488,8 @@ namespace Nz::ShaderAst ExpressionPtr SanitizeVisitor::Clone(SwizzleExpression& node) { - if (node.componentCount > 4) - throw AstError{ "Cannot swizzle more than four elements" }; - - for (UInt32 swizzleIndex : node.components) - { - if (swizzleIndex >= 4) - throw AstError{ "invalid swizzle" }; - } - - MandatoryExpr(node.expression); - auto clone = static_unique_pointer_cast(AstCloner::Clone(node)); - - const ExpressionType& exprType = GetExpressionType(*clone->expression); - if (!IsPrimitiveType(exprType) && !IsVectorType(exprType)) - throw AstError{ "Cannot swizzle this type" }; - - PrimitiveType baseType; - if (IsPrimitiveType(exprType)) - baseType = std::get(exprType); - else - baseType = std::get(exprType).type; - - if (clone->componentCount > 1) - { - clone->cachedExpressionType = VectorType{ - clone->componentCount, - baseType - }; - } - else - clone->cachedExpressionType = baseType; + Validate(*clone); return clone; } @@ -534,50 +497,17 @@ namespace Nz::ShaderAst ExpressionPtr SanitizeVisitor::Clone(UnaryExpression& node) { auto clone = static_unique_pointer_cast(AstCloner::Clone(node)); - - const ExpressionType& exprType = GetExpressionType(MandatoryExpr(clone->expression)); - - switch (node.op) - { - case UnaryType::LogicalNot: - { - if (exprType != ExpressionType(PrimitiveType::Boolean)) - throw AstError{ "logical not is only supported on booleans" }; - - break; - } - - case UnaryType::Minus: - case UnaryType::Plus: - { - ShaderAst::PrimitiveType basicType; - if (IsPrimitiveType(exprType)) - basicType = std::get(exprType); - else if (IsVectorType(exprType)) - basicType = std::get(exprType).type; - else - throw AstError{ "plus and minus unary expressions are only supported on primitive/vectors types" }; - - if (basicType != PrimitiveType::Float32 && basicType != PrimitiveType::Int32 && basicType != PrimitiveType::UInt32) - throw AstError{ "plus and minus unary expressions are only supported on floating points and integers types" }; - - break; - } - } - - clone->cachedExpressionType = exprType; + Validate(*clone); return clone; } ExpressionPtr SanitizeVisitor::Clone(VariableExpression& node) { - if (node.variableId >= m_context->variableTypes.size()) - throw AstError{ "invalid constant index " + std::to_string(node.variableId) }; + auto clone = static_unique_pointer_cast(AstCloner::Clone(node)); + Validate(*clone); - node.cachedExpressionType = m_context->variableTypes[node.variableId]; - - return AstCloner::Clone(node); + return clone; } StatementPtr SanitizeVisitor::Clone(BranchStatement& node) @@ -778,6 +708,9 @@ namespace Nz::ShaderAst m_context->currentFunction = &tempFuncData; + std::vector* previousList = m_context->currentStatementList; + m_context->currentStatementList = &clone->statements; + PushScope(); { for (auto& parameter : clone->parameters) @@ -796,6 +729,7 @@ namespace Nz::ShaderAst } PopScope(); + m_context->currentStatementList = previousList; m_context->currentFunction = nullptr; if (clone->earlyFragmentTests.HasValue() && clone->earlyFragmentTests.GetResultingValue()) @@ -897,33 +831,7 @@ namespace Nz::ShaderAst throw AstError{ "global variables outside of external blocks are forbidden" }; auto clone = static_unique_pointer_cast(AstCloner::Clone(node)); - if (IsNoType(clone->varType)) - { - if (!clone->initialExpression) - throw AstError{ "variable must either have a type or an initial value" }; - - clone->varType = ResolveType(GetExpressionType(*clone->initialExpression)); - } - else - clone->varType = ResolveType(clone->varType); - - if (m_context->options.makeVariableNameUnique && FindIdentifier(clone->varName) != nullptr) - { - // Try to append _X to the variable name until by incrementing X - unsigned int cloneIndex = 2; - std::string candidateName; - do - { - candidateName = clone->varName + "_" + std::to_string(cloneIndex++); - } - while (FindIdentifier(candidateName) != nullptr); - - clone->varName = std::move(candidateName); - } - - clone->varIndex = RegisterVariable(clone->varName, clone->varType); - - SanitizeIdentifier(clone->varName); + Validate(*clone); return clone; } @@ -947,12 +855,18 @@ namespace Nz::ShaderAst StatementPtr SanitizeVisitor::Clone(MultiStatement& node) { - for (auto& statement : node.statements) - MandatoryStatement(statement); - PushScope(); - auto clone = static_unique_pointer_cast(AstCloner::Clone(node)); + auto clone = std::make_unique(); + clone->statements.reserve(node.statements.size()); + + std::vector* previousList = m_context->currentStatementList; + m_context->currentStatementList = &clone->statements; + + for (auto& statement : node.statements) + clone->statements.push_back(AstCloner::Clone(MandatoryStatement(statement))); + + m_context->currentStatementList = previousList; PopScope(); @@ -1009,6 +923,25 @@ namespace Nz::ShaderAst m_context->scopeSizes.pop_back(); } + ExpressionPtr SanitizeVisitor::CacheResult(ExpressionPtr expression) + { + // No need to cache LValues (variables/constants) (TODO: Improve this, as constants doens't need to be cached as well) + if (GetExpressionCategory(*expression) == ExpressionCategory::LValue) + return expression; + + assert(m_context->currentStatementList); + + auto variableDeclaration = ShaderBuilder::DeclareVariable("cachedResult", std::move(expression)); //< Validation will prevent name-clash if required + Validate(*variableDeclaration); + + auto varExpr = std::make_unique(); + varExpr->variableId = *variableDeclaration->varIndex; + + m_context->currentStatementList->push_back(std::move(variableDeclaration)); + + return varExpr; + } + template const T& SanitizeVisitor::ComputeAttributeValue(AttributeValue& attribute) { @@ -1381,6 +1314,83 @@ namespace Nz::ShaderAst node.cachedExpressionType = referenceDeclaration->returnType; } + void SanitizeVisitor::Validate(CastExpression& node) + { + node.cachedExpressionType = node.targetType; + node.targetType = ResolveType(node.targetType); + + // Allow casting a matrix to itself (wtf?) + // FIXME: Make proper rules + if (IsMatrixType(node.targetType) && node.expressions.front()) + { + const ExpressionType& exprType = GetExpressionType(*node.expressions.front()); + if (IsMatrixType(exprType) && !node.expressions[1]) + { + return; + } + } + + auto GetComponentCount = [](const ExpressionType& exprType) -> std::size_t + { + if (IsVectorType(exprType)) + return std::get(exprType).componentCount; + else + { + assert(IsPrimitiveType(exprType)); + return 1; + } + }; + + std::size_t componentCount = 0; + std::size_t requiredComponents = GetComponentCount(node.targetType); + + for (auto& exprPtr : node.expressions) + { + if (!exprPtr) + break; + + const ExpressionType& exprType = GetExpressionType(*exprPtr); + if (!IsPrimitiveType(exprType) && !IsVectorType(exprType)) + throw AstError{ "incompatible type" }; + + componentCount += GetComponentCount(exprType); + } + + if (componentCount != requiredComponents) + throw AstError{ "component count doesn't match required component count" }; + } + + void SanitizeVisitor::Validate(DeclareVariableStatement& node) + { + if (IsNoType(node.varType)) + { + if (!node.initialExpression) + throw AstError{ "variable must either have a type or an initial value" }; + + node.varType = ResolveType(GetExpressionType(*node.initialExpression)); + } + else + node.varType = ResolveType(node.varType); + + if (m_context->options.makeVariableNameUnique && FindIdentifier(node.varName) != nullptr) + { + // Try to make variable name unique by appending _X to its name (incrementing X until it's unique) to the variable name until by incrementing X + unsigned int cloneIndex = 2; + std::string candidateName; + do + { + candidateName = node.varName + "_" + std::to_string(cloneIndex++); + } + while (FindIdentifier(candidateName) != nullptr); + + node.varName = std::move(candidateName); + } + + node.varIndex = RegisterVariable(node.varName, node.varType); + + SanitizeIdentifier(node.varName); + } + void SanitizeVisitor::Validate(IntrinsicExpression& node) { // Parameter validation @@ -1510,6 +1520,90 @@ namespace Nz::ShaderAst } } + void SanitizeVisitor::Validate(SwizzleExpression& node) + { + MandatoryExpr(node.expression); + const ExpressionType& exprType = GetExpressionType(*node.expression); + if (!IsPrimitiveType(exprType) && !IsVectorType(exprType)) + throw AstError{ "Cannot swizzle this type" }; + + PrimitiveType baseType; + std::size_t componentCount; + if (IsPrimitiveType(exprType)) + { + baseType = std::get(exprType); + componentCount = 1; + } + else + { + const VectorType& vecType = std::get(exprType); + baseType = vecType.type; + componentCount = vecType.componentCount; + } + + if (node.componentCount > 4) + throw AstError{ "cannot swizzle more than four elements" }; + + for (UInt32 swizzleIndex : node.components) + { + if (swizzleIndex >= componentCount) + throw AstError{ "invalid swizzle" }; + } + + if (node.componentCount > 1) + { + node.cachedExpressionType = VectorType{ + node.componentCount, + baseType + }; + } + else + node.cachedExpressionType = baseType; + } + + void SanitizeVisitor::Validate(UnaryExpression& node) + { + const ExpressionType& exprType = GetExpressionType(MandatoryExpr(node.expression)); + + switch (node.op) + { + case UnaryType::LogicalNot: + { + if (exprType != ExpressionType(PrimitiveType::Boolean)) + throw AstError{ "logical not is only supported on booleans" }; + + break; + } + + case UnaryType::Minus: + case UnaryType::Plus: + { + ShaderAst::PrimitiveType basicType; + if (IsPrimitiveType(exprType)) + basicType = std::get(exprType); + else if (IsVectorType(exprType)) + basicType = std::get(exprType).type; + else + throw AstError{ "plus and minus unary expressions are only supported on primitive/vectors types" }; + + if (basicType != PrimitiveType::Float32 && basicType != PrimitiveType::Int32 && basicType != PrimitiveType::UInt32) + throw AstError{ "plus and minus unary expressions are only supported on floating points and integers types" }; + + break; + } + } + + node.cachedExpressionType = exprType; + } + + void SanitizeVisitor::Validate(VariableExpression& node) + { + if (node.variableId >= m_context->variableTypes.size()) + throw AstError{ "invalid constant index " + std::to_string(node.variableId) }; + + node.cachedExpressionType = m_context->variableTypes[node.variableId]; + } + ExpressionType SanitizeVisitor::ValidateBinaryOp(BinaryType op, const ExpressionPtr& leftExpr, const ExpressionPtr& rightExpr) { const ExpressionType& leftExprType = GetExpressionType(MandatoryExpr(leftExpr)); diff --git a/src/Nazara/Shader/GlslWriter.cpp b/src/Nazara/Shader/GlslWriter.cpp index 12a96d6b9..8d9e8a9f8 100644 --- a/src/Nazara/Shader/GlslWriter.cpp +++ b/src/Nazara/Shader/GlslWriter.cpp @@ -208,6 +208,7 @@ namespace Nz options.optionValues = std::move(optionValues); options.makeVariableNameUnique = true; options.removeCompoundAssignments = false; + options.removeScalarSwizzling = true; options.reservedIdentifiers = { // All reserved GLSL keywords as of GLSL ES 3.2 "active", "asm", "atomic_uint", "attribute", "bool", "break", "buffer", "bvec2", "bvec3", "bvec4", "case", "cast", "centroid", "class", "coherent", "common", "const", "continue", "default", "discard", "dmat2", "dmat2x2", "dmat2x3", "dmat2x4", "dmat3", "dmat3x2", "dmat3x3", "dmat3x4", "dmat4", "dmat4x2", "dmat4x3", "dmat4x4", "do", "double", "dvec2", "dvec3", "dvec4", "else", "enum", "extern", "external", "false", "filter", "fixed", "flat", "float", "for", "fvec2", "fvec3", "fvec4", "goto", "half", "highp", "hvec2", "hvec3", "hvec4", "if", "iimage1D", "iimage1DArray", "iimage2D", "iimage2DArray", "iimage2DMS", "iimage2DMSArray", "iimage2DRect", "iimage3D", "iimageBuffer", "iimageCube", "iimageCubeArray", "image1D", "image1DArray", "image2D", "image2DArray", "image2DMS", "image2DMSArray", "image2DRect", "image3D", "imageBuffer", "imageCube", "imageCubeArray", "in", "inline", "inout", "input", "int", "interface", "invariant", "isampler1D", "isampler1DArray", "isampler2D", "isampler2DArray", "isampler2DMS", "isampler2DMSArray", "isampler2DRect", "isampler3D", "isamplerBuffer", "isamplerCube", "isamplerCubeArray", "isubpassInput", "isubpassInputMS", "itexture2D", "itexture2DArray", "itexture2DMS", "itexture2DMSArray", "itexture3D", "itextureBuffer", "itextureCube", "itextureCubeArray", "ivec2", "ivec3", "ivec4", "layout", "long", "lowp", "mat2", "mat2x2", "mat2x3", "mat2x4", "mat3", "mat3x2", "mat3x3", "mat3x4", "mat4", "mat4x2", "mat4x3", "mat4x4", "mediump", "namespace", "noinline", "noperspective", "out", "output", "partition", "patch", "precise", "precision", "public", "readonly", "resource", "restrict", "return", "sample", "sampler", "sampler1D", "sampler1DArray", "sampler1DArrayShadow", "sampler1DShadow", "sampler2D", "sampler2DArray", "sampler2DArrayShadow", "sampler2DMS", "sampler2DMSArray", "sampler2DRect", "sampler2DRectShadow", "sampler2DShadow", "sampler3D", "sampler3DRect", "samplerBuffer", "samplerCube", "samplerCubeArray", "samplerCubeArrayShadow", "samplerCubeShadow", "samplerShadow", "shared", "short", "sizeof", "smooth", "static", "struct", "subpassInput", "subpassInputMS", "subroutine", "superp", "switch", "template", "texture2D", "texture2DArray", "texture2DMS", "texture2DMSArray", "texture3D", "textureBuffer", "textureCube", "textureCubeArray", "this", "true", "typedef", "uimage1D", "uimage1DArray", "uimage2D", "uimage2DArray", "uimage2DMS", "uimage2DMSArray", "uimage2DRect", "uimage3D", "uimageBuffer", "uimageCube", "uimageCubeArray", "uint", "uniform", "union", "unsigned", "usampler1D", "usampler1DArray", "usampler2D", "usampler2DArray", "usampler2DMS", "usampler2DMSArray", "usampler2DRect", "usampler3D", "usamplerBuffer", "usamplerCube", "usamplerCubeArray", "using", "usubpassInput", "usubpassInputMS", "utexture2D", "utexture2DArray", "utexture2DMS", "utexture2DMSArray", "utexture3D", "utextureBuffer", "utextureCube", "utextureCubeArray", "uvec2", "uvec3", "uvec4", "varying", "vec2", "vec3", "vec4", "void", "volatile", "while", "writeonly" diff --git a/src/Nazara/Shader/SpirvExpressionStore.cpp b/src/Nazara/Shader/SpirvExpressionStore.cpp index 7bb15e53e..2088b0c9d 100644 --- a/src/Nazara/Shader/SpirvExpressionStore.cpp +++ b/src/Nazara/Shader/SpirvExpressionStore.cpp @@ -147,7 +147,7 @@ namespace Nz std::array newIndices; for (std::size_t i = 0; i < node.componentCount; ++i) { - assert(node.components[i] < node.componentCount); + assert(node.components[i] < swizzledPointer.componentCount); newIndices[i] = swizzledPointer.swizzleIndices[node.components[i]]; }