Browse Source

Adding an API to shaderc (#1280)

attilaz 8 years ago
parent
commit
b8886a3531

+ 1116 - 1042
tools/shaderc/shaderc.cpp

@@ -150,6 +150,77 @@ namespace bgfx
 	};
 	BX_STATIC_ASSERT(BX_COUNTOF(s_uniformTypeName) == UniformType::Count*2);
 
+
+	Options::Options() 
+		: shaderType(' ')
+		, disasm(false)
+		, raw(false)
+		, preprocessOnly(false)
+		, depends(false)
+		, debugInformation(false)
+		, avoidFlowControl(false)
+		, noPreshader(false)
+		, partialPrecision(false)
+		, preferFlowControl(false)
+		, backwardsCompatibility(false)
+		, warningsAreErrors(false)
+		, optimize(false)
+		, optimizationLevel(3)
+	{
+	}
+
+	void Options::dump()
+	{
+		BX_TRACE("Options:\n"
+			"\t  shaderType: %c\n"
+			"\t  platform: %s\n"
+			"\t  profile: %s\n"
+			"\t  inputFile: %s\n"
+			"\t  outputFile: %s\n"
+			"\t  disasm: %s\n"
+			"\t  raw: %s\n"
+			"\t  preprocessOnly: %s\n"
+			"\t  depends: %s\n"
+			"\t  debugInformation: %s\n"
+			"\t  avoidFlowControl: %s\n"
+			"\t  noPreshader: %s\n"
+			"\t  partialPrecision: %s\n"
+			"\t  preferFlowControl: %s\n"
+			"\t  backwardsCompatibility: %s\n"
+			"\t  warningsAreErrors: %s\n"
+			"\t  optimize: %s\n"
+			"\t  optimizationLevel: %d\n"
+
+			, shaderType
+			, platform.c_str()
+			, profile.c_str()
+			, inputFilePath.c_str()
+			, outputFilePath.c_str()
+			, disasm ? "true" : "false"
+			, raw ? "true" : "false"
+			, preprocessOnly ? "true" : "false"
+			, depends ? "true" : "false"
+			, debugInformation ? "true" : "false"
+			, avoidFlowControl ? "true" : "false"
+			, noPreshader ? "true" : "false"
+			, partialPrecision ? "true" : "false"
+			, preferFlowControl ? "true" : "false"
+			, backwardsCompatibility ? "true" : "false"
+			, warningsAreErrors ? "true" : "false"
+			, optimize ? "true" : "false"
+			, optimizationLevel
+			);
+
+		for(size_t i=0; i<includeDirs.size(); ++i)
+			BX_TRACE("\t  include :%s\n", includeDirs[i].c_str());
+
+		for(size_t i=0; i<defines.size(); ++i)
+			BX_TRACE("\t  define :%s\n", defines[i].c_str());
+
+		for(size_t i=0; i<dependencies.size(); ++i)
+			BX_TRACE("\t  dependency :%s\n", dependencies[i].c_str());
+	}
+
 	const char* interpolationDx11(const char* _glsl)
 	{
 		if (0 == bx::strCmp(_glsl, "smooth") )
@@ -794,58 +865,9 @@ namespace bgfx
 			);
 	}
 
