void GLSLESProgramManagerCommon::optimiseShaderSource(GLSLESGpuProgram* gpuProgram)
    {
        if(!gpuProgram->getGLSLProgram()->getIsOptimised())
        {
            GpuProgramType gpuType = gpuProgram->getType();
            const glslopt_shader_type shaderType = (gpuType == GPT_VERTEX_PROGRAM) ? kGlslOptShaderVertex : kGlslOptShaderFragment;
            String shaderSource = gpuProgram->getGLSLProgram()->getSource();
            glslopt_shader* shader = glslopt_optimize(mGLSLOptimiserContext, shaderType, shaderSource.c_str(), 0);

            std::stringstream os;
            if(glslopt_get_status(shader))
            {
                // Write the current version (this forces the driver to fulfill the glsl es standard)
                // TODO: Need to insert the current or compatibility version.  This is not future-proof
                os << "#version 100" << std::endl;

                // Default precision declaration is required in fragment and vertex shaders.
                os << "precision highp float;" << std::endl;
                os << "precision highp int;" << std::endl;
                String source = const_cast<char *>(glslopt_get_output(shader));

                // We want to make sure that there are precision qualifiers so strip out the first line
                // which is the version string. We don't want duplicates since we are adding our own.
                size_t pos = source.find_first_of("\n");
                os << source.substr(pos+1, source.length()-1);

                gpuProgram->getGLSLProgram()->setOptimisedSource(os.str());
                gpuProgram->getGLSLProgram()->setIsOptimised(true);
            }
            else
            {
                LogManager::getSingleton().logMessage("Error from GLSL Optimiser, disabling optimisation for program: " + gpuProgram->getName());
                gpuProgram->getGLSLProgram()->setParameter("use_optimiser", "false");
//                LogManager::getSingleton().logMessage(String(glslopt_get_log(shader)));
//                LogManager::getSingleton().logMessage("Original Shader");
//                LogManager::getSingleton().logMessage(gpuProgram->getGLSLProgram()->getSource());
//                LogManager::getSingleton().logMessage("Optimized Shader");
//                LogManager::getSingleton().logMessage(os.str());
            }
            glslopt_shader_delete(shader);
        }
    }
Exemplo n.º 2
0
extern "C" void compileShader()
{
   // Copy the AS3 string to the C heap (must be free'd later)
   char *src = NULL;
   AS3_MallocString(src, src);

   bool gles = false;
   AS3_CopyScalarToVar(gles, gles);

   glslopt_ctx* gContext = glslopt_initialize(gles);

   int mode;
   AS3_GetScalarFromVar(mode, mode);
   const glslopt_shader_type type = mode == 0 ? kGlslOptShaderVertex : kGlslOptShaderFragment;

   glslopt_shader* shader = glslopt_optimize(gContext, type, src, 0);

   const char* optimizedShader = glslopt_get_output(shader);
   AS3_DeclareVar(outputstr, String);
   AS3_CopyCStringToVar(outputstr, optimizedShader, strlen(optimizedShader));

   glslopt_cleanup(gContext);

   //optimize shader and/or turn sampler instructions into easily
   //replaceable aliases (for generating sampler variations at runtime)
   inline_as3(
      "import com.adobe.AGALOptimiser.translator.transformations.Utils;\n"
      "var shader:Object = null;\n"
      "try { shader = JSON.parse(outputstr) } catch(e:*) { }\n"
      "if(shader != null && shader[\"agalasm\"] != null) {\n"
      "    if(optimize)\n"
      "        shader = Utils.optimizeShader(shader, mode == 0)\n"
      "    shader[\"agalasm\"] = Utils.processSamplers(shader[\"agalasm\"]);\n"
      "    outputstr = JSON.stringify(shader, null, 1)\n"
      "}\n"
   );

   AS3_ReturnAS3Var(outputstr);
}
Exemplo n.º 3
0
static bool compileShader(const char* dstfilename, const char* srcfilename, bool vertexShader)
{
	const char* originalShader = loadFile(srcfilename);
	if( !originalShader )
		return false;

	const glslopt_shader_type type = vertexShader ? kGlslOptShaderVertex : kGlslOptShaderFragment;

	glslopt_shader* shader = glslopt_optimize(gContext, type, originalShader, 0);
	if( !glslopt_get_status(shader) )
	{
		printf( "Failed to compile %s:\n\n%s\n", srcfilename, glslopt_get_log(shader));
		return false;
	}

	const char* optimizedShader = glslopt_get_output(shader);

	if( !saveFile(dstfilename, optimizedShader) )
		return false;

	delete[] originalShader;
	return true;
}
Exemplo n.º 4
0
extern "C" void compileShader()
{
   // Copy the AS3 string to the C heap (must be free'd later)
   char *src = NULL;
   AS3_MallocString(src, src);

   bool gles = false;
   AS3_CopyScalarToVar(gles, gles);


   glslopt_ctx* gContext = glslopt_initialize(gles);

   int mode;
   AS3_GetScalarFromVar(mode, mode);
   const glslopt_shader_type type = mode == 0 ? kGlslOptShaderVertex : kGlslOptShaderFragment;

   glslopt_shader* shader = glslopt_optimize(gContext, type, src, 0);

   const char* optimizedShader = glslopt_get_output(shader);
   AS3_DeclareVar(outputstr, String);
   AS3_CopyCStringToVar(outputstr, optimizedShader, strlen(optimizedShader));

   glslopt_cleanup(gContext);

   inline_as3(
      "import com.adobe.AGALOptimiser.translator.transformations.Utils;\n"
      "if(optimize) {\n"
      "    var shader:Object = JSON.parse(outputstr)\n"
      "    if(shader[\"agalasm\"] != null) {\n"
      "        shader = Utils.optimizeShader(shader, mode == 0)\n"
      "        outputstr = JSON.stringify(shader)\n"
      "    }\n"
      "}\n"
   );

   AS3_ReturnAS3Var(outputstr);
}
    void GLSLESProgramManagerCommon::optimiseShaderSource(GLSLESGpuProgram* gpuProgram)
    {
        if(!gpuProgram->getGLSLProgram()->getIsOptimised())
        {
            GpuProgramType gpuType = gpuProgram->getType();
            const glslopt_shader_type shaderType = (gpuType == GPT_VERTEX_PROGRAM) ? kGlslOptShaderVertex : kGlslOptShaderFragment;
            String shaderSource = gpuProgram->getGLSLProgram()->getSource();
            glslopt_shader* shader = glslopt_optimize(mGLSLOptimiserContext, shaderType, shaderSource.c_str(), 0);

            std::stringstream os;
            if(glslopt_get_status(shader))
            {
                // Write the current version (this forces the driver to fulfill the glsl es standard)
                // TODO: Need to insert the current or compatibility version.  This is not future-proof
                os << "#version 100" << std::endl;
                
                // Default precision declaration is required in fragment and vertex shaders.
                os << "precision mediump float;" << std::endl;
                os << "precision highp int;" << std::endl;
                os << glslopt_get_output(shader);
                gpuProgram->getGLSLProgram()->setSource(os.str());
                gpuProgram->getGLSLProgram()->setIsOptimised(true);
            }
            else
            {
                LogManager::getSingleton().logMessage("Error from GLSL Optimiser, disabling optimisation for program: " + gpuProgram->getName());
                gpuProgram->getGLSLProgram()->setParameter("use_optimiser", "false");
//                LogManager::getSingleton().logMessage(String(glslopt_get_log(shader)));
//                LogManager::getSingleton().logMessage("Original Shader");
//                LogManager::getSingleton().logMessage(gpuProgram->getGLSLProgram()->getSource());
//                LogManager::getSingleton().logMessage("Optimized Shader");
//                LogManager::getSingleton().logMessage(os.str());
            }
            glslopt_shader_delete(shader);
        }
    }
