Sfoglia il codice sorgente

BansheeSL: Refactored code, added a proper importer

Marko Pintera 10 anni fa
parent
commit
634dccf9e0

+ 1 - 1
BansheeCore/Include/BsGpuProgIncludeImporter.h

@@ -7,7 +7,7 @@ namespace BansheeEngine
 {
 	/**
 	 * @brief	Importer using for importing GPU program (i.e. shader) include files.
-	 * 			Include files are just text files ending with ".gpuinc" extension.
+	 * 			Include files are just text files ending with ".bsi" extension.
 	 */
 	class BS_CORE_EXPORT GpuProgIncludeImporter : public SpecificImporter
 	{

+ 8 - 2
BansheeCore/Include/BsShader.h

@@ -299,11 +299,17 @@ namespace BansheeEngine
 		static bool isBuffer(GpuParamObjectType type);
 
 		/**
-		 * @brief	Returns an empty shader object with the specified name. Caller must register
-		 *			techniques with the shader before using it in a Material.
+		 * @brief	Creates a new shader resource using the provided descriptor and techniques.
 		 */
 		static HShader create(const String& name, const SHADER_DESC& desc, const Vector<SPtr<Technique>>& techniques);
 
+		/**
+		 * @brief	Creates a new shader object using the provided descriptor and techniques.
+		 *
+		 * @note	Internal method.
+		 */
+		static ShaderPtr _createPtr(const String& name, const SHADER_DESC& desc, const Vector<SPtr<Technique>>& techniques);
+
 		/**
 		 * @brief	Returns a shader object but doesn't initialize it.
 		 */

+ 1 - 1
BansheeCore/Source/BsGpuProgIncludeImporter.cpp

@@ -21,7 +21,7 @@ namespace BansheeEngine
 		WString lowerCaseExt = ext;
 		StringUtil::toLowerCase(lowerCaseExt);
 
-		return lowerCaseExt == L"gpuinc";
+		return lowerCaseExt == L"bsi";
 	}
 
 	bool GpuProgIncludeImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const

+ 8 - 1
BansheeCore/Source/BsShader.cpp

@@ -310,12 +310,19 @@ namespace BansheeEngine
 	}
 
 	HShader Shader::create(const String& name, const SHADER_DESC& desc, const Vector<SPtr<Technique>>& techniques)
+	{
+		ShaderPtr newShader = _createPtr(name, desc, techniques);
+
+		return static_resource_cast<Shader>(gResources()._createResourceHandle(newShader));
+	}
+
+	ShaderPtr Shader::_createPtr(const String& name, const SHADER_DESC& desc, const Vector<SPtr<Technique>>& techniques)
 	{
 		ShaderPtr newShader = bs_core_ptr<Shader, PoolAlloc>(new (bs_alloc<Shader, PoolAlloc>()) Shader(name, desc, techniques));
 		newShader->_setThisPtr(newShader);
 		newShader->initialize();
 
-		return static_resource_cast<Shader>(gResources()._createResourceHandle(newShader));
+		return newShader;
 	}
 
 	ShaderPtr Shader::createEmpty()

+ 3 - 0
BansheeEditor/Source/BsEditorApplication.cpp

@@ -175,6 +175,9 @@ namespace BansheeEngine
 		/* 								DEBUG CODE                      		*/
 		/************************************************************************/
 
+		HShader dummyParsedShader = Importer::instance().import<Shader>("..\\..\\..\\..\\Data\\Raw\\Engine\\Shaders\\DummyFX.bsl");
+		assert(dummyParsedShader != nullptr);
+
 		RenderAPICore* renderSystem = RenderAPICore::instancePtr();
 
 		HSceneObject testModelGO = SceneObject::create("TestMesh");

+ 4 - 0
BansheeSL/BansheeSL.vcxproj

@@ -264,6 +264,8 @@
     <ClInclude Include="BsLexerFX.h" />
     <ClInclude Include="BsParserFX.h" />
     <ClInclude Include="Include\BsASTFX.h" />
+    <ClInclude Include="Include\BsSLFXCompiler.h" />
+    <ClInclude Include="Include\BsSLImporter.h" />
     <ClInclude Include="Include\BsMMAlloc.h" />
     <ClInclude Include="Include\BsSLPrerequisites.h" />
   </ItemGroup>
@@ -282,6 +284,8 @@
     </ClCompile>
     <ClCompile Include="Source\BsASTFX.c" />
     <ClCompile Include="Source\BSMMAlloc.c" />
+    <ClCompile Include="Source\BsSLFXCompiler.cpp" />
+    <ClCompile Include="Source\BsSLImporter.cpp" />
     <ClCompile Include="Source\BsSLPlugin.cpp" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

+ 12 - 0
BansheeSL/BansheeSL.vcxproj.filters

@@ -30,6 +30,12 @@
     <ClInclude Include="Include\BsMMAlloc.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsSLImporter.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsSLFXCompiler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="BsLexerFX.l" />
@@ -51,5 +57,11 @@
     <ClCompile Include="Source\BSMMAlloc.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsSLImporter.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsSLFXCompiler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 234 - 0
BansheeSL/Include/BsSLFXCompiler.h