-	int compileShader(int _argc, const char* _argv[])
-	{
-		bx::CommandLine cmdLine(_argc, _argv);
-
-		if (cmdLine.hasArg('v', "version") )
-		{
-			fprintf(stderr
-				, "shaderc, bgfx shader compiler tool, version %d.%d.%d.\n"
-				, BGFX_SHADERC_VERSION_MAJOR
-				, BGFX_SHADERC_VERSION_MINOR
-				, BGFX_API_VERSION
-				);
-			return bx::kExitSuccess;
-		}
-
-		if (cmdLine.hasArg('h', "help") )
-		{
-			help();
-			return bx::kExitFailure;
-		}
-
-		g_verbose = cmdLine.hasArg("verbose");
-
-		const char* filePath = cmdLine.findOption('f');
-		if (NULL == filePath)
-		{
-			help("Shader file name must be specified.");
-			return bx::kExitFailure;
-		}
-
-		const char* outFilePath = cmdLine.findOption('o');
-		if (NULL == outFilePath)
-		{
-			help("Output file name must be specified.");
-			return bx::kExitFailure;
-		}
-
-		const char* type = cmdLine.findOption('\0', "type");
-		if (NULL == type)
-		{
-			help("Must specify shader type.");
-			return bx::kExitFailure;
-		}
-
-		const char* platform = cmdLine.findOption('\0', "platform");
-		if (NULL == platform)
-		{
-			platform = "";
-		}
-
-		bool raw = cmdLine.hasArg('\0', "raw");
 
+	bool compileShader(const char* _varying, char* _shader, uint32_t _shaderLen, Options& _options, bx::FileWriter* _writer)
+	{
 		uint32_t glsl  = 0;
 		uint32_t essl  = 0;
 		uint32_t hlsl  = 0;
@@ -853,8 +875,8 @@ namespace bgfx
 		uint32_t metal = 0;
 		uint32_t pssl  = 0;
 		uint32_t spirv = 0;
-		const char* profile = cmdLine.findOption('p', "profile");
-		if (NULL != profile)
+		const char* profile = _options.profile.c_str();
+		if ('\0' != profile[0])
 		{
 			if (0 == bx::strCmp(&profile[1], "s_4_0_level", 11) )
 			{
@@ -895,74 +917,16 @@ namespace bgfx
 			essl = 2;
 		}
 
-		const char* bin2c = NULL;
-		if (cmdLine.hasArg("bin2c") )
-		{
-			bin2c = cmdLine.findOption("bin2c");
-			if (NULL == bin2c)
-			{
-				bin2c = baseName(outFilePath);
-				uint32_t len = (uint32_t)bx::strLen(bin2c);
-				char* temp = (char*)alloca(len+1);
-				for (char *out = temp; *bin2c != '\0';)
-				{
-					char ch = *bin2c++;
-					if (isalnum(ch) )
-					{
-						*out++ = ch;
-					}
-					else
-					{
-						*out++ = '_';
-					}
-				}
-				temp[len] = '\0';
-
-				bin2c = temp;
-			}
-		}
-
-		bool depends = cmdLine.hasArg("depends");
-		bool preprocessOnly = cmdLine.hasArg("preprocess");
-		const char* includeDir = cmdLine.findOption('i');
-
-		BX_TRACE("depends: %d", depends);
-		BX_TRACE("preprocessOnly: %d", preprocessOnly);
-		BX_TRACE("includeDir: %s", includeDir);
-
-		Preprocessor preprocessor(filePath, 0 != essl);
+		Preprocessor preprocessor(_options.inputFilePath.c_str(), 0 != essl);
 
-		for (int ii = 1; NULL != includeDir; ++ii)
-		{
-			preprocessor.addInclude(includeDir);
-			includeDir = cmdLine.findOption(ii, 'i');
-		}
-
-		std::string dir;
-		{
-			const char* base = baseName(filePath);
+		for(size_t i=0; i<_options.includeDirs.size(); ++i)
+			preprocessor.addInclude(_options.includeDirs[i].c_str());
 
-			if (base != filePath)
-			{
-				dir.assign(filePath, base-filePath);
-				preprocessor.addInclude(dir.c_str() );
-			}
-		}
+		for(size_t i=0; i<_options.defines.size(); ++i)
+			preprocessor.setDefine(_options.defines[i].c_str());
 
-		const char* defines = cmdLine.findOption("define");
-		while (NULL != defines
-		&&    '\0'  != *defines)
-		{
-			defines = bx::strws(defines);
-			const char* eol = bx::strFind(defines, ';');
-			if (NULL == eol)
-			{
-				eol = defines + bx::strLen(defines);
-			}
-			std::string define(defines, eol);
-			preprocessor.setDefine(define.c_str() );
-			defines = ';' == *eol ? eol+1 : eol;
-		}
+		for(size_t i=0; i<_options.dependencies.size(); ++i)
+			preprocessor.addDependency(_options.dependencies[i].c_str());
 
 		preprocessor.setDefaultDefine("BX_PLATFORM_ANDROID");
 		preprocessor.setDefaultDefine("BX_PLATFORM_EMSCRIPTEN");
@@ -990,6 +954,8 @@ namespace bgfx
 				, essl ? 1 : glsl
 				);
 
+		const char* platform = _options.platform.c_str();
+
 		if (0 == bx::strCmpI(platform, "android") )
 		{
 			preprocessor.setDefine("BX_PLATFORM_ANDROID=1");
@@ -1041,8 +1007,7 @@ namespace bgfx
 
 		preprocessor.setDefine("M_PI=3.1415926535897932384626433832795");
 
-		char shaderType = bx::toLower(type[0]);
-		switch (shaderType)
+		switch (_options.shaderType)
 		{
 		case 'c':
 			preprocessor.setDefine("BGFX_SHADER_TYPE_COMPUTE=1");
@@ -1057,1157 +1022,1266 @@ namespace bgfx
 			break;
 
 		default:
-			fprintf(stderr, "Unknown type: %s?!", type);
-			return bx::kExitFailure;
+			fprintf(stderr, "Unknown type: %c?!", _options.shaderType);
+			return false;
 		}
 
 		bool compiled = false;
 
-		bx::FileReader reader;
-		if (!bx::open(&reader, filePath) )
-		{
-			fprintf(stderr, "Unable to open file '%s'.\n", filePath);
-		}
-		else
-		{
-			VaryingMap varyingMap;
+		VaryingMap varyingMap;
+		const char* parse = _varying;
 
-			std::string defaultVarying = dir + "varying.def.sc";
-			const char* varyingdef = cmdLine.findOption("varyingdef", defaultVarying.c_str() );
-			File attribdef(varyingdef);
-			const char* parse = attribdef.getData();
-			if (NULL != parse
+		while (NULL != parse
 			&&  *parse != '\0')
+		{
+			parse = bx::strws(parse);
+			const char* eol = bx::strFind(parse, ';');
+			if (NULL == eol)
 			{
-				preprocessor.addDependency(varyingdef);
-			}
-			else
-			{
-				fprintf(stderr, "ERROR: Failed to parse varying def file: \"%s\" No input/output semantics will be generated in the code!\n", varyingdef);
+				eol = bx::streol(parse);
 			}
 
-			while (NULL != parse
-			   &&  *parse != '\0')
+			if (NULL != eol)
 			{
-				parse = bx::strws(parse);
-				const char* eol = bx::strFind(parse, ';');
-				if (NULL == eol)
+				const char* precision = NULL;
+				const char* interpolation = NULL;
+				const char* typen = parse;
+
+				if (0 == bx::strCmp(typen, "lowp", 4)
+				||  0 == bx::strCmp(typen, "mediump", 7)
+				||  0 == bx::strCmp(typen, "highp", 5) )
 				{
-					eol = bx::streol(parse);
+					precision = typen;
+					typen = parse = bx::strws(bx::strword(parse) );
 				}
 
-				if (NULL != eol)
+				if (0 == bx::strCmp(typen, "flat", 4)
+				||  0 == bx::strCmp(typen, "smooth", 6)
+				||  0 == bx::strCmp(typen, "noperspective", 13)
+				||  0 == bx::strCmp(typen, "centroid", 8) )
 				{
-					const char* precision = NULL;
-					const char* interpolation = NULL;
-					const char* typen = parse;
+					interpolation = typen;
+					typen = parse = bx::strws(bx::strword(parse) );
+				}
 
-					if (0 == bx::strCmp(typen, "lowp", 4)
-					||  0 == bx::strCmp(typen, "mediump", 7)
-					||  0 == bx::strCmp(typen, "highp", 5) )
+				const char* name      = parse = bx::strws(bx::strword(parse) );
+				const char* column    = parse = bx::strws(bx::strword(parse) );
+				const char* semantics = parse = bx::strws( (*parse == ':' ? ++parse : parse) );
+				const char* assign    = parse = bx::strws(bx::strword(parse) );
+				const char* init      = parse = bx::strws( (*parse == '=' ? ++parse : parse) );
+
+				if (typen < eol
+				&&  name < eol
+				&&  column < eol
+				&&  ':' == *column
+				&&  semantics < eol)
+				{
+					Varying var;
+					if (NULL != precision)
 					{
-						precision = typen;
-						typen = parse = bx::strws(bx::strword(parse) );
+						var.m_precision.assign(precision, bx::strword(precision)-precision);
 					}
 
-					if (0 == bx::strCmp(typen, "flat", 4)
-					||  0 == bx::strCmp(typen, "smooth", 6)
-					||  0 == bx::strCmp(typen, "noperspective", 13)
-					||  0 == bx::strCmp(typen, "centroid", 8) )
+					if (NULL != interpolation)
 					{
-						interpolation = typen;
-						typen = parse = bx::strws(bx::strword(parse) );
+						var.m_interpolation.assign(interpolation, bx::strword(interpolation)-interpolation);
 					}
 
-					const char* name      = parse = bx::strws(bx::strword(parse) );
-					const char* column    = parse = bx::strws(bx::strword(parse) );
-					const char* semantics = parse = bx::strws( (*parse == ':' ? ++parse : parse) );
-					const char* assign    = parse = bx::strws(bx::strword(parse) );
-					const char* init      = parse = bx::strws( (*parse == '=' ? ++parse : parse) );
-
-					if (typen < eol
-					&&  name < eol
-					&&  column < eol
-					&&  ':' == *column
-					&&  semantics < eol)
-					{
-						Varying var;
-						if (NULL != precision)
-						{
-							var.m_precision.assign(precision, bx::strword(precision)-precision);
-						}
-
-						if (NULL != interpolation)
-						{
-							var.m_interpolation.assign(interpolation, bx::strword(interpolation)-interpolation);
-						}
-
-						var.m_type.assign(typen, bx::strword(typen)-typen);
-						var.m_name.assign(name, bx::strword(name)-name);
-						var.m_semantics.assign(semantics, bx::strword(semantics)-semantics);
+					var.m_type.assign(typen, bx::strword(typen)-typen);
+					var.m_name.assign(name, bx::strword(name)-name);
+					var.m_semantics.assign(semantics, bx::strword(semantics)-semantics);
 
-						if (d3d == 9
-						&&  var.m_semantics == "BITANGENT")
-						{
-							var.m_semantics = "BINORMAL";
-						}
-
-						if (assign < eol
-						&&  '=' == *assign
-						&&  init < eol)
-						{
-							var.m_init.assign(init, eol-init);
-						}
+					if (d3d == 9
+					&&  var.m_semantics == "BITANGENT")
+					{
+						var.m_semantics = "BINORMAL";
+					}
 
-						varyingMap.insert(std::make_pair(var.m_name, var) );
+					if (assign < eol
+					&&  '=' == *assign
+					&&  init < eol)
+					{
+						var.m_init.assign(init, eol-init);
 					}
 
-					parse = bx::strws(bx::strnl(eol) );
+					varyingMap.insert(std::make_pair(var.m_name, var) );
 				}
-			}
-
-			InOut shaderInputs;
-			InOut shaderOutputs;
-			uint32_t inputHash = 0;
-			uint32_t outputHash = 0;
-
-			char* data;
-			char* input;
-			{
-				const size_t padding = 4096;
-				uint32_t size = (uint32_t)bx::getSize(&reader);
-				data = new char[size+padding+1];
-				size = (uint32_t)bx::read(&reader, data, size);
 
-				if (data[0] == '\xef'
-				&&  data[1] == '\xbb'
-				&&  data[2] == '\xbf')
-				{
-					bx::memMove(data, &data[3], size-3);
-					size -= 3;
-				}
+				parse = bx::strws(bx::strnl(eol) );
+			}
+		}
 
-				// Compiler generates "error X3000: syntax error: unexpected end of file"
-				// if input doesn't have empty line at EOF.
-				data[size] = '\n';
-				bx::memSet(&data[size+1], 0, padding);
-				bx::close(&reader);
+		bool raw = _options.raw;
 
-				if (!raw)
-				{
-					// To avoid commented code being recognized as used feature,
-					// first preprocess pass is used to strip all comments before
-					// substituting code.
-					preprocessor.run(data);
-					delete [] data;
-
-					size = (uint32_t)preprocessor.m_preprocessed.size();
-					data = new char[size+padding+1];
-					bx::memCopy(data, preprocessor.m_preprocessed.c_str(), size);
-					bx::memSet(&data[size], 0, padding+1);
-				}
+		InOut shaderInputs;
+		InOut shaderOutputs;
+		uint32_t inputHash = 0;
+		uint32_t outputHash = 0;
 
-				strNormalizeEol(data);
+		char* data;
+		char* input;
+		{
+			data = _shader;
+			uint32_t size = _shaderLen;
 
-				input = const_cast<char*>(bx::strws(data) );
-				while (input[0] == '$')
-				{
-					const char* str = bx::strws(input+1);
-					const char* eol = bx::streol(str);
-					const char* nl  = bx::strnl(eol);
-					input = const_cast<char*>(nl);
+			const size_t padding = 4096;
 
-					if (0 == bx::strCmp(str, "input", 5) )
-					{
-						str += 5;
-						const char* comment = bx::strFind(str, "//");
-						eol = NULL != comment && comment < eol ? comment : eol;
-						inputHash = parseInOut(shaderInputs, str, eol);
-					}
-					else if (0 == bx::strCmp(str, "output", 6) )
-					{
-						str += 6;
-						const char* comment = bx::strFind(str, "//");
-						eol = NULL != comment && comment < eol ? comment : eol;
-						outputHash = parseInOut(shaderOutputs, str, eol);
-					}
-					else if (0 == bx::strCmp(str, "raw", 3) )
-					{
-						raw = true;
-						str += 3;
-					}
+			if (!raw)
+			{
+				// To avoid commented code being recognized as used feature,
+				// first preprocess pass is used to strip all comments before
+				// substituting code.
+				preprocessor.run(data);
+				delete [] data;
 
-					input = const_cast<char*>(bx::strws(input) );
-				}
+				size = (uint32_t)preprocessor.m_preprocessed.size();
+				data = new char[size+padding+1];
+				bx::memCopy(data, preprocessor.m_preprocessed.c_str(), size);
+				bx::memSet(&data[size], 0, padding+1);
 			}
 
-			if (raw)
+			strNormalizeEol(data);
+
+			input = const_cast<char*>(bx::strws(data) );
+			while (input[0] == '$')
 			{
-				bx::FileWriter* writer = NULL;
+				const char* str = bx::strws(input+1);
+				const char* eol = bx::streol(str);
+				const char* nl  = bx::strnl(eol);
+				input = const_cast<char*>(nl);
 
-				if (NULL != bin2c)
+				if (0 == bx::strCmp(str, "input", 5) )
 				{
-					writer = new Bin2cWriter(bin2c);
+					str += 5;
+					const char* comment = bx::strFind(str, "//");
+					eol = NULL != comment && comment < eol ? comment : eol;
+					inputHash = parseInOut(shaderInputs, str, eol);
 				}
-				else
+				else if (0 == bx::strCmp(str, "output", 6) )
 				{
-					writer = new bx::FileWriter;
+					str += 6;
+					const char* comment = bx::strFind(str, "//");
+					eol = NULL != comment && comment < eol ? comment : eol;
+					outputHash = parseInOut(shaderOutputs, str, eol);
 				}
-
-				if (!bx::open(writer, outFilePath) )
+				else if (0 == bx::strCmp(str, "raw", 3) )
 				{
-					fprintf(stderr, "Unable to open output file '%s'.", outFilePath);
-					return bx::kExitFailure;
+					raw = true;
+					str += 3;
 				}
 
-				if ('f' == shaderType)
-				{
-					bx::write(writer, BGFX_CHUNK_MAGIC_FSH);
-					bx::write(writer, inputHash);
-				}
-				else if ('v' == shaderType)
-				{
-					bx::write(writer, BGFX_CHUNK_MAGIC_VSH);
-					bx::write(writer, outputHash);
-				}
-				else
-				{
-					bx::write(writer, BGFX_CHUNK_MAGIC_CSH);
-					bx::write(writer, outputHash);
-				}
+				input = const_cast<char*>(bx::strws(input) );
+			}
+		}
 
-				if (0 != glsl)
-				{
-					bx::write(writer, uint16_t(0) );
+		if (raw)
+		{
+			if ('f' == _options.shaderType)
+			{
+				bx::write(_writer, BGFX_CHUNK_MAGIC_FSH);
+				bx::write(_writer, inputHash);
+			}
+			else if ('v' == _options.shaderType)
+			{
+				bx::write(_writer, BGFX_CHUNK_MAGIC_VSH);
+				bx::write(_writer, outputHash);
+			}
+			else
+			{
+				bx::write(_writer, BGFX_CHUNK_MAGIC_CSH);
+				bx::write(_writer, outputHash);
+			}
 
-					uint32_t shaderSize = (uint32_t)bx::strLen(input);
-					bx::write(writer, shaderSize);
-					bx::write(writer, input, shaderSize);
-					bx::write(writer, uint8_t(0) );
+			if (0 != glsl)
+			{
+				bx::write(_writer, uint16_t(0) );
 
-					compiled = true;
-				}
-				else if (0 != pssl)
-				{
-					compiled = compilePSSLShader(cmdLine, 0, input, writer);
-				}
-				else
-				{
-					compiled = compileHLSLShader(cmdLine, d3d, input, writer);
-				}
+				uint32_t shaderSize = (uint32_t)bx::strLen(input);
+				bx::write(_writer, shaderSize);
+				bx::write(_writer, input, shaderSize);
+				bx::write(_writer, uint8_t(0) );
 
-				bx::close(writer);
-				delete writer;
+				compiled = true;
+			}
+			else if (0 != pssl)
+			{
+				compiled = compilePSSLShader(_options, 0, input, _writer);
+			}
+			else
+			{
+				compiled = compileHLSLShader(_options, d3d, input, _writer);
 			}
-			else if ('c' == shaderType) // Compute
+		}
+		else if ('c' == _options.shaderType) // Compute
+		{
+			char* entry = const_cast<char*>(bx::strFind(input, "void main()") );
+			if (NULL == entry)
+			{
+				fprintf(stderr, "Shader entry point 'void main()' is not found.\n");
+			}
+			else
 			{
-				char* entry = const_cast<char*>(bx::strFind(input, "void main()") );
-				if (NULL == entry)
+				if (0 != glsl
+				||  0 != essl
+				||  0 != metal)
 				{
-					fprintf(stderr, "Shader entry point 'void main()' is not found.\n");
 				}
 				else
 				{
-					if (0 != glsl
-					||  0 != essl
-					||  0 != metal)
+					preprocessor.writef(
+						"#define lowp\n"
+						"#define mediump\n"
+						"#define highp\n"
+						"#define ivec2 int2\n"
+						"#define ivec3 int3\n"
+						"#define ivec4 int4\n"
+						"#define uvec2 uint2\n"
+						"#define uvec3 uint3\n"
+						"#define uvec4 uint4\n"
+						"#define vec2 float2\n"
+						"#define vec3 float3\n"
+						"#define vec4 float4\n"
+						"#define mat2 float2x2\n"
+						"#define mat3 float3x3\n"
+						"#define mat4 float4x4\n"
+						);
+
+					entry[4] = '_';
+
+					preprocessor.writef("#define void_main()");
+					preprocessor.writef(" \\\n\tvoid main(");
+
+					uint32_t arg = 0;
+
+					const bool hasLocalInvocationID    = NULL != bx::strFind(input, "gl_LocalInvocationID");
+					const bool hasLocalInvocationIndex = NULL != bx::strFind(input, "gl_LocalInvocationIndex");
+					const bool hasGlobalInvocationID   = NULL != bx::strFind(input, "gl_GlobalInvocationID");
+					const bool hasWorkGroupID          = NULL != bx::strFind(input, "gl_WorkGroupID");
+
+					if (hasLocalInvocationID)
 					{
+						preprocessor.writef(
+							" \\\n\t%sint3 gl_LocalInvocationID : SV_GroupThreadID"
+							, arg++ > 0 ? ", " : "  "
+							);
 					}
-					else
+
+					if (hasLocalInvocationIndex)
 					{
 						preprocessor.writef(
-							"#define lowp\n"
-							"#define mediump\n"
-							"#define highp\n"
-							"#define ivec2 int2\n"
-							"#define ivec3 int3\n"
-							"#define ivec4 int4\n"
-							"#define uvec2 uint2\n"
-							"#define uvec3 uint3\n"
-							"#define uvec4 uint4\n"
-							"#define vec2 float2\n"
-							"#define vec3 float3\n"
-							"#define vec4 float4\n"
-							"#define mat2 float2x2\n"
-							"#define mat3 float3x3\n"
-							"#define mat4 float4x4\n"
+							" \\\n\t%sint gl_LocalInvocationIndex : SV_GroupIndex"
+							, arg++ > 0 ? ", " : "  "
 							);
+					}
 
-						entry[4] = '_';
+					if (hasGlobalInvocationID)
+					{
+						preprocessor.writef(
+							" \\\n\t%sint3 gl_GlobalInvocationID : SV_DispatchThreadID"
+							, arg++ > 0 ? ", " : "  "
+							);
+					}
 
-						preprocessor.writef("#define void_main()");
-						preprocessor.writef(" \\\n\tvoid main(");
+					if (hasWorkGroupID)
+					{
+						preprocessor.writef(
+							" \\\n\t%sint3 gl_WorkGroupID : SV_GroupID"
+							, arg++ > 0 ? ", " : "  "
+							);
+					}
 
-						uint32_t arg = 0;
+					preprocessor.writef(
+						" \\\n\t)\n"
+						);
+				}
 
-						const bool hasLocalInvocationID    = NULL != bx::strFind(input, "gl_LocalInvocationID");
-						const bool hasLocalInvocationIndex = NULL != bx::strFind(input, "gl_LocalInvocationIndex");
-						const bool hasGlobalInvocationID   = NULL != bx::strFind(input, "gl_GlobalInvocationID");
-						const bool hasWorkGroupID          = NULL != bx::strFind(input, "gl_WorkGroupID");
+				if (preprocessor.run(input) )
+				{
+					//BX_TRACE("Input file: %s", filePath);
+					//BX_TRACE("Output file: %s", outFilePath);
 
-						if (hasLocalInvocationID)
-						{
-							preprocessor.writef(
-								" \\\n\t%sint3 gl_LocalInvocationID : SV_GroupThreadID"
-								, arg++ > 0 ? ", " : "  "
-								);
-						}
+					if (_options.preprocessOnly)
+					{
+						bx::write(_writer, preprocessor.m_preprocessed.c_str(), (int32_t)preprocessor.m_preprocessed.size() );
 
-						if (hasLocalInvocationIndex)
-						{
-							preprocessor.writef(
-								" \\\n\t%sint gl_LocalInvocationIndex : SV_GroupIndex"
-								, arg++ > 0 ? ", " : "  "
-								);
-						}
+						return true;
+					}
+
+					{
+						bx::write(_writer, BGFX_CHUNK_MAGIC_CSH);
+						bx::write(_writer, outputHash);
 
-						if (hasGlobalInvocationID)
+						if (0 != glsl
+						||  0 != essl)
 						{
-							preprocessor.writef(
-								" \\\n\t%sint3 gl_GlobalInvocationID : SV_DispatchThreadID"
-								, arg++ > 0 ? ", " : "  "
-								);
-						}
+							std::string code;
+
+							if (essl)
+							{
+								bx::stringPrintf(code, "#version 310 es\n");
+							}
+							else
+							{
+								bx::stringPrintf(code, "#version %d\n", glsl == 0 ? 430 : glsl);
+							}
+
+							code += preprocessor.m_preprocessed;
+#if 1
+							bx::write(_writer, uint16_t(0) );
 
-						if (hasWorkGroupID)
+							uint32_t shaderSize = (uint32_t)code.size();
+							bx::write(_writer, shaderSize);
+							bx::write(_writer, code.c_str(), shaderSize);
+							bx::write(_writer, uint8_t(0) );
+
+							compiled = true;
+#else
+							compiled = compileGLSLShader(cmdLine, essl, code, writer);
+#endif // 0
+						}
+						else if (0 != spirv)
 						{
-							preprocessor.writef(
-								" \\\n\t%sint3 gl_WorkGroupID : SV_GroupID"
-								, arg++ > 0 ? ", " : "  "
-								);
+							compiled = compileSPIRVShader(_options, 0, preprocessor.m_preprocessed, _writer);
+						}
+						else if (0 != pssl)
+						{
+							compiled = compilePSSLShader(_options, 0, preprocessor.m_preprocessed, _writer);
+						}
+						else
+						{
+							compiled = compileHLSLShader(_options, d3d, preprocessor.m_preprocessed, _writer);
 						}
-
-						preprocessor.writef(
-							" \\\n\t)\n"
-							);
 					}
 
-					if (preprocessor.run(input) )
+					if (compiled)
 					{
-						BX_TRACE("Input file: %s", filePath);
-						BX_TRACE("Output file: %s", outFilePath);
-
-						if (preprocessOnly)
+						if (_options.depends)
 						{
+							std::string ofp = _options.outputFilePath;
+							ofp += ".d";
 							bx::FileWriter writer;
-
-							if (!bx::open(&writer, outFilePath) )
+							if (bx::open(&writer, ofp.c_str() ) )
 							{
-								fprintf(stderr, "Unable to open output file '%s'.", outFilePath);
-								return bx::kExitFailure;
+								writef(&writer, "%s : %s\n", _options.outputFilePath.c_str(), preprocessor.m_depends.c_str() );
+								bx::close(&writer);
 							}
-
-							bx::write(&writer, preprocessor.m_preprocessed.c_str(), (int32_t)preprocessor.m_preprocessed.size() );
-							bx::close(&writer);
-
-							return bx::kExitSuccess;
 						}
+					}
+				}
+			}
+		}
+		else // Vertex/Fragment
+		{
+			char* entry = const_cast<char*>(bx::strFind(input, "void main()") );
+			if (NULL == entry)
+			{
+				fprintf(stderr, "Shader entry point 'void main()' is not found.\n");
+			}
+			else
+			{
+				if (0 != glsl
+				||  0 != essl
+				||  0 != metal)
+				{
+					if (0 == essl)
+					{
+						// bgfx shadow2D/Proj behave like EXT_shadow_samplers
+						// not as GLSL language 1.2 specs shadow2D/Proj.
+						preprocessor.writef(
+							"#define shadow2D(_sampler, _coord) bgfxShadow2D(_sampler, _coord).x\n"
+							"#define shadow2DProj(_sampler, _coord) bgfxShadow2DProj(_sampler, _coord).x\n"
+							);
+					}
 
+					for (InOut::const_iterator it = shaderInputs.begin(), itEnd = shaderInputs.end(); it != itEnd; ++it)
+					{
+						VaryingMap::const_iterator varyingIt = varyingMap.find(*it);
+						if (varyingIt != varyingMap.end() )
 						{
-							bx::FileWriter* writer = NULL;
-
-							if (NULL != bin2c)
-							{
-								writer = new Bin2cWriter(bin2c);
-							}
-							else
-							{
-								writer = new bx::FileWriter;
-							}
-
-							if (!bx::open(writer, outFilePath) )
-							{
-								fprintf(stderr, "Unable to open output file '%s'.", outFilePath);
-								return bx::kExitFailure;
-							}
-
-							bx::write(writer, BGFX_CHUNK_MAGIC_CSH);
-							bx::write(writer, outputHash);
-
-							if (0 != glsl
-							||  0 != essl)
-							{
-								std::string code;
-
-								if (essl)
-								{
-									bx::stringPrintf(code, "#version 310 es\n");
-								}
-								else
-								{
-									bx::stringPrintf(code, "#version %d\n", glsl == 0 ? 430 : glsl);
-								}
-
-								code += preprocessor.m_preprocessed;
-	#if 1
-								bx::write(writer, uint16_t(0) );
-
-								uint32_t shaderSize = (uint32_t)code.size();
-								bx::write(writer, shaderSize);
-								bx::write(writer, code.c_str(), shaderSize);
-								bx::write(writer, uint8_t(0) );
+							const Varying& var = varyingIt->second;
+							const char* name = var.m_name.c_str();
 
-								compiled = true;
-	#else
-								compiled = compileGLSLShader(cmdLine, essl, code, writer);
-	#endif // 0
-							}
-							else if (0 != spirv)
-							{
-								compiled = compileSPIRVShader(cmdLine, 0, preprocessor.m_preprocessed, writer);
-							}
-							else if (0 != pssl)
+							if (0 == bx::strCmp(name, "a_", 2)
+							||  0 == bx::strCmp(name, "i_", 2) )
 							{
-								compiled = compilePSSLShader(cmdLine, 0, preprocessor.m_preprocessed, writer);
+								preprocessor.writef("attribute %s %s %s %s;\n"
+										, var.m_precision.c_str()
+										, var.m_interpolation.c_str()
+										, var.m_type.c_str()
+										, name
+										);
 							}
 							else
 							{
-								compiled = compileHLSLShader(cmdLine, d3d, preprocessor.m_preprocessed, writer);
+								preprocessor.writef("%s varying %s %s %s;\n"
+										, var.m_interpolation.c_str()
+										, var.m_precision.c_str()
+										, var.m_type.c_str()
+										, name
+										);
 							}
-
-							bx::close(writer);
-							delete writer;
 						}
+					}
 
-						if (compiled)
+					for (InOut::const_iterator it = shaderOutputs.begin(), itEnd = shaderOutputs.end(); it != itEnd; ++it)
+					{
+						VaryingMap::const_iterator varyingIt = varyingMap.find(*it);
+						if (varyingIt != varyingMap.end() )
 						{
-							if (depends)
-							{
-								std::string ofp = outFilePath;
-								ofp += ".d";
-								bx::FileWriter writer;
-								if (bx::open(&writer, ofp.c_str() ) )
-								{
-									writef(&writer, "%s : %s\n", outFilePath, preprocessor.m_depends.c_str() );
-									bx::close(&writer);
-								}
-							}
+							const Varying& var = varyingIt->second;
+							preprocessor.writef("%s varying %s %s;\n"
+								, var.m_interpolation.c_str()
+								, var.m_type.c_str()
+								, var.m_name.c_str()
+								);
 						}
 					}
 				}
-			}
-			else // Vertex/Fragment
-			{
-				char* entry = const_cast<char*>(bx::strFind(input, "void main()") );
-				if (NULL == entry)
-				{
-					fprintf(stderr, "Shader entry point 'void main()' is not found.\n");
-				}
 				else
 				{
-					if (0 != glsl
-					||  0 != essl
-					||  0 != metal)
+					preprocessor.writef(
+						"#define lowp\n"
+						"#define mediump\n"
+						"#define highp\n"
+						"#define ivec2 int2\n"
+						"#define ivec3 int3\n"
+						"#define ivec4 int4\n"
+						"#define uvec2 uint2\n"
+						"#define uvec3 uint3\n"
+						"#define uvec4 uint4\n"
+						"#define vec2 float2\n"
+						"#define vec3 float3\n"
+						"#define vec4 float4\n"
+						"#define mat2 float2x2\n"
+						"#define mat3 float3x3\n"
+						"#define mat4 float4x4\n"
+						);
+
+					if (hlsl != 0
+					&&  hlsl < 4)
+					{
+						preprocessor.writef(
+							"#define centroid\n"
+							"#define flat\n"
+							"#define noperspective\n"
+							"#define smooth\n"
+							);
+					}
+
+					entry[4] = '_';
+
+					if ('f' == _options.shaderType)
 					{
-						if (0 == essl)
+						const char* insert = bx::strFind(entry, "{");
+						if (NULL != insert)
 						{
-							// bgfx shadow2D/Proj behave like EXT_shadow_samplers
-							// not as GLSL language 1.2 specs shadow2D/Proj.
-							preprocessor.writef(
-								"#define shadow2D(_sampler, _coord) bgfxShadow2D(_sampler, _coord).x\n"
-								"#define shadow2DProj(_sampler, _coord) bgfxShadow2DProj(_sampler, _coord).x\n"
-								);
+							insert = strInsert(const_cast<char*>(insert+1), "\nvec4 bgfx_VoidFrag = vec4_splat(0.0);\n");
 						}
 
-						for (InOut::const_iterator it = shaderInputs.begin(), itEnd = shaderInputs.end(); it != itEnd; ++it)
+						const bool hasFragColor   = NULL != bx::strFind(input, "gl_FragColor");
+						const bool hasFragCoord   = NULL != bx::strFind(input, "gl_FragCoord") || hlsl > 3 || hlsl == 2;
+						const bool hasFragDepth   = NULL != bx::strFind(input, "gl_FragDepth");
+						const bool hasFrontFacing = NULL != bx::strFind(input, "gl_FrontFacing");
+						const bool hasPrimitiveId = NULL != bx::strFind(input, "gl_PrimitiveID");
+
+						bool hasFragData[8] = {};
+						uint32_t numFragData = 0;
+						for (uint32_t ii = 0; ii < BX_COUNTOF(hasFragData); ++ii)
 						{
-							VaryingMap::const_iterator varyingIt = varyingMap.find(*it);
-							if (varyingIt != varyingMap.end() )
-							{
-								const Varying& var = varyingIt->second;
-								const char* name = var.m_name.c_str();
+							char temp[32];
+							bx::snprintf(temp, BX_COUNTOF(temp), "gl_FragData[%d]", ii);
+							hasFragData[ii] = NULL != bx::strFind(input, temp);
+							numFragData += hasFragData[ii];
+						}
 
-								if (0 == bx::strCmp(name, "a_", 2)
-								||  0 == bx::strCmp(name, "i_", 2) )
-								{
-									preprocessor.writef("attribute %s %s %s %s;\n"
-											, var.m_precision.c_str()
-											, var.m_interpolation.c_str()
-											, var.m_type.c_str()
-											, name
-											);
-								}
-								else
-								{
-									preprocessor.writef("%s varying %s %s %s;\n"
-											, var.m_interpolation.c_str()
-											, var.m_precision.c_str()
-											, var.m_type.c_str()
-											, name
-											);
-								}
+						if (0 == numFragData)
+						{
+							// GL errors when both gl_FragColor and gl_FragData is used.
+							// This will trigger the same error with HLSL compiler too.
+							preprocessor.writef("#define gl_FragColor gl_FragData_0_\n");
+
+							// If it has gl_FragData or gl_FragColor, color target at
+							// index 0 exists, otherwise shader is not modifying color
+							// targets.
+							hasFragData[0] |= hasFragColor || d3d < 11;
+
+							if (NULL != insert
+							&&  d3d < 11
+							&&  !hasFragColor)
+							{
+								insert = strInsert(const_cast<char*>(insert+1), "\ngl_FragColor = bgfx_VoidFrag;\n");
 							}
 						}
 
-						for (InOut::const_iterator it = shaderOutputs.begin(), itEnd = shaderOutputs.end(); it != itEnd; ++it)
+						preprocessor.writef("#define void_main()");
+						preprocessor.writef(" \\\n\tvoid main(");
+
+						uint32_t arg = 0;
+
+						if (hasFragCoord)
+						{
+							preprocessor.writef(" \\\n\tvec4 gl_FragCoord : SV_POSITION");
+							++arg;
+						}
+
+						for (InOut::const_iterator it = shaderInputs.begin(), itEnd = shaderInputs.end(); it != itEnd; ++it)
 						{
 							VaryingMap::const_iterator varyingIt = varyingMap.find(*it);
 							if (varyingIt != varyingMap.end() )
 							{
 								const Varying& var = varyingIt->second;
-								preprocessor.writef("%s varying %s %s;\n"
-									, var.m_interpolation.c_str()
+								preprocessor.writef(" \\\n\t%s%s %s %s : %s"
+									, arg++ > 0 ? ", " : "  "
+									, interpolationDx11(var.m_interpolation.c_str() )
 									, var.m_type.c_str()
 									, var.m_name.c_str()
+									, var.m_semantics.c_str()
 									);
 							}
 						}
-					}
-					else
-					{
-						preprocessor.writef(
-							"#define lowp\n"
-							"#define mediump\n"
-							"#define highp\n"
-							"#define ivec2 int2\n"
-							"#define ivec3 int3\n"
-							"#define ivec4 int4\n"
-							"#define uvec2 uint2\n"
-							"#define uvec3 uint3\n"
-							"#define uvec4 uint4\n"
-							"#define vec2 float2\n"
-							"#define vec3 float3\n"
-							"#define vec4 float4\n"
-							"#define mat2 float2x2\n"
-							"#define mat3 float3x3\n"
-							"#define mat4 float4x4\n"
-							);
 
-						if (hlsl != 0
-						&&  hlsl < 4)
+						const uint32_t maxRT = d3d > 9 ? BX_COUNTOF(hasFragData) : 4;
+
+						for (uint32_t ii = 0; ii < BX_COUNTOF(hasFragData); ++ii)
+						{
+							if (ii < maxRT)
+							{
+								if (hasFragData[ii])
+								{
+									addFragData(preprocessor, input, ii, arg++ > 0);
+								}
+							}
+							else
+							{
+								voidFragData(input, ii);
+							}
+						}
+
+						if (hasFragDepth)
 						{
 							preprocessor.writef(
-								"#define centroid\n"
-								"#define flat\n"
-								"#define noperspective\n"
-								"#define smooth\n"
+								" \\\n\t%sout float gl_FragDepth : SV_DEPTH"
+								, arg++ > 0 ? ", " : "  "
 								);
 						}
 
-						entry[4] = '_';
+						if (hasFrontFacing
+						&&  hlsl >= 3)
+						{
+							preprocessor.writef(
+								" \\\n\t%sfloat __vface : VFACE"
+								, arg++ > 0 ? ", " : "  "
+								);
+						}
 
-						if ('f' == shaderType)
+						if (hasPrimitiveId)
 						{
-							const char* insert = bx::strFind(entry, "{");
-							if (NULL != insert)
+							if (d3d > 9)
+							{
+								preprocessor.writef(
+									" \\\n\t%suint gl_PrimitiveID : SV_PrimitiveID"
+									, arg++ > 0 ? ", " : "  "
+									);
+							}
+							else
 							{
-								insert = strInsert(const_cast<char*>(insert+1), "\nvec4 bgfx_VoidFrag = vec4_splat(0.0);\n");
+								fprintf(stderr, "gl_PrimitiveID builtin is not supported by this D3D9 HLSL.\n");
+								return false;
 							}
+						}
 
-							const bool hasFragColor   = NULL != bx::strFind(input, "gl_FragColor");
-							const bool hasFragCoord   = NULL != bx::strFind(input, "gl_FragCoord") || hlsl > 3 || hlsl == 2;
-							const bool hasFragDepth   = NULL != bx::strFind(input, "gl_FragDepth");
-							const bool hasFrontFacing = NULL != bx::strFind(input, "gl_FrontFacing");
-							const bool hasPrimitiveId = NULL != bx::strFind(input, "gl_PrimitiveID");
+						preprocessor.writef(
+							" \\\n\t)\n"
+							);
 
-							bool hasFragData[8] = {};
-							uint32_t numFragData = 0;
-							for (uint32_t ii = 0; ii < BX_COUNTOF(hasFragData); ++ii)
+						if (hasFrontFacing)
+						{
+							if (hlsl >= 3)
 							{
-								char temp[32];
-								bx::snprintf(temp, BX_COUNTOF(temp), "gl_FragData[%d]", ii);
-								hasFragData[ii] = NULL != bx::strFind(input, temp);
-								numFragData += hasFragData[ii];
+								preprocessor.writef(
+									"#define gl_FrontFacing (__vface <= 0.0)\n"
+									);
 							}
-
-							if (0 == numFragData)
+							else
 							{
-								// GL errors when both gl_FragColor and gl_FragData is used.
-								// This will trigger the same error with HLSL compiler too.
-								preprocessor.writef("#define gl_FragColor gl_FragData_0_\n");
-
-								// If it has gl_FragData or gl_FragColor, color target at
-								// index 0 exists, otherwise shader is not modifying color
-								// targets.
-								hasFragData[0] |= hasFragColor || d3d < 11;
-
-								if (NULL != insert
-								&&  d3d < 11
-								&&  !hasFragColor)
-								{
-									insert = strInsert(const_cast<char*>(insert+1), "\ngl_FragColor = bgfx_VoidFrag;\n");
-								}
+								preprocessor.writef(
+									"#define gl_FrontFacing false\n"
+									);
 							}
+						}
+					}
+					else if ('v' == _options.shaderType)
+					{
+						const bool hasVertexId    = NULL != bx::strFind(input, "gl_VertexID");
+						const bool hasInstanceId  = NULL != bx::strFind(input, "gl_InstanceID");
 
-							preprocessor.writef("#define void_main()");
-							preprocessor.writef(" \\\n\tvoid main(");
-
-							uint32_t arg = 0;
-
-							if (hasFragCoord)
+						const char* brace = bx::strFind(entry, "{");
+						if (NULL != brace)
+						{
+							const char* end = bx::strmb(brace, '{', '}');
+							if (NULL != end)
 							{
-								preprocessor.writef(" \\\n\tvec4 gl_FragCoord : SV_POSITION");
-								++arg;
+								strInsert(const_cast<char*>(end), "__RETURN__;\n");
 							}
+						}
 
-							for (InOut::const_iterator it = shaderInputs.begin(), itEnd = shaderInputs.end(); it != itEnd; ++it)
+						preprocessor.writef(
+							"struct Output\n"
+							"{\n"
+							"\tvec4 gl_Position : SV_POSITION;\n"
+							"#define gl_Position _varying_.gl_Position\n"
+							);
+						for (InOut::const_iterator it = shaderOutputs.begin(), itEnd = shaderOutputs.end(); it != itEnd; ++it)
+						{
+							VaryingMap::const_iterator varyingIt = varyingMap.find(*it);
+							if (varyingIt != varyingMap.end() )
 							{
-								VaryingMap::const_iterator varyingIt = varyingMap.find(*it);
-								if (varyingIt != varyingMap.end() )
-								{
-									const Varying& var = varyingIt->second;
-									preprocessor.writef(" \\\n\t%s%s %s %s : %s"
-										, arg++ > 0 ? ", " : "  "
-										, interpolationDx11(var.m_interpolation.c_str() )
-										, var.m_type.c_str()
-										, var.m_name.c_str()
-										, var.m_semantics.c_str()
-										);
-								}
+								const Varying& var = varyingIt->second;
+								preprocessor.writef("\t%s %s : %s;\n", var.m_type.c_str(), var.m_name.c_str(), var.m_semantics.c_str() );
+								preprocessor.writef("#define %s _varying_.%s\n", var.m_name.c_str(), var.m_name.c_str() );
 							}
+						}
+						preprocessor.writef(
+							"};\n"
+							);
 
-							const uint32_t maxRT = d3d > 9 ? BX_COUNTOF(hasFragData) : 4;
-
-							for (uint32_t ii = 0; ii < BX_COUNTOF(hasFragData); ++ii)
+						preprocessor.writef("#define void_main() \\\n");
+						preprocessor.writef("Output main(");
+						uint32_t arg = 0;
+						for (InOut::const_iterator it = shaderInputs.begin(), itEnd = shaderInputs.end(); it != itEnd; ++it)
+						{
+							VaryingMap::const_iterator varyingIt = varyingMap.find(*it);
+							if (varyingIt != varyingMap.end() )
 							{
-								if (ii < maxRT)
-								{
-									if (hasFragData[ii])
-									{
-										addFragData(preprocessor, input, ii, arg++ > 0);
-									}
-								}
-								else
-								{
-									voidFragData(input, ii);
-								}
+								const Varying& var = varyingIt->second;
+								preprocessor.writef(
+									" \\\n\t%s%s %s : %s"
+									, arg++ > 0 ? ", " : ""
+									, var.m_type.c_str()
+									, var.m_name.c_str()
+									, var.m_semantics.c_str()
+									);
 							}
+						}
 
-							if (hasFragDepth)
+						if (hasVertexId)
+						{
+							if (d3d > 9)
 							{
 								preprocessor.writef(
-									" \\\n\t%sout float gl_FragDepth : SV_DEPTH"
+									" \\\n\t%suint gl_VertexID : SV_VertexID"
 									, arg++ > 0 ? ", " : "  "
 									);
 							}
+							else
+							{
+								fprintf(stderr, "gl_VertexID builtin is not supported by this D3D9 HLSL.\n");
+								return false;
+							}
+						}
 
-							if (hasFrontFacing
-							&&  hlsl >= 3)
+						if (hasInstanceId)
+						{
+							if (d3d > 9)
 							{
 								preprocessor.writef(
-									" \\\n\t%sfloat __vface : VFACE"
+									" \\\n\t%suint gl_InstanceID : SV_InstanceID"
 									, arg++ > 0 ? ", " : "  "
 									);
 							}
-
-							if (hasPrimitiveId)
+							else
 							{
-								if (d3d > 9)
-								{
-									preprocessor.writef(
-										" \\\n\t%suint gl_PrimitiveID : SV_PrimitiveID"
-										, arg++ > 0 ? ", " : "  "
-										);
-								}
-								else
-								{
-									fprintf(stderr, "gl_PrimitiveID builtin is not supported by this D3D9 HLSL.\n");
-									return bx::kExitFailure;
-								}
+								fprintf(stderr, "gl_InstanceID builtin is not supported by this D3D9 HLSL.\n");
+								return false;
 							}
+						}
 
-							preprocessor.writef(
-								" \\\n\t)\n"
-								);
+						preprocessor.writef(
+							") \\\n"
+							"{ \\\n"
+							"\tOutput _varying_;"
+							);
 
-							if (hasFrontFacing)
+						for (InOut::const_iterator it = shaderOutputs.begin(), itEnd = shaderOutputs.end(); it != itEnd; ++it)
+						{
+							VaryingMap::const_iterator varyingIt = varyingMap.find(*it);
+							if (varyingIt != varyingMap.end() )
 							{
-								if (hlsl >= 3)
-								{
-									preprocessor.writef(
-										"#define gl_FrontFacing (__vface <= 0.0)\n"
-										);
-								}
-								else
+								const Varying& var = varyingIt->second;
+								preprocessor.writef(" \\\n\t%s", var.m_name.c_str() );
+								if (!var.m_init.empty() )
 								{
-									preprocessor.writef(
-										"#define gl_FrontFacing false\n"
-										);
+									preprocessor.writef(" = %s", var.m_init.c_str() );
 								}
+								preprocessor.writef(";");
 							}
 						}
-						else if ('v' == shaderType)
+
+						preprocessor.writef(
+							"\n#define __RETURN__ \\\n"
+							"\t} \\\n"
+							);
+
+						if (hlsl != 0
+						&&  hlsl <= 3)
 						{
-							const bool hasVertexId    = NULL != bx::strFind(input, "gl_VertexID");
-							const bool hasInstanceId  = NULL != bx::strFind(input, "gl_InstanceID");
+//								preprocessor.writef(
+//									"\tgl_Position.xy += u_viewTexel.xy * gl_Position.w; \\\n"
+//									);
+						}
 
-							const char* brace = bx::strFind(entry, "{");
-							if (NULL != brace)
-							{
-								const char* end = bx::strmb(brace, '{', '}');
-								if (NULL != end)
-								{
-									strInsert(const_cast<char*>(end), "__RETURN__;\n");
-								}
-							}
+						preprocessor.writef(
+							"\treturn _varying_"
+							);
+					}
+				}
 
-							preprocessor.writef(
-								"struct Output\n"
-								"{\n"
-								"\tvec4 gl_Position : SV_POSITION;\n"
-								"#define gl_Position _varying_.gl_Position\n"
-								);
-							for (InOut::const_iterator it = shaderOutputs.begin(), itEnd = shaderOutputs.end(); it != itEnd; ++it)
+				if (preprocessor.run(input) )
+				{
+					//BX_TRACE("Input file: %s", filePath);
+					//BX_TRACE("Output file: %s", outFilePath);
+
+					if (_options.preprocessOnly)
+					{
+						if (0 != glsl)
+						{
+							if (essl != 0)
 							{
-								VaryingMap::const_iterator varyingIt = varyingMap.find(*it);
-								if (varyingIt != varyingMap.end() )
-								{
-									const Varying& var = varyingIt->second;
-									preprocessor.writef("\t%s %s : %s;\n", var.m_type.c_str(), var.m_name.c_str(), var.m_semantics.c_str() );
-									preprocessor.writef("#define %s _varying_.%s\n", var.m_name.c_str(), var.m_name.c_str() );
-								}
+								writef(_writer
+									, "#ifdef GL_ES\n"
+										"precision highp float;\n"
+										"#endif // GL_ES\n\n"
+									);
 							}
-							preprocessor.writef(
-								"};\n"
-								);
+						}
+						bx::write(_writer, preprocessor.m_preprocessed.c_str(), (int32_t)preprocessor.m_preprocessed.size() );
 
-							preprocessor.writef("#define void_main() \\\n");
-							preprocessor.writef("Output main(");
-							uint32_t arg = 0;
-							for (InOut::const_iterator it = shaderInputs.begin(), itEnd = shaderInputs.end(); it != itEnd; ++it)
-							{
-								VaryingMap::const_iterator varyingIt = varyingMap.find(*it);
-								if (varyingIt != varyingMap.end() )
-								{
-									const Varying& var = varyingIt->second;
-									preprocessor.writef(
-										" \\\n\t%s%s %s : %s"
-										, arg++ > 0 ? ", " : ""
-										, var.m_type.c_str()
-										, var.m_name.c_str()
-										, var.m_semantics.c_str()
-										);
-								}
-							}
-
-							if (hasVertexId)
-							{
-								if (d3d > 9)
-								{
-									preprocessor.writef(
-										" \\\n\t%suint gl_VertexID : SV_VertexID"
-										, arg++ > 0 ? ", " : "  "
-										);
-								}
-								else
-								{
-									fprintf(stderr, "gl_VertexID builtin is not supported by this D3D9 HLSL.\n");
-									return bx::kExitFailure;
-								}
-							}
-
-							if (hasInstanceId)
-							{
-								if (d3d > 9)
-								{
-									preprocessor.writef(
-										" \\\n\t%suint gl_InstanceID : SV_InstanceID"
-										, arg++ > 0 ? ", " : "  "
-										);
-								}
-								else
-								{
-									fprintf(stderr, "gl_InstanceID builtin is not supported by this D3D9 HLSL.\n");
-									return bx::kExitFailure;
-								}
-							}
-
-							preprocessor.writef(
-								") \\\n"
-								"{ \\\n"
-								"\tOutput _varying_;"
-								);
-
-							for (InOut::const_iterator it = shaderOutputs.begin(), itEnd = shaderOutputs.end(); it != itEnd; ++it)
-							{
-								VaryingMap::const_iterator varyingIt = varyingMap.find(*it);
-								if (varyingIt != varyingMap.end() )
-								{
-									const Varying& var = varyingIt->second;
-									preprocessor.writef(" \\\n\t%s", var.m_name.c_str() );
-									if (!var.m_init.empty() )
-									{
-										preprocessor.writef(" = %s", var.m_init.c_str() );
-									}
-									preprocessor.writef(";");
-								}
-							}
-
-							preprocessor.writef(
-								"\n#define __RETURN__ \\\n"
-								"\t} \\\n"
-								);
-
-							if (hlsl != 0
-							&&  hlsl <= 3)
-							{
-//								preprocessor.writef(
-//									"\tgl_Position.xy += u_viewTexel.xy * gl_Position.w; \\\n"
-//									);
-							}
-
-							preprocessor.writef(
-								"\treturn _varying_"
-								);
-						}
+						return true;
 					}
 
-					if (preprocessor.run(input) )
 					{
-						BX_TRACE("Input file: %s", filePath);
-						BX_TRACE("Output file: %s", outFilePath);
-
-						if (preprocessOnly)
+						if ('f' == _options.shaderType)
 						{
-							bx::FileWriter writer;
-
-							if (!bx::open(&writer, outFilePath) )
-							{
-								fprintf(stderr, "Unable to open output file '%s'.", outFilePath);
-								return bx::kExitFailure;
-							}
-
-							if (0 != glsl)
-							{
-								if (NULL == profile)
-								{
-									writef(&writer
-										, "#ifdef GL_ES\n"
-										  "precision highp float;\n"
-										  "#endif // GL_ES\n\n"
-										);
-								}
-							}
-							bx::write(&writer, preprocessor.m_preprocessed.c_str(), (int32_t)preprocessor.m_preprocessed.size() );
-							bx::close(&writer);
-
-							return bx::kExitSuccess;
+							bx::write(_writer, BGFX_CHUNK_MAGIC_FSH);
+							bx::write(_writer, inputHash);
 						}
-
+						else if ('v' == _options.shaderType)
+						{
+							bx::write(_writer, BGFX_CHUNK_MAGIC_VSH);
+							bx::write(_writer, outputHash);
+						}
+						else
 						{
-							bx::FileWriter* writer = NULL;
+							bx::write(_writer, BGFX_CHUNK_MAGIC_CSH);
+							bx::write(_writer, outputHash);
+						}
 
-							if (NULL != bin2c)
-							{
-								writer = new Bin2cWriter(bin2c);
-							}
-							else
-							{
-								writer = new bx::FileWriter;
-							}
+						if (0 != glsl
+						||  0 != essl
+						||  0 != metal)
+						{
+							std::string code;
 
-							if (!bx::open(writer, outFilePath) )
+							if (glsl < 400)
 							{
-								fprintf(stderr, "Unable to open output file '%s'.", outFilePath);
-								return bx::kExitFailure;
-							}
+								const bool usesTextureLod   = false
+									|| !!bx::findIdentifierMatch(input, s_ARB_shader_texture_lod)
+									|| !!bx::findIdentifierMatch(input, s_EXT_shader_texture_lod)
+									;
+								const bool usesInstanceID   = !!bx::strFind(input, "gl_InstanceID");
+								const bool usesGpuShader4   = !!bx::findIdentifierMatch(input, s_EXT_gpu_shader4);
+								const bool usesGpuShader5   = !!bx::findIdentifierMatch(input, s_ARB_gpu_shader5);
+								const bool usesTexelFetch   = !!bx::findIdentifierMatch(input, s_texelFetch);
+								const bool usesTextureMS    = !!bx::findIdentifierMatch(input, s_ARB_texture_multisample);
+								const bool usesTextureArray = !!bx::findIdentifierMatch(input, s_textureArray);
+								const bool usesPacking      = !!bx::findIdentifierMatch(input, s_ARB_shading_language_packing);
+
+								if (0 == essl)
+								{
+									const bool need130 = 120 == glsl && (false
+										|| bx::findIdentifierMatch(input, s_130)
+										|| usesTexelFetch
+										);
 
-							if ('f' == shaderType)
-							{
-								bx::write(writer, BGFX_CHUNK_MAGIC_FSH);
-								bx::write(writer, inputHash);
-							}
-							else if ('v' == shaderType)
-							{
-								bx::write(writer, BGFX_CHUNK_MAGIC_VSH);
-								bx::write(writer, outputHash);
-							}
-							else
-							{
-								bx::write(writer, BGFX_CHUNK_MAGIC_CSH);
-								bx::write(writer, outputHash);
-							}
+									if (0 != metal)
+									{
+										bx::stringPrintf(code, "#version 120\n");
+									}
+									else
+									{
+										bx::stringPrintf(code, "#version %s\n", need130 ? "130" : _options.profile.c_str());
+										glsl = 130;
+									}
 
-							if (0 != glsl
-							||  0 != essl
-							||  0 != metal)
-							{
-								std::string code;
+									if (usesInstanceID)
+									{
+										bx::stringPrintf(code
+											, "#extension GL_ARB_draw_instanced : enable\n"
+											);
+									}
 
-								if (glsl < 400)
-								{
-									const bool usesTextureLod   = false
-										|| !!bx::findIdentifierMatch(input, s_ARB_shader_texture_lod)
-										|| !!bx::findIdentifierMatch(input, s_EXT_shader_texture_lod)
-										;
-									const bool usesInstanceID   = !!bx::strFind(input, "gl_InstanceID");
-									const bool usesGpuShader4   = !!bx::findIdentifierMatch(input, s_EXT_gpu_shader4);
-									const bool usesGpuShader5   = !!bx::findIdentifierMatch(input, s_ARB_gpu_shader5);
-									const bool usesTexelFetch   = !!bx::findIdentifierMatch(input, s_texelFetch);
-									const bool usesTextureMS    = !!bx::findIdentifierMatch(input, s_ARB_texture_multisample);
-									const bool usesTextureArray = !!bx::findIdentifierMatch(input, s_textureArray);
-									const bool usesPacking      = !!bx::findIdentifierMatch(input, s_ARB_shading_language_packing);
-
-									if (0 == essl)
+									if (usesGpuShader4)
 									{
-										const bool need130 = 120 == glsl && (false
-											|| bx::findIdentifierMatch(input, s_130)
-											|| usesTexelFetch
+										bx::stringPrintf(code
+											, "#extension GL_EXT_gpu_shader4 : enable\n"
 											);
+									}
 
-										if (0 != metal)
-										{
-											bx::stringPrintf(code, "#version 120\n");
-										}
-										else
-										{
-											bx::stringPrintf(code, "#version %s\n", need130 ? "130" : profile);
-											glsl = 130;
-										}
+									if (usesGpuShader5)
+									{
+										bx::stringPrintf(code
+											, "#extension GL_ARB_gpu_shader5 : enable\n"
+											);
+									}
 
-										if (usesInstanceID)
-										{
-											bx::stringPrintf(code
-												, "#extension GL_ARB_draw_instanced : enable\n"
-												);
-										}
+									if (usesPacking)
+									{
+										bx::stringPrintf(code
+											, "#extension GL_ARB_shading_language_packing : enable\n"
+											);
+									}
 
-										if (usesGpuShader4)
-										{
-											bx::stringPrintf(code
-												, "#extension GL_EXT_gpu_shader4 : enable\n"
-												);
-										}
+									bool ARB_shader_texture_lod = false;
+									bool EXT_shader_texture_lod = false;
 
-										if (usesGpuShader5)
+									if (usesTextureLod)
+									{
+										if ('f' == _options.shaderType)
 										{
+											ARB_shader_texture_lod = true;
 											bx::stringPrintf(code
-												, "#extension GL_ARB_gpu_shader5 : enable\n"
+												, "#extension GL_ARB_shader_texture_lod : enable\n"
 												);
 										}
-
-										if (usesPacking)
+										else
 										{
+											EXT_shader_texture_lod = true;
 											bx::stringPrintf(code
-												, "#extension GL_ARB_shading_language_packing : enable\n"
+												, "#extension GL_EXT_shader_texture_lod : enable\n"
 												);
 										}
+									}
 
-										bool ARB_shader_texture_lod = false;
-										bool EXT_shader_texture_lod = false;
-
-										if (usesTextureLod)
-										{
-											if ('f' == shaderType)
-											{
-												ARB_shader_texture_lod = true;
-												bx::stringPrintf(code
-													, "#extension GL_ARB_shader_texture_lod : enable\n"
-													);
-											}
-											else
-											{
-												EXT_shader_texture_lod = true;
-												bx::stringPrintf(code
-													, "#extension GL_EXT_shader_texture_lod : enable\n"
-													);
-											}
-										}
+									if (usesTextureMS)
+									{
+										bx::stringPrintf(code
+											, "#extension GL_ARB_texture_multisample : enable\n"
+											);
+									}
 
-										if (usesTextureMS)
-										{
-											bx::stringPrintf(code
-												, "#extension GL_ARB_texture_multisample : enable\n"
-												);
-										}
+									if (usesTextureArray)
+									{
+										bx::stringPrintf(code
+											, "#extension GL_EXT_texture_array : enable\n"
+											);
+									}
 
-										if (usesTextureArray)
-										{
-											bx::stringPrintf(code
-												, "#extension GL_EXT_texture_array : enable\n"
-												);
-										}
+									if (130 > glsl)
+									{
+										bx::stringPrintf(code,
+											"#define ivec2 vec2\n"
+											"#define ivec3 vec3\n"
+											"#define ivec4 vec4\n"
+											);
+									}
 
-										if (130 > glsl)
-										{
-											bx::stringPrintf(code,
-												"#define ivec2 vec2\n"
-												"#define ivec3 vec3\n"
-												"#define ivec4 vec4\n"
-												);
-										}
+									if (ARB_shader_texture_lod)
+									{
+										bx::stringPrintf(code,
+											"#define texture2DProjLod  texture2DProjLodARB\n"
+											"#define texture2DGrad     texture2DGradARB\n"
+											"#define texture2DProjGrad texture2DProjGradARB\n"
+											"#define textureCubeGrad   textureCubeGradARB\n"
+											);
+									}
+									else if (EXT_shader_texture_lod)
+									{
+										bx::stringPrintf(code,
+											"#define texture2DProjLod  texture2DProjLodEXT\n"
+											"#define texture2DGrad     texture2DGradEXT\n"
+											"#define texture2DProjGrad texture2DProjGradEXT\n"
+											"#define textureCubeGrad   textureCubeGradEXT\n"
+											);
+									}
 
-										if (ARB_shader_texture_lod)
-										{
-											bx::stringPrintf(code,
-												"#define texture2DProjLod  texture2DProjLodARB\n"
-												"#define texture2DGrad     texture2DGradARB\n"
-												"#define texture2DProjGrad texture2DProjGradARB\n"
-												"#define textureCubeGrad   textureCubeGradARB\n"
-												);
-										}
-										else if (EXT_shader_texture_lod)
-										{
-											bx::stringPrintf(code,
-												"#define texture2DProjLod  texture2DProjLodEXT\n"
+									if (need130)
+									{
+										bx::stringPrintf(code
+											, "#define bgfxShadow2D(_sampler, _coord)     vec4_splat(texture(_sampler, _coord))\n"
+												"#define bgfxShadow2DProj(_sampler, _coord) vec4_splat(textureProj(_sampler, _coord))\n"
+											);
+									}
+									else
+									{
+										bx::stringPrintf(code
+											, "#define bgfxShadow2D     shadow2D\n"
+												"#define bgfxShadow2DProj shadow2DProj\n"
+											);
+									}
+								}
+								else
+								{
+									// Pretend that all extensions are available.
+									// This will be stripped later.
+									if (usesTextureLod)
+									{
+										bx::stringPrintf(code
+											, "#extension GL_EXT_shader_texture_lod : enable\n"
+												"#define texture2DLod      texture2DLodEXT\n"
 												"#define texture2DGrad     texture2DGradEXT\n"
+												"#define texture2DProjLod  texture2DProjLodEXT\n"
 												"#define texture2DProjGrad texture2DProjGradEXT\n"
+												"#define textureCubeLod    textureCubeLodEXT\n"
 												"#define textureCubeGrad   textureCubeGradEXT\n"
-												);
-										}
-
-										if (need130)
-										{
-											bx::stringPrintf(code
-												, "#define bgfxShadow2D(_sampler, _coord)     vec4_splat(texture(_sampler, _coord))\n"
-												  "#define bgfxShadow2DProj(_sampler, _coord) vec4_splat(textureProj(_sampler, _coord))\n"
-												);
-										}
-										else
-										{
-											bx::stringPrintf(code
-												, "#define bgfxShadow2D     shadow2D\n"
-												  "#define bgfxShadow2DProj shadow2DProj\n"
-												);
-										}
+											);
 									}
-									else
-									{
-										// Pretend that all extensions are available.
-										// This will be stripped later.
-										if (usesTextureLod)
-										{
-											bx::stringPrintf(code
-												, "#extension GL_EXT_shader_texture_lod : enable\n"
-												  "#define texture2DLod      texture2DLodEXT\n"
-												  "#define texture2DGrad     texture2DGradEXT\n"
-												  "#define texture2DProjLod  texture2DProjLodEXT\n"
-												  "#define texture2DProjGrad texture2DProjGradEXT\n"
-												  "#define textureCubeLod    textureCubeLodEXT\n"
-												  "#define textureCubeGrad   textureCubeGradEXT\n"
-												);
-										}
-
-										if (NULL != bx::findIdentifierMatch(input, s_OES_standard_derivatives) )
-										{
-											bx::stringPrintf(code, "#extension GL_OES_standard_derivatives : enable\n");
-										}
-
-										if (NULL != bx::findIdentifierMatch(input, s_OES_texture_3D) )
-										{
-											bx::stringPrintf(code, "#extension GL_OES_texture_3D : enable\n");
-										}
 
-										if (NULL != bx::findIdentifierMatch(input, s_EXT_shadow_samplers) )
-										{
-											bx::stringPrintf(code
-												, "#extension GL_EXT_shadow_samplers : enable\n"
-												  "#define shadow2D shadow2DEXT\n"
-												  "#define shadow2DProj shadow2DProjEXT\n"
-												);
-										}
-
-										if (usesGpuShader5)
-										{
-											bx::stringPrintf(code
-												, "#extension GL_ARB_gpu_shader5 : enable\n"
-												);
-										}
-
-										if (usesPacking)
-										{
-											bx::stringPrintf(code
-												, "#extension GL_ARB_shading_language_packing : enable\n"
-												);
-										}
+									if (NULL != bx::findIdentifierMatch(input, s_OES_standard_derivatives) )
+									{
+										bx::stringPrintf(code, "#extension GL_OES_standard_derivatives : enable\n");
+									}
 
-										if (NULL != bx::findIdentifierMatch(input, "gl_FragDepth") )
-										{
-											bx::stringPrintf(code
-												, "#extension GL_EXT_frag_depth : enable\n"
-												  "#define gl_FragDepth gl_FragDepthEXT\n"
-												);
-										}
+									if (NULL != bx::findIdentifierMatch(input, s_OES_texture_3D) )
+									{
+										bx::stringPrintf(code, "#extension GL_OES_texture_3D : enable\n");
+									}
 
-										if (usesTextureArray)
-										{
-											bx::stringPrintf(code
-												, "#extension GL_EXT_texture_array : enable\n"
-												);
-										}
+									if (NULL != bx::findIdentifierMatch(input, s_EXT_shadow_samplers) )
+									{
+										bx::stringPrintf(code
+											, "#extension GL_EXT_shadow_samplers : enable\n"
+												"#define shadow2D shadow2DEXT\n"
+												"#define shadow2DProj shadow2DProjEXT\n"
+											);
+									}
 
-										bx::stringPrintf(code,
-												"#define ivec2 vec2\n"
-												"#define ivec3 vec3\n"
-												"#define ivec4 vec4\n"
-												);
+									if (usesGpuShader5)
+									{
+										bx::stringPrintf(code
+											, "#extension GL_ARB_gpu_shader5 : enable\n"
+											);
 									}
-								}
-								else
-								{
-									bx::stringPrintf(code, "#version %d\n", glsl);
-								}
 
-								code += preprocessor.m_preprocessed;
+									if (usesPacking)
+									{
+										bx::stringPrintf(code
+											, "#extension GL_ARB_shading_language_packing : enable\n"
+											);
+									}
 
-								if (glsl > 400)
-								{
-									bx::write(writer, uint16_t(0) );
+									if (NULL != bx::findIdentifierMatch(input, "gl_FragDepth") )
+									{
+										bx::stringPrintf(code
+											, "#extension GL_EXT_frag_depth : enable\n"
+												"#define gl_FragDepth gl_FragDepthEXT\n"
+											);
+									}
 
-									uint32_t shaderSize = (uint32_t)code.size();
-									bx::write(writer, shaderSize);
-									bx::write(writer, code.c_str(), shaderSize);
-									bx::write(writer, uint8_t(0) );
+									if (usesTextureArray)
+									{
+										bx::stringPrintf(code
+											, "#extension GL_EXT_texture_array : enable\n"
+											);
+									}
 
-									compiled = true;
-								}
-								else
-								{
-									compiled = compileGLSLShader(cmdLine
-										, metal ? BX_MAKEFOURCC('M', 'T', 'L', 0) : essl
-										, code
-										, writer
-										);
+									bx::stringPrintf(code,
+											"#define ivec2 vec2\n"
+											"#define ivec3 vec3\n"
+											"#define ivec4 vec4\n"
+											);
 								}
 							}
-							else if (0 != spirv)
+							else
 							{
-								compiled = compileSPIRVShader(cmdLine
-									, 0
-									, preprocessor.m_preprocessed
-									, writer
-									);
+								bx::stringPrintf(code, "#version %d\n", glsl);
 							}
-							else if (0 != pssl)
+
+							code += preprocessor.m_preprocessed;
+
+							if (glsl > 400)
 							{
-								compiled = compilePSSLShader(cmdLine
-									, 0
-									, preprocessor.m_preprocessed
-									, writer
-									);
+								bx::write(_writer, uint16_t(0) );
+
+								uint32_t shaderSize = (uint32_t)code.size();
+								bx::write(_writer, shaderSize);
+								bx::write(_writer, code.c_str(), shaderSize);
+								bx::write(_writer, uint8_t(0) );
+
+								compiled = true;
 							}
 							else
 							{
-								compiled = compileHLSLShader(cmdLine
-									, d3d
-									, preprocessor.m_preprocessed
-									, writer
+								compiled = compileGLSLShader(_options
+									, metal ? BX_MAKEFOURCC('M', 'T', 'L', 0) : essl
+									, code
+									, _writer
 									);
 							}
-
-							bx::close(writer);
-							delete writer;
 						}
+						else if (0 != spirv)
+						{
+							compiled = compileSPIRVShader(_options
+								, 0
+								, preprocessor.m_preprocessed
+								, _writer
+								);
+						}
+						else if (0 != pssl)
+						{
+							compiled = compilePSSLShader(_options
+								, 0
+								, preprocessor.m_preprocessed
+								, _writer
+								);
+						}
+						else
+						{
+							compiled = compileHLSLShader(_options
+								, d3d
+								, preprocessor.m_preprocessed
+								, _writer
+								);
+						}
+					}
 
-						if (compiled)
+					if (compiled)
+					{
+						if (_options.depends)
 						{
-							if (depends)
+							std::string ofp = _options.outputFilePath + ".d";
+							bx::FileWriter writer;
+							if (bx::open(&writer, ofp.c_str() ) )
 							{
-								std::string ofp = outFilePath;
-								ofp += ".d";
-								bx::FileWriter writer;
-								if (bx::open(&writer, ofp.c_str() ) )
-								{
-									writef(&writer, "%s : %s\n", outFilePath, preprocessor.m_depends.c_str() );
-									bx::close(&writer);
-								}
+								writef(&writer, "%s : %s\n", _options.outputFilePath.c_str(), preprocessor.m_depends.c_str() );
+								bx::close(&writer);
 							}
 						}
 					}
 				}
 			}
+		}
+
+		delete [] data;
+
+		return compiled;
+	}
+
+	int compileShader(int _argc, const char* _argv[])
+	{
+		bx::CommandLine cmdLine(_argc, _argv);
+
+		if (cmdLine.hasArg('v', "version") )
+		{
+			fprintf(stderr
+				, "shaderc, bgfx shader compiler tool, version %d.%d.%d.\n"
+				, BGFX_SHADERC_VERSION_MAJOR
+				, BGFX_SHADERC_VERSION_MINOR
+				, BGFX_API_VERSION
+				);
+			return bx::kExitSuccess;
+		}
+
+		if (cmdLine.hasArg('h', "help") )
+		{
+			help();
+			return bx::kExitFailure;
+		}
+
+		g_verbose = cmdLine.hasArg("verbose");
+
+		const char* filePath = cmdLine.findOption('f');
+		if (NULL == filePath)
+		{
+			help("Shader file name must be specified.");
+			return bx::kExitFailure;
+		}
+
+		const char* outFilePath = cmdLine.findOption('o');
+		if (NULL == outFilePath)
+		{
+			help("Output file name must be specified.");
+			return bx::kExitFailure;
+		}
+
+		const char* type = cmdLine.findOption('\0', "type");
+		if (NULL == type)
+		{
+			help("Must specify shader type.");
+			return bx::kExitFailure;
+		}
+
+		Options options;
+		options.inputFilePath = filePath;
+		options.outputFilePath = outFilePath;
+		options.shaderType = bx::toLower(type[0]);
+
+		options.disasm = cmdLine.hasArg('\0', "disasm");
+
+		const char* platform = cmdLine.findOption('\0', "platform");
+		if (NULL == platform)
+		{
+			platform = "";
+		}
+
+		options.platform = platform;
+
+		options.raw = cmdLine.hasArg('\0', "raw");
+
+		const char* profile = cmdLine.findOption('p', "profile");
+
+		if ( NULL != profile)
+		{
+			options.profile = profile;
+		}
+
+		{	// hlsl only
+			options.debugInformation = cmdLine.hasArg('\0', "debug");
+			options.avoidFlowControl = cmdLine.hasArg('\0', "avoid-flow-control");
+			options.noPreshader = cmdLine.hasArg('\0', "no-preshader");
+			options.partialPrecision = cmdLine.hasArg('\0', "partial-precision");
+			options.preferFlowControl = cmdLine.hasArg('\0', "prefer-flow-control");
+			options.backwardsCompatibility = cmdLine.hasArg('\0', "backwards-compatibility");
+			options.warningsAreErrors = cmdLine.hasArg('\0', "Werror");
+
+			uint32_t optimization = 3;
+			if (cmdLine.hasArg(optimization, 'O') )
+			{
+				options.optimize = true;
+				options.optimizationLevel = optimization;
+			}
+		}
+
+		const char* bin2c = NULL;
+		if (cmdLine.hasArg("bin2c") )
+		{
+			bin2c = cmdLine.findOption("bin2c");
+			if (NULL == bin2c)
+			{
+				bin2c = baseName(outFilePath);
+				uint32_t len = (uint32_t)bx::strLen(bin2c);
+				char* temp = (char*)alloca(len+1);
+				for (char *out = temp; *bin2c != '\0';)
+				{
+					char ch = *bin2c++;
+					if (isalnum(ch) )
+					{
+						*out++ = ch;
+					}
+					else
+					{
+						*out++ = '_';
+					}
+				}
+				temp[len] = '\0';
+
+				bin2c = temp;
+			}
+		}
+
+		bool depends = cmdLine.hasArg("depends");
+		options.preprocessOnly = cmdLine.hasArg("preprocess");
+		const char* includeDir = cmdLine.findOption('i');
+
+		BX_TRACE("depends: %d", depends);
+		BX_TRACE("preprocessOnly: %d", options.preprocessOnly);
+		BX_TRACE("includeDir: %s", includeDir);
+
+		for (int ii = 1; NULL != includeDir; ++ii)
+		{
+			options.includeDirs.push_back(includeDir);
+			includeDir = cmdLine.findOption(ii, 'i');
+		}
+
+		std::string dir;
+		{
+			const char* base = baseName(filePath);
+
+			if (base != filePath)
+			{
+				dir.assign(filePath, base-filePath);
+				options.includeDirs.push_back(dir.c_str());
+			}
+		}
+
+		const char* defines = cmdLine.findOption("define");
+		while (NULL != defines
+		&&    '\0'  != *defines)
+		{
+			defines = bx::strws(defines);
+			const char* eol = bx::strFind(defines, ';');
+			if (NULL == eol)
+			{
+				eol = defines + bx::strLen(defines);
+			}
+			std::string define(defines, eol);
+			options.defines.push_back(define.c_str() );
+			defines = ';' == *eol ? eol+1 : eol;
+		}
+
+		bool compiled = false;
+
+		bx::FileReader reader;
+		if (!bx::open(&reader, filePath) )
+		{
+			fprintf(stderr, "Unable to open file '%s'.\n", filePath);
+		}
+		else
+		{
+			std::string defaultVarying = dir + "varying.def.sc";
+			const char* varyingdef = cmdLine.findOption("varyingdef", defaultVarying.c_str() );
+			File attribdef(varyingdef);
+			const char* parse = attribdef.getData();
+			if (NULL != parse
+			&&  *parse != '\0')
+			{
+				options.dependencies.push_back(varyingdef);
+			}
+			else
+			{
+				fprintf(stderr, "ERROR: Failed to parse varying def file: \"%s\" No input/output semantics will be generated in the code!\n", varyingdef);
+			}
+
+			const size_t padding = 4096;
+			uint32_t size = (uint32_t)bx::getSize(&reader);
+			char* data = new char[size+padding+1];
+			size = (uint32_t)bx::read(&reader, data, size);
+
+			if (data[0] == '\xef'
+			&&  data[1] == '\xbb'
+			&&  data[2] == '\xbf')
+			{
+				bx::memMove(data, &data[3], size-3);
+				size -= 3;
+			}
+
+			// Compiler generates "error X3000: syntax error: unexpected end of file"
+			// if input doesn't have empty line at EOF.
+			data[size] = '\n';
+			bx::memSet(&data[size+1], 0, padding);
+			bx::close(&reader);
+
+			bx::FileWriter* writer = NULL;
+
+			if (NULL != bin2c)
+			{
+				writer = new Bin2cWriter(bin2c);
+			}
+			else
+			{
+				writer = new bx::FileWriter;
+			}
+
+			if (!bx::open(writer, outFilePath) )
+			{
+				fprintf(stderr, "Unable to open output file '%s'.", outFilePath);
+				return bx::kExitFailure;
+			}
+
+			if ( compileShader(attribdef.getData(), data, size, options, writer) )
+				compiled = true;
 
-			delete [] data;
+			bx::close(writer);
+			delete writer;
 		}
 
 		if (compiled)

+ 39 - 4
tools/shaderc/shaderc.h

@@ -122,6 +122,41 @@ namespace bgfx
 		uint16_t regCount;
 	};
 
+	struct Options
+	{
+		Options();
+
+		void dump();
+
+		char shaderType;
+		std::string platform;
+		std::string profile;
+
+		std::string	inputFilePath;
+		std::string	outputFilePath;
+
+		std::vector<std::string> includeDirs;
+		std::vector<std::string> defines;
+		std::vector<std::string> dependencies;
+
+		bool disasm;
+		bool raw;
+		bool preprocessOnly;
+		bool depends;
+
+		bool debugInformation;
+
+		bool avoidFlowControl;
+		bool noPreshader;
+		bool partialPrecision;
+		bool preferFlowControl;
+		bool backwardsCompatibility;
+		bool warningsAreErrors;
+
+		bool optimize;
+		uint32_t optimizationLevel;
+	};
+
 	typedef std::vector<Uniform> UniformArray;
 
 	void printCode(const char* _code, int32_t _line = 0, int32_t _start = 0, int32_t _end = INT32_MAX, int32_t _column = -1);
@@ -129,10 +164,10 @@ namespace bgfx
 	int32_t writef(bx::WriterI* _writer, const char* _format, ...);
 	void writeFile(const char* _filePath, const void* _data, int32_t _size);
 
-	bool compileGLSLShader(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer);
-	bool compileHLSLShader(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer);
-	bool compilePSSLShader(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer);
-	bool compileSPIRVShader(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer);
+	bool compileGLSLShader(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer);
+	bool compileHLSLShader(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer);
+	bool compilePSSLShader(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer);
+	bool compileSPIRVShader(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer);
 
 } // namespace bgfx
 

+ 6 - 7
tools/shaderc/shaderc_glsl.cpp

@@ -8,9 +8,9 @@
 
 namespace bgfx { namespace glsl
 {
-	static bool compile(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
+	static bool compile(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
 	{
-		char ch = char(tolower(_cmdLine.findOption('\0', "type")[0]) );
+		char ch = _options.shaderType;
 		const glslopt_shader_type type = ch == 'f'
 			? kGlslOptShaderFragment
 			: (ch == 'c' ? kGlslOptShaderCompute : kGlslOptShaderVertex);
@@ -292,10 +292,9 @@ namespace bgfx { namespace glsl
 		uint8_t nul = 0;
 		bx::write(_writer, nul);
 
-		if (_cmdLine.hasArg('\0', "disasm") )
+		if (_options.disasm )
 		{
-			std::string disasmfp = _cmdLine.findOption('o');
-			disasmfp += ".disasm";
+			std::string disasmfp = _options.outputFilePath + ".disasm";
 			writeFile(disasmfp.c_str(), optimizedShader, shaderSize);
 		}
 
@@ -306,9 +305,9 @@ namespace bgfx { namespace glsl
 
 } // namespace glsl
 
-	bool compileGLSLShader(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
+	bool compileGLSLShader(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
 	{
-		return glsl::compile(_cmdLine, _version, _code, _writer);
+		return glsl::compile(_options, _version, _code, _writer);
 	}
 
 } // namespace bgfx

+ 21 - 23
tools/shaderc/shaderc_hlsl.cpp

@@ -545,10 +545,11 @@ namespace bgfx { namespace hlsl
 		return true;
 	}
 
-	static bool compile(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer, bool _firstPass)
+	static bool compile(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer, bool _firstPass)
 	{
-		const char* profile = _cmdLine.findOption('p', "profile");
-		if (NULL == profile)
+		const char* profile = _options.profile.c_str();
+
+		if (profile[0] == '\0')
 		{
 			fprintf(stderr, "Error: Shader profile must be specified.\n");
 			return false;
@@ -557,27 +558,26 @@ namespace bgfx { namespace hlsl
 		s_compiler = load();
 
 		bool result = false;
-		bool debug = _cmdLine.hasArg('\0', "debug");
+		bool debug = _options.debugInformation;
 
 		uint32_t flags = D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY;
 		flags |= debug ? D3DCOMPILE_DEBUG : 0;
-		flags |= _cmdLine.hasArg('\0', "avoid-flow-control") ? D3DCOMPILE_AVOID_FLOW_CONTROL : 0;
-		flags |= _cmdLine.hasArg('\0', "no-preshader") ? D3DCOMPILE_NO_PRESHADER : 0;
-		flags |= _cmdLine.hasArg('\0', "partial-precision") ? D3DCOMPILE_PARTIAL_PRECISION : 0;
-		flags |= _cmdLine.hasArg('\0', "prefer-flow-control") ? D3DCOMPILE_PREFER_FLOW_CONTROL : 0;
-		flags |= _cmdLine.hasArg('\0', "backwards-compatibility") ? D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY : 0;
+		flags |= _options.avoidFlowControl ? D3DCOMPILE_AVOID_FLOW_CONTROL : 0;
+		flags |= _options.noPreshader ? D3DCOMPILE_NO_PRESHADER : 0;
+		flags |= _options.partialPrecision ? D3DCOMPILE_PARTIAL_PRECISION : 0;
+		flags |= _options.preferFlowControl ? D3DCOMPILE_PREFER_FLOW_CONTROL : 0;
+		flags |= _options.backwardsCompatibility ? D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY : 0;
 
-		bool werror = _cmdLine.hasArg('\0', "Werror");
+		bool werror = _options.warningsAreErrors;
 
 		if (werror)
 		{
 			flags |= D3DCOMPILE_WARNINGS_ARE_ERRORS;
 		}
 
-		uint32_t optimization = 3;
-		if (_cmdLine.hasArg(optimization, 'O') )
+		if (_options.optimize )
 		{
-			optimization = bx::uint32_min(optimization, BX_COUNTOF(s_optimizationLevelD3D11) - 1);
+			uint32_t optimization = bx::uint32_min(_options.optimizationLevel, BX_COUNTOF(s_optimizationLevelD3D11) - 1);
 			flags |= s_optimizationLevelD3D11[optimization];
 		}
 		else
@@ -598,8 +598,7 @@ namespace bgfx { namespace hlsl
 
 		if (debug)
 		{
-			hlslfp = _cmdLine.findOption('o');
-			hlslfp += ".hlsl";
+			hlslfp = _options.outputFilePath + ".hlsl";
 			writeFile(hlslfp.c_str(), _code.c_str(), (int32_t)_code.size() );
 		}
 
@@ -708,7 +707,7 @@ namespace bgfx { namespace hlsl
 				}
 
 				// recompile with the unused uniforms converted to statics
-				return compile(_cmdLine, _version, output.c_str(), _writer, false);
+				return compile(_options, _version, output.c_str(), _writer, false);
 			}
 		}
 
@@ -771,7 +770,7 @@ namespace bgfx { namespace hlsl
 			bx::write(_writer, size);
 		}
 
-		if (_cmdLine.hasArg('\0', "disasm") )
+		if (_options.disasm )
 		{
 			ID3DBlob* disasm;
 			D3DDisassemble(code->GetBufferPointer()
@@ -783,8 +782,7 @@ namespace bgfx { namespace hlsl
 
 			if (NULL != disasm)
 			{
-				std::string disasmfp = _cmdLine.findOption('o');
-				disasmfp += ".disasm";
+				std::string disasmfp = _options.outputFilePath + ".disasm";
 
 				writeFile(disasmfp.c_str(), disasm->GetBufferPointer(), (uint32_t)disasm->GetBufferSize() );
 				disasm->Release();
@@ -806,9 +804,9 @@ namespace bgfx { namespace hlsl
 
 } // namespace hlsl
 
-	bool compileHLSLShader(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
+	bool compileHLSLShader(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
 	{
-		return hlsl::compile(_cmdLine, _version, _code, _writer, true);
+		return hlsl::compile(_options, _version, _code, _writer, true);
 	}
 
 } // namespace bgfx
@@ -817,9 +815,9 @@ namespace bgfx { namespace hlsl
 
 namespace bgfx
 {
-	bool compileHLSLShader(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
+	bool compileHLSLShader(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
 	{
-		BX_UNUSED(_cmdLine, _version, _code, _writer);
+		BX_UNUSED(_options, _version, _code, _writer);
 		fprintf(stderr, "HLSL compiler is not supported on this platform.\n");
 		return false;
 	}

+ 2 - 2
tools/shaderc/shaderc_pssl.cpp

@@ -7,9 +7,9 @@
 
 namespace bgfx
 {
-	bool compilePSSLShader(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
+	bool compilePSSLShader(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
 	{
-		BX_UNUSED(_cmdLine, _version, _code, _writer);
+		BX_UNUSED(_options, _version, _code, _writer);
 		fprintf(stderr, "PSSL compiler is not supported.\n");
 		return false;
 	}

+ 7 - 14
tools/shaderc/shaderc_spirv.cpp

@@ -545,25 +545,18 @@ namespace bgfx { namespace spirv
 //		fprintf(stderr, "%s\n", _message);
 //	}
 
-	static bool compile(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
+	static bool compile(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
 	{
-		BX_UNUSED(_cmdLine, _version, _code, _writer);
-
-		const char* type = _cmdLine.findOption('\0', "type");
-		if (NULL == type)
-		{
-			fprintf(stderr, "Error: Shader type must be specified.\n");
-			return false;
-		}
+		BX_UNUSED(_version);
 
 		glslang::InitializeProcess();
 
 		glslang::TProgram* program = new glslang::TProgram;
 
-		EShLanguage stage = getLang(type[0]);
+		EShLanguage stage = getLang(_options.shaderType);
 		if (EShLangCount == stage)
 		{
-			fprintf(stderr, "Error: Unknown shader type %s.\n", type);
+			fprintf(stderr, "Error: Unknown shader type %s.\n", _options.shaderType);
 			return false;
 		}
 		glslang::TShader* shader = new glslang::TShader(stage);
@@ -649,7 +642,7 @@ namespace bgfx { namespace spirv
 					uint16_t count = (uint16_t)program->getNumLiveUniformVariables();
 					bx::write(_writer, count);
 
-					uint32_t fragmentBit = type[0] == 'f' ? BGFX_UNIFORM_FRAGMENTBIT : 0;
+					uint32_t fragmentBit = _options.shaderType == 'f' ? BGFX_UNIFORM_FRAGMENTBIT : 0;
 					for (uint16_t ii = 0; ii < count; ++ii)
 					{
 						Uniform un;
@@ -767,9 +760,9 @@ namespace bgfx { namespace spirv
 
 } // namespace spirv
 
-	bool compileSPIRVShader(const bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
+	bool compileSPIRVShader(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
 	{
-		return spirv::compile(_cmdLine, _version, _code, _writer);
+		return spirv::compile(_options, _version, _code, _writer);
 	}
 
 } // namespace bgfx