//-----------------------------------------------------------------------
	GLSLESProgramManagerCommon::GLSLESProgramManagerCommon(void) : mActiveVertexGpuProgram(NULL),
		mActiveFragmentGpuProgram(NULL)
	{
		// Fill in the relationship between type names and enums
		mTypeEnumMap.insert(StringToEnumMap::value_type("float", GL_FLOAT));
		mTypeEnumMap.insert(StringToEnumMap::value_type("vec2", GL_FLOAT_VEC2));
		mTypeEnumMap.insert(StringToEnumMap::value_type("vec3", GL_FLOAT_VEC3));
		mTypeEnumMap.insert(StringToEnumMap::value_type("vec4", GL_FLOAT_VEC4));
		mTypeEnumMap.insert(StringToEnumMap::value_type("sampler2D", GL_SAMPLER_2D));
		mTypeEnumMap.insert(StringToEnumMap::value_type("samplerCube", GL_SAMPLER_CUBE));
		mTypeEnumMap.insert(StringToEnumMap::value_type("sampler2DShadow", GL_SAMPLER_2D_SHADOW_EXT));
		mTypeEnumMap.insert(StringToEnumMap::value_type("int", GL_INT));
		mTypeEnumMap.insert(StringToEnumMap::value_type("ivec2", GL_INT_VEC2));
		mTypeEnumMap.insert(StringToEnumMap::value_type("ivec3", GL_INT_VEC3));
		mTypeEnumMap.insert(StringToEnumMap::value_type("ivec4", GL_INT_VEC4));
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat2", GL_FLOAT_MAT2));
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat3", GL_FLOAT_MAT3));
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat4", GL_FLOAT_MAT4));
#if OGRE_NO_GLES3_SUPPORT == 0
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat2x3", GL_FLOAT_MAT2x3));
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat3x2", GL_FLOAT_MAT3x2));
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat3x4", GL_FLOAT_MAT3x4));
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat4x3", GL_FLOAT_MAT4x3));
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat2x4", GL_FLOAT_MAT2x4));
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat4x2", GL_FLOAT_MAT4x2));
		mTypeEnumMap.insert(StringToEnumMap::value_type("bvec2", GL_BOOL_VEC2));
		mTypeEnumMap.insert(StringToEnumMap::value_type("bvec3", GL_BOOL_VEC3));
		mTypeEnumMap.insert(StringToEnumMap::value_type("bvec4", GL_BOOL_VEC4));
        mTypeEnumMap.insert(StringToEnumMap::value_type("uint", GL_UNSIGNED_INT));
		mTypeEnumMap.insert(StringToEnumMap::value_type("uvec2", GL_UNSIGNED_INT_VEC2));
		mTypeEnumMap.insert(StringToEnumMap::value_type("uvec3", GL_UNSIGNED_INT_VEC3));
		mTypeEnumMap.insert(StringToEnumMap::value_type("uvec4", GL_UNSIGNED_INT_VEC4));
		mTypeEnumMap.insert(StringToEnumMap::value_type("sampler3D", GL_SAMPLER_3D));
		mTypeEnumMap.insert(StringToEnumMap::value_type("sampler2DShadow", GL_SAMPLER_2D_SHADOW));
		mTypeEnumMap.insert(StringToEnumMap::value_type("samplerCubeShadow", GL_SAMPLER_CUBE_SHADOW));
		mTypeEnumMap.insert(StringToEnumMap::value_type("sampler2DArray", GL_SAMPLER_2D_ARRAY));
		mTypeEnumMap.insert(StringToEnumMap::value_type("sampler2DArrayShadow", GL_SAMPLER_2D_ARRAY_SHADOW));
		mTypeEnumMap.insert(StringToEnumMap::value_type("isampler2D", GL_INT_SAMPLER_2D));
		mTypeEnumMap.insert(StringToEnumMap::value_type("isampler3D", GL_INT_SAMPLER_3D));
		mTypeEnumMap.insert(StringToEnumMap::value_type("isamplerCube", GL_INT_SAMPLER_CUBE));
		mTypeEnumMap.insert(StringToEnumMap::value_type("isampler2DArray", GL_INT_SAMPLER_2D_ARRAY));
		mTypeEnumMap.insert(StringToEnumMap::value_type("usampler2D", GL_UNSIGNED_INT_SAMPLER_2D));
		mTypeEnumMap.insert(StringToEnumMap::value_type("usampler3D", GL_UNSIGNED_INT_SAMPLER_3D));
		mTypeEnumMap.insert(StringToEnumMap::value_type("usamplerCube", GL_UNSIGNED_INT_SAMPLER_CUBE));
		mTypeEnumMap.insert(StringToEnumMap::value_type("usampler2DArray", GL_UNSIGNED_INT_SAMPLER_2D_ARRAY));
#endif
        
#if !OGRE_NO_GLES2_GLSL_OPTIMISER
#if OGRE_NO_GLES3_SUPPORT == 0
        mGLSLOptimiserContext = glslopt_initialize(kGlslTargetOpenGLES30);
#else
        mGLSLOptimiserContext = glslopt_initialize(kGlslTargetOpenGLES20);
#endif
#endif
	}
	//-----------------------------------------------------------------------
	GLSLESProgramManagerCommon::GLSLESProgramManagerCommon(void) : mActiveVertexGpuProgram(NULL),
		mActiveFragmentGpuProgram(NULL)
	{
		// Fill in the relationship between type names and enums
		mTypeEnumMap.insert(StringToEnumMap::value_type("float", GL_FLOAT));
		mTypeEnumMap.insert(StringToEnumMap::value_type("vec2", GL_FLOAT_VEC2));
		mTypeEnumMap.insert(StringToEnumMap::value_type("vec3", GL_FLOAT_VEC3));
		mTypeEnumMap.insert(StringToEnumMap::value_type("vec4", GL_FLOAT_VEC4));
		mTypeEnumMap.insert(StringToEnumMap::value_type("sampler2D", GL_SAMPLER_2D));
		mTypeEnumMap.insert(StringToEnumMap::value_type("samplerCube", GL_SAMPLER_CUBE));
#if GL_EXT_shadow_samplers
		mTypeEnumMap.insert(StringToEnumMap::value_type("sampler2DShadow", GL_SAMPLER_2D_SHADOW_EXT));
#endif
		mTypeEnumMap.insert(StringToEnumMap::value_type("int", GL_INT));
		mTypeEnumMap.insert(StringToEnumMap::value_type("ivec2", GL_INT_VEC2));
		mTypeEnumMap.insert(StringToEnumMap::value_type("ivec3", GL_INT_VEC3));
		mTypeEnumMap.insert(StringToEnumMap::value_type("ivec4", GL_INT_VEC4));
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat2", GL_FLOAT_MAT2));
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat3", GL_FLOAT_MAT3));
		mTypeEnumMap.insert(StringToEnumMap::value_type("mat4", GL_FLOAT_MAT4));
        
#if !OGRE_NO_GLES2_GLSL_OPTIMISER
        mGLSLOptimiserContext = glslopt_initialize(true);
#endif
	}