@@ -0,0 +1,234 @@
+#pragma once
+
+#include "BsSLPrerequisites.h"
+#include "BsShader.h"
+#include "BsGpuProgram.h"
+
+extern "C" {
+#include "BsASTFX.h"
+}
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Transforms a source file written in BSL FX syntax into a Shader object.
+	 */
+	class BSLFXCompiler
+	{
+		/**
+		 * @brief	Represents a block of code written in a GPU program language for
+		 *			a specific GPU program type. (i.e. non-FX code)
+		 */
+		struct CodeBlock
+		{
+			GpuProgramType type;
+			String code;
+		};
+
+	public:
+		/**
+		 * @brief	Transforms a source file written in BSL FX syntax into a Shader object.
+		 *
+		 * @note	If error occurs a nullptr will be returned and the error will be logged.
+		 */
+		static ShaderPtr compile(const String& source);
+
+	private:
+		/**
+		 * @brief	Converts the provided source into an abstract syntax tree using the
+		 *			lexer & parser for BSL FX syntax.
+		 */
+		static void parseFX(ParseState* parseState, const char* source);
+
+		/**
+		 * @brief	Retrieves non-FX code blocks (i.e. actual shader code) from the source code and
+		 *			removes them from the input so all that remains is a pure FX code. Found blocks
+		 *			are returned so they may be compiled using their appropriate compiler.
+		 */
+		static Vector<CodeBlock> parseCodeBlocks(String& source);
+
+		/**
+		 * @brief	Converts FX renderer name into an in-engine renderer identifier.
+		 */
+		static StringID parseRenderer(const String& name);
+
+		/**
+		 * @brief	Converts FX language into an in-engine shader language (e.g. hlsl, glsl) and
+		 *			a rendering API that supports the provided language.
+		 */
+		static void parseLanguage(const String& name, StringID& renderAPI, String& language);
+
+		/**
+		 * @brief	Maps FX buffer usage enum into in-engine param block usage.
+		 */
+		static GpuParamBlockUsage parseBlockUsage(BufferUsageValue usage);
+
+		/**
+		 * @brief	Maps FX filter mode enum into in-engine filter mode.
+		 */
+		static UINT32 parseFilterMode(FilterValue filter);
+
+		/**
+		 * @brief	Maps FX comparison function enum into in-engine compare function.
+		 */
+		static CompareFunction parseCompFunc(CompFuncValue compFunc);
+
+		/**
+		 * @brief	Maps FX addressing mode enum into in-engine addressing mode.
+		 */
+		static TextureAddressingMode parseAddrMode(AddrModeValue addrMode);
+
+		/**
+		 * @brief	Maps FX operation to in-engine blend factor.
+		 */
+		static BlendFactor parseBlendFactor(OpValue factor);
+
+		/**
+		 * @brief	Maps FX blend operation to in-engine blend operation.
+		 */
+		static BlendOperation parseBlendOp(BlendOpValue op);
+
+		/**
+		 * @brief	Maps FX parameter type to in-engine shader parameter.
+		 *
+		 * @param	type		Input FX parameter type.
+		 * @param	isObjType	Output parameter signaling whether the in-engine parameter is
+		 *						a data or an object type.
+		 * @param	typeId		Type ID corresponding to a value of in-game GpuParamDataType or GpuParamObjectType
+		 *						enum (depending on "isObjType").
+		 */
+		static void parseParamType(ParamType type, bool& isObjType, UINT32& typeId);
+
+		/**
+		 * @brief	Maps FX operation to in-engine stencil operation.
+		 */
+		static StencilOperation parseStencilOp(OpValue op);
+		
+		/**
+		 * @brief	Maps FX cull mode enum to in-engine cull mode.
+		 */
+		static CullingMode parseCullMode(CullModeValue cm);
+
+		/**
+		 * @brief	Maps FX fill mode enum to in-engine fill mode.
+		 */
+		static PolygonMode parseFillMode(FillModeValue fm);
+
+		/**
+		 * @brief	Populates the front facing operation portion of the depth-stencil state descriptor
+		 *			from the provided stencil-op AST node.
+		 */
+		static void parseStencilFront(DEPTH_STENCIL_STATE_DESC& desc, ASTFXNode* stencilOpNode);
+
+		/**
+		 * @brief	Populates the back backing operation portion of the depth-stencil state descriptor
+		 *			from the provided stencil-op AST node.
+		 */
+		static void parseStencilBack(DEPTH_STENCIL_STATE_DESC& desc, ASTFXNode* stencilOpNode);
+
+		/**
+		 * @brief	Populates the addressing mode portion of the sampler state descriptor for U/V/W axes from
+		 *			the provided addressing mode AST node.
+		 */
+		static void parseAddrMode(SAMPLER_STATE_DESC& desc, ASTFXNode* addrModeNode);
+
+		/**
+		 * @brief	Populates the color (RGB) portion of the blend state descriptor from the provided
+		 *			blend definition AST node.
+		 */
+		static void parseColorBlendDef(RENDER_TARGET_BLEND_STATE_DESC& desc, ASTFXNode* blendDefNode);
+
+		/**
+		 * @brief	Populates the alpha portion of the blend state descriptor from the provided
+		 *			blend definition AST node.
+		 */
+		static void parseAlphaBlendDef(RENDER_TARGET_BLEND_STATE_DESC& desc, ASTFXNode* blendDefNode);
+
+		/**
+		 * @brief	Populates blend state descriptor for a single render target from the provided
+		 *			AST node. Which target gets updated depends on the index set in the AST node.
+		 */
+		static void parseRenderTargetBlendState(BLEND_STATE_DESC& desc, ASTFXNode* targetNode);
+
+		/**
+		 * @brief	Parses the blend state AST node and outputs a blend state object, or an invalid
+		 *			handle in case AST node is empty. 
+		 */
+		static HBlendState parseBlendState(ASTFXNode* passNode);
+
+		/**
+		 * @brief	Parses the rasterizer state AST node and outputs a rasterizer state object, or an invalid
+		 *			handle in case AST node is empty. 
+		 */
+		static HRasterizerState parseRasterizerState(ASTFXNode* passNode);
+
+		/**
+		 * @brief	Parses the depth-stencil state AST node and outputs a depth-stencil state object, or an invalid
+		 *			handle in case AST node is empty. 
+		 */
+		static HDepthStencilState parseDepthStencilState(ASTFXNode* passNode);
+
+		/**
+		 * @brief	Parses the sampler state AST node and outputs a sampler state object, or an invalid
+		 *			handle in case AST node is empty. 
+		 */
+		static HSamplerState parseSamplerState(ASTFXNode* samplerStateNode);
+
+		/**
+		 * @brief	Parses the pass AST node and generates a single pass object. Returns null
+		 *			if no pass can be parsed. This method will generate any child state objects and
+		 *			compile any child GPU programs.
+		 *
+		 * @param	passNode		Node to parse.
+		 * @param	codeBlocks		GPU program source code retrieved from "parseCodeBlocks".
+		 * @param	includes		Files paths relative to the current executable that will be injected
+		 *							into GPU program code before compilation.
+		 * @param	renderAPI		API to use for compiling the GPU programs.
+		 * @param	language		GPU program language to use for parsing the provided code blocks.
+		 */
+		static PassPtr parsePass(ASTFXNode* passNode, const Vector<CodeBlock>& codeBlocks, const Vector<String>& includes, const StringID& renderAPI, const String& language);
+
+		/**
+		 * @brief	Parses the technique AST node and generates a single technique object. Returns null
+		 *			if no technique can be parsed.
+		 *
+		 * @param	techniqueNode	Node to parse.
+		 * @param	codeBlocks		GPU program source code retrieved from "parseCodeBlocks".
+		 */
+		static TechniquePtr parseTechnique(ASTFXNode* techniqueNode, const Vector<CodeBlock>& codeBlocks);
+
+		/**
+		 * @brief	Parses the parameters AST node and populates the shader descriptor with information
+		 *			about GPU program parameters and their default values.
+		 */
+		static void parseParameters(SHADER_DESC& desc, ASTFXNode* parametersNode);
+
+		/**
+		 * @brief	Parses the blocks AST node and populates the shader descriptor with information
+		 *			about GPU program parameter blocks.
+		 */
+		static void parseBlocks(SHADER_DESC& desc, ASTFXNode* blocksNode);
+
+		/**
+		 * @brief	Parses the AST node hierarchy and generates a shader object.
+		 *
+		 * @param	name		Optional name for the shader.
+		 * @param	rootNode	Root node of the AST hierarchy retrieved from "parseFX".
+		 * @param	codeBlocks	GPU program source code retrieved from "parseCodeBlocks".
+		 *
+		 * @return	A generated shader object, or a nullptr if shader was invalid.
+		 */
+		static ShaderPtr parseShader(const String& name, ASTFXNode* rootNode, const Vector<CodeBlock>& codeBlocks);
+
+		/**
+		 * @brief	Converts a null-terminated string into a standard string, and eliminates quotes that are assumed
+		 *			to be at the first and last index.
+		 */
+		static String removeQuotes(const char* input);
+
+		/**
+		 * @brief	Retrieves a GPU program profile to use with the specified API and GPU program type.
+		 */
+		static GpuProgramProfile getProfile(const StringID& renderAPI, GpuProgramType type);
+	};
+}

