static TOperator getMatrixConstructOp(const TIntermTyped& intermediate) { switch (intermediate.getColsCount()) { case 2: switch (intermediate.getRowsCount()) { case 2: return EOpConstructMat2x2; case 3: return EOpConstructMat2x3; case 4: return EOpConstructMat2x4; } break; case 3: switch (intermediate.getRowsCount()) { case 2: return EOpConstructMat3x2; case 3: return EOpConstructMat3x3; case 4: return EOpConstructMat3x4; } break; case 4: switch (intermediate.getRowsCount()) { case 2: return EOpConstructMat4x2; case 3: return EOpConstructMat4x3; case 4: return EOpConstructMat4x4; } break; } assert(false); return EOpNull; }
TIntermTyped *TIntermediate::addComma(TIntermTyped *left, TIntermTyped *right, const TSourceLoc &line, int shaderVersion) { TQualifier resultQualifier = EvqConst; // ESSL3.00 section 12.43: The result of a sequence operator is not a constant-expression. if (shaderVersion >= 300 || left->getQualifier() != EvqConst || right->getQualifier() != EvqConst) { resultQualifier = EvqTemporary; } TIntermTyped *commaNode = nullptr; if (!left->hasSideEffects()) { commaNode = right; } else { commaNode = growAggregate(left, right, line); commaNode->getAsAggregate()->setOp(EOpComma); commaNode->setType(right->getType()); } commaNode->getTypePointer()->setQualifier(resultQualifier); return commaNode; }
void TIntermAggregate::setBuiltInFunctionPrecision() { // All built-ins returning bool should be handled as ops, not functions. ASSERT(getBasicType() != EbtBool); TPrecision precision = EbpUndefined; TIntermSequence::iterator childIter = mSequence.begin(); while (childIter != mSequence.end()) { TIntermTyped *typed = (*childIter)->getAsTyped(); // ESSL spec section 8: texture functions get their precision from the sampler. if (typed && IsSampler(typed->getBasicType())) { precision = typed->getPrecision(); break; } ++childIter; } // ESSL 3.0 spec section 8: textureSize always gets highp precision. // All other functions that take a sampler are assumed to be texture functions. if (mName.find("textureSize") == 0) mType.setPrecision(EbpHigh); else mType.setPrecision(precision); }
TIntermTyped* ir_add_vector_swizzle(TVectorFields& fields, TIntermTyped* arg, TSourceLoc lineDot, TSourceLoc lineIndex) { // swizzle on a constant -> fold it if (arg->getType().getQualifier() == EvqConst) { TIntermTyped* res = ir_add_const_vector_swizzle(fields, arg, lineIndex); if (res) return res; } TIntermTyped* res = NULL; if (fields.num == 1) { TIntermConstant* index = ir_add_constant(TType(EbtInt, EbpUndefined, EvqConst), lineIndex); index->setValue(fields.offsets[0]); res = ir_add_index(EOpIndexDirect, arg, index, lineDot); res->setType(TType(arg->getBasicType(), arg->getPrecision())); } else { TIntermTyped* index = ir_add_swizzle(fields, lineIndex); res = ir_add_index(EOpVectorSwizzle, arg, index, lineDot); res->setType(TType(arg->getBasicType(), arg->getPrecision(), EvqTemporary, 1, fields.num)); } return res; }
bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate* node) { bool visitChildren = true; switch (node->getOp()) { case EOpSequence: // We need to visit sequence children to get to global or inner scope. visitChildren = true; break; case EOpDeclaration: { const TIntermSequence& sequence = node->getSequence(); TQualifier qualifier = sequence.front()->getAsTyped()->getQualifier(); if ((qualifier == EvqInvariantVaryingIn) || (qualifier == EvqInvariantVaryingOut)) { updateVersion(GLSL_VERSION_120); } break; } case EOpParameters: { const TIntermSequence& params = node->getSequence(); for (TIntermSequence::const_iterator iter = params.begin(); iter != params.end(); ++iter) { const TIntermTyped* param = (*iter)->getAsTyped(); if (param->isArray()) { TQualifier qualifier = param->getQualifier(); if ((qualifier == EvqOut) || (qualifier == EvqInOut)) { updateVersion(GLSL_VERSION_120); break; } } } // Fully processed. No need to visit children. visitChildren = false; break; } case EOpConstructMat2: case EOpConstructMat3: case EOpConstructMat4: { const TIntermSequence& sequence = node->getSequence(); if (sequence.size() == 1) { TIntermTyped* typed = sequence.front()->getAsTyped(); if (typed && typed->isMatrix()) { updateVersion(GLSL_VERSION_120); } } break; } default: break; } return visitChildren; }
// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value // for the parameter to the constructor (passed to this function). Essentially, it converts // the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a // float, then float is converted to int. // // Returns 0 for an error or the constructed node. // TIntermTyped* TParseContext::constructBuiltIn(const TType* type, TOperator op, TIntermNode* node, TSourceLoc line, bool subset) { TIntermTyped* newNode; TOperator basicOp; // // First, convert types as needed. // switch (op) { case EOpConstructVec2: case EOpConstructVec3: case EOpConstructVec4: case EOpConstructMat2: case EOpConstructMat3: case EOpConstructMat4: case EOpConstructFloat: basicOp = EOpConstructFloat; break; case EOpConstructIVec2: case EOpConstructIVec3: case EOpConstructIVec4: case EOpConstructInt: basicOp = EOpConstructInt; break; case EOpConstructBVec2: case EOpConstructBVec3: case EOpConstructBVec4: case EOpConstructBool: basicOp = EOpConstructBool; break; default: error(line, "unsupported construction", "", ""); recover(); return 0; } newNode = intermediate.addUnaryMath(basicOp, node, node->getLine(), symbolTable); if (newNode == 0) { error(line, "can't convert", "constructor", ""); return 0; } // // Now, if there still isn't an operation to do the construction, and we need one, add one. // // Otherwise, skip out early. if (subset || (newNode != node && newNode->getType() == *type)) return newNode; // setAggregateOperator will insert a new node for the constructor, as needed. return intermediate.setAggregateOperator(newNode, op, line); }
TIntermTyped* TIntermediate::addComma(TIntermTyped* left, TIntermTyped* right, TSourceLoc line) { if (left->getType().getQualifier() == EvqConst && right->getType().getQualifier() == EvqConst) { return right; } else { TIntermTyped *commaAggregate = growAggregate(left, right, line); commaAggregate->getAsAggregate()->setOp(EOpComma); commaAggregate->setType(right->getType()); commaAggregate->getTypePointer()->setQualifier(EvqTemporary); return commaAggregate; } }
bool ValidateLimitations::validateIndexing(TIntermBinary* node) { ASSERT((node->getOp() == EOpIndexDirect) || (node->getOp() == EOpIndexIndirect)); bool valid = true; TIntermTyped* index = node->getRight(); // The index expression must have integral type. if (!index->isScalar() || (index->getBasicType() != EbtInt)) { error(index->getLine(), "Index expression must have integral type", index->getCompleteString().c_str()); valid = false; } // The index expession must be a constant-index-expression unless // the operand is a uniform in a vertex shader. TIntermTyped* operand = node->getLeft(); bool skip = (mShaderType == SH_VERTEX_SHADER) && (operand->getQualifier() == EvqUniform); if (!skip && !isConstIndexExpr(index)) { error(index->getLine(), "Index expression must be constant", "[]"); valid = false; } return valid; }
TIntermTyped* ir_add_comma(TIntermTyped* left, TIntermTyped* right, TSourceLoc line) { if (left->getType().getQualifier() == EvqConst && right->getType().getQualifier() == EvqConst) { return right; } else { TIntermTyped *commaAggregate = ir_grow_aggregate(left, right, line); commaAggregate->getAsAggregate()->setOperator(EOpComma); commaAggregate->setType(right->getType()); commaAggregate->getTypePointer()->changeQualifier(EvqTemporary); return commaAggregate; } }
bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node) { if (node->getOp() == EOpConstruct && node->getType().isMatrix()) { const TIntermSequence &sequence = *(node->getSequence()); if (sequence.size() == 1) { TIntermTyped *typed = sequence.front()->getAsTyped(); if (typed && typed->isMatrix()) { ensureVersionIsAtLeast(GLSL_VERSION_120); } } } return true; }
// // Make a constant vector node or constant scalar node, representing a given // constant vector and constant swizzle into it. // TIntermTyped* TIntermediate::foldSwizzle(TIntermTyped* node, TVectorFields& fields, const TSourceLoc& loc) { const TConstUnionArray& unionArray = node->getAsConstantUnion()->getConstArray(); TConstUnionArray constArray(fields.num); for (int i = 0; i < fields.num; i++) constArray[i] = unionArray[fields.offsets[i]]; TIntermTyped* result = addConstantUnion(constArray, node->getType(), loc); if (result == 0) result = node; else result->setType(TType(node->getBasicType(), EvqConst, fields.num)); return result; }
TIntermDeclaration* ir_grow_declaration(TIntermDeclaration* declaration, TIntermSymbol *symbol, TIntermTyped *initializer, TInfoSink& infoSink) { TIntermTyped* added_decl = symbol; if (initializer) added_decl = ir_add_assign(EOpAssign, symbol, initializer, symbol->getLine(), infoSink); if (declaration->isSingleDeclaration()) { TIntermTyped* current = declaration->getDeclaration(); TIntermAggregate* aggregate = ir_make_aggregate(current, current->getLine()); aggregate->setOperator(EOpComma); declaration->getDeclaration() = aggregate; } TIntermAggregate* aggregate = ir_grow_aggregate(declaration->getDeclaration(), added_decl, added_decl->getLine()); aggregate->setOperator(EOpComma); declaration->getDeclaration() = aggregate; return declaration; }
void TIntermAggregate::setPrecisionFromChildren() { if (getBasicType() == EbtBool) { mType.setPrecision(EbpUndefined); return; } TPrecision precision = EbpUndefined; TIntermSequence::iterator childIter = mSequence.begin(); while (childIter != mSequence.end()) { TIntermTyped *typed = (*childIter)->getAsTyped(); if (typed) precision = GetHigherPrecision(typed->getPrecision(), precision); ++childIter; } mType.setPrecision(precision); }
void TDependencyGraphBuilder::visitAssignment(TIntermBinary* intermAssignment) { TIntermTyped* intermLeft = intermAssignment->getLeft(); if (!intermLeft) return; TGraphSymbol* leftmostSymbol = NULL; { TNodeSetMaintainer nodeSetMaintainer(this); { TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mLeftSubtree); intermLeft->traverse(this); leftmostSymbol = mLeftmostSymbols.top(); // After traversing the left subtree of this assignment, we should have found a real // leftmost symbol, and the leftmost symbol should not be a placeholder. ASSERT(leftmostSymbol != &mLeftSubtree); ASSERT(leftmostSymbol != &mRightSubtree); } if (TIntermTyped* intermRight = intermAssignment->getRight()) { TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mRightSubtree); intermRight->traverse(this); } if (TParentNodeSet* assignmentNodes = mNodeSets.getTopSet()) connectMultipleNodesToSingleNode(assignmentNodes, leftmostSymbol); } // Push the leftmost symbol of this assignment into the current set of dependent symbols to // represent the result of this assignment. // An expression like "a = (b = c)" will yield a dependency graph like "c -> b -> a". // This line essentially passes the leftmost symbol of the nested assignment ("b" in this // example) back up to the earlier visitAssignment call for the outer assignment, which will // create the connection "b -> a". mNodeSets.insertIntoTopSet(leftmostSymbol); }
bool TIntermSelection::promoteTernary(TInfoSink& infoSink) { if (!condition->isVector()) return true; int size = condition->getRowsCount(); TIntermTyped* trueb = trueBlock->getAsTyped(); TIntermTyped* falseb = falseBlock->getAsTyped(); if (!trueb || !falseb) return false; if (trueb->getRowsCount() == size && falseb->getRowsCount() == size) return true; // Base assumption: just make the type a float vector TPrecision higherPrecision = GetHigherPrecision(trueb->getPrecision(), falseb->getPrecision()); setType(TType(EbtFloat, higherPrecision, EvqTemporary, 1, size, condition->isMatrix())); TOperator convert = EOpNull; { convert = TOperator( EOpConstructVec2 + size - 2); TIntermAggregate *node = new TIntermAggregate(convert); node->setLine(trueb->getLine()); node->setType(TType(condition->getBasicType(), higherPrecision, trueb->getQualifier() == EvqConst ? EvqConst : EvqTemporary, 1, size, condition->isMatrix())); node->getNodes().push_back(trueb); trueBlock = node; } { convert = TOperator( EOpConstructVec2 + size - 2); TIntermAggregate *node = new TIntermAggregate(convert); node->setLine(falseb->getLine()); node->setType(TType(condition->getBasicType(), higherPrecision, falseb->getQualifier() == EvqConst ? EvqConst : EvqTemporary, 1, size, condition->isMatrix())); node->getNodes().push_back(falseb); falseBlock = node; } return true; }
// // Constant folding of a bracket (array-style) dereference or struct-like dot // dereference. Can handle any thing except a multi-character swizzle, though // all swizzles may go to foldSwizzle(). // TIntermTyped* TIntermediate::foldDereference(TIntermTyped* node, int index, const TSourceLoc& loc) { TType dereferencedType(node->getType(), index); dereferencedType.getQualifier().storage = EvqConst; TIntermTyped* result = 0; int size = dereferencedType.computeNumComponents(); int start; if (node->isStruct()) { start = 0; for (int i = 0; i < index; ++i) start += (*node->getType().getStruct())[i].type->computeNumComponents(); } else start = size * index; result = addConstantUnion(TConstUnionArray(node->getAsConstantUnion()->getConstArray(), start, size), node->getType(), loc); if (result == 0) result = node; else result->setType(dereferencedType); return result; }
bool CollectVariables::visitBinary(Visit, TIntermBinary *binaryNode) { if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock) { // NOTE: we do not determine static use for individual blocks of an array TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped(); ASSERT(blockNode); TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion(); ASSERT(constantUnion); const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock(); InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks); ASSERT(namedBlock); namedBlock->staticUse = true; unsigned int fieldIndex = constantUnion->getUConst(0); ASSERT(fieldIndex < namedBlock->fields.size()); namedBlock->fields[fieldIndex].staticUse = true; return false; } return true; }
// constructor // : type argument_list // bool HlslGrammar::acceptConstructor(TIntermTyped*& node) { // type TType type; if (acceptType(type)) { TFunction* constructorFunction = parseContext.handleConstructorCall(token.loc, type); if (constructorFunction == nullptr) return false; // arguments TIntermTyped* arguments = nullptr; if (! acceptArguments(constructorFunction, arguments)) { expected("constructor arguments"); return false; } // hook it up node = parseContext.handleFunctionCall(arguments->getLoc(), constructorFunction, arguments); return true; } return false; }
bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate* node) { bool visitChildren = true; switch (node->getOp()) { case EOpSequence: // We need to visit sequence children to get to global or inner scope. visitChildren = true; break; case EOpDeclaration: { const TIntermSequence& sequence = node->getSequence(); TQualifier qualifier = sequence.front()->getAsTyped()->getQualifier(); if ((qualifier == EvqInvariantVaryingIn) || (qualifier == EvqInvariantVaryingOut)) { updateVersion(GLSL_VERSION_120); } break; } case EOpConstructMat2: case EOpConstructMat3: case EOpConstructMat4: { const TIntermSequence& sequence = node->getSequence(); if (sequence.size() == 1) { TIntermTyped* typed = sequence.front()->getAsTyped(); if (typed && typed->isMatrix()) { updateVersion(GLSL_VERSION_120); } } break; } default: break; } return visitChildren; }
bool IntermNodePatternMatcher::match(TIntermDeclaration *node) { if ((mMask & kMultiDeclaration) != 0) { if (node->getSequence()->size() > 1) { return true; } } if ((mMask & kArrayDeclaration) != 0) { if (node->getSequence()->front()->getAsTyped()->getType().isStructureContainingArrays()) { return true; } // Need to check from all declarators whether they are arrays since that may vary between // declarators. for (TIntermNode *declarator : *node->getSequence()) { if (declarator->getAsTyped()->isArray()) { return true; } } } if ((mMask & kNamelessStructDeclaration) != 0) { TIntermTyped *declarator = node->getSequence()->front()->getAsTyped(); if (declarator->getBasicType() == EbtStruct && declarator->getType().getStruct()->name() == "") { return true; } } return false; }
static TOperator getMatrixConstructOp(const TIntermTyped& intermediate, TParseContext& ctx) { // before GLSL 1.20, only square matrices if (ctx.targetVersion < ETargetGLSL_120) { const int c = intermediate.getColsCount(); const int r = intermediate.getRowsCount(); if (c == 2 && r == 2) return EOpConstructMat2x2FromMat; if (c == 3 && r == 3) return EOpConstructMat3x3FromMat; if (c == 4 && r == 4) return EOpConstructMat4x4; ctx.error(intermediate.getLine(), " non-square matrices not supported", "", "(%ix%i)", r, c); return EOpNull; } switch (intermediate.getColsCount()) { case 2: switch (intermediate.getRowsCount()) { case 2: return EOpConstructMat2x2; case 3: return EOpConstructMat2x3; case 4: return EOpConstructMat2x4; } break; case 3: switch (intermediate.getRowsCount()) { case 2: return EOpConstructMat3x2; case 3: return EOpConstructMat3x3; case 4: return EOpConstructMat3x4; } break; case 4: switch (intermediate.getRowsCount()) { case 2: return EOpConstructMat4x2; case 3: return EOpConstructMat4x3; case 4: return EOpConstructMat4x4; } break; } assert(false); return EOpNull; }
// Special case for matrix[idx1][idx2]: output as matrix[idx2][idx1] static bool Check2DMatrixIndex (TGlslOutputTraverser* goit, std::stringstream& out, TIntermTyped* left, TIntermTyped* right) { if (left->isVector() && !left->isArray()) { TIntermBinary* leftBin = left->getAsBinaryNode(); if (leftBin && (leftBin->getOp() == EOpIndexDirect || leftBin->getOp() == EOpIndexIndirect)) { TIntermTyped* superLeft = leftBin->getLeft(); TIntermTyped* superRight = leftBin->getRight(); if (superLeft->isMatrix() && !superLeft->isArray()) { superLeft->traverse (goit); out << "["; right->traverse(goit); out << "]["; superRight->traverse(goit); out << "]"; return true; } } } return false; }
bool TGlslOutputTraverser::traverseBinary( bool preVisit, TIntermBinary *node, TIntermTraverser *it ) { TString op = "??"; TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it); GlslFunction *current = goit->current; std::stringstream& out = current->getActiveOutput(); bool infix = true; bool assign = false; bool needsParens = true; switch (node->getOp()) { case EOpAssign: op = "="; infix = true; needsParens = false; break; case EOpAddAssign: op = "+="; infix = true; needsParens = false; break; case EOpSubAssign: op = "-="; infix = true; needsParens = false; break; case EOpMulAssign: op = "*="; infix = true; needsParens = false; break; case EOpVectorTimesMatrixAssign: op = "*="; infix = true; needsParens = false; break; case EOpVectorTimesScalarAssign: op = "*="; infix = true; needsParens = false; break; case EOpMatrixTimesScalarAssign: op = "*="; infix = true; needsParens = false; break; case EOpMatrixTimesMatrixAssign: op = "*="; infix = true; needsParens = false; break; case EOpDivAssign: op = "/="; infix = true; needsParens = false; break; case EOpModAssign: op = "%="; infix = true; needsParens = false; break; case EOpAndAssign: op = "&="; infix = true; needsParens = false; break; case EOpInclusiveOrAssign: op = "|="; infix = true; needsParens = false; break; case EOpExclusiveOrAssign: op = "^="; infix = true; needsParens = false; break; case EOpLeftShiftAssign: op = "<<="; infix = true; needsParens = false; break; case EOpRightShiftAssign: op = "??="; infix = true; needsParens = false; break; case EOpIndexDirect: { TIntermTyped *left = node->getLeft(); TIntermTyped *right = node->getRight(); assert( left && right); current->beginStatement(); if (Check2DMatrixIndex (goit, out, left, right)) return false; if (left->isMatrix() && !left->isArray()) { if (right->getAsConstant()) { current->addLibFunction (EOpMatrixIndex); out << "xll_matrixindex ("; left->traverse(goit); out << ", "; right->traverse(goit); out << ")"; return false; } else { current->addLibFunction (EOpTranspose); current->addLibFunction (EOpMatrixIndex); current->addLibFunction (EOpMatrixIndexDynamic); out << "xll_matrixindexdynamic ("; left->traverse(goit); out << ", "; right->traverse(goit); out << ")"; return false; } } left->traverse(goit); // Special code for handling a vector component select (this improves readability) if (left->isVector() && !left->isArray() && right->getAsConstant()) { char swiz[] = "xyzw"; goit->visitConstantUnion = TGlslOutputTraverser::traverseImmediateConstant; goit->generatingCode = false; right->traverse(goit); assert( goit->indexList.size() == 1); assert( goit->indexList[0] < 4); out << "." << swiz[goit->indexList[0]]; goit->indexList.clear(); goit->visitConstantUnion = TGlslOutputTraverser::traverseConstantUnion; goit->generatingCode = true; } else { out << "["; right->traverse(goit); out << "]"; } return false; } case EOpIndexIndirect: { TIntermTyped *left = node->getLeft(); TIntermTyped *right = node->getRight(); current->beginStatement(); if (Check2DMatrixIndex (goit, out, left, right)) return false; if (left && right && left->isMatrix() && !left->isArray()) { if (right->getAsConstant()) { current->addLibFunction (EOpMatrixIndex); out << "xll_matrixindex ("; left->traverse(goit); out << ", "; right->traverse(goit); out << ")"; return false; } else { current->addLibFunction (EOpTranspose); current->addLibFunction (EOpMatrixIndex); current->addLibFunction (EOpMatrixIndexDynamic); out << "xll_matrixindexdynamic ("; left->traverse(goit); out << ", "; right->traverse(goit); out << ")"; return false; } } if (left) left->traverse(goit); out << "["; if (right) right->traverse(goit); out << "]"; return false; } case EOpIndexDirectStruct: { current->beginStatement(); GlslStruct *s = goit->createStructFromType(node->getLeft()->getTypePointer()); if (node->getLeft()) node->getLeft()->traverse(goit); // The right child is always an offset into the struct, switch to get an // immediate constant, and put it back afterwords goit->visitConstantUnion = TGlslOutputTraverser::traverseImmediateConstant; goit->generatingCode = false; if (node->getRight()) { node->getRight()->traverse(goit); assert( goit->indexList.size() == 1); assert( goit->indexList[0] < s->memberCount()); out << "." << s->getMember(goit->indexList[0]).name; } goit->indexList.clear(); goit->visitConstantUnion = TGlslOutputTraverser::traverseConstantUnion; goit->generatingCode = true; } return false; case EOpVectorSwizzle: current->beginStatement(); if (node->getLeft()) node->getLeft()->traverse(goit); goit->visitConstantUnion = TGlslOutputTraverser::traverseImmediateConstant; goit->generatingCode = false; if (node->getRight()) { node->getRight()->traverse(goit); assert( goit->indexList.size() <= 4); out << '.'; const char fields[] = "xyzw"; for (int ii = 0; ii < (int)goit->indexList.size(); ii++) { int val = goit->indexList[ii]; assert( val >= 0); assert( val < 4); out << fields[val]; } } goit->indexList.clear(); goit->visitConstantUnion = TGlslOutputTraverser::traverseConstantUnion; goit->generatingCode = true; return false; case EOpMatrixSwizzle: // This presently only works for swizzles as rhs operators if (node->getRight()) { goit->visitConstantUnion = TGlslOutputTraverser::traverseImmediateConstant; goit->generatingCode = false; node->getRight()->traverse(goit); goit->visitConstantUnion = TGlslOutputTraverser::traverseConstantUnion; goit->generatingCode = true; std::vector<int> elements = goit->indexList; goit->indexList.clear(); if (elements.size() > 4 || elements.size() < 1) { goit->infoSink.info << "Matrix swizzle operations can must contain at least 1 and at most 4 element selectors."; return true; } unsigned column[4] = {0}, row[4] = {0}; for (unsigned i = 0; i != elements.size(); ++i) { unsigned val = elements[i]; column[i] = val % 4; row[i] = val / 4; } bool sameColumn = true; for (unsigned i = 1; i != elements.size(); ++i) sameColumn &= column[i] == column[i-1]; static const char* fields = "xyzw"; if (sameColumn) { //select column, then swizzle row if (node->getLeft()) node->getLeft()->traverse(goit); out << "[" << column[0] << "]."; for (unsigned i = 0; i < elements.size(); ++i) out << fields[row[i]]; } else { // Insert constructor, and dereference individually // Might need to account for different types here assert( elements.size() != 1); //should have hit same collumn case out << "vec" << elements.size() << "("; if (node->getLeft()) node->getLeft()->traverse(goit); out << "[" << column[0] << "]."; out << fields[row[0]]; for (unsigned i = 1; i < elements.size(); ++i) { out << ", "; if (node->getLeft()) node->getLeft()->traverse(goit); out << "[" << column[i] << "]."; out << fields[row[i]]; } out << ")"; } } return false; case EOpAdd: op = "+"; infix = true; break; case EOpSub: op = "-"; infix = true; break; case EOpMul: op = "*"; infix = true; break; case EOpDiv: op = "/"; infix = true; break; case EOpMod: op = "mod"; infix = false; break; case EOpRightShift: op = "<<"; infix = true; break; case EOpLeftShift: op = ">>"; infix = true; break; case EOpAnd: op = "&"; infix = true; break; case EOpInclusiveOr: op = "|"; infix = true; break; case EOpExclusiveOr: op = "^"; infix = true; break; case EOpEqual: writeComparison ( "==", "equal", node, goit ); return false; case EOpNotEqual: writeComparison ( "!=", "notEqual", node, goit ); return false; case EOpLessThan: writeComparison ( "<", "lessThan", node, goit ); return false; case EOpGreaterThan: writeComparison ( ">", "greaterThan", node, goit ); return false; case EOpLessThanEqual: writeComparison ( "<=", "lessThanEqual", node, goit ); return false; case EOpGreaterThanEqual: writeComparison ( ">=", "greaterThanEqual", node, goit ); return false; case EOpVectorTimesScalar: op = "*"; infix = true; break; case EOpVectorTimesMatrix: op = "*"; infix = true; break; case EOpMatrixTimesVector: op = "*"; infix = true; break; case EOpMatrixTimesScalar: op = "*"; infix = true; break; case EOpMatrixTimesMatrix: op = "*"; infix = true; break; case EOpLogicalOr: op = "||"; infix = true; break; case EOpLogicalXor: op = "^^"; infix = true; break; case EOpLogicalAnd: op = "&&"; infix = true; break; default: assert(0); } current->beginStatement(); if (infix) { // special case for swizzled matrix assignment if (node->getOp() == EOpAssign && node->getLeft() && node->getRight()) { TIntermBinary* lval = node->getLeft()->getAsBinaryNode(); if (lval && lval->getOp() == EOpMatrixSwizzle) { static const char* vec_swizzles = "xyzw"; TIntermTyped* rval = node->getRight(); TIntermTyped* lexp = lval->getLeft(); goit->visitConstantUnion = TGlslOutputTraverser::traverseImmediateConstant; goit->generatingCode = false; lval->getRight()->traverse(goit); goit->visitConstantUnion = TGlslOutputTraverser::traverseConstantUnion; goit->generatingCode = true; std::vector<int> swizzles = goit->indexList; goit->indexList.clear(); char temp_rval[128]; unsigned n_swizzles = swizzles.size(); if (n_swizzles > 1) { snprintf(temp_rval, 128, "xlat_swiztemp%d", goit->swizzleAssignTempCounter++); current->beginStatement(); out << "vec" << n_swizzles << " " << temp_rval << " = "; rval->traverse(goit); current->endStatement(); } for (unsigned i = 0; i != n_swizzles; ++i) { unsigned col = swizzles[i] / 4; unsigned row = swizzles[i] % 4; current->beginStatement(); lexp->traverse(goit); out << "[" << row << "][" << col << "] = "; if (n_swizzles > 1) out << temp_rval << "." << vec_swizzles[i]; else rval->traverse(goit); current->endStatement(); } return false; } } if (needsParens) out << '('; if (node->getLeft()) node->getLeft()->traverse(goit); out << ' ' << op << ' '; if (node->getRight()) node->getRight()->traverse(goit); if (needsParens) out << ')'; } else { if (assign) { // Need to traverse the left child twice to allow for the assign and the op // This is OK, because we know it is an lvalue if (node->getLeft()) node->getLeft()->traverse(goit); out << " = " << op << '('; if (node->getLeft()) node->getLeft()->traverse(goit); out << ", "; if (node->getRight()) node->getRight()->traverse(goit); out << ')'; } else { out << op << '('; if (node->getLeft()) node->getLeft()->traverse(goit); out << ", "; if (node->getRight()) node->getRight()->traverse(goit); out << ')'; } } return false; }
void ScalarizeVecAndMatConstructorArgs::scalarizeArgs( TIntermAggregate *aggregate, bool scalarizeVector, bool scalarizeMatrix) { ASSERT(aggregate); int size = 0; switch (aggregate->getOp()) { case EOpConstructVec2: case EOpConstructBVec2: case EOpConstructIVec2: size = 2; break; case EOpConstructVec3: case EOpConstructBVec3: case EOpConstructIVec3: size = 3; break; case EOpConstructVec4: case EOpConstructBVec4: case EOpConstructIVec4: case EOpConstructMat2: size = 4; break; case EOpConstructMat2x3: case EOpConstructMat3x2: size = 6; break; case EOpConstructMat2x4: case EOpConstructMat4x2: size = 8; break; case EOpConstructMat3: size = 9; break; case EOpConstructMat3x4: case EOpConstructMat4x3: size = 12; break; case EOpConstructMat4: size = 16; break; default: break; } TIntermSequence *sequence = aggregate->getSequence(); TIntermSequence original(*sequence); sequence->clear(); for (size_t ii = 0; ii < original.size(); ++ii) { ASSERT(size > 0); TIntermTyped *node = original[ii]->getAsTyped(); ASSERT(node); TString varName = createTempVariable(node); if (node->isScalar()) { TIntermSymbol *symbolNode = new TIntermSymbol(-1, varName, node->getType()); sequence->push_back(symbolNode); size--; } else if (node->isVector()) { if (scalarizeVector) { int repeat = std::min(size, node->getNominalSize()); size -= repeat; for (int index = 0; index < repeat; ++index) { TIntermSymbol *symbolNode = new TIntermSymbol(-1, varName, node->getType()); TIntermBinary *newNode = ConstructVectorIndexBinaryNode( symbolNode, index); sequence->push_back(newNode); } } else { TIntermSymbol *symbolNode = new TIntermSymbol(-1, varName, node->getType()); sequence->push_back(symbolNode); size -= node->getNominalSize(); } } else { ASSERT(node->isMatrix()); if (scalarizeMatrix) { int colIndex = 0, rowIndex = 0; int repeat = std::min(size, node->getCols() * node->getRows()); size -= repeat; while (repeat > 0) { TIntermSymbol *symbolNode = new TIntermSymbol(-1, varName, node->getType()); TIntermBinary *newNode = ConstructMatrixIndexBinaryNode( symbolNode, colIndex, rowIndex); sequence->push_back(newNode); rowIndex++; if (rowIndex >= node->getRows()) { rowIndex = 0; colIndex++; } repeat--; } } else { TIntermSymbol *symbolNode = new TIntermSymbol(-1, varName, node->getType()); sequence->push_back(symbolNode); size -= node->getCols() * node->getRows(); } } } }
// // Add one node as the parent of another that it operates on. // // Returns the added node. // TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermNode* childNode, TSourceLoc line, TSymbolTable& symbolTable) { TIntermUnary* node; TIntermTyped* child = childNode->getAsTyped(); if (child == 0) { infoSink.info.message(EPrefixInternalError, "Bad type in AddUnaryMath", line); return 0; } switch (op) { case EOpLogicalNot: if (child->getType().getBasicType() != EbtBool || child->getType().isMatrix() || child->getType().isArray() || child->getType().isVector()) { return 0; } break; case EOpPostIncrement: case EOpPreIncrement: case EOpPostDecrement: case EOpPreDecrement: case EOpNegative: if (child->getType().getBasicType() == EbtStruct || child->getType().isArray()) return 0; default: break; } // // Do we need to promote the operand? // // Note: Implicit promotions were removed from the language. // TBasicType newType = EbtVoid; switch (op) { case EOpConstructInt: newType = EbtInt; break; case EOpConstructBool: newType = EbtBool; break; case EOpConstructFloat: newType = EbtFloat; break; default: break; } if (newType != EbtVoid) { child = addConversion(op, TType(newType, child->getPrecision(), EvqTemporary, child->getNominalSize(), child->isMatrix(), child->isArray()), child); if (child == 0) return 0; } // // For constructors, we are now done, it's all in the conversion. // switch (op) { case EOpConstructInt: case EOpConstructBool: case EOpConstructFloat: return child; default: break; } TIntermConstantUnion *childTempConstant = 0; if (child->getAsConstantUnion()) childTempConstant = child->getAsConstantUnion(); // // Make a new node for the operator. // node = new TIntermUnary(op); if (line == 0) line = child->getLine(); node->setLine(line); node->setOperand(child); if (! node->promote(infoSink)) return 0; if (childTempConstant) { TIntermTyped* newChild = childTempConstant->fold(op, 0, infoSink); if (newChild) return newChild; } return node; }
// Add one node as the parent of another that it operates on. TIntermTyped* ir_add_unary_math(TOperator op, TIntermNode* childNode, TSourceLoc line, TParseContext& ctx) { TIntermUnary* node; TIntermTyped* child = childNode->getAsTyped(); if (child == 0) { ctx.infoSink.info.message(EPrefixInternalError, "Bad type in AddUnaryMath", line); return 0; } switch (op) { case EOpLogicalNot: if (!child->isScalar()) return 0; break; case EOpPostIncrement: case EOpPreIncrement: case EOpPostDecrement: case EOpPreDecrement: case EOpNegative: if (child->getType().getBasicType() == EbtStruct || child->getType().isArray()) return 0; default: break; } // // Do we need to promote the operand? // // Note: Implicit promotions were removed from the language. // TBasicType newType = EbtVoid; switch (op) { case EOpConstructInt: newType = EbtInt; break; case EOpConstructBool: newType = EbtBool; break; case EOpConstructFloat: newType = EbtFloat; break; case EOpLogicalNot: newType = EbtBool; break; default: break; } if (newType != EbtVoid) { child = ir_add_conversion(op, TType(newType, child->getPrecision(), EvqTemporary, child->getColsCount(), child->getRowsCount(), child->isMatrix(), child->isArray()), child, ctx.infoSink); if (child == 0) return 0; } // // For constructors, we are now done, it's all in the conversion. // switch (op) { case EOpConstructInt: case EOpConstructBool: case EOpConstructFloat: return child; default: break; } TIntermConstant* childConst = child->getAsConstant(); // // Make a new node for the operator. // node = new TIntermUnary(op); if (line.line == 0) line = child->getLine(); node->setLine(line); node->setOperand(child); if (! node->promote(ctx)) return 0; // // See if we can fold constants if (childConst) { TIntermConstant* FoldUnaryConstantExpression(TOperator op, TIntermConstant* node); TIntermConstant* res = FoldUnaryConstantExpression(node->getOp(), childConst); if (res) { delete node; return res; } } return node; }
// // Add one node as the parent of another that it operates on. // // Returns the added node. // TIntermTyped *TIntermediate::addUnaryMath( TOperator op, TIntermNode *childNode, const TSourceLoc &line) { TIntermUnary *node; TIntermTyped *child = childNode->getAsTyped(); if (child == NULL) { mInfoSink.info.message(EPrefixInternalError, line, "Bad type in AddUnaryMath"); return NULL; } switch (op) { case EOpLogicalNot: if (child->getType().getBasicType() != EbtBool || child->getType().isMatrix() || child->getType().isArray() || child->getType().isVector()) { return NULL; } break; case EOpPostIncrement: case EOpPreIncrement: case EOpPostDecrement: case EOpPreDecrement: case EOpNegative: case EOpPositive: if (child->getType().getBasicType() == EbtStruct || child->getType().isArray()) { return NULL; } default: break; } TIntermConstantUnion *childTempConstant = 0; if (child->getAsConstantUnion()) childTempConstant = child->getAsConstantUnion(); // // Make a new node for the operator. // node = new TIntermUnary(op); node->setLine(line); node->setOperand(child); if (!node->promote(mInfoSink)) return 0; if (childTempConstant) { TIntermTyped *newChild = childTempConstant->fold(op, 0, mInfoSink); if (newChild) return newChild; } return node; }
// // Make sure there is enough data provided to the constructor to build // something of the type of the constructor. Also returns the type of // the constructor. // // Returns true if there was an error in construction. // bool TParseContext::constructorErrorCheck(int line, TIntermNode* node, TFunction& function, TOperator op, TType* type) { *type = function.getReturnType(); bool constructingMatrix = false; switch(op) { case EOpConstructMat2: case EOpConstructMat3: case EOpConstructMat4: constructingMatrix = true; break; default: break; } // // Note: It's okay to have too many components available, but not okay to have unused // arguments. 'full' will go to true when enough args have been seen. If we loop // again, there is an extra argument, so 'overfull' will become true. // int size = 0; bool constType = true; bool full = false; bool overFull = false; bool matrixInMatrix = false; bool arrayArg = false; for (int i = 0; i < function.getParamCount(); ++i) { const TParameter& param = function.getParam(i); size += param.type->getObjectSize(); if (constructingMatrix && param.type->isMatrix()) matrixInMatrix = true; if (full) overFull = true; if (op != EOpConstructStruct && !type->isArray() && size >= type->getObjectSize()) full = true; if (param.type->getQualifier() != EvqConst) constType = false; if (param.type->isArray()) arrayArg = true; } if (constType) type->setQualifier(EvqConst); if (type->isArray() && type->getArraySize() != function.getParamCount()) { error(line, "array constructor needs one argument per array element", "constructor", ""); return true; } if (arrayArg && op != EOpConstructStruct) { error(line, "constructing from a non-dereferenced array", "constructor", ""); return true; } if (matrixInMatrix && !type->isArray()) { if (function.getParamCount() != 1) { error(line, "constructing matrix from matrix can only take one argument", "constructor", ""); return true; } } if (overFull) { error(line, "too many arguments", "constructor", ""); return true; } if (op == EOpConstructStruct && !type->isArray() && int(type->getStruct()->size()) != function.getParamCount()) { error(line, "Number of constructor parameters does not match the number of structure fields", "constructor", ""); return true; } if (!type->isMatrix() || !matrixInMatrix) { if ((op != EOpConstructStruct && size != 1 && size < type->getObjectSize()) || (op == EOpConstructStruct && size < type->getObjectSize())) { error(line, "not enough data provided for construction", "constructor", ""); return true; } } TIntermTyped *typed = node ? node->getAsTyped() : 0; if (typed == 0) { error(line, "constructor argument does not have a type", "constructor", ""); return true; } if (op != EOpConstructStruct && IsSampler(typed->getBasicType())) { error(line, "cannot convert a sampler", "constructor", ""); return true; } if (typed->getBasicType() == EbtVoid) { error(line, "cannot convert a void", "constructor", ""); return true; } return false; }
bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node) { bool visitChildren = true; switch (node->getOp()) { case EOpDeclaration: { const TIntermSequence &sequence = *(node->getSequence()); if (sequence.front()->getAsTyped()->getType().isInvariant()) { ensureVersionIsAtLeast(GLSL_VERSION_120); } break; } case EOpInvariantDeclaration: ensureVersionIsAtLeast(GLSL_VERSION_120); break; case EOpParameters: { const TIntermSequence ¶ms = *(node->getSequence()); for (TIntermSequence::const_iterator iter = params.begin(); iter != params.end(); ++iter) { const TIntermTyped *param = (*iter)->getAsTyped(); if (param->isArray()) { TQualifier qualifier = param->getQualifier(); if ((qualifier == EvqOut) || (qualifier == EvqInOut)) { ensureVersionIsAtLeast(GLSL_VERSION_120); break; } } } // Fully processed. No need to visit children. visitChildren = false; break; } case EOpConstructMat2: case EOpConstructMat2x3: case EOpConstructMat2x4: case EOpConstructMat3x2: case EOpConstructMat3: case EOpConstructMat3x4: case EOpConstructMat4x2: case EOpConstructMat4x3: case EOpConstructMat4: { const TIntermSequence &sequence = *(node->getSequence()); if (sequence.size() == 1) { TIntermTyped *typed = sequence.front()->getAsTyped(); if (typed && typed->isMatrix()) { ensureVersionIsAtLeast(GLSL_VERSION_120); } } break; } default: break; } return visitChildren; }
bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary* node) { bool visitChildren = true; TInfoSinkBase& out = objSink(); switch (node->getOp()) { case EOpInitialize: if (visit == InVisit) { out << " = "; // RHS of initialize is not being declared. mDeclaringVariables = false; } break; case EOpAssign: writeTriplet(visit, "(", " = ", ")"); break; case EOpAddAssign: writeTriplet(visit, "(", " += ", ")"); break; case EOpSubAssign: writeTriplet(visit, "(", " -= ", ")"); break; case EOpDivAssign: writeTriplet(visit, "(", " /= ", ")"); break; // Notice the fall-through. case EOpMulAssign: case EOpVectorTimesMatrixAssign: case EOpVectorTimesScalarAssign: case EOpMatrixTimesScalarAssign: case EOpMatrixTimesMatrixAssign: writeTriplet(visit, "(", " *= ", ")"); break; case EOpIndexDirect: writeTriplet(visit, NULL, "[", "]"); break; case EOpIndexIndirect: if (node->getAddIndexClamp()) { if (visit == InVisit) { if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC) { out << "[int(clamp(float("; } else { out << "[webgl_int_clamp("; } } else if (visit == PostVisit) { int maxSize; TIntermTyped *left = node->getLeft(); TType leftType = left->getType(); if (left->isArray()) { // The shader will fail validation if the array length is not > 0. maxSize = leftType.getArraySize() - 1; } else { maxSize = leftType.getNominalSize() - 1; } if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC) { out << "), 0.0, float(" << maxSize << ")))]"; } else { out << ", 0, " << maxSize << ")]"; } } } else { writeTriplet(visit, NULL, "[", "]"); } break; case EOpIndexDirectStruct: if (visit == InVisit) { out << "."; // TODO(alokp): ASSERT TString fieldName = node->getType().getFieldName(); const TType& structType = node->getLeft()->getType(); if (!mSymbolTable.findBuiltIn(structType.getTypeName())) fieldName = hashName(fieldName); out << fieldName; visitChildren = false; } break; case EOpVectorSwizzle: if (visit == InVisit) { out << "."; TIntermAggregate* rightChild = node->getRight()->getAsAggregate(); TIntermSequence& sequence = rightChild->getSequence(); for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); ++sit) { TIntermConstantUnion* element = (*sit)->getAsConstantUnion(); ASSERT(element->getBasicType() == EbtInt); ASSERT(element->getNominalSize() == 1); const ConstantUnion& data = element->getUnionArrayPointer()[0]; ASSERT(data.getType() == EbtInt); switch (data.getIConst()) { case 0: out << "x"; break; case 1: out << "y"; break; case 2: out << "z"; break; case 3: out << "w"; break; default: UNREACHABLE(); break; } } visitChildren = false; } break; case EOpAdd: writeTriplet(visit, "(", " + ", ")"); break; case EOpSub: writeTriplet(visit, "(", " - ", ")"); break; case EOpMul: writeTriplet(visit, "(", " * ", ")"); break; case EOpDiv: writeTriplet(visit, "(", " / ", ")"); break; case EOpMod: UNIMPLEMENTED(); break; case EOpEqual: writeTriplet(visit, "(", " == ", ")"); break; case EOpNotEqual: writeTriplet(visit, "(", " != ", ")"); break; case EOpLessThan: writeTriplet(visit, "(", " < ", ")"); break; case EOpGreaterThan: writeTriplet(visit, "(", " > ", ")"); break; case EOpLessThanEqual: writeTriplet(visit, "(", " <= ", ")"); break; case EOpGreaterThanEqual: writeTriplet(visit, "(", " >= ", ")"); break; // Notice the fall-through. case EOpVectorTimesScalar: case EOpVectorTimesMatrix: case EOpMatrixTimesVector: case EOpMatrixTimesScalar: case EOpMatrixTimesMatrix: writeTriplet(visit, "(", " * ", ")"); break; case EOpLogicalOr: writeTriplet(visit, "(", " || ", ")"); break; case EOpLogicalXor: writeTriplet(visit, "(", " ^^ ", ")"); break; case EOpLogicalAnd: writeTriplet(visit, "(", " && ", ")"); break; default: UNREACHABLE(); break; } return visitChildren; }