Example #3
0
static bool init(glslopt_target target)
{
	gContext = glslopt_initialize(target);
	if( !gContext )
		return false;
	return true;
}
	//-------------------------------------------------------------------------------------------------------
	void OpenGLGraphicSystem::OnFrameworkEntry( float time, BohgeEngine::Framework& framework )
	{
		glEnable(GL_TEXTURE_2D);//默认打开2D纹理
		glDisable(GL_DITHER);
		glDepthRangef( 0.0, 1.0 );
		glDisable(GL_DEPTH_TEST);
		glDisable(GL_CULL_FACE);
		glDepthMask(false);
		glDisable(GL_BLEND);
		glEnable(GL_PROGRAM_POINT_SIZE);
		glEnable( GL_POINT_SPRITE );
		m_pGlslopt_ctx = glslopt_initialize( kGlslTargetOpenGL );
	}
Example #5
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);
}
Example #6
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);
}
Example #7
0
int main (int argc, const char** argv)
{
	if (argc < 2)
	{
		printf ("USAGE: glsloptimizer testfolder\n");
		return 1;
	}

	bool hasOpenGL = InitializeOpenGL ();
	glslopt_ctx* ctx[2] = {
		glslopt_initialize(true),
		glslopt_initialize(false),
	};

	std::string baseFolder = argv[1];

	clock_t time0 = clock();

	static const char* kTypeName[2] = { "vertex", "fragment" };
	size_t tests = 0;
	size_t errors = 0;
	for (int type = 0; type < 2; ++type)
	{
		std::string testFolder = baseFolder + "/" + kTypeName[type];

		static const char* kAPIName[2] = { "OpenGL ES 2.0", "OpenGL" };
		static const char* kApiIn [2] = {"-inES.txt", "-in.txt"};
		static const char* kApiIR [2] = {"-irES.txt", "-ir.txt"};
		static const char* kApiOut[2] = {"-outES.txt", "-out.txt"};
		for (int api = 0; api < 2; ++api)
		{
			printf ("\n** running %s tests for %s...\n", kTypeName[type], kAPIName[api]);
			StringVector inputFiles = GetFiles (testFolder, kApiIn[api]);

			size_t n = inputFiles.size();
			for (size_t i = 0; i < n; ++i)
			{
				std::string inname = inputFiles[i];
				//if (inname != "ast-in.txt")
				//	continue;
				std::string hirname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiIR[api];
				std::string outname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiOut[api];
				bool ok = TestFile (ctx[api], type==0, inname, testFolder + "/" + inname, testFolder + "/" + hirname, testFolder + "/" + outname, api==0, hasOpenGL);
				if (!ok)
				{
					++errors;
				}
				++tests;
			}
		}
	}
	clock_t time1 = clock();
	float timeDelta = float(time1-time0)/CLOCKS_PER_SEC;

	if (errors != 0)
		printf ("\n**** %i tests (%.2fsec), %i !!!FAILED!!!\n", tests, timeDelta, errors);
	else
		printf ("\n**** %i tests (%.2fsec) succeeded\n", tests, timeDelta);
	
	// 3.25s
	// with builtin call linking, 3.84s

	for (int i = 0; i < 2; ++i)
		glslopt_cleanup (ctx[i]);

	return errors ? 1 : 0;
}
Example #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;
}
Example #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;
	}