+ 27 - 0
BansheeSL/Include/BsSLImporter.h

@@ -0,0 +1,27 @@
+#pragma once
+
+#include "BsSLPrerequisites.h"
+#include "BsSpecificImporter.h"
+
+namespace BansheeEngine
+{
+	/**
+	* @brief	Importer using for importing a shader written using the BSL syntax.
+	* 			Shader files are plain text files ending with ".bsl" extension.
+	*/
+	class BS_SL_EXPORT SLImporter : public SpecificImporter
+	{
+	public:
+		SLImporter();
+		virtual ~SLImporter();
+
+		/** @copydoc SpecificImporter::isExtensionSupported */
+		virtual bool isExtensionSupported(const WString& ext) const;
+
+		/** @copydoc SpecificImporter::isMagicNumberSupported */
+		virtual bool isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const;
+
+		/** @copydoc SpecificImporter::import */
+		virtual ResourcePtr import(const Path& filePath, ConstImportOptionsPtr importOptions);
+	};
+}

+ 1139 - 0
BansheeSL/Source/BsSLFXCompiler.cpp

@@ -0,0 +1,1139 @@
+#include "BsSLFXCompiler.h"
+#include "BsGpuProgram.h"
+#include <regex>
+#include "BsShader.h"
+#include "BsTechnique.h"
+#include "BsPass.h"
+#include "BsSamplerState.h"
+#include "BsRasterizerState.h"
+#include "BsDepthStencilState.h"
+#include "BsBlendState.h"
+#include "BsRenderAPI.h"
+#include "BsDebug.h"
+
+extern "C" {
+#include "BsMMAlloc.h"
+#include "BsParserFX.h"
+#include "BsLexerFX.h"
+}
+
+namespace BansheeEngine
+{
+	// Print out the FX AST, only for debug purposes
+	void SLFXDebugPrint(ASTFXNode* node, String indent)
+	{
+		LOGWRN(indent + "NODE " + toString(node->type));
+
+		for (int i = 0; i < node->options->count; i++)
+		{
+			OptionDataType odt = OPTION_LOOKUP[(int)node->options->entries[i].type].dataType;
+			if (odt == ODT_Complex)
+			{
+				LOGWRN(indent + toString(i) + ". " + toString(node->options->entries[i].type));
+				SLFXDebugPrint(node->options->entries[i].value.nodePtr, indent + "\t");
+				continue;
+			}
+
+			String value;
+			switch (odt)
+			{
+			case ODT_Bool:
+				value = toString(node->options->entries[i].value.intValue != 0);
+				break;
+			case ODT_Int:
+				value = toString(node->options->entries[i].value.intValue);
+				break;
+			case ODT_Float:
+				value = toString(node->options->entries[i].value.floatValue);
+				break;
+			case ODT_String:
+				value = node->options->entries[i].value.strValue;
+				break;
+			case ODT_Matrix:
+			{
+				Matrix4 mat4 = *(Matrix4*)(node->options->entries[i].value.matrixValue);
+				value = toString(mat4);
+			}
+				break;
+			}
+
+			LOGWRN(indent + toString(i) + ". " + toString(node->options->entries[i].type) + " = " + value);
+		}
+	}
+
+	ShaderPtr BSLFXCompiler::compile(const String& source)
+	{
+		String parsedSource = source;
+
+		ParseState* parseState = parseStateCreate();
+		Vector<CodeBlock> codeBlocks = parseCodeBlocks(parsedSource);
+		parseFX(parseState, parsedSource.c_str());
+
+		ShaderPtr output;
+		if (parseState->hasError > 0)
+		{
+			LOGERR("Error while parsing a Shader: " + String(parseState->errorMessage) + ". Location: " +
+				toString(parseState->errorLine) + " (" + toString(parseState->errorColumn) + ")");
+		}
+		else
+		{
+			output = parseShader("Shader", parseState->rootNode, codeBlocks);
+
+			// Only enable for debug purposes
+			//SLFXDebugPrint(parseState->rootNode, "");
+		}
+
+		parseStateDelete(parseState);
+
+		return output;
+	}
+
+	void BSLFXCompiler::parseFX(ParseState* parseState, const char* source)
+	{
+		yyscan_t scanner;
+		YY_BUFFER_STATE state;
+
+		if (yylex_init_extra(parseState, &scanner))
+			return;
+
+		state = yy_scan_string(source, scanner);
+
+		if (yyparse(parseState, scanner))
+			return;
+
+		yy_delete_buffer(state, scanner);
+		yylex_destroy(scanner);
+	}
+
+	Vector<BSLFXCompiler::CodeBlock> BSLFXCompiler::parseCodeBlocks(String& source)
+	{
+		std::regex pattern = std::regex(R"((Vertex|Fragment|Geometry|Hull|Domain|Compute)\s*=\s*\{)");
+		std::smatch matches;
+
+		Vector<CodeBlock> codeBlocks;
+
+		UINT32 offset = 0;
+		while (std::regex_search(source.cbegin() + offset, source.cend(), matches, pattern))
+		{
+			UINT32 idx = (UINT32)codeBlocks.size();
+
+			codeBlocks.push_back(CodeBlock());
+			CodeBlock& newBlock = codeBlocks.back();
+
+			std::string type = matches[1].str();
+			if (type == "Vertex")
+				newBlock.type = GPT_VERTEX_PROGRAM;
+			else if (type == "Fragment")
+				newBlock.type = GPT_FRAGMENT_PROGRAM;
+			else if (type == "Geometry")
+				newBlock.type = GPT_GEOMETRY_PROGRAM;
+			else if (type == "Hull")
+				newBlock.type = GPT_HULL_PROGRAM;
+			else if (type == "Domain")
+				newBlock.type = GPT_DOMAIN_PROGRAM;
+			else if (type == "Compute")
+				newBlock.type = GPT_COMPUTE_PROGRAM;
+
+			offset += (UINT32)matches.position() + (UINT32)matches.length();
+
+			StringStream newDataStream;
+			newDataStream << "Index = " + toString(idx) + ";";
+
+			StringStream codeStream;
+			UINT32 ummatchedBrackets = 1;
+			for (UINT32 i = offset; i < (UINT32)source.length(); i++)
+			{
+				if (source[i] == '{')
+					ummatchedBrackets++;
+
+				if (source[i] == '}')
+					ummatchedBrackets--;
+
+				if (ummatchedBrackets == 0)
+					break;
+
+				if (source[i] == '\r' || source[i] == '\n')
+					newDataStream << source[i];
+
+				codeStream << source[i];
+			}
+
+			newBlock.code = codeStream.str();
+
+			source.erase(source.cbegin() + offset, source.cbegin() + offset + (UINT32)newBlock.code.size());
+
+			String newData = newDataStream.str();
+			source.insert(offset, newData);
+			offset += (UINT32)newData.size();
+		}
+
+		return codeBlocks;
+	}
+
+	StringID BSLFXCompiler::parseRenderer(const String& name)
+	{
+		if (name == "Any")
+			return RendererAny;
+		else if (name == "Default")
+			return RendererDefault;
+
+		return RendererAny;
+	}
+
+	void BSLFXCompiler::parseLanguage(const String& name, StringID& renderAPI, String& language)
+	{
+		if (name == "HLSL" || name == "HLSL11")
+		{
+			renderAPI = RenderAPIDX11;
+			language = "hlsl";
+		}
+		else if (name == "HLSL9")
+		{
+			renderAPI = RenderAPIDX9;
+			language = "hlsl";
+		}
+		else if (name == "GLSL")
+		{
+			renderAPI = RenderAPIOpenGL;
+			language = "glsl";
+		}
+		else
+		{
+			renderAPI = RenderAPIAny;
+			language = "";
+		}
+	}
+
+	GpuParamBlockUsage BSLFXCompiler::parseBlockUsage(BufferUsageValue usage)
+	{
+		if (usage == BUV_Dynamic)
+			return GPBU_DYNAMIC;
+
+		return GPBU_STATIC;
+	}
+
+	UINT32 BSLFXCompiler::parseFilterMode(FilterValue filter)
+	{
+		switch (filter)
+		{
+		case FV_Point:
+			return FO_POINT;
+		case FV_Linear:
+			return FO_LINEAR;
+		case FV_Anisotropic:
+			return FO_ANISOTROPIC;
+		case FV_PointCmp:
+			return FO_POINT | FO_USE_COMPARISON;
+		case FV_LinearCmp:
+			return FO_LINEAR | FO_USE_COMPARISON;
+		case FV_AnisotropicCmp:
+			return FO_ANISOTROPIC | FO_USE_COMPARISON;
+		}
+
+		return FO_NONE;
+	}
+
+	CompareFunction BSLFXCompiler::parseCompFunc(CompFuncValue compFunc)
+	{
+		switch (compFunc)
+		{
+		case CFV_Pass:
+			return CMPF_ALWAYS_PASS;
+		case CFV_Fail:
+			return CMPF_ALWAYS_FAIL;
+		case CFV_LT:
+			return CMPF_LESS;
+		case CFV_LTE:
+			return CMPF_LESS_EQUAL;
+		case CFV_EQ:
+			return CMPF_EQUAL;
+		case CFV_NEQ:
+			return CMPF_NOT_EQUAL;
+		case CFV_GT:
+			return CMPF_GREATER;
+		case CFV_GTE:
+			return CMPF_GREATER_EQUAL;
+		}
+
+		return CMPF_ALWAYS_PASS;
+	}
+
+	TextureAddressingMode BSLFXCompiler::parseAddrMode(AddrModeValue addrMode)
+	{
+		switch (addrMode)
+		{
+		case AMV_Wrap:
+			return TAM_WRAP;
+		case AMV_Mirror:
+			return TAM_MIRROR;
+		case AMV_Clamp:
+			return TAM_CLAMP;
+		case AMV_Border:
+			return TAM_BORDER;
+		}
+
+		return TAM_WRAP;
+	}
+
+	BlendFactor BSLFXCompiler::parseBlendFactor(OpValue factor)
+	{
+		switch (factor)
+		{
+		case OV_One:
+			return BF_ONE;
+		case OV_Zero:
+			return BF_ZERO;
+		case OV_DestColor:
+			return BF_DEST_COLOR;
+		case OV_SrcColor:
+			return BF_SOURCE_COLOR;
+		case OV_InvDestColor:
+			return BF_INV_DEST_COLOR;
+		case OV_InvSrcColor:
+			return BF_INV_SOURCE_COLOR;
+		case OV_DestAlpha:
+			return BF_DEST_ALPHA;
+		case OV_SrcAlpha:
+			return BF_SOURCE_ALPHA;
+		case OV_InvDestAlpha:
+			return BF_INV_DEST_ALPHA;
+		case OV_InvSrcAlpha:
+			return BF_INV_SOURCE_ALPHA;
+		}
+
+		return BF_ONE;
+	}
+
+	BlendOperation BSLFXCompiler::parseBlendOp(BlendOpValue op)
+	{
+		switch (op)
+		{
+		case BOV_Add:
+			return BO_ADD;
+		case BOV_Max:
+			return BO_MAX;
+		case BOV_Min:
+			return BO_MIN;
+		case BOV_Subtract:
+			return BO_SUBTRACT;
+		case BOV_RevSubtract:
+			return BO_REVERSE_SUBTRACT;
+		}
+
+		return BO_ADD;
+	}
+
+	void BSLFXCompiler::parseParamType(ParamType type, bool& isObjType, UINT32& typeId)
+	{
+		struct ParamData
+		{
+			UINT32 type;
+			bool isObjType;
+		};
+
+		static bool initialized = false;
+		static ParamData lookup[PT_Count];
+
+		if (!initialized)
+		{
+			lookup[PT_Float] = { { GPDT_FLOAT1 }, false };
+			lookup[PT_Float2] = { { GPDT_FLOAT2 }, false };
+			lookup[PT_Float3] = { { GPDT_FLOAT3 }, false };
+			lookup[PT_Float4] = { { GPDT_FLOAT4 }, false };
+
+			lookup[PT_Mat2x2] = { { GPDT_MATRIX_2X2 }, false };
+			lookup[PT_Mat2x3] = { { GPDT_MATRIX_2X3 }, false };
+			lookup[PT_Mat2x4] = { { GPDT_MATRIX_2X4 }, false };
+
+			lookup[PT_Mat3x2] = { { GPDT_MATRIX_3X2 }, false };
+			lookup[PT_Mat3x3] = { { GPDT_MATRIX_3X3 }, false };
+			lookup[PT_Mat3x4] = { { GPDT_MATRIX_3X4 }, false };
+
+			lookup[PT_Mat4x2] = { { GPDT_MATRIX_4X2 }, false };
+			lookup[PT_Mat4x3] = { { GPDT_MATRIX_4X3 }, false };
+			lookup[PT_Mat4x4] = { { GPDT_MATRIX_4X4 }, false };
+
+			lookup[PT_Sampler1D] = { { GPOT_SAMPLER1D }, true };
+			lookup[PT_Sampler2D] = { { GPOT_SAMPLER2D }, true };
+			lookup[PT_Sampler3D] = { { GPOT_SAMPLER3D }, true };
+			lookup[PT_SamplerCUBE] = { { GPOT_SAMPLERCUBE }, true };
+			lookup[PT_Sampler2DMS] = { { GPOT_SAMPLER2DMS }, true };
+
+			lookup[PT_Texture1D] = { { GPOT_TEXTURE1D }, true };
+			lookup[PT_Texture2D] = { { GPOT_TEXTURE2D }, true };
+			lookup[PT_Texture3D] = { { GPOT_TEXTURE3D }, true };
+			lookup[PT_TextureCUBE] = { { GPOT_TEXTURECUBE }, true };
+			lookup[PT_Texture2DMS] = { { GPOT_TEXTURE2DMS }, true };
+
+			lookup[PT_ByteBuffer] = { { GPOT_BYTE_BUFFER }, true };
+			lookup[PT_StructBuffer] = { { GPOT_STRUCTURED_BUFFER }, true };
+			lookup[PT_TypedBufferRW] = { { GPOT_RWTYPED_BUFFER }, true };
+			lookup[PT_ByteBufferRW] = { { GPOT_RWBYTE_BUFFER }, true };
+			lookup[PT_StructBufferRW] = { { GPOT_RWSTRUCTURED_BUFFER }, true };
+			lookup[PT_AppendBuffer] = { { GPOT_RWAPPEND_BUFFER }, true };
+			lookup[PT_ConsumeBuffer] = { { GPOT_RWCONSUME_BUFFER }, true };
+
+			initialized = true;
+		}
+
+		isObjType = lookup[type].isObjType;
+		typeId = lookup[type].type;
+	}
+
+	StencilOperation BSLFXCompiler::parseStencilOp(OpValue op)
+	{
+		switch (op)
+		{
+		case OV_Keep:
+			return SOP_KEEP;
+		case OV_Zero:
+			return SOP_ZERO;
+		case OV_Replace:
+			return SOP_REPLACE;
+		case OV_Incr:
+			return SOP_INCREMENT;
+		case OV_Decr:
+			return SOP_DECREMENT;
+		case OV_IncrWrap:
+			return SOP_INCREMENT_WRAP;
+		case OV_DecrWrap:
+			return SOP_DECREMENT_WRAP;
+		case OV_Invert:
+			return SOP_INVERT;
+		}
+
+		return SOP_KEEP;
+	}
+
+	CullingMode BSLFXCompiler::parseCullMode(CullModeValue cm)
+	{
+		switch (cm)
+		{
+		case CMV_None:
+			return CULL_NONE;
+		case CMV_CW:
+			return CULL_CLOCKWISE;
+		case CMV_CCW:
+			return CULL_COUNTERCLOCKWISE;
+		}
+
+		return CULL_COUNTERCLOCKWISE;
+	}
+
+	PolygonMode BSLFXCompiler::parseFillMode(FillModeValue fm)
+	{
+		if (fm == FMV_Wire)
+			return PM_WIREFRAME;
+
+		return PM_SOLID;
+	}
+
+	void BSLFXCompiler::parseStencilFront(DEPTH_STENCIL_STATE_DESC& desc, ASTFXNode* stencilOpNode)
+	{
+		if (stencilOpNode == nullptr || stencilOpNode->type != NT_StencilOp)
+			return;
+
+		for (int i = 0; i < stencilOpNode->options->count; i++)
+		{
+			NodeOption* option = &stencilOpNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_Fail:
+				desc.frontStencilFailOp = parseStencilOp((OpValue)option->value.intValue);
+				break;
+			case OT_ZFail:
+				desc.frontStencilZFailOp = parseStencilOp((OpValue)option->value.intValue);
+				break;
+			case OT_PassOp:
+				desc.frontStencilPassOp = parseStencilOp((OpValue)option->value.intValue);
+				break;
+			case OT_CompareFunc:
+				desc.frontStencilComparisonFunc = parseCompFunc((CompFuncValue)option->value.intValue);
+				break;
+			}
+		}
+	}
+
+	void BSLFXCompiler::parseStencilBack(DEPTH_STENCIL_STATE_DESC& desc, ASTFXNode* stencilOpNode)
+	{
+		if (stencilOpNode == nullptr || stencilOpNode->type != NT_StencilOp)
+			return;
+
+		for (int i = 0; i < stencilOpNode->options->count; i++)
+		{
+			NodeOption* option = &stencilOpNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_Fail:
+				desc.backStencilFailOp = parseStencilOp((OpValue)option->value.intValue);
+				break;
+			case OT_ZFail:
+				desc.backStencilZFailOp = parseStencilOp((OpValue)option->value.intValue);
+				break;
+			case OT_PassOp:
+				desc.backStencilPassOp = parseStencilOp((OpValue)option->value.intValue);
+				break;
+			case OT_CompareFunc:
+				desc.backStencilComparisonFunc = parseCompFunc((CompFuncValue)option->value.intValue);
+				break;
+			}
+		}
+	}
+
+	void BSLFXCompiler::parseAddrMode(SAMPLER_STATE_DESC& desc, ASTFXNode* addrModeNode)
+	{
+		if (addrModeNode == nullptr || addrModeNode->type != NT_AddrMode)
+			return;
+
+		for (int i = 0; i < addrModeNode->options->count; i++)
+		{
+			NodeOption* option = &addrModeNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_U:
+				desc.addressMode.u = parseAddrMode((AddrModeValue)option->value.intValue);
+				break;
+			case OT_V:
+				desc.addressMode.v = parseAddrMode((AddrModeValue)option->value.intValue);
+				break;
+			case OT_W:
+				desc.addressMode.w = parseAddrMode((AddrModeValue)option->value.intValue);
+				break;
+			}
+		}
+	}
+
+	void BSLFXCompiler::parseColorBlendDef(RENDER_TARGET_BLEND_STATE_DESC& desc, ASTFXNode* blendDefNode)
+	{
+		if (blendDefNode == nullptr || blendDefNode->type != NT_BlendDef)
+			return;
+
+		for (int i = 0; i < blendDefNode->options->count; i++)
+		{
+			NodeOption* option = &blendDefNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_Source:
+				desc.srcBlend = parseBlendFactor((OpValue)option->value.intValue);
+				break;
+			case OT_Dest:
+				desc.dstBlend = parseBlendFactor((OpValue)option->value.intValue);
+				break;
+			case OT_Op:
+				desc.blendOp = parseBlendOp((BlendOpValue)option->value.intValue);
+				break;
+			}
+		}
+	}
+
+	void BSLFXCompiler::parseAlphaBlendDef(RENDER_TARGET_BLEND_STATE_DESC& desc, ASTFXNode* blendDefNode)
+	{
+		if (blendDefNode == nullptr || blendDefNode->type != NT_BlendDef)
+			return;
+
+		for (int i = 0; i < blendDefNode->options->count; i++)
+		{
+			NodeOption* option = &blendDefNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_Source:
+				desc.srcBlendAlpha = parseBlendFactor((OpValue)option->value.intValue);
+				break;
+			case OT_Dest:
+				desc.dstBlendAlpha = parseBlendFactor((OpValue)option->value.intValue);
+				break;
+			case OT_Op:
+				desc.blendOpAlpha = parseBlendOp((BlendOpValue)option->value.intValue);
+				break;
+			}
+		}
+	}
+
+	void BSLFXCompiler::parseRenderTargetBlendState(BLEND_STATE_DESC& desc, ASTFXNode* targetNode)
+	{
+		if (targetNode == nullptr || targetNode->type != NT_Target)
+			return;
+
+		UINT32 index = 0;
+
+		for (int i = 0; i < targetNode->options->count; i++)
+		{
+			NodeOption* option = &targetNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_Index:
+				index = option->value.intValue;
+				break;
+			}
+		}
+
+		if (index >= BS_MAX_MULTIPLE_RENDER_TARGETS)
+			return;
+
+		RENDER_TARGET_BLEND_STATE_DESC& rtDesc = desc.renderTargetDesc[index];
+		for (int i = 0; i < targetNode->options->count; i++)
+		{
+			NodeOption* option = &targetNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_Blend:
+				rtDesc.blendEnable = option->value.intValue > 0;
+				break;
+			case OT_Color:
+				parseColorBlendDef(rtDesc, option->value.nodePtr);
+				break;
+			case OT_Alpha:
+				parseAlphaBlendDef(rtDesc, option->value.nodePtr);
+				break;
+			case OT_WriteMask:
+				rtDesc.renderTargetWriteMask = option->value.intValue;
+				break;
+			}
+		}
+	}
+
+	HBlendState BSLFXCompiler::parseBlendState(ASTFXNode* passNode)
+	{
+		if (passNode == nullptr || passNode->type != NT_Pass)
+			return HBlendState();
+
+		BLEND_STATE_DESC desc;
+		bool default = true;
+
+		for (int i = 0; i < passNode->options->count; i++)
+		{
+			NodeOption* option = &passNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_AlphaToCoverage:
+				desc.alphaToCoverageEnable = option->value.intValue > 0;
+				default = false;
+				break;
+			case OT_IndependantBlend:
+				desc.independantBlendEnable = option->value.intValue > 0;
+				default = false;
+				break;
+			case OT_Target:
+				parseRenderTargetBlendState(desc, option->value.nodePtr);
+				default = false;
+				break;
+			}
+		}
+
+		if (default)
+			return HBlendState();
+
+		return BlendState::create(desc);
+	}
+
+	HRasterizerState BSLFXCompiler::parseRasterizerState(ASTFXNode* passNode)
+	{
+		if (passNode == nullptr || passNode->type != NT_Pass)
+			return HRasterizerState();
+
+		RASTERIZER_STATE_DESC desc;
+		bool default = true;
+
+		for (int i = 0; i < passNode->options->count; i++)
+		{
+			NodeOption* option = &passNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_FillMode:
+				desc.polygonMode = parseFillMode((FillModeValue)option->value.intValue);
+				default = false;
+				break;
+			case OT_CullMode:
+				desc.cullMode = parseCullMode((CullModeValue)option->value.intValue);
+				default = false;
+				break;
+			case OT_DepthBias:
+				desc.depthBias = option->value.floatValue;
+				default = false;
+				break;
+			case OT_SDepthBias:
+				desc.slopeScaledDepthBias = option->value.floatValue;
+				default = false;
+				break;
+			case OT_DepthClip:
+				desc.depthClipEnable = option->value.intValue > 0;
+				default = false;
+				break;
+			case OT_Scissor:
+				desc.scissorEnable = option->value.intValue > 0;
+				default = false;
+				break;
+			case OT_Multisample:
+				desc.multisampleEnable = option->value.intValue > 0;
+				default = false;
+				break;
+			case OT_AALine:
+				desc.antialiasedLineEnable = option->value.intValue > 0;
+				default = false;
+				break;
+			}
+		}
+
+		if (default)
+			return HRasterizerState();
+
+		return RasterizerState::create(desc);
+	}
+
+	HDepthStencilState BSLFXCompiler::parseDepthStencilState(ASTFXNode* passNode)
+	{
+		if (passNode == nullptr || passNode->type != NT_Pass)
+			return HDepthStencilState();
+
+		DEPTH_STENCIL_STATE_DESC desc;
+		bool default = true;
+
+		for (int i = 0; i < passNode->options->count; i++)
+		{
+			NodeOption* option = &passNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_DepthRead:
+				desc.depthReadEnable = option->value.intValue > 0;
+				default = false;
+				break;
+			case OT_DepthWrite:
+				desc.depthWriteEnable = option->value.intValue > 0;
+				default = false;
+				break;
+			case OT_CompareFunc:
+				desc.depthComparisonFunc = parseCompFunc((CompFuncValue)option->value.intValue);
+				default = false;
+				break;
+			case OT_Stencil:
+				desc.stencilEnable = option->value.intValue > 0;
+				default = false;
+				break;
+			case OT_StencilReadMask:
+				desc.stencilReadMask = (UINT8)option->value.intValue;
+				default = false;
+				break;
+			case OT_StencilWriteMask:
+				desc.stencilWriteMask = (UINT8)option->value.intValue;
+				default = false;
+				break;
+			case OT_StencilOpFront:
+				parseStencilFront(desc, option->value.nodePtr);
+				default = false;
+				break;
+			case OT_StencilOpBack:
+				parseStencilBack(desc, option->value.nodePtr);
+				default = false;
+				break;
+			}
+		}
+
+		if (default)
+			return HDepthStencilState();
+
+		return DepthStencilState::create(desc);
+	}
+
+	HSamplerState BSLFXCompiler::parseSamplerState(ASTFXNode* samplerStateNode)
+	{
+		if (samplerStateNode == nullptr || samplerStateNode->type != NT_SamplerState)
+			return HSamplerState();
+
+		SAMPLER_STATE_DESC desc;
+		bool default = true;
+
+		for (int i = 0; i < samplerStateNode->options->count; i++)
+		{
+			NodeOption* option = &samplerStateNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_AddrMode:
+				parseAddrMode(desc, option->value.nodePtr);
+				default = false;
+				break;
+			case OT_MinFilter:
+				desc.minFilter = (FilterOptions)parseFilterMode((FilterValue)option->value.intValue);
+				default = false;
+				break;
+			case OT_MagFilter:
+				desc.magFilter = (FilterOptions)parseFilterMode((FilterValue)option->value.intValue);
+				default = false;
+				break;
+			case OT_MipFilter:
+				desc.mipFilter = (FilterOptions)parseFilterMode((FilterValue)option->value.intValue);
+				default = false;
+				break;
+			case OT_MaxAniso:
+				desc.maxAniso = option->value.intValue;
+				default = false;
+				break;
+			case OT_MipBias:
+				desc.mipmapBias = option->value.floatValue;
+				default = false;
+				break;
+			case OT_MipMin:
+				desc.mipMin = option->value.floatValue;
+				default = false;
+				break;
+			case OT_MipMax:
+				desc.mipMax = option->value.floatValue;
+				default = false;
+				break;
+			case OT_BorderColor:
+				desc.borderColor = Color(option->value.matrixValue[0], option->value.matrixValue[1],
+					option->value.matrixValue[2], option->value.matrixValue[3]);
+				default = false;
+				break;
+			case OT_CompareFunc:
+				desc.comparisonFunc = parseCompFunc((CompFuncValue)option->value.intValue);
+				default = false;
+				break;
+			}
+		}
+
+		if (default)
+			return HSamplerState();
+
+		return SamplerState::create(desc);
+	}
+
+	PassPtr BSLFXCompiler::parsePass(ASTFXNode* passNode, const Vector<CodeBlock>& codeBlocks, const Vector<String>& includes, const StringID& renderAPI, const String& language)
+	{
+		if (passNode == nullptr || passNode->type != NT_Pass)
+			return nullptr;
+
+		PASS_DESC passDesc;
+
+		passDesc.blendState = parseBlendState(passNode);
+		passDesc.rasterizerState = parseRasterizerState(passNode);
+		passDesc.depthStencilState = parseDepthStencilState(passNode);
+
+		for (int i = 0; i < passNode->options->count; i++)
+		{
+			NodeOption* option = &passNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_StencilRef:
+				passDesc.stencilRefValue = option->value.intValue;
+				break;
+			case OT_Code:
+			{
+				ASTFXNode* codeNode = option->value.nodePtr;
+
+				if (codeNode != nullptr && codeNode->type == NT_Code)
+				{
+					UINT32 index = (UINT32)-1;
+					for (int j = 0; j < codeNode->options->count; j++)
+					{
+						if (codeNode->options->entries[j].type == OT_Index)
+							index = codeNode->options->entries[j].value.intValue;
+					}
+
+					if (index != (UINT32)-1 && index < (UINT32)codeBlocks.size())
+					{
+						const CodeBlock& codeBlock = codeBlocks[index];
+						switch (codeBlock.type)
+						{
+						case GPT_VERTEX_PROGRAM:
+							passDesc.vertexProgram = GpuProgram::create(codeBlock.code, "main", language,
+								codeBlock.type, getProfile(renderAPI, codeBlock.type));
+							break;
+						case GPT_FRAGMENT_PROGRAM:
+							passDesc.fragmentProgram = GpuProgram::create(codeBlock.code, "main", language,
+								codeBlock.type, getProfile(renderAPI, codeBlock.type));
+							break;
+						case GPT_GEOMETRY_PROGRAM:
+							passDesc.geometryProgram = GpuProgram::create(codeBlock.code, "main", language,
+								codeBlock.type, getProfile(renderAPI, codeBlock.type));
+							break;
+						case GPT_HULL_PROGRAM:
+							passDesc.hullProgram = GpuProgram::create(codeBlock.code, "main", language,
+								codeBlock.type, getProfile(renderAPI, codeBlock.type));
+							break;
+						case GPT_DOMAIN_PROGRAM:
+							passDesc.domainProgram = GpuProgram::create(codeBlock.code, "main", language,
+								codeBlock.type, getProfile(renderAPI, codeBlock.type));
+							break;
+						case GPT_COMPUTE_PROGRAM:
+							passDesc.computeProgram = GpuProgram::create(codeBlock.code, "main", language,
+								codeBlock.type, getProfile(renderAPI, codeBlock.type));
+							break;
+						}
+					}
+				}
+			}
+				break;
+			}
+		}
+
+		return Pass::create(passDesc);
+	}
+
+	TechniquePtr BSLFXCompiler::parseTechnique(ASTFXNode* techniqueNode, const Vector<CodeBlock>& codeBlocks)
+	{
+		if (techniqueNode == nullptr || techniqueNode->type != NT_Technique)
+			return nullptr;
+
+		Vector<ASTFXNode*> passNodes;
+		StringID renderer = RendererAny;
+		StringID renderAPI = RenderAPIAny;
+		String language;
+		Vector<String> includes; // TODO - Need to figure out what to do with these
+
+		for (int i = 0; i < techniqueNode->options->count; i++)
+		{
+			NodeOption* option = &techniqueNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_Pass:
+				passNodes.push_back(option->value.nodePtr);
+				break;
+			case OT_Include:
+			{
+				String include = removeQuotes(option->value.strValue);
+				includes.push_back(include);
+			}
+				break;
+			case OT_Renderer:
+				renderer = parseRenderer(removeQuotes(option->value.strValue));
+				break;
+			case OT_Language:
+				parseLanguage(removeQuotes(option->value.strValue), renderAPI, language);
+				break;
+			}
+		}
+
+		Vector<PassPtr> passes;
+		for (auto& passNode : passNodes)
+		{
+			PassPtr pass = parsePass(passNode, codeBlocks, includes, renderAPI, language);
+			if (pass != nullptr)
+				passes.push_back(pass);
+		}
+
+		if (passes.size() > 0)
+			return Technique::create(renderAPI, renderer, passes);
+
+		return nullptr;
+	}
+
+	void BSLFXCompiler::parseParameters(SHADER_DESC& desc, ASTFXNode* parametersNode)
+	{
+		if (parametersNode == nullptr || parametersNode->type != NT_Parameters)
+			return;
+
+		for (int i = 0; i < parametersNode->options->count; i++)
+		{
+			NodeOption* option = &parametersNode->options->entries[i];
+
+			if (option->type != OT_Parameter)
+				continue;
+
+			ASTFXNode* parameter = option->value.nodePtr;
+
+			String name;
+			String alias;
+
+			float defaultValue[16];
+			UINT32 typeId;
+			bool isObjType = false;
+			StringID semantic;
+
+			for (int j = 0; j < parameter->options->count; j++)
+			{
+				NodeOption* paramOption = &parameter->options->entries[j];
+
+				switch (paramOption->type)
+				{
+				case OT_Identifier:
+					name = paramOption->value.strValue;
+					break;
+				case OT_Alias:
+					alias = removeQuotes(paramOption->value.strValue);
+					break;
+				case OT_ParamValue:
+					memcpy(defaultValue, paramOption->value.matrixValue, sizeof(defaultValue));
+					break;
+				case OT_ParamType:
+					parseParamType((ParamType)paramOption->value.intValue, isObjType, typeId);
+					break;
+				case OT_Auto:
+					semantic = removeQuotes(paramOption->value.strValue);
+					break;
+				case OT_SamplerState:
+					HSamplerState samplerState = parseSamplerState(paramOption->value.nodePtr);
+					// TODO - How to deal with sampler-state default value?
+					break;
+				}
+			}
+
+			if (name.empty())
+				continue;
+
+			if (isObjType)
+				desc.addParameter(name, name, (GpuParamObjectType)typeId, semantic);
+			else
+				desc.addParameter(name, name, (GpuParamDataType)typeId, semantic); // TODO - Add default value
+
+			if (!alias.empty())
+			{
+				if (isObjType)
+					desc.addParameter(name, alias, (GpuParamObjectType)typeId, semantic);
+				else
+					desc.addParameter(name, alias, (GpuParamDataType)typeId, semantic); // TODO - Add default value
+			}
+		}
+	}
+
+	void BSLFXCompiler::parseBlocks(SHADER_DESC& desc, ASTFXNode* blocksNode)
+	{
+		if (blocksNode == nullptr || blocksNode->type != NT_Blocks)
+			return;
+
+		for (int i = 0; i < blocksNode->options->count; i++)
+		{
+			NodeOption* option = &blocksNode->options->entries[i];
+
+			if (option->type != OT_Block)
+				continue;
+
+			ASTFXNode* parameter = option->value.nodePtr;
+
+			String name;
+			bool shared;
+			GpuParamBlockUsage usage;
+			StringID semantic;
+
+			for (int j = 0; j < parameter->options->count; j++)
+			{
+				NodeOption* paramOption = &parameter->options->entries[j];
+
+				switch (paramOption->type)
+				{
+				case OT_Identifier:
+					name = paramOption->value.strValue;
+					break;
+				case OT_Shared:
+					shared = paramOption->value.intValue > 0;
+					break;
+				case OT_Usage:
+					usage = parseBlockUsage((BufferUsageValue)paramOption->value.intValue);
+					break;
+				case OT_Auto:
+					semantic = removeQuotes(paramOption->value.strValue);
+					break;
+				}
+			}
+
+			if (name.empty())
+				continue;
+
+			desc.setParamBlockAttribs(name, shared, usage, semantic);
+		}
+	}
+
+	ShaderPtr BSLFXCompiler::parseShader(const String& name, ASTFXNode* rootNode, const Vector<CodeBlock>& codeBlocks)
+	{
+		SHADER_DESC shaderDesc;
+		Vector<TechniquePtr> techniques;
+
+		if (rootNode->type == NT_Shader)
+		{
+			for (int i = 0; i < rootNode->options->count; i++)
+			{
+				NodeOption* option = &rootNode->options->entries[i];
+
+				switch (option->type)
+				{
+				case OT_Separable:
+					shaderDesc.separablePasses = option->value.intValue > 1;
+					break;
+				case OT_Queue:
+					shaderDesc.queuePriority = option->value.intValue;
+					break;
+				case OT_Priority:
+					shaderDesc.queuePriority = option->value.intValue;
+					break;
+				case OT_Technique:
+				{
+					TechniquePtr technique = parseTechnique(option->value.nodePtr, codeBlocks);
+					if (technique != nullptr)
+						techniques.push_back(technique);
+
+					break;
+				}
+				case OT_Parameters:
+					parseParameters(shaderDesc, option->value.nodePtr);
+					break;
+				case OT_Blocks:
+					parseBlocks(shaderDesc, option->value.nodePtr);
+					break;
+				}
+			}
+		}
+
+		return Shader::_createPtr(name, shaderDesc, techniques);
+	}
+
+	String BSLFXCompiler::removeQuotes(const char* input)
+	{
+		UINT32 len = (UINT32)strlen(input);
+		String output(len - 2, ' ');
+
+		for (UINT32 i = 0; i < (len - 2); i++)
+			output[i] = input[i + 1];
+
+		return output;
+	}
+
+	GpuProgramProfile BSLFXCompiler::getProfile(const StringID& renderAPI, GpuProgramType type)
+	{
+		StringID target = renderAPI;
+		if (target == RenderAPIAny)
+			target = RenderAPICore::instance().getName();
+
+		if (target == RenderAPIDX11 || target == RenderAPIOpenGL)
+		{
+			switch (type)
+			{
+			case GPT_VERTEX_PROGRAM:
+				return GPP_VS_5_0;
+			case GPT_FRAGMENT_PROGRAM:
+				return GPP_FS_5_0;
+			case GPT_GEOMETRY_PROGRAM:
+				return GPP_GS_5_0;
+			case GPT_HULL_PROGRAM:
+				return GPP_HS_5_0;
+			case GPT_DOMAIN_PROGRAM:
+				return GPP_DS_5_0;
+			case GPT_COMPUTE_PROGRAM:
+				return GPP_CS_5_0;
+			}
+		}
+		else if (target == RenderAPIDX9)
+		{
+			switch (type)
+			{
+			case GPT_VERTEX_PROGRAM:
+				return GPP_VS_3_0;
+			case GPT_FRAGMENT_PROGRAM:
+				return GPP_FS_3_0;
+			}
+		}
+
+		return GPP_NONE;
+	}
+}

