void writeFuncCall( const TString &name, TIntermAggregate *node, TGlslOutputTraverser* goit, bool bGenMatrix = false ) { TIntermSequence::iterator sit; TIntermSequence &sequence = node->getSequence(); GlslFunction *current = goit->current; std::stringstream& out = current->getActiveOutput(); current->beginStatement(); if ( bGenMatrix ) { if ( node->isMatrix () ) { out << "xll_"; current->addLibFunction ( node->getOp() ); } } out << name << "("; for (sit = sequence.begin(); sit != sequence.end(); ++sit) { if (sit !=sequence.begin()) out << ", "; (*sit)->traverse(goit); } out << ")"; }
static bool sortFunctionsTopologically (std::vector<GlslFunction*>& dst, const std::vector<GlslFunction*>& src) { dst.clear(); // Build function use counts. FunctionUseCounts useCounts; for (std::vector<GlslFunction*>::const_iterator funcIter = src.begin(); funcIter != src.end(); ++funcIter) useCounts[(*funcIter)->getMangledName()] = 0; for (std::vector<GlslFunction*>::const_iterator funcIter = src.begin(); funcIter != src.end(); ++funcIter) { std::set<std::string> calledNames = (*funcIter)->getCalledFunctions(); for (std::set<std::string>::const_iterator callIter = calledNames.begin(); callIter != calledNames.end(); ++callIter) useCounts[*callIter] += 1; } std::vector<GlslFunction*> liveSet; // Init live set with functions that have use count 0 (should be only main()) for (std::vector<GlslFunction*>::const_iterator funcIter = src.begin(); funcIter != src.end(); ++funcIter) { if (useCounts[(*funcIter)->getMangledName()] == 0) liveSet.push_back(*funcIter); } // Process until live set is empty. while (!liveSet.empty()) { GlslFunction* curFunction = liveSet.back(); liveSet.pop_back(); dst.push_back(curFunction); // Decrement use counts and add to live set if reaches zero. std::set<std::string> calledNames = curFunction->getCalledFunctions(); for (std::set<std::string>::const_iterator callIter = calledNames.begin(); callIter != calledNames.end(); ++callIter) { int& useCount = useCounts[*callIter]; useCount -= 1; if (useCount == 0) { GlslFunction* newLiveFunc = resolveFunctionByMangledName(src, *callIter); if (!newLiveFunc) return false; // Not found - why? liveSet.push_back(newLiveFunc); } } } // If dependency graph contains cycles, some functions will never end up in live set and from there // to sorted list. This checks if sort succeeded. return dst.size() == src.size(); }
bool TGlslOutputTraverser::traverseLoop( bool preVisit, TIntermLoop *node, TIntermTraverser *it ) { TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it); GlslFunction *current = goit->current; std::stringstream& out = current->getActiveOutput(); current->beginStatement(); if ( node->getTerminal()) { // Process for loop, initial statement was promoted outside the loop out << "for ( ; "; node->getTest()->traverse(goit); out << "; "; node->getTerminal()->traverse(goit); out << ") "; current->beginBlock(); if (node->getBody()) node->getBody()->traverse(goit); current->endBlock(); } else { if ( node->testFirst()) { // Process while loop out << "while ( "; node->getTest()->traverse(goit); out << " ) "; current->beginBlock(); if (node->getBody()) node->getBody()->traverse(goit); current->endBlock(); } else { // Process do loop out << "do "; current->beginBlock(); if (node->getBody()) node->getBody()->traverse(goit); current->endBlock(); current->indent(); out << "while ( "; node->getTest()->traverse(goit); out << " )\n"; } } return false; }
void setupUnaryBuiltInFuncCall( const TString &name, TIntermUnary *node, TString &opStr, bool &funcStyle, bool &prefix, TGlslOutputTraverser* goit ) { GlslFunction *current = goit->current; funcStyle = true; prefix = true; if ( node->isMatrix() ) { current->addLibFunction( node->getOp() ); opStr = "xll_" + name; } else { opStr = name; } }
void TGlslOutputTraverser::traverseConstantUnion( TIntermConstant *node, TIntermTraverser *it ) { TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it); GlslFunction *current = goit->current; std::stringstream& out = current->getActiveOutput(); EGlslSymbolType type = translateType( node->getTypePointer()); GlslStruct *str = 0; current->beginStatement(); if (type == EgstStruct) { str = goit->createStructFromType( node->getTypePointer()); } writeConstantConstructor (out, type, goit->m_UsePrecision?node->getPrecision():EbpUndefined, node, str); }
void TGlslOutputTraverser::traverseParameterSymbol(TIntermSymbol *node, TIntermTraverser *it) { TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it); GlslFunction *current = goit->current; int array = node->getTypePointer()->isArray() ? node->getTypePointer()->getArraySize() : 0; const char* semantic = ""; if (node->getInfo()) semantic = node->getInfo()->getSemantic().c_str(); GlslSymbol * sym = new GlslSymbol( node->getSymbol().c_str(), semantic, node->getId(), translateType(node->getTypePointer()), goit->m_UsePrecision?node->getPrecision():EbpUndefined, translateQualifier(node->getQualifier()), array); current->addParameter(sym); if (sym->getType() == EgstStruct) { GlslStruct *s = goit->createStructFromType( node->getTypePointer()); sym->setStruct(s); } }
bool TGlslOutputTraverser::traverseBranch( bool preVisit, TIntermBranch *node, TIntermTraverser *it ) { TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it); GlslFunction *current = goit->current; std::stringstream& out = current->getActiveOutput(); current->beginStatement(); switch (node->getFlowOp()) { case EOpKill: out << "discard"; break; case EOpBreak: out << "break"; break; case EOpContinue: out << "continue"; break; case EOpReturn: out << "return "; break; default: assert(0); break; } if (node->getExpression()) { node->getExpression()->traverse(it); } return false; }
void TGlslOutputTraverser::traverseSymbol(TIntermSymbol *node, TIntermTraverser *it) { TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it); GlslFunction *current = goit->current; std::stringstream& out = current->getActiveOutput(); current->beginStatement(); if ( ! current->hasSymbol( node->getId())) { //check to see if it is a global we can share if ( goit->global->hasSymbol( node->getId())) { current->addSymbol( &goit->global->getSymbol( node->getId())); } else { int array = node->getTypePointer()->isArray() ? node->getTypePointer()->getArraySize() : 0; const char* semantic = ""; if (node->getInfo()) semantic = node->getInfo()->getSemantic().c_str(); GlslSymbol * sym = new GlslSymbol( node->getSymbol().c_str(), semantic, node->getId(), translateType(node->getTypePointer()), goit->m_UsePrecision?node->getPrecision():EbpUndefined, translateQualifier(node->getQualifier()), array); sym->setIsGlobal(node->isGlobal()); current->addSymbol(sym); if (sym->getType() == EgstStruct) { GlslStruct *s = goit->createStructFromType( node->getTypePointer()); sym->setStruct(s); } } } // If we're at the global scope, emit the non-mutable names of uniforms. bool globalScope = current == goit->global; out << current->getSymbol(node->getId()).getName(!globalScope); }
bool TGlslOutputTraverser::traverseUnary( bool preVisit, TIntermUnary *node, TIntermTraverser *it ) { TString op("??"); TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it); GlslFunction *current = goit->current; std::stringstream& out = current->getActiveOutput(); bool funcStyle = false; bool prefix = true; char zero[] = "0"; current->beginStatement(); switch (node->getOp()) { case EOpNegative: op = "-"; funcStyle = false; prefix = true; break; case EOpVectorLogicalNot: case EOpLogicalNot: op = "!"; funcStyle = false; prefix = true; break; case EOpBitwiseNot: op = "-"; funcStyle = false; prefix = true; break; case EOpPostIncrement: op = "++"; funcStyle = false; prefix = false; break; case EOpPostDecrement: op = "--"; funcStyle = false; prefix = false; break; case EOpPreIncrement: op = "++"; funcStyle = false; prefix = true; break; case EOpPreDecrement: op = "--"; funcStyle = false; prefix = true; break; case EOpConvIntToBool: case EOpConvFloatToBool: op = "bool"; if ( node->getTypePointer()->getNominalSize() > 1) { zero[0] += node->getTypePointer()->getNominalSize(); op = TString("bvec") + zero; } funcStyle = true; prefix = true; break; case EOpConvBoolToFloat: case EOpConvIntToFloat: op = "float"; if ( node->getTypePointer()->getNominalSize() > 1) { zero[0] += node->getTypePointer()->getNominalSize(); op = TString("vec") + zero; } funcStyle = true; prefix = true; break; case EOpConvFloatToInt: case EOpConvBoolToInt: op = "int"; if ( node->getTypePointer()->getNominalSize() > 1) { zero[0] += node->getTypePointer()->getNominalSize(); op = TString("ivec") + zero; } funcStyle = true; prefix = true; break; case EOpRadians: setupUnaryBuiltInFuncCall ( "radians", node, op, funcStyle, prefix, goit ); break; case EOpDegrees: setupUnaryBuiltInFuncCall ( "degrees", node, op, funcStyle, prefix, goit ); break; case EOpSin: setupUnaryBuiltInFuncCall ( "sin", node, op, funcStyle, prefix, goit ); break; case EOpCos: setupUnaryBuiltInFuncCall ( "cos", node, op, funcStyle, prefix, goit ); break; case EOpTan: setupUnaryBuiltInFuncCall ( "tan", node, op, funcStyle, prefix, goit ); break; case EOpAsin: setupUnaryBuiltInFuncCall ( "asin", node, op, funcStyle, prefix, goit ); break; case EOpAcos: setupUnaryBuiltInFuncCall ( "acos", node, op, funcStyle, prefix, goit ); break; case EOpAtan: setupUnaryBuiltInFuncCall ( "atan", node, op, funcStyle, prefix, goit ); break; case EOpExp: setupUnaryBuiltInFuncCall ( "exp", node, op, funcStyle, prefix, goit ); break; case EOpLog: setupUnaryBuiltInFuncCall ( "log", node, op, funcStyle, prefix, goit ); break; case EOpExp2: setupUnaryBuiltInFuncCall ( "exp2", node, op, funcStyle, prefix, goit ); break; case EOpLog2: setupUnaryBuiltInFuncCall ( "log2", node, op, funcStyle, prefix, goit ); break; case EOpSqrt: setupUnaryBuiltInFuncCall ( "sqrt", node, op, funcStyle, prefix, goit ); break; case EOpInverseSqrt: setupUnaryBuiltInFuncCall ( "inversesqrt", node, op, funcStyle, prefix, goit ); break; case EOpAbs: setupUnaryBuiltInFuncCall ( "abs", node, op, funcStyle, prefix, goit ); break; case EOpSign: setupUnaryBuiltInFuncCall ( "sign", node, op, funcStyle, prefix, goit ); break; case EOpFloor: setupUnaryBuiltInFuncCall ( "floor", node, op, funcStyle, prefix, goit ); break; case EOpCeil: setupUnaryBuiltInFuncCall ( "ceil", node, op, funcStyle, prefix, goit ); break; case EOpFract: setupUnaryBuiltInFuncCall ( "fract", node, op, funcStyle, prefix, goit ); break; case EOpLength: op = "length"; funcStyle = true; prefix = true; break; case EOpNormalize: op = "normalize"; funcStyle = true; prefix = true; break; case EOpDPdx: current->addLibFunction(EOpDPdx); op = "xll_dFdx"; funcStyle = true; prefix = true; break; case EOpDPdy: current->addLibFunction(EOpDPdy); op = "xll_dFdy"; funcStyle = true; prefix = true; break; case EOpFwidth: current->addLibFunction(EOpFwidth); op = "xll_fwidth"; funcStyle = true; prefix = true; break; case EOpFclip: current->addLibFunction(EOpFclip); op = "xll_clip"; funcStyle = true; prefix = true; break; case EOpAny: op = "any"; funcStyle = true; prefix = true; break; case EOpAll: op = "all"; funcStyle = true; prefix = true; break; //these are HLSL specific and they map to the lib functions case EOpSaturate: current->addLibFunction(EOpSaturate); op = "xll_saturate"; funcStyle = true; prefix = true; break; case EOpTranspose: current->addLibFunction(EOpTranspose); op = "xll_transpose"; funcStyle = true; prefix = true; break; case EOpDeterminant: current->addLibFunction(EOpDeterminant); op = "xll_determinant"; funcStyle = true; prefix = true; break; case EOpLog10: current->addLibFunction(EOpLog10); op = "xll_log10"; funcStyle = true; prefix = true; break; case EOpD3DCOLORtoUBYTE4: current->addLibFunction(EOpD3DCOLORtoUBYTE4); op = "xll_D3DCOLORtoUBYTE4"; funcStyle = true; prefix = true; break; default: assert(0); } if (funcStyle) out << op << '('; else { out << '('; if (prefix) out << op; } node->getOperand()->traverse(goit); if (! funcStyle && !prefix) out << op; out << ')'; 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; }
bool TGlslOutputTraverser::traverseDeclaration(bool preVisit, TIntermDeclaration* decl, TIntermTraverser* it) { TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it); GlslFunction *current = goit->current; std::stringstream& out = current->getActiveOutput(); EGlslSymbolType symbol_type = translateType(decl->getTypePointer()); TType& type = *decl->getTypePointer(); bool emit_osx10_6_arrays = goit->m_EmitSnowLeopardCompatibleArrayInitializers && decl->containsArrayInitialization(); if (emit_osx10_6_arrays) { assert(decl->isSingleInitialization() && "Emission of multiple in-line array declarations isn't supported when running in OS X 10.6 compatible mode."); current->indent(out); out << "#if defined(OSX_SNOW_LEOPARD)" << std::endl; current->increaseDepth(); TQualifier q = type.getQualifier(); if (q == EvqConst) q = EvqTemporary; current->beginStatement(); if (q != EvqTemporary && q != EvqGlobal) out << type.getQualifierString() << " "; TIntermBinary* assign = decl->getDeclaration()->getAsBinaryNode(); TIntermSymbol* sym = assign->getLeft()->getAsSymbolNode(); TIntermSequence& init = assign->getRight()->getAsAggregate()->getSequence(); writeType(out, symbol_type, NULL, goit->m_UsePrecision ? decl->getPrecision() : EbpUndefined); out << "[" << type.getArraySize() << "] " << sym->getSymbol(); current->endStatement(); unsigned n_vals = init.size(); for (unsigned i = 0; i != n_vals; ++i) { current->beginStatement(); sym->traverse(goit); out << "[" << i << "]" << " = "; init[i]->traverse(goit); current->endStatement(); } current->decreaseDepth(); current->indent(out); out << "#else" << std::endl; current->increaseDepth(); } current->beginStatement(); if (type.getQualifier() != EvqTemporary && type.getQualifier() != EvqGlobal) out << type.getQualifierString() << " "; if (type.getBasicType() == EbtStruct) out << type.getTypeName(); else writeType(out, symbol_type, NULL, goit->m_UsePrecision ? decl->getPrecision() : EbpUndefined); if (type.isArray()) out << "[" << type.getArraySize() << "]"; out << " "; decl->getDeclaration()->traverse(goit); current->endStatement(); if (emit_osx10_6_arrays) { current->decreaseDepth(); current->indent(out); out << "#endif" << std::endl; } return false; }
bool TGlslOutputTraverser::traverseAggregate( bool preVisit, TIntermAggregate *node, TIntermTraverser *it ) { TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it); GlslFunction *current = goit->current; std::stringstream& out = current->getActiveOutput(); int argCount = (int) node->getSequence().size(); if (node->getOp() == EOpNull) { goit->infoSink.info << "node is still EOpNull!\n"; return true; } switch (node->getOp()) { case EOpSequence: if (goit->generatingCode) { goit->outputLineDirective (node->getLine()); TIntermSequence::iterator sit; TIntermSequence &sequence = node->getSequence(); for (sit = sequence.begin(); sit != sequence.end(); ++sit) { goit->outputLineDirective((*sit)->getLine()); (*sit)->traverse(it); //out << ";\n"; current->endStatement(); } } else { TIntermSequence::iterator sit; TIntermSequence &sequence = node->getSequence(); for (sit = sequence.begin(); sit != sequence.end(); ++sit) { (*sit)->traverse(it); } } return false; case EOpFunction: { GlslFunction *func = new GlslFunction( node->getPlainName().c_str(), node->getName().c_str(), translateType(node->getTypePointer()), goit->m_UsePrecision?node->getPrecision():EbpUndefined, node->getSemantic().c_str(), node->getLine()); if (func->getReturnType() == EgstStruct) { GlslStruct *s = goit->createStructFromType( node->getTypePointer()); func->setStruct(s); } goit->functionList.push_back( func); goit->current = func; goit->current->beginBlock( false); TIntermSequence::iterator sit; TIntermSequence &sequence = node->getSequence(); for (sit = sequence.begin(); sit != sequence.end(); ++sit) { (*sit)->traverse(it); } goit->current->endBlock(); goit->current = goit->global; return false; } case EOpParameters: it->visitSymbol = traverseParameterSymbol; { TIntermSequence::iterator sit; TIntermSequence &sequence = node->getSequence(); for (sit = sequence.begin(); sit != sequence.end(); ++sit) (*sit)->traverse(it); } it->visitSymbol = traverseSymbol; return false; case EOpConstructFloat: writeFuncCall( "float", node, goit); return false; case EOpConstructVec2: writeFuncCall( "vec2", node, goit); return false; case EOpConstructVec3: writeFuncCall( "vec3", node, goit); return false; case EOpConstructVec4: writeFuncCall( "vec4", node, goit); return false; case EOpConstructBool: writeFuncCall( "bool", node, goit); return false; case EOpConstructBVec2: writeFuncCall( "bvec2", node, goit); return false; case EOpConstructBVec3: writeFuncCall( "bvec3", node, goit); return false; case EOpConstructBVec4: writeFuncCall( "bvec4", node, goit); return false; case EOpConstructInt: writeFuncCall( "int", node, goit); return false; case EOpConstructIVec2: writeFuncCall( "ivec2", node, goit); return false; case EOpConstructIVec3: writeFuncCall( "ivec3", node, goit); return false; case EOpConstructIVec4: writeFuncCall( "ivec4", node, goit); return false; case EOpConstructMat2FromMat: case EOpConstructMat2: writeFuncCall( "mat2", node, goit); return false; case EOpConstructMat3FromMat: case EOpConstructMat3: writeFuncCall( "mat3", node, goit); return false; case EOpConstructMat4: writeFuncCall( "mat4", node, goit); return false; case EOpConstructStruct: writeFuncCall( node->getTypePointer()->getTypeName(), node, goit); return false; case EOpConstructArray: writeFuncCall( buildArrayConstructorString(*node->getTypePointer()), node, goit); return false; case EOpComma: { TIntermSequence::iterator sit; TIntermSequence &sequence = node->getSequence(); for (sit = sequence.begin(); sit != sequence.end(); ++sit) { (*sit)->traverse(it); if ( sit+1 != sequence.end()) out << ", "; } } return false; case EOpFunctionCall: current->addCalledFunction(node->getName().c_str()); writeFuncCall( node->getPlainName(), node, goit); return false; case EOpLessThan: writeFuncCall( "lessThan", node, goit); return false; case EOpGreaterThan: writeFuncCall( "greaterThan", node, goit); return false; case EOpLessThanEqual: writeFuncCall( "lessThanEqual", node, goit); return false; case EOpGreaterThanEqual: writeFuncCall( "greaterThanEqual", node, goit); return false; case EOpVectorEqual: writeFuncCall( "equal", node, goit); return false; case EOpVectorNotEqual: writeFuncCall( "notEqual", node, goit); return false; case EOpMod: writeFuncCall( "mod", node, goit, true); return false; case EOpPow: writeFuncCall( "pow", node, goit, true); return false; case EOpAtan2: writeFuncCall( "atan", node, goit, true); return false; case EOpMin: writeFuncCall( "min", node, goit, true); return false; case EOpMax: writeFuncCall( "max", node, goit, true); return false; case EOpClamp: writeFuncCall( "clamp", node, goit, true); return false; case EOpMix: writeFuncCall( "mix", node, goit, true); return false; case EOpStep: writeFuncCall( "step", node, goit, true); return false; case EOpSmoothStep: writeFuncCall( "smoothstep", node, goit, true); return false; case EOpDistance: writeFuncCall( "distance", node, goit); return false; case EOpDot: writeFuncCall( "dot", node, goit); return false; case EOpCross: writeFuncCall( "cross", node, goit); return false; case EOpFaceForward: writeFuncCall( "faceforward", node, goit); return false; case EOpReflect: writeFuncCall( "reflect", node, goit); return false; case EOpRefract: writeFuncCall( "refract", node, goit); return false; case EOpMul: { //This should always have two arguments assert(node->getSequence().size() == 2); current->beginStatement(); out << '('; node->getSequence()[0]->traverse(goit); out << " * "; node->getSequence()[1]->traverse(goit); out << ')'; return false; } //HLSL texture functions case EOpTex1D: if (argCount == 2) writeTex( "texture1D", node, goit); else { current->addLibFunction(EOpTex1DGrad); writeTex( "xll_tex1Dgrad", node, goit); } return false; case EOpTex1DProj: writeTex( "texture1DProj", node, goit); return false; case EOpTex1DLod: current->addLibFunction(EOpTex1DLod); writeTex( "xll_tex1Dlod", node, goit); return false; case EOpTex1DBias: current->addLibFunction(EOpTex1DBias); writeTex( "xll_tex1Dbias", node, goit); return false; case EOpTex1DGrad: current->addLibFunction(EOpTex1DGrad); writeTex( "xll_tex1Dgrad", node, goit); return false; case EOpTex2D: if (argCount == 2) writeTex( "texture2D", node, goit); else { current->addLibFunction(EOpTex2DGrad); writeTex( "xll_tex2Dgrad", node, goit); } return false; case EOpTex2DProj: writeTex( "texture2DProj", node, goit); return false; case EOpTex2DLod: current->addLibFunction(EOpTex2DLod); writeTex( "xll_tex2Dlod", node, goit); return false; case EOpTex2DBias: current->addLibFunction(EOpTex2DBias); writeTex( "xll_tex2Dbias", node, goit); return false; case EOpTex2DGrad: current->addLibFunction(EOpTex2DGrad); writeTex( "xll_tex2Dgrad", node, goit); return false; case EOpTex3D: if (argCount == 2) writeTex( "texture3D", node, goit); else { current->addLibFunction(EOpTex3DGrad); writeTex( "xll_tex3Dgrad", node, goit); } return false; case EOpTex3DProj: writeTex( "texture3DProj", node, goit); return false; case EOpTex3DLod: current->addLibFunction(EOpTex3DLod); writeTex( "xll_tex3Dlod", node, goit); return false; case EOpTex3DBias: current->addLibFunction(EOpTex3DBias); writeTex( "xll_tex3Dbias", node, goit); return false; case EOpTex3DGrad: current->addLibFunction(EOpTex3DGrad); writeTex( "xll_tex3Dgrad", node, goit); return false; case EOpTexCube: if (argCount == 2) writeTex( "textureCube", node, goit); else { current->addLibFunction(EOpTexCubeGrad); writeTex( "xll_texCUBEgrad", node, goit); } return false; case EOpTexCubeProj: writeTex( "textureCubeProj", node, goit); return false; case EOpTexCubeLod: current->addLibFunction(EOpTexCubeLod); writeTex( "xll_texCUBElod", node, goit); return false; case EOpTexCubeBias: current->addLibFunction(EOpTexCubeBias); writeTex( "xll_texCUBEbias", node, goit); return false; case EOpTexCubeGrad: current->addLibFunction(EOpTexCubeGrad); writeTex( "xll_texCUBEgrad", node, goit); return false; case EOpTexRect: writeTex( "texture2DRect", node, goit); return false; case EOpTexRectProj: writeTex( "texture2DRectProj", node, goit); return false; case EOpModf: current->addLibFunction(EOpModf); writeFuncCall( "xll_modf", node, goit); break; case EOpLdexp: current->addLibFunction(EOpLdexp); writeFuncCall( "xll_ldexp", node, goit); break; case EOpSinCos: current->addLibFunction(EOpSinCos); writeFuncCall ( "xll_sincos", node, goit); break; case EOpLit: current->addLibFunction(EOpLit); writeFuncCall( "xll_lit", node, goit); break; default: goit->infoSink.info << "Bad aggregation op\n"; } return false; }
void writeComparison( const TString &compareOp, const TString &compareCall, TIntermBinary *node, TGlslOutputTraverser* goit ) { GlslFunction *current = goit->current; std::stringstream& out = current->getActiveOutput(); bool bUseCompareCall = false; // Determine whether we need the vector or scalar comparison function if ( ( node->getLeft() && node->getLeft()->getNominalSize() > 1 ) || ( node->getRight() && node->getRight()->getNominalSize() > 1 ) ) { bUseCompareCall = true; } current->beginStatement (); // Output vector comparison if ( bUseCompareCall ) { out << compareCall << "( "; if (node->getLeft()) { // If it is a float, need to smear to the size of the right hand side if ( node->getLeft()->getNominalSize() == 1 ) { out << "vec" << node->getRight()->getNominalSize() << "( "; node->getLeft()->traverse(goit); out << " )"; } else { node->getLeft()->traverse(goit); } } out << ", "; if (node->getRight()) { // If it is a float, need to smear to the size of the left hand side if ( node->getRight()->getNominalSize() == 1 ) { out << "vec" << node->getLeft()->getNominalSize() << "( "; node->getRight()->traverse(goit); out << " )"; } else { node->getRight()->traverse(goit); } } out << ")"; } // Output scalar comparison else { out << "("; if (node->getLeft()) node->getLeft()->traverse(goit); out << " " << compareOp << " "; if (node->getRight()) node->getRight()->traverse(goit); out << ")"; } }
bool TGlslOutputTraverser::traverseSelection( bool preVisit, TIntermSelection *node, TIntermTraverser *it ) { TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it); GlslFunction *current = goit->current; std::stringstream& out = current->getActiveOutput(); current->beginStatement(); if (node->getBasicType() == EbtVoid) { // if/else selection out << "if ("; node->getCondition()->traverse(goit); out << ')'; current->beginBlock(); node->getTrueBlock()->traverse(goit); current->endBlock(); if (node->getFalseBlock()) { current->indent(); out << "else"; current->beginBlock(); node->getFalseBlock()->traverse(goit); current->endBlock(); } } else if (node->isVector() && node->getCondition()->getAsTyped()->isVector()) { // ?: selection on vectors, e.g. bvec4 ? vec4 : vec4 // emulate HLSL's component-wise selection here current->addLibFunction(EOpVecTernarySel); out << "xll_vecTSel ("; node->getCondition()->traverse(goit); out << ", "; node->getTrueBlock()->traverse(goit); out << ", "; if (node->getFalseBlock()) { node->getFalseBlock()->traverse(goit); } else assert(0); out << ")"; } else { // simple ?: selection out << "(( "; node->getCondition()->traverse(goit); out << " ) ? ( "; node->getTrueBlock()->traverse(goit); out << " ) : ( "; if (node->getFalseBlock()) { node->getFalseBlock()->traverse(goit); } else assert(0); out << " ))"; } return false; }
bool HlslLinker::link(HlslCrossCompiler* compiler, const char* entryFunc, bool usePrecision) { std::vector<GlslFunction*> globalList; std::vector<GlslFunction*> functionList; std::string entryPoint; GlslFunction* funcMain = NULL; FunctionSet calledFunctions; std::set<TOperator> libFunctions; std::map<std::string,GlslSymbol*> globalSymMap; std::map<std::string,GlslStruct*> structMap; if (!compiler) { infoSink.info << "No shader compiler provided\n"; return false; } EShLanguage lang = compiler->getLanguage(); if (!entryFunc) { infoSink.info << "No shader entry function provided\n"; return false; } entryPoint = GetEntryName (entryFunc); //build the list of functions HlslCrossCompiler *comp = static_cast<HlslCrossCompiler*>(compiler); std::vector<GlslFunction*> &fl = comp->functionList; for ( std::vector<GlslFunction*>::iterator fit = fl.begin(); fit < fl.end(); fit++) { if ( (*fit)->getName() == "__global__") globalList.push_back( *fit); else functionList.push_back( *fit); if ((*fit)->getName() == entryPoint) { if (funcMain) { infoSink.info << kShaderTypeNames[lang] << " entry function cannot be overloaded\n"; return false; } funcMain = *fit; } } // check to ensure that we found the entry function if (!funcMain) { infoSink.info << "Failed to find entry function: '" << entryPoint <<"'\n"; return false; } //add all the called functions to the list calledFunctions.push_back (funcMain); if (!addCalledFunctions (funcMain, calledFunctions, functionList)) { infoSink.info << "Failed to resolve all called functions in the " << kShaderTypeNames[lang] << " shader\n"; } //iterate over the functions, building a global list of structure declaractions and symbols // assume a single compilation unit for expediency (eliminates name clashes, as type checking // withing a single compilation unit has been performed) for (FunctionSet::iterator it=calledFunctions.begin(); it != calledFunctions.end(); it++) { //get each symbol and each structure, and add them to the map // checking that any previous entries are equivalent const std::vector<GlslSymbol*> &symList = (*it)->getSymbols(); for (std::vector<GlslSymbol*>::const_iterator cit = symList.begin(); cit < symList.end(); cit++) { if ( (*cit)->getIsGlobal()) { //should check for already added ones here globalSymMap[(*cit)->getName()] = *cit; } } //take each referenced library function, and add it to the set const std::set<TOperator> &libSet = (*it)->getLibFunctions(); libFunctions.insert( libSet.begin(), libSet.end()); } // The following code is what is used to generate the actual shader and "main" // function. The process is to take all the components collected above, and // write them to the appropriate code stream. Finally, a main function is // generated that calls the specified entrypoint. That main function uses // semantics on the arguments and return values to connect items appropriately. // // Write Library Functions & required extensions std::string shaderExtensions, shaderLibFunctions; if (!libFunctions.empty()) { for (std::set<TOperator>::iterator it = libFunctions.begin(); it != libFunctions.end(); it++) { const std::string &func = getHLSLSupportCode(*it, shaderExtensions, lang==EShLangVertex); if (!func.empty()) { shaderLibFunctions += func; shaderLibFunctions += '\n'; } } } shader << shaderExtensions; shader << shaderLibFunctions; // //Structure addition hack // Presently, structures are not tracked per function, just dump them all // This could be improved by building a complete list of structures for the // shaders based on the variables in each function // { HlslCrossCompiler *comp = static_cast<HlslCrossCompiler*>(compiler); std::vector<GlslStruct*> &sList = comp->structList; if (!sList.empty()) { for (std::vector<GlslStruct*>::iterator it = sList.begin(); it < sList.end(); it++) { shader << (*it)->getDecl() << "\n"; } } } // // Write global variables // if (!globalSymMap.empty()) { for (std::map<std::string,GlslSymbol*>::iterator sit = globalSymMap.begin(); sit != globalSymMap.end(); sit++) { sit->second->writeDecl(shader,false,false); shader << ";\n"; if ( sit->second->getIsMutable() ) { sit->second->writeDecl(shader, true, false); shader << ";\n"; } } } // // Write function declarations and definitions // EmitCalledFunctions (shader, calledFunctions); // // Gather the uniforms into the uniform list // for (std::map<std::string, GlslSymbol*>::iterator it = globalSymMap.begin(); it != globalSymMap.end(); it++) { if (it->second->getQualifier() != EqtUniform) continue; ShUniformInfo infoStruct; infoStruct.name = new char[it->first.size()+1]; strcpy( infoStruct.name, it->first.c_str()); if (it->second->getSemantic() != "") { infoStruct.semantic = new char[it->second->getSemantic().size()+1]; strcpy( infoStruct.semantic, it->second->getSemantic().c_str()); } else infoStruct.semantic = 0; //gigantic hack, the enumerations are kept in alignment infoStruct.type = (EShType)it->second->getType(); infoStruct.arraySize = it->second->getArraySize(); if ( it->second->hasInitializer() ) { int initSize = it->second->initializerSize(); infoStruct.init = new float[initSize]; memcpy( infoStruct.init, it->second->getInitializer(), sizeof(float) * initSize); } else infoStruct.init = 0; //TODO: need to add annotation uniforms.push_back( infoStruct); } // // Generate the main function // std::stringstream attrib; std::stringstream uniform; std::stringstream preamble; std::stringstream postamble; std::stringstream varying; std::stringstream call; const int pCount = funcMain->getParameterCount(); preamble << "void main() {\n"; const EGlslSymbolType retType = funcMain->getReturnType(); GlslStruct *retStruct = funcMain->getStruct(); if ( retType == EgstStruct) { assert(retStruct); preamble << " " << retStruct->getName() << " xl_retval;\n"; } else { if ( retType != EgstVoid) { preamble << " "; writeType (preamble, retType, NULL, usePrecision?funcMain->getPrecision():EbpUndefined); preamble << " xl_retval;\n"; } } // Write all mutable initializations if ( calledFunctions.size() > 0 ) { for (FunctionSet::iterator fit = calledFunctions.begin(); fit != calledFunctions.end(); fit++) { std::string mutableDecls = (*fit)->getMutableDecls(1, calledFunctions.begin(), fit); if ( mutableDecls.size() > 0 ) { preamble << mutableDecls; } } } call << " "; if (retType != EgstVoid) call << "xl_retval = " << funcMain->getName() << "( "; else call << funcMain->getName() << "( "; // pass main function parameters for (int ii=0; ii<pCount; ii++) { GlslSymbol *sym = funcMain->getParameter(ii); EAttribSemantic attrSem = parseAttributeSemantic( sym->getSemantic()); switch (sym->getQualifier()) { // -------- IN & OUT parameters case EqtIn: case EqtInOut: if ( sym->getType() != EgstStruct) { std::string name, ctor; int pad; if ( getArgumentData( sym, lang==EShLangVertex ? EClassAttrib : EClassVarIn, name, ctor, pad) ) { // In fragment shader, pass zero for POSITION inputs bool ignoredPositionInFragment = false; if (lang == EShLangFragment && attrSem == EAttrSemPosition) { call << ctor << "(0.0)"; ignoredPositionInFragment = true; } // For "in" parameters, just call directly to the main else if ( sym->getQualifier() != EqtInOut ) { call << ctor << "(" << name; for (int ii = 0; ii<pad; ii++) call << ", 0.0"; call << ")"; } // For "inout" parameters, declare a temp and initialize the temp else { preamble << " "; writeType (preamble, sym->getType(), NULL, usePrecision?sym->getPrecision():EbpUndefined); preamble << " xlt_" << sym->getName() << " = "; preamble << ctor << "(" << name; for (int ii = 0; ii<pad; ii++) preamble << ", 0.0"; preamble << ");\n"; } if (lang == EShLangVertex) // vertex shader: deal with gl_ attributes { if ( strncmp( name.c_str(), "gl_", 3)) { int typeOffset = 0; // If the type is integer or bool based, we must convert to a float based // type. This is because GLSL does not allow int or bool based vertex attributes. if ( sym->getType() >= EgstInt && sym->getType() <= EgstInt4) { typeOffset += 4; } if ( sym->getType() >= EgstBool && sym->getType() <= EgstBool4) { typeOffset += 8; } // This is an undefined attribute attrib << "attribute " << getTypeString((EGlslSymbolType)(sym->getType() + typeOffset)) << " " << name << ";\n"; } } if (lang == EShLangFragment) // deal with varyings { if (!ignoredPositionInFragment) AddToVaryings (varying, sym->getPrecision(), ctor, name); } } else { //should deal with fall through cases here assert(0); infoSink.info << "Unsupported type for shader entry parameter ("; infoSink.info << getTypeString(sym->getType()) << ")\n"; } } else { //structs must pass the struct, then process per element GlslStruct *Struct = sym->getStruct(); assert(Struct); //first create the temp std::string tempVar = "xlt_" + sym->getName(); preamble << " " << Struct->getName() << " "; preamble << tempVar <<";\n"; call << tempVar; const int elem = Struct->memberCount(); for (int jj=0; jj<elem; jj++) { const GlslStruct::member ¤t = Struct->getMember(jj); EAttribSemantic memberSem = parseAttributeSemantic (current.semantic); std::string name, ctor; int pad; int numArrayElements = 1; bool bIsArray = false; // If it is an array, loop over each member if ( current.arraySize > 0 ) { numArrayElements = current.arraySize; bIsArray = true; } for ( int arrayIndex = 0; arrayIndex < numArrayElements; arrayIndex++ ) { if ( getArgumentData2( current.name, current.semantic, current.type, lang==EShLangVertex ? EClassAttrib : EClassVarIn, name, ctor, pad, arrayIndex ) ) { preamble << " "; preamble << tempVar << "." << current.name; if ( bIsArray ) preamble << "[" << arrayIndex << "]"; // In fragment shader, pass zero for POSITION inputs bool ignoredPositionInFragment = false; if (lang == EShLangFragment && memberSem == EAttrSemPosition) { preamble << " = " << ctor << "(0.0);\n"; ignoredPositionInFragment = true; } else { preamble << " = " << ctor << "( " << name; for (int ii = 0; ii<pad; ii++) preamble << ", 0.0"; preamble << ");\n"; } if (lang == EShLangVertex) // vertex shader: gl_ attributes { if ( strncmp( name.c_str(), "gl_", 3)) { int typeOffset = 0; // If the type is integer or bool based, we must convert to a float based // type. This is because GLSL does not allow int or bool based vertex attributes. if ( current.type >= EgstInt && current.type <= EgstInt4) { typeOffset += 4; } if ( current.type >= EgstBool && current.type <= EgstBool4) { typeOffset += 8; } // This is an undefined attribute attrib << "attribute " << getTypeString((EGlslSymbolType)(current.type + typeOffset)) << " " << name << ";\n"; } } if (lang == EShLangFragment) // deal with varyings { if (!ignoredPositionInFragment) AddToVaryings (varying, current.precision, ctor, name); } } else { //should deal with fall through cases here assert(0); infoSink.info << "Unsupported type for struct element in shader entry parameter ("; infoSink.info << getTypeString(current.type) << ")\n"; } } } } // // NOTE: This check only breaks out of the case if we have an "in" parameter, for // "inout" it will fallthrough to the next case // if ( sym->getQualifier() != EqtInOut ) { break; } // -------- OUT parameters; also fall-through for INOUT (see the check above) case EqtOut: if ( sym->getType() != EgstStruct) { std::string name, ctor; int pad; if ( getArgumentData( sym, lang==EShLangVertex ? EClassVarOut : EClassRes, name, ctor, pad) ) { // For "inout" parameters, the preamble was already written so no need to do it here. if ( sym->getQualifier() != EqtInOut ) { preamble << " "; writeType (preamble, sym->getType(), NULL, usePrecision?sym->getPrecision():EbpUndefined); preamble << " xlt_" << sym->getName() << ";\n"; } if (lang == EShLangVertex) // deal with varyings { AddToVaryings (varying, sym->getPrecision(), ctor, name); } call << "xlt_" << sym->getName(); postamble << " "; postamble << name << " = " << ctor << "( xlt_" <<sym->getName(); for (int ii = 0; ii<pad; ii++) postamble << ", 0.0"; postamble << ");\n"; } else { //should deal with fall through cases here assert(0); infoSink.info << "Unsupported type for shader entry parameter ("; infoSink.info << getTypeString(sym->getType()) << ")\n"; } } else { //structs must pass the struct, then process per element GlslStruct *Struct = sym->getStruct(); assert(Struct); //first create the temp std::string tempVar = "xlt_" + sym->getName(); // For "inout" parmaeters the preamble and call were already written, no need to do it here if ( sym->getQualifier() != EqtInOut ) { preamble << " " << Struct->getName() << " "; preamble << tempVar <<";\n"; call << tempVar; } const int elem = Struct->memberCount(); for (int ii=0; ii<elem; ii++) { const GlslStruct::member ¤t = Struct->getMember(ii); std::string name, ctor; int pad; if ( getArgumentData2( current.name, current.semantic, current.type, lang==EShLangVertex ? EClassVarOut : EClassRes, name, ctor, pad, 0) ) { postamble << " "; postamble << name << " = " << ctor; postamble << "( " << tempVar << "." << current.name; for (int ii = 0; ii<pad; ii++) postamble << ", 0.0"; postamble << ");\n"; if (lang == EShLangVertex) // deal with varyings { AddToVaryings (varying, current.precision, ctor, name); } } else { //should deal with fall through cases here assert(0); infoSink.info << "Unsupported type in struct element for shader entry parameter ("; infoSink.info << getTypeString(current.type) << ")\n"; } } } break; case EqtUniform: uniform << "uniform "; writeType (uniform, sym->getType(), NULL, usePrecision?sym->getPrecision():EbpUndefined); uniform << " xlu_" << sym->getName() << ";\n"; call << "xlu_" << sym->getName(); break; default: assert(0); }; if (ii != pCount -1) call << ", "; } call << ");\n"; // -------- return value of main entry point if (retType != EgstVoid) { if (retType != EgstStruct) { std::string name, ctor; int pad; if ( getArgumentData2( "", funcMain->getSemantic(), retType, lang==EShLangVertex ? EClassVarOut : EClassRes, name, ctor, pad, 0) ) { postamble << " "; postamble << name << " = " << ctor << "( xl_retval"; for (int ii = 0; ii<pad; ii++) postamble << ", 0.0"; postamble << ");\n"; if (lang == EShLangVertex) // deal with varyings { AddToVaryings (varying, funcMain->getPrecision(), ctor, name); } } else { //should deal with fall through cases here assert(0); infoSink.info << (lang==EShLangVertex ? "Unsupported type for shader return value (" : "Unsupported return type for shader entry function ("); infoSink.info << getTypeString(retType) << ")\n"; } } else { const int elem = retStruct->memberCount(); for (int ii=0; ii<elem; ii++) { const GlslStruct::member ¤t = retStruct->getMember(ii); std::string name, ctor; int pad; int numArrayElements = 1; bool bIsArray = false; if (lang == EShLangVertex) // vertex shader { // If it is an array, loop over each member if ( current.arraySize > 0 ) { numArrayElements = current.arraySize; bIsArray = true; } } for ( int arrayIndex = 0; arrayIndex < numArrayElements; arrayIndex++ ) { if ( getArgumentData2( current.name, current.semantic, current.type, lang==EShLangVertex ? EClassVarOut : EClassRes, name, ctor, pad, arrayIndex) ) { postamble << " "; postamble << name; postamble << " = " << ctor; postamble << "( xl_retval." << current.name; if ( bIsArray ) { postamble << "[" << arrayIndex << "]"; } for (int ii = 0; ii<pad; ii++) postamble << ", 0.0"; postamble << ");\n"; if (lang == EShLangVertex) // deal with varyings { AddToVaryings (varying, current.precision, ctor, name); } } else { //should deal with fall through cases here //assert(0); infoSink.info << (lang==EShLangVertex ? "Unsupported element type in struct for shader return value (" : "Unsupported struct element type in return type for shader entry function ("); infoSink.info << getTypeString(current.type) << ")\n"; return false; } } } } } else { if (lang == EShLangFragment) // fragment shader { // If no return type, close off the output postamble << ";\n"; } } postamble << "}\n\n"; EmitIfNotEmpty (shader, uniform); EmitIfNotEmpty (shader, attrib); EmitIfNotEmpty (shader, varying); shader << preamble.str() << "\n"; shader << call.str() << "\n"; shader << postamble.str() << "\n"; return true; }
bool HlslLinker::link(HlslCrossCompiler* compiler, const char* entryFunc, ETargetVersion targetVersion, unsigned options) { m_Target = targetVersion; m_Options = options; m_Extensions.clear(); if (!linkerSanityCheck(compiler, entryFunc)) return false; const bool usePrecision = Hlsl2Glsl_VersionUsesPrecision(targetVersion); EShLanguage lang = compiler->getLanguage(); std::string entryPoint = GetEntryName (entryFunc); // figure out all relevant functions GlslFunction* globalFunction = NULL; std::vector<GlslFunction*> functionList; FunctionSet calledFunctions; GlslFunction* funcMain = NULL; if (!buildFunctionLists(compiler, lang, entryPoint, globalFunction, functionList, calledFunctions, funcMain)) return false; assert(globalFunction); assert(funcMain); // uniforms and used built-in functions std::vector<GlslSymbol*> constants; std::set<TOperator> libFunctions; buildUniformsAndLibFunctions(calledFunctions, constants, libFunctions); // add built-in functions possibly used by uniform initializers const std::set<TOperator>& referencedGlobalFunctions = globalFunction->getLibFunctions(); libFunctions.insert (referencedGlobalFunctions.begin(), referencedGlobalFunctions.end()); buildUniformReflection (constants); // print all the components collected above. emitLibraryFunctions (libFunctions, lang, usePrecision); emitStructs(compiler); emitGlobals (globalFunction, constants); EmitCalledFunctions (shader, calledFunctions); // Generate a main function that calls the specified entrypoint. // That main function uses semantics on the arguments and return values to // connect items appropriately. std::stringstream attrib; std::stringstream uniform; std::stringstream preamble; std::stringstream postamble; std::stringstream varying; std::stringstream call; markDuplicatedInSemantics(funcMain); // Declare return value const EGlslSymbolType retType = funcMain->getReturnType(); emitMainStart(compiler, retType, funcMain, m_Options, usePrecision, preamble, constants); // Call the entry point call << " "; if (retType != EgstVoid) call << "xl_retval = "; call << funcMain->getName() << "( "; // Entry point parameters const int pCount = funcMain->getParameterCount(); for (int ii=0; ii<pCount; ii++) { GlslSymbol *sym = funcMain->getParameter(ii); EAttribSemantic attrSem = parseAttributeSemantic( sym->getSemantic()); add_extension_from_semantic(attrSem, m_Target, m_Extensions); switch (sym->getQualifier()) { // -------- IN & OUT parameters case EqtIn: case EqtInOut: case EqtConst: if (sym->getType() != EgstStruct) { emitInputNonStructParam(sym, lang, usePrecision, attrSem, attrib, varying, preamble, call); } else { emitInputStructParam(sym, lang, attrib, varying, preamble, call); } // NOTE: for "inout" parameters need to fallthrough to the next case if (sym->getQualifier() != EqtInOut) { break; } // -------- OUT parameters; also fall-through for INOUT (see the check above) case EqtOut: if ( sym->getType() != EgstStruct) { emitOutputNonStructParam(sym, lang, usePrecision, attrSem, varying, preamble, postamble, call); } else { emitOutputStructParam(sym, lang, usePrecision, attrSem, varying, preamble, postamble, call); } break; case EqtUniform: uniform << "uniform "; writeType (uniform, sym->getType(), NULL, usePrecision?sym->getPrecision():EbpUndefined); uniform << " xlu_" << sym->getName(); if(sym->getArraySize()) uniform << "[" << sym->getArraySize() << "]"; uniform << ";\n"; call << "xlu_" << sym->getName(); break; default: assert(0); }; if (ii != pCount -1) call << ", "; } call << ");\n"; // Entry point return value if (!emitReturnValue(retType, funcMain, lang, varying, postamble)) return false; postamble << "}\n\n"; // Generate final code of the pieces above. { shaderPrefix << kTargetVersionStrings[targetVersion]; ExtensionSet::const_iterator it = m_Extensions.begin(), end = m_Extensions.end(); for (; it != end; ++it) shaderPrefix << "#extension " << *it << " : require" << std::endl; } EmitIfNotEmpty (shader, uniform); EmitIfNotEmpty (shader, attrib); EmitIfNotEmpty (shader, varying); shader << preamble.str() << "\n"; shader << call.str() << "\n"; shader << postamble.str() << "\n"; return true; }