// Called recursively and appends (to list) any symbols that have semantic sem. void HlslLinker::appendDuplicatedInSemantics(GlslSymbolOrStructMemberBase* sym, EAttribSemantic sem, std::vector<GlslSymbolOrStructMemberBase*>& list) { EGlslQualifier qual = sym->getQualifier(); // fields in structures in structures can have EqtNone as a qualifier. if ( (qual == EqtIn || qual == EqtInOut || qual == EqtNone) && parseAttributeSemantic(sym->getSemantic()) == sem ) list.push_back(sym); else if (sym->getStruct()) { int mc = sym->getStruct()->memberCount(); for (int i = 0; i < mc; ++i) appendDuplicatedInSemantics(const_cast<StructMember*>(&sym->getStruct()->getMember(i)),sem,list); } }
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::getArgumentData2( const std::string &name, const std::string &semantic, EGlslSymbolType type, EClassifier c, std::string &outName, std::string &ctor, int &pad, int semanticOffset) { int size; EGlslSymbolType base = EgstVoid; EAttribSemantic sem = parseAttributeSemantic( semantic ); // Offset the semantic for the case of an array sem = static_cast<EAttribSemantic>( (int)sem + semanticOffset ); //clear the return values outName = ""; ctor = ""; pad = 0; //compute the # of elements in the type switch (type) { case EgstBool: case EgstBool2: case EgstBool3: case EgstBool4: base = EgstBool; size = type - EgstBool + 1; break; case EgstInt: case EgstInt2: case EgstInt3: case EgstInt4: base = EgstInt; size = type - EgstInt + 1; break; case EgstFloat: case EgstFloat2: case EgstFloat3: case EgstFloat4: base = EgstFloat; size = type - EgstFloat + 1; break; default: return false; }; if ( c != EClassUniform) { ctor = getTypeString( (EGlslSymbolType)((int)base + size - 1)); //default constructor pad = 0; switch (c) { case EClassNone: return false; case EClassAttrib: // If the user has specified a user attrib name, use a user attribute if ( userAttribString[sem][0] != '\0') { outName = userAttribString[sem]; } // Otherwise, use the built-in attribute name else { outName = attribString[sem]; if (sem == EAttrSemNormal && size == 4) pad = 1; else if ( sem == EAttrSemUnknown || outName[0] == '\0' ) { //handle the blind data outName = "xlat_attrib_"; outName += semantic; } } break; case EClassVarOut: // If using user varyings, create a user varying name if ( (bUserVaryings && sem != EAttrSemPosition) || varOutString[sem][0] == 0 ) { outName = kUserVaryingPrefix; outName += semantic; // If an array element, add the semantic offset to the name if ( semanticOffset != 0 ) { outName += "_"; outName += ( semanticOffset + '0' ); } } else { // Use built-in varying name outName = varOutString[sem]; } break; case EClassVarIn: // If using user varyings, create a user varying name if ( (bUserVaryings && sem != EAttrSemVPos && sem != EAttrSemVFace) || varInString[sem][0] == 0 ) { outName = kUserVaryingPrefix; outName += stripSemanticModifier ( semantic, false ); // If an array element, add the semantic offset to the name if ( semanticOffset != 0 ) { outName += "_"; outName += ( semanticOffset + '0' ); } } else { // Use built-in varying name outName = varInString[sem]; } break; case EClassRes: outName = resultString[sem]; if ( sem != EAttrSemDepth) { pad = 4 - size; ctor = "vec4"; } else { ctor = "float"; } break; case EClassUniform: assert(0); // this should have been stripped return false; }; } else { //these should always match exactly outName = "xlu_"; outName += name; } return true; }
bool HlslLinker::getArgumentData2( GlslSymbolOrStructMemberBase const* symOrStructMember, EClassifier c, std::string &outName, std::string &ctor, int &pad, int semanticOffset) { int size; EGlslSymbolType base = EgstVoid; // Get the type before suppression because we may not want all of the elements. // note, suppressedBy should *never* be smaller than the suppressed. EGlslSymbolType type = symOrStructMember->type; GlslSymbolOrStructMemberBase const* suppressedBy = symOrStructMember->outputSuppressedBy(); if (suppressedBy) symOrStructMember = suppressedBy; EAttribSemantic sem = parseAttributeSemantic( symOrStructMember->semantic ); const std::string& semantic = symOrStructMember->semantic; // Offset the semantic for the case of an array if ( semanticOffset > 0 ) sem = static_cast<EAttribSemantic>( (int)sem + semanticOffset ); //clear the return values outName = ""; ctor = ""; pad = 0; //compute the # of elements in the type switch (type) { case EgstBool: case EgstBool2: case EgstBool3: case EgstBool4: base = EgstBool; size = type - EgstBool + 1; break; case EgstInt: case EgstInt2: case EgstInt3: case EgstInt4: base = EgstInt; size = type - EgstInt + 1; break; case EgstFloat: case EgstFloat2: case EgstFloat3: case EgstFloat4: base = EgstFloat; size = type - EgstFloat + 1; break; default: return false; }; if ( c != EClassUniform) { ctor = getTypeString( (EGlslSymbolType)((int)base + size - 1)); //default constructor pad = 0; switch (c) { case EClassNone: return false; case EClassAttrib: getAttributeName( symOrStructMember, outName, sem, semanticOffset ); if (outName == "gl_Normal" && size == 4) pad = 1; break; case EClassVarOut: // If using user varyings, create a user varying name // WHY ON EARTH such funny if if ( (bUserVaryings && sem != EAttrSemPosition && sem != EAttrSemPrimitiveID && sem != EAttrSemPSize) || varOutString[sem][0] == 0 ) { outName = kUserVaryingPrefix; outName += semantic; // If an array element, add the semantic offset to the name if ( semanticOffset > 0 ) { outName += "_"; outName += ( semanticOffset + '0' ); } } else { // Use built-in varying name outName = varOutString[sem]; // Always pad built-in varying outputs to 4 elements // exception: psize must be kept float1 if(sem != EAttrSemPSize) { pad = 4 - size; ctor = "vec4"; } } break; case EClassVarIn: // inout COLORn variables translate to framebuffer fetch for GLES if (IsArgumentForFramebufferFetch(symOrStructMember, sem, m_Target)) { int index = sem - EAttrSemColor0; outName = (m_Target == ETargetGLSL_ES_100 ? "gl_LastFragData" : "gl_FragData"); outName += '['; outName += char('0'+index); outName += ']'; m_Extensions.insert("GL_EXT_shader_framebuffer_fetch"); } // If using user varyings, create a user varying name else if ( (bUserVaryings && sem != EAttrSemVPos && sem != EAttrSemVFace && sem != EAttrSemPrimitiveID) || varInString[sem][0] == 0 ) { outName = kUserVaryingPrefix; outName += stripSemanticModifier (semantic, false); // If an array element, add the semantic offset to the name if ( semanticOffset > 0 ) { outName += "_"; outName += ( semanticOffset + '0' ); } } else { // Use built-in varying name outName = varInString[sem]; } break; case EClassRes: outName = resultString[sem]; if (sem == EAttrSemDepth) { if (m_Target == ETargetGLSL_ES_100) { outName = "gl_FragDepthEXT"; m_Extensions.insert("GL_EXT_frag_depth"); } ctor = "float"; } else if (sem == EAttrSemCoverage) ctor = "int"; else { pad = 4 - size; ctor = "vec4"; } break; case EClassUniform: assert(0); // this should have been stripped return false; }; } else { //these should always match exactly outName = "xlu_"; outName += symOrStructMember->name; } 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; }
// 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; }