예제 #1
0
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 &current = 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 &current = 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 &current = 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;
}
예제 #2
0
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;
}