int main (int argc, const char** argv)
{
#ifndef __S3E__
	if (argc < 2)
	{
		printf ("USAGE: glsloptimizer testfolder\n");
		return 1;
	}
#endif

	bool hasOpenGL = InitializeOpenGL ();
	bool hasMetal = InitializeMetal ();
	glslopt_ctx* ctx[3] = {
		glslopt_initialize(kGlslTargetOpenGLES20),
		glslopt_initialize(kGlslTargetOpenGLES30),
		glslopt_initialize(kGlslTargetOpenGL),
	};
	glslopt_ctx* ctxMetal = glslopt_initialize(kGlslTargetMetal);

#ifndef __S3E__
	std::string baseFolder = argv[1];
#else
	std::string baseFolder = "." ;
#endif

	clock_t time0 = clock();

	// 2.39s
	// ralloc fix 256 initial: 1.35s

	static const char* kTypeName[2] = { "vertex", "fragment" };
	size_t tests = 0;
	size_t errors = 0;

	int type = 0 ; // vertex
	std::string testFolder = baseFolder + "/" + kTypeName[type];

	static const char* kAPIName[3] = { "OpenGL ES 2.0", "OpenGL ES 3.0", "OpenGL" };
	static const char* kApiIn [3] = {"-inES.txt", "-inES3.txt", "-in.txt"};
	static const char* kApiOut[3] = {"-outES.txt", "-outES3.txt", "-out.txt"};
	static const char* kApiOutMetal[3] = {"-outESMetal.txt", "-outES3Metal.txt", "-outMetal.txt"};

	int api= 0 ;		// OpenGL ES 2.0

	printf ("\n** running %s tests for %s...\n", kTypeName[type], kAPIName[api]);

	std::string inname = "ogre-inES.txt";
	std::string outname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiOut[api];
	std::string outnameMetal = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiOutMetal[api];
	const bool useMetal = (api == 1);
#ifdef __S3E__
	// with s3e we wont perform GLES check
	hasOpenGL = false ;
#endif
	bool ok = TestFile (ctx[api], type==0, inname, testFolder + "/" + inname, testFolder + "/" + outname, api<=1, hasOpenGL, false);
	if (!ok)
	{
		++errors;
	}

	// for __S3E__ we normally wont test Metal
	if (useMetal)
	{
		ok = TestFile (ctxMetal, type==0, inname, testFolder + "/" + inname, testFolder + "/" + outnameMetal, api==0, false, hasMetal);
		if (!ok)
		{
			++errors;
		}
	}
	++tests;

	clock_t time1 = clock();
	float timeDelta = float(time1-time0)/CLOCKS_PER_SEC;

	if (errors != 0)
		printf ("\n**** %i tests (%.2fsec), %i !!!FAILED!!!\n", (int)tests, timeDelta, (int)errors);
	else
		printf ("\n**** %i tests (%.2fsec) succeeded\n", (int)tests, timeDelta);
	
	// 3.25s
	// with builtin call linking, 3.84s

	for (int i = 0; i < 2; ++i)
		glslopt_cleanup (ctx[i]);
	glslopt_cleanup (ctxMetal);
	CleanupGL();

	return errors ? 1 : 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));
	}
Example #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;
}