+ 39 - 0
BansheeSL/Source/BsSLImporter.cpp

@@ -0,0 +1,39 @@
+#include "BsSLImporter.h"
+#include "BsDataStream.h"
+#include "BsFileSystem.h"
+#include "BsSLFXCompiler.h"
+
+namespace BansheeEngine
+{
+	SLImporter::SLImporter()
+		:SpecificImporter()
+	{
+
+	}
+
+	SLImporter::~SLImporter()
+	{
+
+	}
+
+	bool SLImporter::isExtensionSupported(const WString& ext) const
+	{
+		WString lowerCaseExt = ext;
+		StringUtil::toLowerCase(lowerCaseExt);
+
+		return lowerCaseExt == L"bsl";
+	}
+
+	bool SLImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const
+	{
+		return true; // Plain-text so I don't even check for magic number
+	}
+
+	ResourcePtr SLImporter::import(const Path& filePath, ConstImportOptionsPtr importOptions)
+	{
+		DataStreamPtr stream = FileSystem::openFile(filePath);
+		String source = stream->getAsString();
+
+		return BSLFXCompiler::compile(source);
+	}
+}

File diff suppressed because it is too large
+ 2 - 1084
BansheeSL/Source/BsSLPlugin.cpp


+ 3 - 5
TODO.txt

@@ -128,11 +128,10 @@ ShaderManager will also be needed when I'm changing shader defines
 ShaderManager will also be needed when switching render APIs during runtime (if that will be possible)
  - It can also serve as a place where we perform shader caching
 
--------------------
+ - Separate languages for DX9 and DX11
+ - If I import a shader that tries to compile for a language different than we have available, create a null GpuProgram
 
-TODO STAGE 1:
- - Testing:
-   - If there are any new problems due to semantic and techniques using StringID
+-------------------
 
 TODO STAGE 2:
  - Handle include files (include files should just be plain text files with no additional parsing)
@@ -149,7 +148,6 @@ TODO STAGE 2:
 
 TODO STAGE 3:
  - Move all builtin shaders to the new .shader file model
- - Store example shader somewhere in Banshee data so I can use it for easy testing.
 
 TODO LATER:
  - Add GpuProgram caching (e.g. save compiled programs somewhere for later use)

Some files were not shown because too many files changed in this diff