bool HlslLinker::getArgumentData( GlslSymbol* sym, EClassifier c, std::string &outName, std::string &ctor, int &pad) { const std::string &name = sym->getName(); const std::string &semantic = sym->getSemantic(); EGlslSymbolType type = sym->getType(); return getArgumentData2( name, semantic, type, c, outName, ctor, pad, 0); }
bool HlslLinker::emitReturnValue(const EGlslSymbolType retType, GlslFunction* funcMain, EShLanguage lang, std::stringstream& varying, std::stringstream& postamble) { // void return type if (retType == EgstVoid) { if (lang == EShLangFragment) // fragment shader { // If no return type, close off the output postamble << ";\n"; } return true; } // non-struct return type assert (retType != EgstVoid); if (retType != EgstStruct) { std::string name, ctor; int pad; GlslSymbolOrStructMemberBase fakedMainSym("", funcMain->getSemantic(), retType, EqtNone, EbpMedium, 0); if (!getArgumentData2(&fakedMainSym, lang==EShLangVertex ? EClassVarOut : EClassRes, name, ctor, pad, -1)) { assert(0); infoSink.info << (lang==EShLangVertex ? "Unsupported type for shader return value (" : "Unsupported return type for shader entry function ("); infoSink.info << getTypeString(retType) << ")\n"; return true; //@TODO: real error and return false? } postamble << " "; postamble << name << " = "; emitSymbolWithPad (postamble, ctor, "xl_retval", pad); postamble << ";\n"; // In vertex shader, add to varyings if (lang == EShLangVertex) AddToVaryings (varying, lang, m_Target, funcMain->getPrecision(), ctor, name); return true; } // struct return type assert (retType == EgstStruct); GlslStruct *retStruct = funcMain->getStruct(); assert (retStruct); return emitReturnStruct(retStruct, std::string("xl_retval."), lang, varying, postamble); }
void HlslLinker::emitOutputStructParam(GlslSymbol* sym, EShLanguage lang, bool usePrecision, EAttribSemantic attrSem, std::stringstream& varying, std::stringstream& preamble, std::stringstream& postamble, std::stringstream& call) { //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 StructMember ¤t = Struct->getMember(ii); std::string name, ctor; int pad; if (!getArgumentData2( ¤t, lang==EShLangVertex ? EClassVarOut : EClassRes, name, ctor, pad, -1)) { //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"; continue; } postamble << " "; postamble << name << " = "; emitSymbolWithPad (postamble, ctor, tempVar+"."+current.name, pad); postamble << ";\n"; // In vertex shader, add to varyings if (lang == EShLangVertex) AddVertexOutput (varying, m_Target, current.precision, ctor, name); } }
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::getArgumentData( GlslSymbol* sym, EClassifier c, std::string &outName, std::string &ctor, int &pad) { return getArgumentData2( sym, c, outName, ctor, pad, -1); }
// This function calls itself recursively if it finds structs in structs. bool HlslLinker::emitReturnStruct(GlslStruct *retStruct, std::string parentName, EShLanguage lang, std::stringstream& varying, std::stringstream& postamble) { const int elem = retStruct->memberCount(); for (int ii=0; ii<elem; ii++) { const StructMember ¤t = retStruct->getMember(ii); std::string name, ctor; int pad; int arraySize = 1; bool isArray = false; if (lang == EShLangVertex) // vertex shader { // If it is an array, loop over each member if ( current.arraySize > 0 ) { arraySize = current.arraySize; isArray = true; } } for (int idx = 0; idx < arraySize; ++idx) { if (!getArgumentData2( ¤t, lang==EShLangVertex ? EClassVarOut : EClassRes, name, ctor, pad, isArray?idx:-1)) { GlslStruct *subStruct = current.structType; if (subStruct) { if (!emitReturnStruct(current.structType, parentName+current.name+std::string("."), lang, varying, postamble)) { return false; } } else { 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 { postamble << " "; postamble << name; postamble << " = " << ctor; postamble << "(" << parentName << current.name; if (isArray) { postamble << "[" << idx << "]"; } for (int jj = 0; jj<pad; jj++) postamble << ", 0.0"; postamble << ");\n"; // In vertex shader, add to varyings if (lang == EShLangVertex) AddVertexOutput (varying, m_Target, current.precision, ctor, name); } } } return true; }
// This function calls itself recursively if it finds structs in structs. bool HlslLinker::emitInputStruct(const GlslStruct* str, std::string parentName, EShLanguage lang, std::stringstream& attrib, std::stringstream& varying, std::stringstream& preamble) { // process struct members const int elem = str->memberCount(); for (int jj=0; jj<elem; jj++) { const StructMember ¤t = str->getMember(jj); EAttribSemantic memberSem = parseAttributeSemantic (current.semantic); add_extension_from_semantic(memberSem, m_Target, m_Extensions); // if member of the struct is an array, we have to loop over all array elements int arraySize = 1; bool isArray = false; if (current.arraySize > 0) { arraySize = current.arraySize; isArray = true; } std::string name, ctor; for (int idx = 0; idx < arraySize; ++idx) { int pad; if (!getArgumentData2 (¤t, lang==EShLangVertex ? EClassAttrib : EClassVarIn, name, ctor, pad, isArray?idx:-1)) { const GlslStruct* subStruct = current.structType; if (subStruct) { //should deal with fall through cases here emitInputStruct(subStruct, parentName+current.name+std::string("."), lang, attrib, varying, preamble); continue; } else { infoSink.info << "Unsupported type for struct element in shader entry parameter ("; infoSink.info << getTypeString(current.type) << ")\n"; return false; } } preamble << " "; preamble << parentName << current.name; if (isArray) preamble << "[" << idx << "]"; // In fragment shader, pass zero for POSITION inputs if (lang == EShLangFragment && memberSem == EAttrSemPosition) { preamble << " = " << ctor << "(0.0);\n"; continue; // nothing more to do } else { preamble << " = "; emitSymbolWithPad (preamble, ctor, name, pad); preamble << ";\n"; } if (!current.outputSuppressedBy()) emitSingleInputVariable (lang, m_Target, name, ctor, current.type, current.precision, attrib, varying); } } return true; }