Exemplo n.º 6
0
	void OpenGLGraphicSystem::ProcessShader( BohgeEngine::ShaderProperty::ShaderCode sc, eastl::string& code )
	{
		Utility::RemoveTargetString( code, "precision highp float;" );
		Utility::RemoveTargetString( code, "precision mediump float;" );
		Utility::RemoveTargetString( code, "precision lowp float;" );
		Utility::RemoveTargetString( code, "highp " );
		Utility::RemoveTargetString( code, "mediump " );
		Utility::RemoveTargetString( code, "lowp " );
		code.insert(0, "#define	MAXJOINTS	128\n" );
		code.insert(0, "#define	MAXARRAYSIZE	256\n" );
		code.insert(0, "#define	_WINDOWS_\n" );
		glslopt_shader_type type;
		switch( sc )
		{
		case ShaderProperty::SC_VERTEX: type = kGlslOptShaderVertex; break;
		case ShaderProperty::SC_FRAGMENT: type = kGlslOptShaderFragment; break;
		default:ASSERT(false);
		}

		glslopt_shader* shader = glslopt_optimize( m_pGlslopt_ctx, type, code.c_str(), 0 );
		if ( glslopt_get_status(shader) )
		{
			code = glslopt_get_output( shader );
		}
		else
		{
			const char* log = glslopt_get_log( shader );
			DEBUGLOG("Can't optimize shader, caz %s\n", log );
		}
		if ( ShaderProperty::SC_VERTEX == sc )
		{
			size_t mainbegin = _FindMainFunction(code);
			_FixAttributeIndexingType( code, mainbegin );
		}
		glslopt_shader_delete( shader );
	}
