// // Copyright 2002 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // Scalarize vector and matrix constructor args, so that vectors built from components don't have // matrix arguments, and matrices built from components don't have vector arguments. This avoids // driver bugs around vector and matrix constructors. // #include "compiler/translator/tree_ops/ScalarizeVecAndMatConstructorArgs.h" #include "common/debug.h" #include #include "angle_gl.h" #include "common/angleutils.h" #include "compiler/translator/Compiler.h" #include "compiler/translator/tree_util/IntermNodePatternMatcher.h" #include "compiler/translator/tree_util/IntermNode_util.h" #include "compiler/translator/tree_util/IntermTraverse.h" namespace sh { namespace { TIntermBinary *ConstructVectorIndexBinaryNode(TIntermSymbol *symbolNode, int index) { return new TIntermBinary(EOpIndexDirect, symbolNode, CreateIndexNode(index)); } TIntermBinary *ConstructMatrixIndexBinaryNode(TIntermSymbol *symbolNode, int colIndex, int rowIndex) { TIntermBinary *colVectorNode = ConstructVectorIndexBinaryNode(symbolNode, colIndex); return new TIntermBinary(EOpIndexDirect, colVectorNode, CreateIndexNode(rowIndex)); } class ScalarizeArgsTraverser : public TIntermTraverser { public: ScalarizeArgsTraverser(sh::GLenum shaderType, bool fragmentPrecisionHigh, TSymbolTable *symbolTable) : TIntermTraverser(true, false, false, symbolTable), mShaderType(shaderType), mFragmentPrecisionHigh(fragmentPrecisionHigh), mNodesToScalarize(IntermNodePatternMatcher::kScalarizedVecOrMatConstructor) {} protected: bool visitAggregate(Visit visit, TIntermAggregate *node) override; bool visitBlock(Visit visit, TIntermBlock *node) override; private: void scalarizeArgs(TIntermAggregate *aggregate, bool scalarizeVector, bool scalarizeMatrix); // If we have the following code: // mat4 m(0); // vec4 v(1, m); // We will rewrite to: // mat4 m(0); // mat4 s0 = m; // vec4 v(1, s0[0][0], s0[0][1], s0[0][2]); // This function is to create nodes for "mat4 s0 = m;" and insert it to the code sequence. This // way the possible side effects of the constructor argument will only be evaluated once. TVariable *createTempVariable(TIntermTyped *original); std::vector mBlockStack; sh::GLenum mShaderType; bool mFragmentPrecisionHigh; IntermNodePatternMatcher mNodesToScalarize; }; bool ScalarizeArgsTraverser::visitAggregate(Visit visit, TIntermAggregate *node) { ASSERT(visit == PreVisit); if (mNodesToScalarize.match(node, getParentNode())) { if (node->getType().isVector()) { scalarizeArgs(node, false, true); } else { ASSERT(node->getType().isMatrix()); scalarizeArgs(node, true, false); } } return true; } bool ScalarizeArgsTraverser::visitBlock(Visit visit, TIntermBlock *node) { mBlockStack.push_back(TIntermSequence()); { for (TIntermNode *child : *node->getSequence()) { ASSERT(child != nullptr); child->traverse(this); mBlockStack.back().push_back(child); } } if (mBlockStack.back().size() > node->getSequence()->size()) { node->getSequence()->clear(); *(node->getSequence()) = mBlockStack.back(); } mBlockStack.pop_back(); return false; } void ScalarizeArgsTraverser::scalarizeArgs(TIntermAggregate *aggregate, bool scalarizeVector, bool scalarizeMatrix) { ASSERT(aggregate); ASSERT(!aggregate->isArray()); int size = static_cast(aggregate->getType().getObjectSize()); TIntermSequence *sequence = aggregate->getSequence(); TIntermSequence originalArgs(*sequence); sequence->clear(); for (TIntermNode *originalArgNode : originalArgs) { ASSERT(size > 0); TIntermTyped *originalArg = originalArgNode->getAsTyped(); ASSERT(originalArg); TVariable *argVariable = createTempVariable(originalArg); if (originalArg->isScalar()) { sequence->push_back(CreateTempSymbolNode(argVariable)); size--; } else if (originalArg->isVector()) { if (scalarizeVector) { int repeat = std::min(size, originalArg->getNominalSize()); size -= repeat; for (int index = 0; index < repeat; ++index) { TIntermSymbol *symbolNode = CreateTempSymbolNode(argVariable); TIntermBinary *newNode = ConstructVectorIndexBinaryNode(symbolNode, index); sequence->push_back(newNode); } } else { TIntermSymbol *symbolNode = CreateTempSymbolNode(argVariable); sequence->push_back(symbolNode); size -= originalArg->getNominalSize(); } } else { ASSERT(originalArg->isMatrix()); if (scalarizeMatrix) { int colIndex = 0, rowIndex = 0; int repeat = std::min(size, originalArg->getCols() * originalArg->getRows()); size -= repeat; while (repeat > 0) { TIntermSymbol *symbolNode = CreateTempSymbolNode(argVariable); TIntermBinary *newNode = ConstructMatrixIndexBinaryNode(symbolNode, colIndex, rowIndex); sequence->push_back(newNode); rowIndex++; if (rowIndex >= originalArg->getRows()) { rowIndex = 0; colIndex++; } repeat--; } } else { TIntermSymbol *symbolNode = CreateTempSymbolNode(argVariable); sequence->push_back(symbolNode); size -= originalArg->getCols() * originalArg->getRows(); } } } } TVariable *ScalarizeArgsTraverser::createTempVariable(TIntermTyped *original) { ASSERT(original); TType *type = new TType(original->getType()); type->setQualifier(EvqTemporary); if (mShaderType == GL_FRAGMENT_SHADER && type->getBasicType() == EbtFloat && type->getPrecision() == EbpUndefined) { // We use the highest available precision for the temporary variable // to avoid computing the actual precision using the rules defined // in GLSL ES 1.0 Section 4.5.2. type->setPrecision(mFragmentPrecisionHigh ? EbpHigh : EbpMedium); } TVariable *variable = CreateTempVariable(mSymbolTable, type); ASSERT(mBlockStack.size() > 0); TIntermSequence &sequence = mBlockStack.back(); TIntermDeclaration *declaration = CreateTempInitDeclarationNode(variable, original); sequence.push_back(declaration); return variable; } } // namespace bool ScalarizeVecAndMatConstructorArgs(TCompiler *compiler, TIntermBlock *root, sh::GLenum shaderType, bool fragmentPrecisionHigh, TSymbolTable *symbolTable) { ScalarizeArgsTraverser scalarizer(shaderType, fragmentPrecisionHigh, symbolTable); root->traverse(&scalarizer); return compiler->validateAST(root); } } // namespace sh