Exemplo n.º 7
0
static bool TestFile (glslopt_ctx* ctx, bool vertex,
	const std::string& testName,
	const std::string& inputPath,
	const std::string& hirPath,
	const std::string& outputPath,
	bool gles,
	bool doCheckGLSL)
{
	std::string input;
	if (!ReadStringFromFile (inputPath.c_str(), input))
	{
		printf ("\n  %s: failed to read input file\n", testName.c_str());
		return false;
	}
	if (doCheckGLSL)
	{
		if (!CheckGLSL (vertex, gles, testName, "input", input.c_str()))
			return false;
	}

	if (gles)
	{
		if (vertex)
			MassageVertexForGLES (input);
		else
			MassageFragmentForGLES (input);
	}

	bool res = true;

	glslopt_shader_type type = vertex ? kGlslOptShaderVertex : kGlslOptShaderFragment;
	glslopt_shader* shader = glslopt_optimize (ctx, type, input.c_str(), 0);

	bool optimizeOk = glslopt_get_status(shader);
	if (optimizeOk)
	{
		std::string textHir = glslopt_get_raw_output (shader);
		std::string textOpt = glslopt_get_output (shader);
		std::string outputHir;
		ReadStringFromFile (hirPath.c_str(), outputHir);
		std::string outputOpt;
		ReadStringFromFile (outputPath.c_str(), outputOpt);

		if (textHir != outputHir)
		{
			// write output
			FILE* f = fopen (hirPath.c_str(), "wb");
			if (!f)
			{
				printf ("\n  %s: can't write to IR file!\n", testName.c_str());
			}
			else
			{
				fwrite (textHir.c_str(), 1, textHir.size(), f);
				fclose (f);
			}
			printf ("\n  %s: does not match raw output\n", testName.c_str());
			res = false;
		}

		if (textOpt != outputOpt)
		{
			// write output
			FILE* f = fopen (outputPath.c_str(), "wb");
			if (!f)
			{
				printf ("\n  %s: can't write to optimized file!\n", testName.c_str());
			}
			else
			{
				fwrite (textOpt.c_str(), 1, textOpt.size(), f);
				fclose (f);
			}
			printf ("\n  %s: does not match optimized output\n", testName.c_str());
			res = false;
		}
		if (res && doCheckGLSL && !CheckGLSL (vertex, gles, testName, "raw", textHir.c_str()))
			res = false;
		if (res && doCheckGLSL && !CheckGLSL (vertex, gles, testName, "optimized", textOpt.c_str()))
			res = false;
	}
	else
	{
		printf ("\n  %s: optimize error: %s\n", testName.c_str(), glslopt_get_log(shader));
		res = false;
	}

	glslopt_shader_delete (shader);

	return res;
}
Exemplo n.º 8
0
bool compileGLSLShader(bx::CommandLine& _cmdLine, uint32_t _gles, const std::string& _code, bx::WriterI* _writer)
{
	char ch = tolower(_cmdLine.findOption('\0', "type")[0]);
	const glslopt_shader_type type = ch == 'f'
		? kGlslOptShaderFragment
		: (ch == 'c' ? kGlslOptShaderCompute : kGlslOptShaderVertex);

    glslopt_target target = kGlslTargetOpenGL;
	switch (_gles)
	{
	case BX_MAKEFOURCC('M', 'T', 'L', 0):
		target = kGlslTargetMetal;
		break;

	case 2:
		target = kGlslTargetOpenGLES20;
		break;

	case 3:
		target = kGlslTargetOpenGLES30;
		break;

	default:
		target = kGlslTargetOpenGL;
		break;
	}

	glslopt_ctx* ctx = glslopt_initialize(target);

	glslopt_shader* shader = glslopt_optimize(ctx, type, _code.c_str(), 0);

	if (!glslopt_get_status(shader) )
	{
		const char* log = glslopt_get_log(shader);
		int32_t source = 0;
		int32_t line = 0;
		int32_t column = 0;
		int32_t start = 0;
		int32_t end = INT32_MAX;

		if (3 == sscanf(log, "%u:%u(%u):", &source, &line, &column)
		&&  0 != line)
		{
			start = bx::uint32_imax(1, line-10);
			end = start + 20;
		}

		printCode(_code.c_str(), line, start, end);
		fprintf(stderr, "Error: %s\n", log);
		glslopt_cleanup(ctx);
		return false;
	}

	const char* optimizedShader = glslopt_get_output(shader);

	// Trim all directives.
	while ('#' == *optimizedShader)
	{
		optimizedShader = bx::strnl(optimizedShader);
	}

	if (0 != _gles)
	{
		char* code = const_cast<char*>(optimizedShader);
		strreplace(code, "gl_FragDepthEXT", "gl_FragDepth");

		strreplace(code, "texture2DLodEXT", "texture2DLod");
		strreplace(code, "texture2DProjLodEXT", "texture2DProjLod");
		strreplace(code, "textureCubeLodEXT", "textureCubeLod");
		strreplace(code, "texture2DGradEXT", "texture2DGrad");
		strreplace(code, "texture2DProjGradEXT", "texture2DProjGrad");
		strreplace(code, "textureCubeGradEXT", "textureCubeGrad");

		strreplace(code, "shadow2DEXT", "shadow2D");
		strreplace(code, "shadow2DProjEXT", "shadow2DProj");
	}

	UniformArray uniforms;

	{
		const char* parse = optimizedShader;

		while (NULL != parse
			&&  *parse != '\0')
		{
			parse = bx::strws(parse);
			const char* eol = strchr(parse, ';');
			if (NULL != eol)
			{
				const char* qualifier = parse;
				parse = bx::strws(bx::strword(parse) );

				if (0 == strncmp(qualifier, "attribute", 9)
				||  0 == strncmp(qualifier, "varying", 7) )
				{
					// skip attributes and varyings.
					parse = eol + 1;
					continue;
				}

				if (0 != strncmp(qualifier, "uniform", 7) )
				{
					// end if there is no uniform keyword.
					parse = NULL;
					continue;
				}

				const char* precision = NULL;
				const char* typen = parse;

				if (0 == strncmp(typen, "lowp", 4)
				||  0 == strncmp(typen, "mediump", 7)
				||  0 == strncmp(typen, "highp", 5) )
				{
					precision = typen;
					typen = parse = bx::strws(bx::strword(parse) );
				}

				BX_UNUSED(precision);

				char uniformType[256];
				parse = bx::strword(parse);

				if (0 == strncmp(typen, "sampler", 7) )
				{
					strcpy(uniformType, "int");
				}
				else
				{
					bx::strlcpy(uniformType, typen, parse-typen+1);
				}

				const char* name = parse = bx::strws(parse);

				char uniformName[256];
				uint8_t num = 1;
				const char* array = bx::strnstr(name, "[", eol-parse);
				if (NULL != array)
				{
					bx::strlcpy(uniformName, name, array-name+1);

					char arraySize[32];
					const char* end = bx::strnstr(array, "]", eol-array);
					bx::strlcpy(arraySize, array+1, end-array);
					num = atoi(arraySize);
				}
				else
				{
					bx::strlcpy(uniformName, name, eol-name+1);
				}

				Uniform un;
				un.type = nameToUniformTypeEnum(uniformType);

				if (UniformType::Count != un.type)
				{
					BX_TRACE("name: %s (type %d, num %d)", uniformName, un.type, num);

					un.name = uniformName;
					un.num = num;
					un.regIndex = 0;
					un.regCount = num;
					uniforms.push_back(un);
				}

				parse = eol + 1;
			}
		}
	}

	uint16_t count = (uint16_t)uniforms.size();
	bx::write(_writer, count);

	for (UniformArray::const_iterator it = uniforms.begin(); it != uniforms.end(); ++it)
	{
		const Uniform& un = *it;
		uint8_t nameSize = (uint8_t)un.name.size();
		bx::write(_writer, nameSize);
		bx::write(_writer, un.name.c_str(), nameSize);
		uint8_t uniformType = un.type;
		bx::write(_writer, uniformType);
		bx::write(_writer, un.num);
		bx::write(_writer, un.regIndex);
		bx::write(_writer, un.regCount);

		BX_TRACE("%s, %s, %d, %d, %d"
			, un.name.c_str()
			, getUniformTypeName(un.type)
			, un.num
			, un.regIndex
			, un.regCount
			);
	}

	uint32_t shaderSize = (uint32_t)strlen(optimizedShader);
	bx::write(_writer, shaderSize);
	bx::write(_writer, optimizedShader, shaderSize);
	uint8_t nul = 0;
	bx::write(_writer, nul);

	glslopt_cleanup(ctx);

	return true;
}
Exemplo n.º 9
0
	static bool compile(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
	{
		char ch = _options.shaderType;
		const glslopt_shader_type type = ch == 'f'
			? kGlslOptShaderFragment
			: (ch == 'c' ? kGlslOptShaderCompute : kGlslOptShaderVertex);

		glslopt_target target = kGlslTargetOpenGL;
		switch (_version)
		{
		case BX_MAKEFOURCC('M', 'T', 'L', 0):
			target = kGlslTargetMetal;
			break;

		case 2:
			target = kGlslTargetOpenGLES20;
			break;

		case 3:
			target = kGlslTargetOpenGLES30;
			break;

		default:
			target = kGlslTargetOpenGL;
			break;
		}

		glslopt_ctx* ctx = glslopt_initialize(target);

		glslopt_shader* shader = glslopt_optimize(ctx, type, _code.c_str(), 0);

		if (!glslopt_get_status(shader) )
		{
			const char* log = glslopt_get_log(shader);
			int32_t source  = 0;
			int32_t line    = 0;
			int32_t column  = 0;
			int32_t start   = 0;
			int32_t end     = INT32_MAX;

			bool found = false
				|| 3 == sscanf(log, "%u:%u(%u):", &source, &line, &column)
				|| 2 == sscanf(log, "(%u,%u):", &line, &column)
				;

			if (found
			&&  0 != line)
			{
				start = bx::uint32_imax(1, line-10);
				end   = start + 20;
			}

			printCode(_code.c_str(), line, start, end, column);
			fprintf(stderr, "Error: %s\n", log);
			glslopt_shader_delete(shader);
			glslopt_cleanup(ctx);
			return false;
		}

		const char* optimizedShader = glslopt_get_output(shader);

		// Trim all directives.
		while ('#' == *optimizedShader)
		{
			optimizedShader = bx::strnl(optimizedShader);
		}

		{
			char* code = const_cast<char*>(optimizedShader);
			strReplace(code, "gl_FragDepthEXT", "gl_FragDepth");

			strReplace(code, "texture2DLodARB", "texture2DLod");
			strReplace(code, "texture2DLodEXT", "texture2DLod");
			strReplace(code, "texture2DGradARB", "texture2DGrad");
			strReplace(code, "texture2DGradEXT", "texture2DGrad");

			strReplace(code, "textureCubeLodARB", "textureCubeLod");
			strReplace(code, "textureCubeLodEXT", "textureCubeLod");
			strReplace(code, "textureCubeGradARB", "textureCubeGrad");
			strReplace(code, "textureCubeGradEXT", "textureCubeGrad");

			strReplace(code, "texture2DProjLodARB", "texture2DProjLod");
			strReplace(code, "texture2DProjLodEXT", "texture2DProjLod");
			strReplace(code, "texture2DProjGradARB", "texture2DProjGrad");
			strReplace(code, "texture2DProjGradEXT", "texture2DProjGrad");

			strReplace(code, "shadow2DARB", "shadow2D");
			strReplace(code, "shadow2DEXT", "shadow2D");
			strReplace(code, "shadow2DProjARB", "shadow2DProj");
			strReplace(code, "shadow2DProjEXT", "shadow2DProj");
		}

		UniformArray uniforms;

		if (target != kGlslTargetMetal)
		{
			const char* parse = optimizedShader;

			while (NULL != parse
				&&  *parse != '\0')
			{
				parse = bx::strws(parse);
				const char* eol = bx::strFind(parse, ';');
				if (NULL != eol)
				{
					const char* qualifier = parse;
					parse = bx::strws(bx::strSkipWord(parse) );

					if (0 == bx::strCmp(qualifier, "attribute", 9)
					||  0 == bx::strCmp(qualifier, "varying",   7)
					||  0 == bx::strCmp(qualifier, "in",        2)
					||  0 == bx::strCmp(qualifier, "out",       3)
					   )
					{
						// skip attributes and varyings.
						parse = eol + 1;
						continue;
					}

					if (0 == bx::strCmp(parse, "tmpvar", 6) )
					{
						// skip temporaries
						parse = eol + 1;
						continue;
					}

					if (0 != bx::strCmp(qualifier, "uniform", 7) )
					{
						// end if there is no uniform keyword.
						parse = NULL;
						continue;
					}

					const char* precision = NULL;
					const char* typen = parse;

					if (0 == bx::strCmp(typen, "lowp", 4)
					||  0 == bx::strCmp(typen, "mediump", 7)
					||  0 == bx::strCmp(typen, "highp", 5) )
					{
						precision = typen;
						typen = parse = bx::strws(bx::strSkipWord(parse) );
					}

					BX_UNUSED(precision);

					char uniformType[256];
					parse = bx::strSkipWord(parse);

					if (0 == bx::strCmp(typen, "sampler", 7) )
					{
						bx::strCopy(uniformType, BX_COUNTOF(uniformType), "int");
					}
					else
					{
						bx::strCopy(uniformType, int32_t(parse-typen+1), typen);
					}

					const char* name = parse = bx::strws(parse);

					char uniformName[256];
					uint8_t num = 1;
					const char* array = bx::strFind(bx::StringView(name, int32_t(eol-parse) ), "[");
					if (NULL != array)
					{
						bx::strCopy(uniformName, int32_t(array-name+1), name);

						char arraySize[32];
						const char* end = bx::strFind(bx::StringView(array, int32_t(eol-array) ), "]");
						bx::strCopy(arraySize, int32_t(end-array), array+1);
						num = uint8_t(atoi(arraySize) );
					}
					else
					{
						bx::strCopy(uniformName, int32_t(eol-name+1), name);
					}

					Uniform un;
					un.type = nameToUniformTypeEnum(uniformType);

					if (UniformType::Count != un.type)
					{
						BX_TRACE("name: %s (type %d, num %d)", uniformName, un.type, num);

						un.name = uniformName;
						un.num = num;
						un.regIndex = 0;
						un.regCount = num;
						uniforms.push_back(un);
					}

					parse = eol + 1;
				}
			}
		}
		else
		{
			const char* parse = bx::strFind(optimizedShader, "struct xlatMtlShaderUniform {");
			const char* end   = parse;
			if (NULL != parse)
			{
				parse += bx::strLen("struct xlatMtlShaderUniform {");
				end   =  bx::strFind(parse, "};");
			}

			while ( parse < end
			&&     *parse != '\0')
			{
				parse = bx::strws(parse);
				const char* eol = bx::strFind(parse, ';');
				if (NULL != eol)
				{
					const char* typen = parse;

					char uniformType[256];
					parse = bx::strSkipWord(parse);
					bx::strCopy(uniformType, int32_t(parse-typen+1), typen);
					const char* name = parse = bx::strws(parse);

					char uniformName[256];
					uint8_t num = 1;
					const char* array = bx::strFind(bx::StringView(name, int32_t(eol-parse) ), "[");
					if (NULL != array)
					{
						bx::strCopy(uniformName, int32_t(array-name+1), name);

						char arraySize[32];
						const char* arrayEnd = bx::strFind(bx::StringView(array, int32_t(eol-array) ), "]");
						bx::strCopy(arraySize, int32_t(arrayEnd-array), array+1);
						num = uint8_t(atoi(arraySize) );
					}
					else
					{
						bx::strCopy(uniformName, int32_t(eol-name+1), name);
					}

					Uniform un;
					un.type = nameToUniformTypeEnum(uniformType);

					if (UniformType::Count != un.type)
					{
						BX_TRACE("name: %s (type %d, num %d)", uniformName, un.type, num);

						un.name = uniformName;
						un.num = num;
						un.regIndex = 0;
						un.regCount = num;
						uniforms.push_back(un);
					}

					parse = eol + 1;
				}
			}
		}

		uint16_t count = (uint16_t)uniforms.size();
		bx::write(_writer, count);

		for (UniformArray::const_iterator it = uniforms.begin(); it != uniforms.end(); ++it)
		{
			const Uniform& un = *it;
			uint8_t nameSize = (uint8_t)un.name.size();
			bx::write(_writer, nameSize);
			bx::write(_writer, un.name.c_str(), nameSize);
			uint8_t uniformType = uint8_t(un.type);
			bx::write(_writer, uniformType);
			bx::write(_writer, un.num);
			bx::write(_writer, un.regIndex);
			bx::write(_writer, un.regCount);

			BX_TRACE("%s, %s, %d, %d, %d"
				, un.name.c_str()
				, getUniformTypeName(un.type)
				, un.num
				, un.regIndex
				, un.regCount
				);
		}

		uint32_t shaderSize = (uint32_t)bx::strLen(optimizedShader);
		bx::write(_writer, shaderSize);
		bx::write(_writer, optimizedShader, shaderSize);
		uint8_t nul = 0;
		bx::write(_writer, nul);

		if (_options.disasm )
		{
			std::string disasmfp = _options.outputFilePath + ".disasm";
			writeFile(disasmfp.c_str(), optimizedShader, shaderSize);
		}

		glslopt_shader_delete(shader);
		glslopt_cleanup(ctx);

		return true;
	}
Exemplo n.º 10
0
static bool TestFile (glslopt_ctx* ctx, bool vertex,
	const std::string& testName,
	const std::string& inputPath,
	const std::string& outputPath,
	bool gles,
	bool doCheckGLSL,
	bool doCheckMetal)
{
	std::string input;
	if (!ReadStringFromFile (inputPath.c_str(), input))
	{
		printf ("\n  %s: failed to read input file\n", testName.c_str());
		return false;
	}
	if (doCheckGLSL)
	{
		if (!CheckGLSL (vertex, gles, testName, "input", input.c_str()))
			return false;
	}

	if (gles)
	{
		if (vertex)
			MassageVertexForGLES (input);
		else
			MassageFragmentForGLES (input);
	}

	bool res = true;

	glslopt_shader_type type = vertex ? kGlslOptShaderVertex : kGlslOptShaderFragment;
	glslopt_shader* shader = glslopt_optimize (ctx, type, input.c_str(), 0);

	bool optimizeOk = glslopt_get_status(shader);
	if (optimizeOk)
	{
		std::string textHir = glslopt_get_raw_output (shader);
		std::string textOpt = glslopt_get_output (shader);

		// append stats
		char buffer[1000];
		int statsAlu, statsTex, statsFlow;
		glslopt_shader_get_stats (shader, &statsAlu, &statsTex, &statsFlow);
		sprintf(buffer, "\n// stats: %i alu %i tex %i flow\n", statsAlu, statsTex, statsFlow);
		textOpt += buffer;
		
		// append inputs
		const int inputCount = glslopt_shader_get_input_count (shader);
		if (inputCount > 0)
		{
			sprintf(buffer, "// inputs: %i\n", inputCount);
			textOpt += buffer;
		}
		for (int i = 0; i < inputCount; ++i)
		{
			const char* parName;
			glslopt_basic_type parType;
			glslopt_precision parPrec;
			int parVecSize, parMatSize, parArrSize, location;
			glslopt_shader_get_input_desc(shader, i, &parName, &parType, &parPrec, &parVecSize, &parMatSize, &parArrSize, &location);
			if (location >= 0)
				sprintf(buffer, "//  #%i: %s (%s %s) %ix%i [%i] loc %i\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize, location);
			else
				sprintf(buffer, "//  #%i: %s (%s %s) %ix%i [%i]\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize);
			textOpt += buffer;
		}
		// append uniforms
		const int uniformCount = glslopt_shader_get_uniform_count (shader);
		const int uniformSize = glslopt_shader_get_uniform_total_size (shader);
		if (uniformCount > 0)
		{
			sprintf(buffer, "// uniforms: %i (total size: %i)\n", uniformCount, uniformSize);
			textOpt += buffer;
		}
		for (int i = 0; i < uniformCount; ++i)
		{
			const char* parName;
			glslopt_basic_type parType;
			glslopt_precision parPrec;
			int parVecSize, parMatSize, parArrSize, location;
			glslopt_shader_get_uniform_desc(shader, i, &parName, &parType, &parPrec, &parVecSize, &parMatSize, &parArrSize, &location);
			if (location >= 0)
				sprintf(buffer, "//  #%i: %s (%s %s) %ix%i [%i] loc %i\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize, location);
			else
				sprintf(buffer, "//  #%i: %s (%s %s) %ix%i [%i]\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize);
			textOpt += buffer;
		}
		// append textures
		const int textureCount = glslopt_shader_get_texture_count (shader);
		if (textureCount > 0)
		{
			sprintf(buffer, "// textures: %i\n", textureCount);
			textOpt += buffer;
		}
		for (int i = 0; i < textureCount; ++i)
		{
			const char* parName;
			glslopt_basic_type parType;
			glslopt_precision parPrec;
			int parVecSize, parMatSize, parArrSize, location;
			glslopt_shader_get_texture_desc(shader, i, &parName, &parType, &parPrec, &parVecSize, &parMatSize, &parArrSize, &location);
			if (location >= 0)
				sprintf(buffer, "//  #%i: %s (%s %s) %ix%i [%i] loc %i\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize, location);
			else
				sprintf(buffer, "//  #%i: %s (%s %s) %ix%i [%i]\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize);
			textOpt += buffer;
		}

		std::string outputOpt;
		ReadStringFromFile (outputPath.c_str(), outputOpt);

		if (res && doCheckMetal && !CheckMetal (vertex, gles, testName, "metal", textOpt.c_str()))
			res = false;
		
		if (textOpt != outputOpt)
		{
			// write output
			FILE* f = fopen (outputPath.c_str(), "wb");
			if (!f)
			{
				printf ("\n  %s: can't write to optimized file!\n", testName.c_str());
			}
			else
			{
				fwrite (textOpt.c_str(), 1, textOpt.size(), f);
				fclose (f);
			}
			printf ("\n  %s: does not match optimized output\n", testName.c_str());
			res = false;
		}
		if (res && doCheckGLSL && !CheckGLSL (vertex, gles, testName, "raw", textHir.c_str()))
			res = false;
		if (res && doCheckGLSL && !CheckGLSL (vertex, gles, testName, "optimized", textOpt.c_str()))
			res = false;
	}
	else
	{
		printf ("\n  %s: optimize error: %s\n", testName.c_str(), glslopt_get_log(shader));
		res = false;
	}

	glslopt_shader_delete (shader);

	return res;
}
Exemplo n.º 11
0
	// ------------------------------------------------------------------------------------------
	//! Generates the HLSL shader Byte code.
	//! @param ShaderByteCodeOutputStream Output for the shader code.
	//! @param ShaderParsedData Parsed shader data, produced by @ref HLSLParser
	//! @param ShaderType Type of the shader to generate code.
	//! @param ShaderEntryName Name of the shader entry point.
	//! @param ShaderTargetPlatform Target platform for the GLSL code.
	void GLSLCompiler::GenerateAndCompileShader(UniquePtr<OutputStream> const& ShaderByteCodeOutputStream
		, HLSLScope const* const ShaderParsedData, IGraphicsShaderType const ShaderType, String const& ShaderEntryName
		, DHLSLShaderCrossCompilerTarget const ShaderTargetPlatform)
	{
		StringBuilder GLSLGeneratorBuilder;
		GLSLGenerator().GenerateShader(GLSLGeneratorBuilder, ShaderParsedData, ShaderEntryName, ShaderTargetPlatform, ShaderType);

		// Now we need just preprocess generated code to reduce loading time.
		StringBuilder GLSLPreprocessedBuilder;
		GLSLPreprocessedBuilder.Append(GLSLInsertations::GLSLCopyright);
		GLSLPreprocessedBuilder.Append(GLSLInsertations::GLSLPreambule[ShaderTargetPlatform - DHLSL_CC_TARGET_GLSL430]);

		// We use MCPP preprocessor because it is faster than our one.
		//! @todo Implement temporary files access here.
		String const MCPPSourceFilePath = "a.tmp";// Path::GetTemporaryFileName();
		String const MCPPOutputFilePath = "b.tmp";// Path::GetTemporaryFileName();
		CStr   const MCPPArguments[] = { nullptr, "-P", MCPPSourceFilePath.CStr(), MCPPOutputFilePath.CStr() };

		// Saving output to some temporary file to make is accessible from MCPP
		FileOutputStream MCPPSourceFile(/*MCPPSourceFilePath*/L"a.tmp");
		MCPPSourceFile.Write(GLSLGeneratorBuilder.CStr(), GLSLGeneratorBuilder.GetLength(), sizeof(Char));
		MCPPSourceFile.Close();

		{ 
		//	MCPP is not thread-safe.
		//	CriticalSection static MCPPCriticalSection;
		//	ScopedCriticalSection MCPPLock(MCPPCriticalSection);
			int const MCPPResult = mcpp_lib_main(GD_ARRAY_LENGTH(MCPPArguments), const_cast<char**>(&MCPPArguments[0]));
			GD_DEBUG_ASSERT(MCPPResult == 0, "Failed to preprocess shader source.");
		}
		// Reading MCPP result.
		{
			FileInputStream MCPPOutputFile(/*MCPPOutputFilePath*/L"b.tmp");
			SizeTp const GLSLPreprocessedBuilderPos = GLSLPreprocessedBuilder.GetLength();
			GLSLPreprocessedBuilder.Resize(GLSLPreprocessedBuilder.GetLength() + (UInt32) MCPPOutputFile.GetLength());
			MCPPOutputFile.Read(GLSLPreprocessedBuilder.CStr() + GLSLPreprocessedBuilderPos, MCPPOutputFile.GetLength(), 1);
			MCPPOutputFile.Close();
			GLSLPreprocessedBuilder.Append('\0');
			GD_DEBUG_ASSERT(::remove(MCPPSourceFilePath.CStr()) == 0, "Failed to remove mcpp source file.");
			GD_DEBUG_ASSERT(::remove(MCPPOutputFilePath.CStr()) == 0, "Failed to remove mcpp output file.");
		}

		// Trying to optimize the GLSL code..
		StringBuilder GLSLOptimizerBuilder;
		/**/if (ShaderTargetPlatform != DHLSL_CC_TARGET_GLSL430									// glsl_optimizer supports only desktop GLSL 140 version. We are using 430...			
			&& (ShaderType != IGRAPHICS_SHADER_TYPE_VERTEX && ShaderType != IGRAPHICS_SHADER_TYPE_PIXEL))	// glsl_optimizer supports only vertex and pixel shaders...
		{
			// Optimizing is supported for this shader type and target.
			glslopt_target static const IGraphicsGLSLangTargetsTable[] = {
				/* DHLSL_CC_TARGET_GLSL430 */ kGlslTargetOpenGL,
				/* DHLSL_CC_TARGET_GLSLES3 */ kGlslTargetOpenGLES20,
				/* DHLSL_CC_TARGET_GLSLES2 */ kGlslTargetOpenGLES30,
			};
			glslopt_shader_type static const IGraphicsGLSLangShaderTypesTable[] = {
				/* IGRAPHICS_SHADER_TYPE_VERTEX */ kGlslOptShaderVertex,
				/* IGRAPHICS_SHADER_TYPE_PIXEL  */ kGlslOptShaderFragment,
			};

			auto const OptimizerContext = glslopt_initialize(IGraphicsGLSLangTargetsTable[ShaderTargetPlatform]);
			auto const OptimizedShader = glslopt_optimize(OptimizerContext, IGraphicsGLSLangShaderTypesTable[ShaderType - DHLSL_CC_TARGET_GLSL430]
				, GLSLPreprocessedBuilder.CStr(), kGlslOptionSkipPreprocessor);
			if (!glslopt_get_status(OptimizedShader))
			{
				// Shader was not optimized..
				// This does not mean that our shader is incorrect, just MESA-based optimizer may not parse
#if 0			// our code correctly.
				CStr const ShaderOptimizationLog = glslopt_get_log(OptimizedShader);
				throw GLSLCompilerErrorException("shader optimization failed with following log: \n%s", ShaderOptimizationLog);
#endif	// if 0
				GLSLOptimizerBuilder = Move(GLSLPreprocessedBuilder);
			}
			else
			{
				// Shader was optimized..
				GLSLOptimizerBuilder.Append(glslopt_get_output(OptimizedShader));
			}

			glslopt_shader_delete(OptimizedShader);
			glslopt_cleanup(OptimizerContext);
		}
		else
		{
			// Shader was not optimized - using default version.
			GLSLOptimizerBuilder = Move(GLSLPreprocessedBuilder);
		}

		// No we need to validate our shader.. 
		TBuiltInResource static const GLSLangDefaultResources{ INT_MAX };
		EShLanguage static const IGraphicsGLSLangShaderTypesTable[IGRAPHICS_SHADER_TYPES_COUNT] = {
			/* IGRAPHICS_SHADER_TYPE_VERTEX   */ EShLangVertex,
			/* IGRAPHICS_SHADER_TYPE_PIXEL    */ EShLangFragment,
			/* IGRAPHICS_SHADER_TYPE_GEOMETRY */ EShLangGeometry,
			/* IGRAPHICS_SHADER_TYPE_HULL     */ EShLangTessControl,
			/* IGRAPHICS_SHADER_TYPE_DOMAIN   */ EShLangTessEvaluation,
			/* IGRAPHICS_SHADER_TYPE_COMPUTE  */ EShLangCompute,
			/* IGRAPHICS_SHADER_TYPE_UNKNOWN  */ EShLangCount,
		};

		glslang::InitializeProcess();

		// Trying to compile our shader..
		char const* const GLSLOptimizerBuilderPtr = GLSLOptimizerBuilder.CStr();
		glslang::TShader GLSLangShader(IGraphicsGLSLangShaderTypesTable[ShaderType]);
		GLSLangShader.setStrings(&GLSLOptimizerBuilderPtr, 1);
		if (!GLSLangShader.parse(&GLSLangDefaultResources, 100, true, EShMsgDefault))
		{ 
			// Failed to compile our shader.
			throw GLSLCompilerErrorException("GLSLang compilation returned errors: \n%s\nGenerated code:\n%s", GLSLangShader.getInfoLog(), GLSLOptimizerBuilderPtr);
		}
		else
		{
#if 0
			Debug::Log(GLSLangShader.getInfoLog());
#endif	// if 0
		}

		// Trying to link our shader program..
		glslang::TProgram GLSLangProgram;
		GLSLangProgram.addShader(&GLSLangShader);
		if (!GLSLangProgram.link(EShMsgDefault))
		{ 
			throw GLSLCompilerErrorException("GLSLang linking returned errors: \n%s", GLSLangProgram.getInfoLog());
		}
		else
		{
#if 0
			Debug::Log(GLSLangProgram.getInfoLog());
#endif	// if 0
		}

		glslang::FinalizeProcess();

		ShaderByteCodeOutputStream->Write(GLSLOptimizerBuilder.CStr(), GLSLOptimizerBuilder.GetLength(), sizeof(Char));
	}
Exemplo n.º 12
0
bool GLSLTool::Assemble(
	Engine &engine,
	const r::Material &material,
	const Shader::Ref &shader,
	const r::MaterialInputMappings &mapping,
	const Shader::TexCoordMapping &tcMapping,
	r::Shader::Pass pass,
	AssembleFlags flags,
	std::ostream &out
) {
	std::stringstream ss;

	const bool GLES = (flags & kAssemble_GLES) ? true : false;

	if (!(flags&(kAssemble_VertexShader|kAssemble_PixelShader)))
		flags |= kAssemble_VertexShader;

	bool vertexShader = (flags & kAssemble_VertexShader) ? true : false;

	if (!GLES)
		ss << "#version 120\r\n";

	if (vertexShader) {
		ss << "#define VERTEX\r\n";
	} else {
		ss << "#define FRAGMENT\r\n";
		ss << "#define MATERIAL\r\n";
	}

	switch (shader->precisionMode) {
	case Shader::kPrecision_Low:
		ss << "#define PFLOAT FIXED\r\n";
		ss << "#define PFLOAT2 FIXED2\r\n";
		ss << "#define PFLOAT3 FIXED3\r\n";
		ss << "#define PFLOAT4 FIXED4\r\n";
		ss << "#define PFLOAT4X4 FIXED4X4\r\n";
		break;
	case Shader::kPrecision_Medium:
		ss << "#define PFLOAT HALF\r\n";
		ss << "#define PFLOAT2 HALF2\r\n";
		ss << "#define PFLOAT3 HALF3\r\n";
		ss << "#define PFLOAT4 HALF4\r\n";
		ss << "#define PFLOAT4X4 HALF4X4\r\n";
		break;
	case Shader::kPrecision_High:
		ss << "#define PFLOAT FLOAT\r\n";
		ss << "#define PFLOAT2 FLOAT2\r\n";
		ss << "#define PFLOAT3 FLOAT3\r\n";
		ss << "#define PFLOAT4 FLOAT4\r\n";
		ss << "#define PFLOAT4X4 FLOAT4X4\r\n";
		break;
	}

	if (pass != r::Shader::kPass_Preview) {
		if (material.skinMode == r::Material::kSkinMode_Sprite)
			ss << "#define SKIN_SPRITE\r\n";
		if (material.skinMode == r::Material::kSkinMode_Billboard)
			ss << "#define SKIN_BILLBOARD\r\n";
	}

	if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Color) > 0)
		ss << "#define SHADER_COLOR\r\n";

	if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_VertexColor) > 0)
		ss << "#define SHADER_VERTEX_COLOR\r\n";

	if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Vertex) > 0)
		ss << "#define SHADER_POSITION\r\n";

	if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_MV) > 0)
		ss << "#define SHADER_MV\r\n";
	
	if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_PRJ) > 0)
		ss << "#define SHADER_PRJ\r\n";

	if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_MVP) > 0)
		ss << "#define SHADER_MVP\r\n";

	if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_InverseMV) > 0)
		ss << "#define SHADER_INVERSE_MV\r\n";

	if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_InverseMVP) > 0)
		ss << "#define SHADER_INVERSE_MVP\r\n";

	if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_InversePRJ) > 0)
		ss << "#define SHADER_INVERSE_PRJ\r\n";

	if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_PFXVars) > 0)
		ss << "#define PFX_VARS\r\n";
		
	if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_EyeVertex) > 0)
		ss << "#define SHADER_EYE_VERTEX\r\n";

	int numTextures = 0;
	for (int i = 0; i < r::kMaxTextures; ++i) {
		if (mapping.textures[i][0] == r::kInvalidMapping)
			break; // no more texture bindings
		switch (mapping.textures[i][0]) {
		case r::kMaterialTextureSource_Texture:
			ss << "#define T" << i << "TYPE sampler2D" << "\r\n";
			break;
		default:
			break;
		}
		switch(shader->SamplerPrecisionMode(i)) {
		case Shader::kPrecision_Low:
			ss << "#define T" << i << "PRECISION lowp" << "\r\n";
			break;
		case Shader::kPrecision_Medium:
			ss << "#define T" << i << "PRECISION mediump" << "\r\n";
			break;
		case Shader::kPrecision_High:
			ss << "#define T" << i << "PRECISION highp" << "\r\n";
			break;
		}
		++numTextures;
	}

	if (numTextures > 0)
		ss << "#define TEXTURES " << numTextures << "\r\n";
	
	const Shader::IntSet &tcUsage = shader->AttributeUsage(pass, Shader::kMaterialSource_TexCoord);
	int numTexCoords = (int)tcUsage.size();
	if (numTexCoords > 0) {
		// Shaders files can read from r::kMaxTexture unique texture coordinate slots.
		// The set of read texcoord registers may be sparse. Additionally the engine only supports
		// 2 UV channels in most model data. We do some work here to map the set of read texcoord
		// registers onto the smallest possible set:
		//
		// A tcMod may take an input UV channel and modify it, meaning that texture channel is unique,
		// however there are a lot of cases where the tcMod is identity and therefore a texture coordinate
		// register in a shader can be mapped to a commmon register.

		int numTCMods = 0;
		for (int i = 0; i < r::kMaterialTextureSource_MaxIndices; ++i) {
			if (mapping.tcMods[i] == r::kInvalidMapping)
				break;
			++numTCMods;
		}
		
		// numTCMods is the number of unique texture coordinates read by the pixel shader.
		// (which have to be generated by the vertex shader).

		ss << "#define TEXCOORDS " << numTCMods << "\r\n";

		RAD_VERIFY(tcUsage.size() == tcMapping.size());

		if (vertexShader) {
			Shader::IntSet tcInputs;
			
			bool genReflect = false;
			bool genProject = false;

			for (int i = 0; i < r::kMaterialTextureSource_MaxIndices; ++i) {
				if (mapping.tcMods[i] == r::kInvalidMapping)
					break;
				int tcIndex = (int)mapping.tcMods[i];
				tcInputs.insert(material.TCUVIndex(tcIndex));

				int tcMod = material.TCModFlags(tcIndex);
				if (tcMod & r::Material::kTCModFlag_Rotate)
					ss << "#define TEXCOORD" << i << "_ROTATE\r\n";
				if (tcMod & r::Material::kTCModFlag_Scale)
					ss << "#define TEXCOORD" << i << "_SCALE\r\n";
				if (tcMod & r::Material::kTCModFlag_Shift)
					ss << "#define TEXCOORD" << i << "_SHIFT\r\n";
				if (tcMod & r::Material::kTCModFlag_Scroll)
					ss << "#define TEXCOORD" << i << "_SCROLL\r\n";
				if (tcMod & r::Material::kTCModFlag_Turb)
					ss << "#define TEXCOORD" << i << "_TURB\r\n";

				int tcGen = material.TCGen(tcIndex);
				if (tcGen == r::Material::kTCGen_EnvMap) {
					if (!genReflect) {
						genReflect = true;
						ss << "#define GENREFLECT\r\n";
					}
					ss << "#define TEXCOORD" << i << "_GENREFLECT\r\n";
				} else if (tcGen == r::Material::kTCGen_Projected) {
					if (!genProject) {
						genProject = true;
						ss << "#define GENPROJECT\r\n";
					}
					ss << "#define TEXCOORD" << i << "_GENPROJECT\r\n";
				}
			}

			ss << "#define TCINPUTS " << tcInputs.size() << "\r\n";

			for (int i = 0; i < r::kMaterialTextureSource_MaxIndices; ++i) {
				if (mapping.tcMods[i] == r::kInvalidMapping)
					break;
				int tcIndex = (int)mapping.tcMods[i];
				int uvIndex = material.TCUVIndex(tcIndex);

				int ofs = 0;
				for (Shader::IntSet::const_iterator it2 = tcInputs.begin(); it2 != tcInputs.end(); ++it2) {
					if (uvIndex == *it2)
						break;
					++ofs;
				}

				RAD_VERIFY(ofs < (int)tcInputs.size());
				ss << "#define TEXCOORD" << i << " tc" << ofs << "\r\n";
			}
		} else {
			// fragment shader inputs used generated tc's, which may have expanded from the
			// the vertex shader inputs.
			int ofs = 0;
			for (Shader::IntSet::const_iterator it = tcUsage.begin(); it != tcUsage.end(); ++it, ++ofs) {
				ss << "#define TEXCOORD" << ofs << " tc" << tcMapping[ofs].first << "\r\n";
			}
		}
	}
	
	int numColors = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Color);

	if (numColors > 0)
		ss << "#define COLORS " << numColors << "\r\n";

	int numSpecularColors = std::max(
		shader->MaterialSourceUsage(pass, Shader::kMaterialSource_SpecularColor),
		shader->MaterialSourceUsage(pass, Shader::kMaterialSource_SpecularExponent)
	);
	if (numSpecularColors > 0)
		ss << "#define SHADER_SPECULAR_COLORS " << numSpecularColors << "\r\n";

	int numLightPos = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightPos);
	int numLightVec = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightVec);
	int numLightHalfVec = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightHalfVec);
	int numLightVertex = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightVertex);
	int numLightTanVec = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightTanVec);
	int numLightTanHalfVec = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightTanHalfVec);
	int numLightDiffuseColor = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightDiffuseColor);
	int numLightSpecularColor = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightSpecularColor);

	int numShaderNormals = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Normal);
	int numShaderTangents = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Tangent);
	int numShaderBitangents = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Bitangent);

	int numNormals = numShaderNormals;
	int numTangents = numShaderTangents;
	int numBitangents = numShaderBitangents;

	bool needTangents = vertexShader && (numLightTanVec || numLightTanHalfVec);
	if (needTangents) {
		numNormals = std::max(1, numShaderNormals);
		numTangents = std::max(1, numShaderTangents);
		numBitangents = std::max(1, numShaderBitangents);

		ss << "#define TANGENT_FRAME" << "\r\n";
	}

	if (numNormals > 0)
		ss << "#define NORMALS " << numNormals << "\r\n";
	if (numTangents > 0)
		ss << "#define TANGENTS " << numTangents << "\r\n";
	if (numBitangents > 0)
		ss << "#define BITANGENTS " << numBitangents << "\r\n";

	if (numShaderNormals > 0)
		ss << "#define NUM_SHADER_NORMALS " << numShaderNormals << "\r\n";
	if (numShaderTangents > 0)
		ss << "#define NUM_SHADER_TANGENTS " << numShaderTangents << "\r\n";
	if (numShaderBitangents > 0)
		ss << "#define NUM_SHADER_BITANGENTS " << numShaderBitangents << "\r\n";

	int numLights = std::max(
		numLightPos, 
		std::max(
			numLightVec, 
			std::max(
				numLightHalfVec,
				std::max(
					numLightVertex,
					std::max(
						numLightTanVec,
						std::max(
							numLightTanHalfVec,
							std::max(numLightDiffuseColor, numLightSpecularColor)
						)
					)
				)
			)
		)
	);

	if (numLights > 0) {
		ss << "#define LIGHTS " << numLights << "\r\n";
		if (numLightPos > 0)
			ss << "#define SHADER_LIGHT_POS " << numLightPos << "\r\n";
		if (numLightVec > 0)
			ss << "#define SHADER_LIGHT_VEC " << numLightVec << "\r\n";
		if (numLightHalfVec > 0)
			ss << "#define SHADER_LIGHT_HALFVEC " << numLightHalfVec << "\r\n";
		if (numLightVertex > 0)
			ss << "#define SHADER_LIGHT_VERTEXPOS " << numLightVertex << "\r\n";
		if (numLightTanVec > 0)
			ss << "#define SHADER_LIGHT_TANVEC " << numLightTanVec << "\r\n";
		if (numLightTanHalfVec > 0)
			ss << "#define SHADER_LIGHT_TANHALFVEC " << numLightTanHalfVec << "\r\n";
		if (numLightDiffuseColor > 0)
			ss << "#define SHADER_LIGHT_DIFFUSE_COLOR " << numLightDiffuseColor << "\r\n";
		if (numLightSpecularColor > 0)
			ss << "#define SHADER_LIGHT_SPECULAR_COLOR " << numLightSpecularColor << "\r\n";
	}

	if (GLES) {
		ss << "#define _GLES\r\n";
		ss << "#define MOBILE\r\n";
	}
	
	if (!Inject(engine, "@r:/Source/Shaders/Nodes/GLSL.c", ss))
		return false;
	if (!Inject(engine, "@r:/Source/Shaders/Nodes/Common.c", ss))
		return false;
	if (!Inject(engine, "@r:/Source/Shaders/Nodes/Shader.c", ss))
		return false;

	std::stringstream ex;
	if (!ExpandIncludes(*this, ss, ex))
		return false;

	if (flags & kAssemble_Optimize) {
		String in(ex.str());

		glslopt_ctx *glslopt = glslopt_initialize(GLES);

		glslopt_shader *opt_shader = glslopt_optimize(
			glslopt,
			vertexShader ? kGlslOptShaderVertex : kGlslOptShaderFragment,
			in.c_str, 
			0
		);

		char szPass[32];
		sprintf(szPass, "_pass%d", (int)pass);
		
		{
			engine.sys->files->CreateDirectory("@r:/Temp/Shaders/Logs");
			String path(CStr("@r:/Temp/Shaders/Logs/"));
			path += shader->name;
			path += szPass;
			path += "_unoptimized";
			if (vertexShader) {
				path += ".vert.glsl";
			} else {
				path += ".frag.glsl";
			}
			tools::shader_utils::SaveText(engine, path.c_str, in.c_str);
		}

		if (!glslopt_get_status(opt_shader)) {
			COut(C_Error) << "Error optimizing shader: " << std::endl << in << std::endl << glslopt_get_log(opt_shader) << std::endl;
			engine.sys->files->CreateDirectory("@r:/Temp/Shaders/Logs");

			String path;
			for (int i = 0;; ++i) {
				path.PrintfASCII("@r:/Temp/Shaders/Logs/%s_error_%d.log", shader->name.get(), i);
				if (!engine.sys->files->FileExists(path.c_str)) {
					SaveText(engine, path.c_str, in.c_str);
					break;
				}
			}
			glslopt_shader_delete(opt_shader);
			glslopt_cleanup(glslopt);
			return false;
		}

		std::stringstream z;
		if (GLES) {
			switch (shader->precisionMode) {
			case Shader::kPrecision_Low:
				z << "precision lowp float;\r\n";
				break;
			case Shader::kPrecision_Medium:
				z << "precision mediump float;\r\n";
				break;
			case Shader::kPrecision_High:
				z << "precision highp float;\r\n";
				break;
			}
		}
		z << glslopt_get_output(opt_shader);
		glslopt_shader_delete(opt_shader);
		glslopt_cleanup(glslopt);

		Copy(z, out);

		{
			engine.sys->files->CreateDirectory("@r:/Temp/Shaders/Logs");
			String path(CStr("@r:/Temp/Shaders/Logs/"));
			path += shader->name;
			path += szPass;
			path += "_optimized";
			if (vertexShader) {
				path += ".vert.glsl";
			} else {
				path += ".frag.glsl";
			}
			tools::shader_utils::SaveText(engine, path.c_str, z.str().c_str());
		}
	} else {
		Copy(ex, out);
	}

	return true;
}