Jelajahi Sumber

WIP improved include file handling

BearishSun 10 tahun lalu
induk
melakukan
d4b5e13f63

File diff ditekan karena terlalu besar
+ 487 - 409
BansheeSL/BsLexerFX.c


+ 117 - 2
BansheeSL/BsLexerFX.h

@@ -10,6 +10,8 @@
 
 /* A lexical scanner generated by flex */
 
+/* %not-for-header */
+
 #define FLEX_SCANNER
 #define YY_FLEX_MAJOR_VERSION 2
 #define YY_FLEX_MINOR_VERSION 5
@@ -18,16 +20,32 @@
 #define FLEX_BETA
 #endif
 
+/* %if-c++-only */
+/* %endif */
+
+/* %if-c-only */
+    
+/* %endif */
+
+/* %if-c-only */
+
+/* %endif */
+
 /* First, we deal with  platform-specific or compiler-specific issues. */
 
 /* begin standard C headers. */
+/* %if-c-only */
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
 #include <stdlib.h>
+/* %endif */
 
+/* %if-tables-serialization */
+/* %endif */
 /* end standard C headers. */
 
+/* %if-c-or-c++ */
 /* flex integer type definitions */
 
 #ifndef FLEXINT_H
@@ -92,6 +110,11 @@ typedef unsigned int flex_uint32_t;
 
 #endif /* ! FLEXINT_H */
 
+/* %endif */
+
+/* %if-c++-only */
+/* %endif */
+
 #ifdef __cplusplus
 
 /* The "const" storage-class-modifier is valid. */
@@ -113,6 +136,12 @@ typedef unsigned int flex_uint32_t;
 #define yyconst
 #endif
 
+/* %not-for-header */
+
+/* %not-for-header */
+
+/* %if-reentrant */
+
 /* An opaque pointer. */
 #ifndef YY_TYPEDEF_YY_SCANNER_T
 #define YY_TYPEDEF_YY_SCANNER_T
@@ -130,6 +159,11 @@ typedef void* yyscan_t;
 #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
 #define yy_flex_debug yyg->yy_flex_debug_r
 
+/* %endif */
+
+/* %if-not-reentrant */
+/* %endif */
+
 /* Size of default input buffer. */
 #ifndef YY_BUF_SIZE
 #define YY_BUF_SIZE 16384
@@ -145,11 +179,24 @@ typedef struct yy_buffer_state *YY_BUFFER_STATE;
 typedef size_t yy_size_t;
 #endif
 
+/* %if-not-reentrant */
+/* %endif */
+
+/* %if-c-only */
+/* %if-not-reentrant */
+/* %endif */
+/* %endif */
+
 #ifndef YY_STRUCT_YY_BUFFER_STATE
 #define YY_STRUCT_YY_BUFFER_STATE
 struct yy_buffer_state
 	{
+/* %if-c-only */
 	FILE *yy_input_file;
+/* %endif */
+
+/* %if-c++-only */
+/* %endif */
 
 	char *yy_ch_buf;		/* input buffer */
 	char *yy_buf_pos;		/* current position in input buffer */
@@ -196,6 +243,18 @@ struct yy_buffer_state
 	};
 #endif /* !YY_STRUCT_YY_BUFFER_STATE */
 
+/* %if-c-only Standard (non-C++) definition */
+/* %not-for-header */
+
+/* %endif */
+
+/* %if-c-only Standard (non-C++) definition */
+
+/* %if-not-reentrant */
+/* %not-for-header */
+
+/* %endif */
+
 void yyrestart (FILE *input_file ,yyscan_t yyscanner );
 void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
 YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
@@ -208,17 +267,26 @@ YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
 YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
 YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
 
+/* %endif */
+
 void *yyalloc (yy_size_t ,yyscan_t yyscanner );
 void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
 void yyfree (void * ,yyscan_t yyscanner );
 
+/* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */
 /* Begin user sect3 */
 
 #define yywrap(yyscanner) 1
 #define YY_SKIP_YYWRAP
 
+#define FLEX_DEBUG
+
 #define yytext_ptr yytext_r
 
+/* %if-c-only Standard (non-C++) definition */
+
+/* %endif */
+
 #ifdef YY_HEADER_EXPORT_START_CONDITIONS
 #define INITIAL 0
 #define INCLUDE 1
@@ -232,10 +300,23 @@ void yyfree (void * ,yyscan_t yyscanner );
     
 #define YY_EXTRA_TYPE struct tagParseState *
 
+/* %if-c-only Reentrant structure and macros (non-C++). */
+/* %if-reentrant */
+
+/* %if-c-only */
+
+/* %endif */
+
+/* %if-reentrant */
+
 int yylex_init (yyscan_t* scanner);
 
 int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
 
+/* %endif */
+
+/* %endif End reentrant structures and macros. */
+
 /* Accessor methods to globals.
    These are made visible to non-reentrant scanners for convenience. */
 
@@ -269,6 +350,8 @@ int yyget_column  (yyscan_t yyscanner );
 
 void yyset_column (int column_no ,yyscan_t yyscanner );
 
+/* %if-bison-bridge */
+
 YYSTYPE * yyget_lval (yyscan_t yyscanner );
 
 void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
@@ -277,6 +360,8 @@ void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
     
         void yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
     
+/* %endif */
+
 /* Macros after this point can all be overridden by user definitions in
  * section 1.
  */
@@ -289,6 +374,10 @@ extern int yywrap (yyscan_t yyscanner );
 #endif
 #endif
 
+/* %not-for-header */
+
+/* %endif */
+
 #ifndef yytext_ptr
 static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
 #endif
@@ -298,9 +387,16 @@ static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
 #endif
 
 #ifndef YY_NO_INPUT
+/* %if-c-only Standard (non-C++) definition */
+/* %not-for-header */
 
+/* %endif */
 #endif
 
+/* %if-c-only */
+
+/* %endif */
+
 /* Amount of stuff to slurp up with each read. */
 #ifndef YY_READ_BUF_SIZE
 #define YY_READ_BUF_SIZE 8192
@@ -311,21 +407,40 @@ static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
 #define YY_START_STACK_INCR 25
 #endif
 
+/* %if-tables-serialization structures and prototypes */
+/* %not-for-header */
+
+/* %not-for-header */
+
 /* Default declaration of generated scanner - a define so the user can
  * easily add parameters.
  */
 #ifndef YY_DECL
 #define YY_DECL_IS_OURS 1
+/* %if-c-only Standard (non-C++) definition */
 
 extern int yylex \
                (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
 
 #define YY_DECL int yylex \
                (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
+/* %endif */
+/* %if-c++-only C++ definition */
+/* %endif */
 #endif /* !YY_DECL */
 
+/* %not-for-header */
+
+/* %if-c++-only */
+/* %not-for-header */
+
+/* %endif */
+
 /* yy_get_previous_state - get the state just before the EOB char was reached */
 
+/* %if-c-only */
+/* %not-for-header */
+
 #undef YY_NEW_FILE
 #undef YY_FLUSH_BUFFER
 #undef yy_set_bol
@@ -338,9 +453,9 @@ extern int yylex \
 #undef YY_DECL
 #endif
 
-#line 266 "BsLexerFX.l"
+#line 265 "BsLexerFX.l"
 
 
-#line 345 "BsLexerFX.h"
+#line 460 "BsLexerFX.h"
 #undef yyIN_HEADER
 #endif /* yyHEADER_H */

+ 4 - 5
BansheeSL/BsLexerFX.l

@@ -5,7 +5,7 @@
 #define YY_USER_INIT yylineno = 0; yycolumn = 0;
 %}
  
-%option yylineno reentrant noyywrap nounistd never-interactive warn nodefault bison-bridge bison-locations
+%option yylineno reentrant noyywrap nounistd never-interactive warn nodefault bison-bridge bison-locations debug
 %option outfile="BsLexerFX.c" header-file="BsLexerFX.h"
 %option extra-type="struct tagParseState *"
 
@@ -90,7 +90,6 @@ Blocks			{ return TOKEN_BLOCKS; }
 	/* Technique keywords */
 Renderer		{ return TOKEN_RENDERER; }
 Language		{ return TOKEN_LANGUAGE; }
-Include			{ return TOKEN_INCLUDE; }
 Pass			{ return TOKEN_PASS; }
 
 	/* Pass keywords */
@@ -257,11 +256,11 @@ DYNAMIC			{ yylval->intValue = BUV_Dynamic; return TOKEN_BUFFERUSAGE; }
 <INCLUDE>.				{ return yytext[0]; }
 
 <<EOF>>					{
+	if(!yyextra->includeStack)
+		yyterminate();
+
 	yypop_buffer_state(yyscanner);
 	includePop(yyextra);
-
-	if(!YY_CURRENT_BUFFER)
-		yyterminate();
 }
 
 %%

File diff ditekan karena terlalu besar
+ 539 - 543
BansheeSL/BsParserFX.c


+ 53 - 54
BansheeSL/BsParserFX.h

@@ -34,7 +34,7 @@
 # define YY_YY_BSPARSERFX_H_INCLUDED
 /* Enabling traces.  */
 #ifndef YYDEBUG
-# define YYDEBUG 0
+# define YYDEBUG 1
 #endif
 #if YYDEBUG
 extern int yydebug;
@@ -141,57 +141,56 @@ extern int yydebug;
      TOKEN_TECHNIQUE = 318,
      TOKEN_RENDERER = 319,
      TOKEN_LANGUAGE = 320,
-     TOKEN_INCLUDE = 321,
-     TOKEN_PASS = 322,
-     TOKEN_VERTEX = 323,
-     TOKEN_FRAGMENT = 324,
-     TOKEN_GEOMETRY = 325,
-     TOKEN_HULL = 326,
-     TOKEN_DOMAIN = 327,
-     TOKEN_COMPUTE = 328,
-     TOKEN_COMMON = 329,
-     TOKEN_STENCILREF = 330,
-     TOKEN_FILLMODE = 331,
-     TOKEN_CULLMODE = 332,
-     TOKEN_DEPTHBIAS = 333,
-     TOKEN_SDEPTHBIAS = 334,
-     TOKEN_DEPTHCLIP = 335,
-     TOKEN_SCISSOR = 336,
-     TOKEN_MULTISAMPLE = 337,
-     TOKEN_AALINE = 338,
-     TOKEN_DEPTHREAD = 339,
-     TOKEN_DEPTHWRITE = 340,
-     TOKEN_COMPAREFUNC = 341,
-     TOKEN_STENCIL = 342,
-     TOKEN_STENCILREADMASK = 343,
-     TOKEN_STENCILWRITEMASK = 344,
-     TOKEN_STENCILOPFRONT = 345,
-     TOKEN_STENCILOPBACK = 346,
-     TOKEN_FAIL = 347,
-     TOKEN_ZFAIL = 348,
-     TOKEN_ALPHATOCOVERAGE = 349,
-     TOKEN_INDEPENDANTBLEND = 350,
-     TOKEN_TARGET = 351,
-     TOKEN_INDEX = 352,
-     TOKEN_BLEND = 353,
-     TOKEN_COLOR = 354,
-     TOKEN_ALPHA = 355,
-     TOKEN_WRITEMASK = 356,
-     TOKEN_SOURCE = 357,
-     TOKEN_DEST = 358,
-     TOKEN_OP = 359,
-     TOKEN_ADDRMODE = 360,
-     TOKEN_MINFILTER = 361,
-     TOKEN_MAGFILTER = 362,
-     TOKEN_MIPFILTER = 363,
-     TOKEN_MAXANISO = 364,
-     TOKEN_MIPBIAS = 365,
-     TOKEN_MIPMIN = 366,
-     TOKEN_MIPMAX = 367,
-     TOKEN_BORDERCOLOR = 368,
-     TOKEN_U = 369,
-     TOKEN_V = 370,
-     TOKEN_W = 371
+     TOKEN_PASS = 321,
+     TOKEN_VERTEX = 322,
+     TOKEN_FRAGMENT = 323,
+     TOKEN_GEOMETRY = 324,
+     TOKEN_HULL = 325,
+     TOKEN_DOMAIN = 326,
+     TOKEN_COMPUTE = 327,
+     TOKEN_COMMON = 328,
+     TOKEN_STENCILREF = 329,
+     TOKEN_FILLMODE = 330,
+     TOKEN_CULLMODE = 331,
+     TOKEN_DEPTHBIAS = 332,
+     TOKEN_SDEPTHBIAS = 333,
+     TOKEN_DEPTHCLIP = 334,
+     TOKEN_SCISSOR = 335,
+     TOKEN_MULTISAMPLE = 336,
+     TOKEN_AALINE = 337,
+     TOKEN_DEPTHREAD = 338,
+     TOKEN_DEPTHWRITE = 339,
+     TOKEN_COMPAREFUNC = 340,
+     TOKEN_STENCIL = 341,
+     TOKEN_STENCILREADMASK = 342,
+     TOKEN_STENCILWRITEMASK = 343,
+     TOKEN_STENCILOPFRONT = 344,
+     TOKEN_STENCILOPBACK = 345,
+     TOKEN_FAIL = 346,
+     TOKEN_ZFAIL = 347,
+     TOKEN_ALPHATOCOVERAGE = 348,
+     TOKEN_INDEPENDANTBLEND = 349,
+     TOKEN_TARGET = 350,
+     TOKEN_INDEX = 351,
+     TOKEN_BLEND = 352,
+     TOKEN_COLOR = 353,
+     TOKEN_ALPHA = 354,
+     TOKEN_WRITEMASK = 355,
+     TOKEN_SOURCE = 356,
+     TOKEN_DEST = 357,
+     TOKEN_OP = 358,
+     TOKEN_ADDRMODE = 359,
+     TOKEN_MINFILTER = 360,
+     TOKEN_MAGFILTER = 361,
+     TOKEN_MIPFILTER = 362,
+     TOKEN_MAXANISO = 363,
+     TOKEN_MIPBIAS = 364,
+     TOKEN_MIPMIN = 365,
+     TOKEN_MIPMAX = 366,
+     TOKEN_BORDERCOLOR = 367,
+     TOKEN_U = 368,
+     TOKEN_V = 369,
+     TOKEN_W = 370
    };
 #endif
 
@@ -199,7 +198,7 @@ extern int yydebug;
 typedef union YYSTYPE
 {
 /* Line 2579 of glr.c  */
-#line 46 "BsParserFX.y"
+#line 47 "BsParserFX.y"
 
 	int intValue;
 	float floatValue;
@@ -211,7 +210,7 @@ typedef union YYSTYPE
 
 
 /* Line 2579 of glr.c  */
-#line 215 "BsParserFX.h"
+#line 214 "BsParserFX.h"
 } YYSTYPE;
 # define YYSTYPE_IS_TRIVIAL 1
 # define yystype YYSTYPE /* obsolescent; will be withdrawn */

+ 2 - 2
BansheeSL/BsParserFX.y

@@ -42,6 +42,7 @@ void yyerror(YYLTYPE *locp, ParseState* parse_state, yyscan_t scanner, const cha
 %parse-param { ParseState* parse_state }
 %parse-param { yyscan_t scanner }
 %glr-parser
+%debug
 
 %union {
 	int intValue;
@@ -122,7 +123,7 @@ void yyerror(YYLTYPE *locp, ParseState* parse_state, yyscan_t scanner, const cha
 %token TOKEN_PARAMETERS TOKEN_BLOCKS TOKEN_TECHNIQUE
 
 	/* Technique keywords */
-%token	TOKEN_RENDERER TOKEN_LANGUAGE TOKEN_INCLUDE TOKEN_PASS
+%token	TOKEN_RENDERER TOKEN_LANGUAGE TOKEN_PASS
 
 	/* Pass keywords */
 %token	TOKEN_VERTEX TOKEN_FRAGMENT TOKEN_GEOMETRY TOKEN_HULL TOKEN_DOMAIN TOKEN_COMPUTE TOKEN_COMMON
@@ -262,7 +263,6 @@ shader_option
 	: TOKEN_SEPARABLE '=' TOKEN_BOOLEAN ';'		{ $$.type = OT_Separable; $$.value.intValue = $3; }
 	| TOKEN_QUEUE '=' TOKEN_INTEGER ';'			{ $$.type = OT_Queue; $$.value.intValue = $3; }
 	| TOKEN_PRIORITY '=' TOKEN_INTEGER ';'		{ $$.type = OT_Priority; $$.value.intValue = $3; }
-	| TOKEN_INCLUDE '=' TOKEN_STRING ';'		{ $$.type = OT_Include; $$.value.strValue = $3; }
 	| TOKEN_TRANSPARENT '=' TOKEN_BOOLEAN ';'	{ $$.type = OT_Transparent; $$.value.intValue = $3; }
 	;
 

+ 15 - 1
BansheeSL/Include/BsASTFX.h

@@ -19,6 +19,7 @@ typedef struct tagASTFXNode ASTFXNode;
 typedef struct tagNodeLink NodeLink;
 typedef struct tagIncludeData IncludeData;
 typedef struct tagIncludeLink IncludeLink;
+typedef struct tagCodeString CodeString;
 typedef enum tagFillModeValue FillModeValue;
 typedef enum tagCullModeValue CullModeValue;
 typedef enum tagCompFuncValue CompFuncValue;
@@ -55,7 +56,6 @@ enum tagOptionType
 	OT_Technique,
 	OT_Renderer,
 	OT_Language,
-	OT_Include,
 	OT_Pass,
 	OT_FillMode,
 	OT_CullMode,
@@ -204,6 +204,15 @@ struct tagIncludeLink
 	IncludeLink* next;
 };
 
+struct tagCodeString
+{
+	char* code;
+	int type;
+	int index;
+
+	CodeString* next;
+};
+
 struct tagParseState
 {
 	ASTFXNode* rootNode;
@@ -217,6 +226,9 @@ struct tagParseState
 
 	NodeLink* nodeStack;
 	IncludeLink* includeStack;
+	IncludeLink* includes;
+	CodeString* codeStrings;
+	int numCodeStrings;
 };
 
 struct tagOptionInfo
@@ -270,6 +282,8 @@ void nodeDelete(ASTFXNode* node);
 void nodePush(ParseState* parseState, ASTFXNode* node);
 void nodePop(ParseState* parseState);
 
+void addCodeBlock(ParseState* parseState, int type, char* code, int codeLength);
+
 ParseState* parseStateCreate();
 void parseStateDelete(ParseState* parseState);
 

+ 18 - 82
BansheeSL/Include/BsSLFXCompiler.h

@@ -48,6 +48,8 @@ namespace BansheeEngine
 			BLEND_STATE_DESC blendDesc;
 			RASTERIZER_STATE_DESC rasterizerDesc;
 			DEPTH_STENCIL_STATE_DESC depthStencilDesc;
+			UINT32 stencilRefValue = 0;
+			UINT32 seqIdx = 0;
 
 			bool blendIsDefault = true;
 			bool rasterizerIsDefault = true;
@@ -62,6 +64,16 @@ namespace BansheeEngine
 			String computeCode;
 		};
 
+		/** Temporary data for describing a technique during parsing. */
+		struct TechniqueData
+		{
+			StringID renderer = RendererAny;
+			StringID renderAPI = RenderAPIAny;
+			String language;
+
+			Vector<PassData> passes;
+		};
+
 	public:
 		/**	Transforms a source file written in BSL FX syntax into a Shader object. */
 		static BSLFXCompileResult compile(const String& source);
@@ -77,28 +89,6 @@ namespace BansheeEngine
 		 */
 		static Vector<CodeBlock> parseCodeBlocks(String& source);
 
-		/**
-		 * Merges two shader AST nodes. The first node will contain the combination of both after the method executes.
-		 *
-		 * @param[in, out]	mergeInto		Parse state containing the node to be merged into.
-		 * @param[in]		mergeFrom		Second of the nodes to be merged. All the contents of this node will be merged
-		 *									into the first node. This node and its children will remain unmodified.
-		 * @param[in]		codeBlockOffset	Offset to apply to any code block indexes belonging to the second node.
-		 */
-		static void mergeAST(ParseState* mergeInto, ASTFXNode* mergeFrom, UINT32 codeBlockOffset);
-
-		/**
-		 * Finds the blocks node in the root node of the provided parse state, and merges any entries from @p blocksNode
-		 * into it. A new node is created if one doesn't exist.
-		 */
-		static void mergeBlocks(ParseState* mergeInto, ASTFXNode* blocksNode);
-
-		/**
-		 * Finds the parameters node in the root node of the provided parse state, and merges any entries from 
-		 * @p paramsNode into it. A new node is created if one doesn't exist.
-		 */
-		static void mergeParameters(ParseState* mergeInto, ASTFXNode* paramsNode);
-
 		/**
 		 * Retrieves the renderer and language specified for the technique. These two values are considered a unique 
 		 * identifier for a technique.
@@ -106,57 +96,7 @@ namespace BansheeEngine
 		static void getTechniqueIdentifier(ASTFXNode* technique, StringID& renderer, String& language);
 
 		/** Checks if two techniques can be matched based on the options specified in their child nodes. */
-		static bool isTechniqueMergeValid(ASTFXNode* into, ASTFXNode* from);
-
-		/**
-		 * Copies an existing AST node option and inserts it into another node options list.
-		 *
-		 * @param[in, out]	into	Parse state of the into which the node option will be inserted to.
-		 * @param[in]		parent	A set of node options to insert the node option copy into.
-		 * @param[in]		option	Node option to copy.
-		 * @return					True if the copied node was a complex type.
-		 */
-		static bool copyNodeOption(ParseState* into, NodeOptions* parent, NodeOption* option);
-
-		/**
-		 * Merges pass states and code blocks. All code blocks from @p mergeFromNode will have their indexes incremented by
-		 * @p codeBlockOffset.
-		 */
-		static void mergePass(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, 
-			UINT32 codeBlockOffset);
-
-		/**
-		 * Merges code blocks. All code blocks from @p mergeFromNode will have their indexes incremented by 
-		 * @p codeBlockOffset.
-		 */
-		static void mergeCode(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, 
-			UINT32 codeBlockOffset);
-
-		/**
-		 * Merges all pass states by copying all child nodes and their options to the destination node. 
-		 *			
-		 * @note	
-		 * Certain node types are ignored as we handle their merging specially. Should only be called on Technique nodes or
-		 * its children.
-		 */
-		static void mergePassStates(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode);
-
-		/**
-		 * Merges two techniques. All technique states, code blocks and passes will be merged. Passes will be merged 
-		 * according to the pass index (new passes will be inserted if the destination doesn't already have a pass with an
-		 * index belonging to the source pass). All code blocks from @p mergeFromNode will have their indexes incremented
-		 * by @p codeBlockOffset.
-		 */
-		static void mergeTechnique(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, 
-			UINT32 codeBlockOffset);
-
-		/**
-		 * Find matching techniques from the root shader node in @p mergeInto and merges them with @p techniqueNode.
-		 * All code blocks from @p techniqueNode will have their indexes incremented by @p codeBlockOffset.
-		 *
-		 * @see		BSLFXCompiler::mergeTechnique
-		 */
-		static void mergeTechniques(ParseState* mergeInto, ASTFXNode* techniqueNode, UINT32 codeBlockOffset);
+		static bool doTechniquesMatch(ASTFXNode* into, ASTFXNode* from);
 
 		/**	Converts FX renderer name into an in-engine renderer identifier. */
 		static StringID parseRenderer(const String& name);
@@ -266,18 +206,13 @@ namespace BansheeEngine
 		static void parseCodeBlock(ASTFXNode* codeNode, const Vector<CodeBlock>& codeBlocks, PassData& passData);
 
 		/**
-		 * 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.
+		 * Parses the pass AST node and populates the provided @passData with all relevant pass parameters.
 		 *
 		 * @param[in]	passNode		Node to parse.
 		 * @param[in]	codeBlocks		GPU program source code retrieved from parseCodeBlocks().
-		 * @param[in]	passData		Data containing pass render state descriptors.
-		 * @param[in]	renderAPI		API to use for compiling the GPU programs.
-		 * @param[in]	language		GPU program language to use for parsing the provided code blocks.
-		 * @param[in]	seqIdx			Output sequential index of the pass that determines its rendering order.
+		 * @param[out]	passData		Will contain pass data after parsing. 
 		 */
-		static PassPtr parsePass(ASTFXNode* passNode, const Vector<CodeBlock>& codeBlocks, PassData& passData, 
-			const StringID& renderAPI, const String& language, UINT32& seqIdx);
+		static void parsePass(ASTFXNode* passNode, const Vector<CodeBlock>& codeBlocks, PassData& passData);
 
 		/**
 		 * Parses the technique AST node and generates a single technique object. Returns null if no technique can be 
@@ -285,8 +220,9 @@ namespace BansheeEngine
 		 *
 		 * @param[in]	techniqueNode	Node to parse.
 		 * @param[in]	codeBlocks		GPU program source code retrieved from parseCodeBlocks().
+		 * @param[out]	techniqueData	Will contain technique data after parsing.
 		 */
-		static TechniquePtr parseTechnique(ASTFXNode* techniqueNode, const Vector<CodeBlock>& codeBlocks);
+		static void parseTechnique(ASTFXNode* techniqueNode, const Vector<CodeBlock>& codeBlocks, TechniqueData& techniqueData);
 
 		/**
 		 * Parses the parameters AST node and populates the shader descriptor with information about GPU program parameters

+ 19 - 1
BansheeSL/Source/BsASTFX.c

@@ -13,7 +13,6 @@ OptionInfo OPTION_LOOKUP[] =
 	{ OT_Technique, ODT_Complex }, 
 	{ OT_Renderer, ODT_String }, 
 	{ OT_Language, ODT_String }, 
-	{ OT_Include, ODT_String }, 
 	{ OT_Pass, ODT_Complex }, 
 	{ OT_FillMode, ODT_Int }, 
 	{ OT_CullMode, ODT_Int },
@@ -197,6 +196,22 @@ void nodePop(ParseState* parseState)
 	mmfree(toRemove);
 }
 
+void addCodeBlock(ParseState* parseState, int type, char* code, int codeLength)
+{
+	char* buffer = mmalloc(parseState->memContext, sizeof(CodeString) + codeLength);
+	
+	CodeString* codeString = (CodeString*)buffer;
+	codeString->type = type;
+	codeString->index = parseState->numCodeStrings;
+	codeString->code = buffer + sizeof(CodeString);
+	codeString->next = parseState->codeStrings;
+
+	memcpy(codeString->code, code, codeLength);
+
+	parseState->numCodeStrings++;
+	parseState->codeStrings = codeString;
+}
+
 ParseState* parseStateCreate()
 {
 	ParseState* parseState = (ParseState*)malloc(sizeof(ParseState));
@@ -205,6 +220,9 @@ ParseState* parseStateCreate()
 	parseState->topNode = 0;
 	parseState->nodeStack = 0;
 	parseState->includeStack = 0;
+	parseState->includes = 0;
+	parseState->codeStrings = 0;
+	parseState->numCodeStrings = 0;
 
 	parseState->hasError = 0;
 	parseState->errorLine = 0;

+ 28 - 19
BansheeSL/Source/BsIncludeHandler.cpp

@@ -11,45 +11,49 @@ using namespace BansheeEngine;
 
 char* includePush(ParseState* state, const char* filename, int line, int column, int* size)
 {
-	HShaderInclude include = ShaderManager::instance().findInclude(filename);
+	int filenameQuotesLen = (int)strlen(filename);
+	char* filenameNoQuote = (char*)mmalloc(state->memContext, filenameQuotesLen - 1);
+	memcpy(filenameNoQuote, filename + 1, filenameQuotesLen - 2);
+	filenameNoQuote[filenameQuotesLen - 2] = '\0';
+
+	HShaderInclude include = ShaderManager::instance().findInclude(filenameNoQuote);
 
 	if (include != nullptr)
 		include.blockUntilLoaded();
 
-	int filenameLen = (int)strlen(filename);
+	int filenameLen = (int)strlen(filenameNoQuote);
 	if (include.isLoaded())
 	{
 		String includeSource = include->getString();
 
 		*size = (int)includeSource.size() + 2;
-		int totalSize = *size + sizeof(IncludeLink) + sizeof(IncludeData) + filenameLen + 1;
-		char* output = (char*)mmalloc(state->memContext, totalSize);
-		char* ptr = output;
+		char* output = (char*)mmalloc(state->memContext, *size);
 
-		memcpy(ptr, includeSource.data(), *size - 2);
-		ptr[*size - 2] = 0;
-		ptr[*size - 1] = 0;
+		memcpy(output, includeSource.data(), *size - 2);
+		output[*size - 2] = 0;
+		output[*size - 1] = 0;
 
-		ptr += *size;
-		IncludeLink* next = state->includeStack;
+		int linkSize =  sizeof(IncludeLink) + sizeof(IncludeData) + filenameLen + 1;
+		char* linkData = (char*)mmalloc(state->memContext, linkSize);
 
-		IncludeLink* newLink = (IncludeLink*)ptr;
-		ptr += sizeof(IncludeLink);
+		IncludeLink* newLink = (IncludeLink*)linkData;
+		linkData += sizeof(IncludeLink);
 
-		IncludeData* includeData = (IncludeData*)ptr;
-		ptr += sizeof(IncludeData);
+		IncludeData* includeData = (IncludeData*)linkData;
+		linkData += sizeof(IncludeData);
 
-		memcpy(ptr, filename, filenameLen);
-		ptr[filenameLen] = '\0';
+		memcpy(linkData, filenameNoQuote, filenameLen);
+		linkData[filenameLen] = '\0';
 
-		includeData->filename = ptr; 
+		includeData->filename = linkData;
 		includeData->buffer = output;
 
 		newLink->data = includeData;
-		newLink->next = next;
+		newLink->next = state->includeStack;
 
 		state->includeStack = newLink;
-		
+
+		mmfree(filenameNoQuote);
 		return output;
 	}
 
@@ -68,6 +72,7 @@ char* includePush(ParseState* state, const char* filename, int line, int column,
 	state->errorColumn = column;
 	state->errorMessage = message;
 
+	mmfree(filenameNoQuote);
 	return nullptr;
 }
 
@@ -79,5 +84,9 @@ void includePop(ParseState* state)
 		return;
 
 	state->includeStack = current->next;
+	current->next = state->includes;
+	state->includes = current;
+
 	mmfree(current->data->buffer);
+	current->data->buffer = nullptr;
 }

+ 163 - 498
BansheeSL/Source/BsSLFXCompiler.cpp

@@ -149,6 +149,12 @@ namespace BansheeEngine
 		if (yylex_init_extra(parseState, &scanner))
 			return;
 
+		// If debug output from lexer is needed uncomment this and add %debug option to lexer file
+		yyset_debug(true, scanner);
+
+		// If debug output from parser is needed uncomment this and add %debug option to parser file
+		yydebug = true;
+
 		state = yy_scan_string(source, scanner);
 
 		if (yyparse(parseState, scanner))
@@ -225,88 +231,6 @@ namespace BansheeEngine
 		return codeBlocks;
 	}
 
-	void BSLFXCompiler::mergeBlocks(ParseState* mergeInto, ASTFXNode* blocksNode)
-	{
-		if (blocksNode == nullptr || blocksNode->type != NT_Blocks)
-			return;
-
-		ASTFXNode* destBlocksNode = nullptr;
-		for (int i = 0; i < mergeInto->rootNode->options->count; i++)
-		{
-			NodeOption* option = &mergeInto->rootNode->options->entries[i];
-
-			if (option->type == OT_Blocks)
-			{
-				destBlocksNode = option->value.nodePtr;
-				break;
-			}
-		}
-
-		if (destBlocksNode == nullptr)
-		{
-			destBlocksNode = nodeCreate(mergeInto->memContext, NT_Blocks);
-			NodeOption blocksOption; blocksOption.type = OT_Blocks; blocksOption.value.nodePtr = destBlocksNode;
-			nodeOptionsAdd(mergeInto->memContext, mergeInto->rootNode->options, &blocksOption);
-		}
-
-		for (int i = 0; i < blocksNode->options->count; i++)
-		{
-			NodeOption* option = &blocksNode->options->entries[i];
-
-			if (option->type == OT_Block)
-			{
-				ASTFXNode* dstBlock = nodeCreate(mergeInto->memContext, NT_Block);
-				NodeOption blockOption; blockOption.type = OT_Block; blockOption.value.nodePtr = dstBlock;
-				nodeOptionsAdd(mergeInto->memContext, destBlocksNode->options, &blockOption);
-
-				ASTFXNode* srcBlock = option->value.nodePtr;
-				for (int j = 0; j < srcBlock->options->count; j++)
-					copyNodeOption(mergeInto, dstBlock->options, &srcBlock->options->entries[j]);
-			}
-		}
-	}
-
-	void BSLFXCompiler::mergeParameters(ParseState* mergeInto, ASTFXNode* paramsNode)
-	{
-		if (paramsNode == nullptr || paramsNode->type != NT_Parameters)
-			return;
-
-		ASTFXNode* destParamsNode = nullptr;
-		for (int i = 0; i < mergeInto->rootNode->options->count; i++)
-		{
-			NodeOption* option = &mergeInto->rootNode->options->entries[i];
-
-			if (option->type == OT_Parameters)
-			{
-				destParamsNode = option->value.nodePtr;
-				break;
-			}
-		}
-
-		if (destParamsNode == nullptr)
-		{
-			destParamsNode = nodeCreate(mergeInto->memContext, NT_Parameters);
-			NodeOption paramsOption; paramsOption.type = OT_Parameters; paramsOption.value.nodePtr = destParamsNode;
-			nodeOptionsAdd(mergeInto->memContext, mergeInto->rootNode->options, &paramsOption);
-		}
-
-		for (int i = 0; i < paramsNode->options->count; i++)
-		{
-			NodeOption* option = &paramsNode->options->entries[i];
-
-			if (option->type == OT_Parameter)
-			{
-				ASTFXNode* dstParam = nodeCreate(mergeInto->memContext, NT_Parameter);
-				NodeOption paramOption; paramOption.type = OT_Parameter; paramOption.value.nodePtr = dstParam;
-				nodeOptionsAdd(mergeInto->memContext, destParamsNode->options, &paramOption);
-
-				ASTFXNode* srcParam = option->value.nodePtr;
-				for (int j = 0; j < srcParam->options->count; j++)
-					copyNodeOption(mergeInto, dstParam->options, &srcParam->options->entries[j]);
-			}
-		}
-	}
-
 	void BSLFXCompiler::getTechniqueIdentifier(ASTFXNode* technique, StringID& renderer, String& language)
 	{
 		renderer = RendererAny;
@@ -328,7 +252,7 @@ namespace BansheeEngine
 		}
 	}
 
-	bool BSLFXCompiler::isTechniqueMergeValid(ASTFXNode* into, ASTFXNode* from)
+	bool BSLFXCompiler::doTechniquesMatch(ASTFXNode* into, ASTFXNode* from)
 	{
 		StringID intoRenderer = RendererAny;
 		String intoLanguage = "Any";
@@ -342,231 +266,6 @@ namespace BansheeEngine
 		return (intoRenderer == fromRenderer || fromRenderer == RendererAny) && (intoLanguage == fromLanguage || fromLanguage == "Any");
 	}
 
-	bool BSLFXCompiler::copyNodeOption(ParseState* into, NodeOptions* parent, NodeOption* option)
-	{
-		OptionDataType dataType = OPTION_LOOKUP[(int)option->type].dataType;
-		if (dataType == ODT_Complex)
-		{
-			ASTFXNode* newNode = nullptr;
-
-			if (option->value.nodePtr != nullptr)
-				newNode = nodeCreate(into->memContext, option->value.nodePtr->type);
-
-			NodeOption newOption;
-			newOption.type = option->type;
-			newOption.value.nodePtr = newNode;
-
-			nodeOptionsAdd(into->memContext, parent, &newOption);
-
-			return true;
-		}
-		else if (dataType == ODT_String)
-		{
-			NodeOption newOption;
-			newOption.type = option->type;
-			newOption.value.strValue = mmalloc_strdup(into->memContext, option->value.strValue);
-
-			nodeOptionsAdd(into->memContext, parent, &newOption);
-			return false;
-		}
-		else
-		{
-			nodeOptionsAdd(into->memContext, parent, option);
-			return false;
-		}
-	}
-
-	void BSLFXCompiler::mergePass(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, UINT32 codeBlockOffset)
-	{
-		if (mergeIntoNode == nullptr || mergeIntoNode->type != NT_Pass || mergeFromNode == nullptr || mergeFromNode->type != NT_Pass)
-			return;
-
-		mergeCode(mergeInto, mergeIntoNode, mergeFromNode, codeBlockOffset);
-		mergePassStates(mergeInto, mergeIntoNode, mergeFromNode);
-	}
-
-	void BSLFXCompiler::mergeCode(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, UINT32 codeBlockOffset)
-	{
-		for (int i = 0; i < mergeFromNode->options->count; i++)
-		{
-			NodeOption* option = &mergeFromNode->options->entries[i];
-
-			if (option->type == OT_Code)
-			{
-				ASTFXNode* srcCodeNode = option->value.nodePtr;
-
-				UINT32 origIndex = 0;
-				for (int j = 0; j < srcCodeNode->options->count; j++)
-				{
-					NodeOption* codeOption = &srcCodeNode->options->entries[j];
-					if (codeOption->type == OT_Index)
-						origIndex = codeOption->value.intValue;
-				}
-
-				ASTFXNode* destCodeNode = nodeCreate(mergeInto->memContext, NT_Code);
-				NodeOption index; index.type = OT_Index; index.value.intValue = codeBlockOffset + origIndex;
-				nodeOptionsAdd(mergeInto->memContext, destCodeNode->options, &index);
-
-				NodeOption code; code.type = OT_Code; code.value.nodePtr = destCodeNode;
-				nodeOptionsAdd(mergeInto->memContext, mergeIntoNode->options, &code);
-			}
-		}
-	}
-
-	void BSLFXCompiler::mergePassStates(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode)
-	{
-		for (int i = 0; i < mergeFromNode->options->count; i++)
-		{
-			NodeOption* option = &mergeFromNode->options->entries[i];
-
-			// Skip these as we handle them specially elsewhere
-			if (option->type == OT_Code || option->type == OT_Pass || option->type == OT_Renderer || option->type == OT_Language)
-				continue;
-
-			if (copyNodeOption(mergeInto, mergeIntoNode->options, option))
-			{
-				ASTFXNode* newNode = mergeIntoNode->options->entries[mergeIntoNode->options->count - 1].value.nodePtr;
-				mergePassStates(mergeInto, newNode, option->value.nodePtr);
-			}
-		}
-	}
-
-	void BSLFXCompiler::mergeTechnique(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, UINT32 codeBlockOffset)
-	{
-		if (mergeIntoNode == nullptr || mergeIntoNode->type != NT_Technique || mergeFromNode == nullptr || mergeFromNode->type != NT_Technique)
-			return;
-
-		mergeCode(mergeInto, mergeIntoNode, mergeFromNode, codeBlockOffset);
-		mergePassStates(mergeInto, mergeIntoNode, mergeFromNode);
-
-		Map<UINT32, ASTFXNode*, std::greater<UINT32>> fromPasses;
-
-		UINT32 nextPassIdx = 0;
-		for (int i = 0; i < mergeFromNode->options->count; i++)
-		{
-			NodeOption* option = &mergeFromNode->options->entries[i];
-
-			if (option->type == OT_Pass)
-			{
-				ASTFXNode* passNode = option->value.nodePtr;
-				UINT32 passIdx = nextPassIdx;
-				for (int j = 0; j < passNode->options->count; j++)
-				{
-					NodeOption* passOption = &passNode->options->entries[i];
-
-					if (passOption->type == OT_Index)
-						passIdx = (UINT32)passOption->value.intValue;
-				}
-
-				fromPasses[passIdx] = passNode;
-				nextPassIdx = std::max(nextPassIdx, passIdx) + 1;
-			}
-		}
-
-		Map<UINT32, ASTFXNode*, std::greater<UINT32>> intoPasses;
-
-		nextPassIdx = 0;
-		for (int i = 0; i < mergeIntoNode->options->count; i++)
-		{
-			NodeOption* option = &mergeIntoNode->options->entries[i];
-
-			if (option->type == OT_Pass)
-			{
-				ASTFXNode* passNode = option->value.nodePtr;
-				UINT32 passIdx = nextPassIdx;
-				for (int j = 0; j < passNode->options->count; j++)
-				{
-					NodeOption* passOption = &passNode->options->entries[i];
-
-					if (passOption->type == OT_Index)
-						passIdx = (UINT32)passOption->value.intValue;
-				}
-
-				intoPasses[passIdx] = passNode;
-				nextPassIdx = std::max(nextPassIdx, passIdx) + 1;
-			}
-		}
-
-		for (auto& fromPair : fromPasses)
-		{
-			auto iterFind = intoPasses.find(fromPair.first);
-			if (iterFind != intoPasses.end())
-			{
-				mergePass(mergeInto, iterFind->second, fromPair.second, codeBlockOffset);
-			}
-			else
-			{
-				ASTFXNode* passNode = nodeCreate(mergeInto->memContext, NT_Pass);
-				NodeOption passOption; passOption.type = OT_Pass; passOption.value.nodePtr = passNode;
-				nodeOptionsAdd(mergeInto->memContext, mergeIntoNode->options, &passOption);
-
-				mergePass(mergeInto, passNode, fromPair.second, codeBlockOffset);
-			}
-		}
-	}
-
-	void BSLFXCompiler::mergeTechniques(ParseState* mergeInto, ASTFXNode* techniqueNode, UINT32 codeBlockOffset)
-	{
-		if (techniqueNode == nullptr || techniqueNode->type != NT_Technique)
-			return;
-
-		bool anyMerged = false;
-		for (int i = 0; i < mergeInto->rootNode->options->count; i++)
-		{
-			NodeOption* option = &mergeInto->rootNode->options->entries[i];
-
-			if (option->type == OT_Technique)
-			{
-				if (isTechniqueMergeValid(option->value.nodePtr, techniqueNode))
-				{
-					mergeTechnique(mergeInto, option->value.nodePtr, techniqueNode, codeBlockOffset);
-					anyMerged = true;
-					break;
-				}
-			}
-		}
-
-		// If this is a language specific technique and it wasn't merged with anything, create a new technique node
-		if (!anyMerged)
-		{
-			ASTFXNode* techniqueNode = nodeCreate(mergeInto->memContext, NT_Technique);
-			NodeOption techniqueOption; techniqueOption.type = OT_Technique; techniqueOption.value.nodePtr = techniqueNode;
-			nodeOptionsAdd(mergeInto->memContext, mergeInto->rootNode->options, &techniqueOption);
-
-			mergeTechnique(mergeInto, techniqueNode, techniqueNode, codeBlockOffset);
-		}
-	}
-
-	void BSLFXCompiler::mergeAST(ParseState* mergeInto, ASTFXNode* mergeFrom, UINT32 codeBlockOffset)
-	{
-		if (mergeInto->rootNode->type != NT_Shader || mergeFrom == nullptr || mergeFrom->type != NT_Shader)
-			return;
-
-		for (int i = 0; i < mergeFrom->options->count; i++)
-		{
-			NodeOption* option = &mergeFrom->options->entries[i];
-
-			switch (option->type)
-			{
-			case OT_Separable:
-			case OT_Queue:
-			case OT_Priority:
-			case OT_Transparent:
-				nodeOptionsAdd(mergeInto->memContext, mergeInto->rootNode->options, option);
-				break;
-			case OT_Technique:
-				mergeTechniques(mergeInto, option->value.nodePtr, codeBlockOffset);
-				break;
-			case OT_Parameters:
-				mergeParameters(mergeInto, option->value.nodePtr);
-				break;
-			case OT_Blocks:
-				mergeBlocks(mergeInto, option->value.nodePtr);
-				break;
-			}
-		}
-	}
-
 	StringID BSLFXCompiler::parseRenderer(const String& name)
 	{
 		if (name == "Any")
@@ -1240,95 +939,38 @@ namespace BansheeEngine
 		}
 	}
 
-	PassPtr BSLFXCompiler::parsePass(ASTFXNode* passNode, const Vector<CodeBlock>& codeBlocks, PassData& passData, 
-		const StringID& renderAPI, const String& language, UINT32& seqIdx)
+	void BSLFXCompiler::parsePass(ASTFXNode* passNode, const Vector<CodeBlock>& codeBlocks, PassData& passData)
 	{
 		if (passNode == nullptr || passNode->type != NT_Pass)
-			return nullptr;
-
-		PASS_DESC passDesc;
+			return;
 
 		passData.blendIsDefault &= !parseBlendState(passData.blendDesc, passNode);
 		passData.rasterizerIsDefault &= !parseRasterizerState(passData.rasterizerDesc, passNode);
 		passData.depthStencilIsDefault &= !parseDepthStencilState(passData.depthStencilDesc, passNode);
 
-		if (!passData.blendIsDefault)
-			passDesc.blendState = BlendState::create(passData.blendDesc);
-
-		if (!passData.rasterizerIsDefault)
-			passDesc.rasterizerState = RasterizerState::create(passData.rasterizerDesc);
-
-		if (!passData.depthStencilIsDefault)
-			passDesc.depthStencilState = DepthStencilState::create(passData.depthStencilDesc);
-
 		for (int i = 0; i < passNode->options->count; i++)
 		{
 			NodeOption* option = &passNode->options->entries[i];
 
 			switch (option->type)
 			{
-			case OT_Index:
-				seqIdx = option->value.intValue;
-				break;
 			case OT_StencilRef:
-				passDesc.stencilRefValue = option->value.intValue;
+				passData.stencilRefValue = option->value.intValue;
 				break;
 			case OT_Code:
 				parseCodeBlock(option->value.nodePtr, codeBlocks, passData);
 				break;
 			}
 		}
-
-		if (!passData.vertexCode.empty())
-		{
-			passDesc.vertexProgram = GpuProgram::create(passData.commonCode + passData.vertexCode, "main", language,
-				GPT_VERTEX_PROGRAM, getProfile(renderAPI, GPT_VERTEX_PROGRAM));
-		}
-
-		if (!passData.fragmentCode.empty())
-		{
-			passDesc.fragmentProgram = GpuProgram::create(passData.commonCode + passData.fragmentCode, "main", language,
-				GPT_FRAGMENT_PROGRAM, getProfile(renderAPI, GPT_FRAGMENT_PROGRAM));
-		}
-
-		if (!passData.geometryCode.empty())
-		{
-			passDesc.geometryProgram = GpuProgram::create(passData.commonCode + passData.geometryCode, "main", language,
-				GPT_GEOMETRY_PROGRAM, getProfile(renderAPI, GPT_GEOMETRY_PROGRAM));
-		}
-
-		if (!passData.hullCode.empty())
-		{
-			passDesc.hullProgram = GpuProgram::create(passData.commonCode + passData.hullCode, "main", language,
-				GPT_HULL_PROGRAM, getProfile(renderAPI, GPT_HULL_PROGRAM));
-		}
-
-		if (!passData.domainCode.empty())
-		{
-			passDesc.domainProgram = GpuProgram::create(passData.commonCode + passData.domainCode, "main", language,
-				GPT_DOMAIN_PROGRAM, getProfile(renderAPI, GPT_DOMAIN_PROGRAM));
-		}
-
-		if (!passData.computeCode.empty())
-		{
-			passDesc.computeProgram = GpuProgram::create(passData.commonCode + passData.computeCode, "main", language,
-				GPT_COMPUTE_PROGRAM, getProfile(renderAPI, GPT_COMPUTE_PROGRAM));
-		}
-
-		return Pass::create(passDesc);
 	}
 
-	TechniquePtr BSLFXCompiler::parseTechnique(ASTFXNode* techniqueNode, const Vector<CodeBlock>& codeBlocks)
+	void BSLFXCompiler::parseTechnique(ASTFXNode* techniqueNode, const Vector<CodeBlock>& codeBlocks, TechniqueData& techniqueData)
 	{
 		if (techniqueNode == nullptr || techniqueNode->type != NT_Technique)
-			return nullptr;
+			return;
 
-		Vector<ASTFXNode*> passNodes;
-		StringID renderer = RendererAny;
-		StringID renderAPI = RenderAPIAny;
-		String language;
-		
 		PassData commonPassData;
+		UINT32 nextPassIdx = 0;
 		for (int i = 0; i < techniqueNode->options->count; i++)
 		{
 			NodeOption* option = &techniqueNode->options->entries[i];
@@ -1336,53 +978,71 @@ namespace BansheeEngine
 			switch (option->type)
 			{
 			case OT_Pass:
-				passNodes.push_back(option->value.nodePtr);
+			{
+				UINT32 passIdx = nextPassIdx;
+
+				ASTFXNode* passNode = option->value.nodePtr;
+				for (int j = 0; j < passNode->options->count; j++)
+				{
+					NodeOption* passOption = &passNode->options->entries[j];
+
+					switch (passOption->type)
+					{
+					case OT_Index:
+						passIdx = passOption->value.intValue;
+						break;
+					}
+				}
+
+				PassData* passData = nullptr;
+				for (auto& entry : techniqueData.passes)
+				{
+					if (entry.seqIdx == passIdx)
+						passData = &entry;
+				}
+
+				if (passData == nullptr)
+				{
+					techniqueData.passes.push_back(PassData());
+					passData = &techniqueData.passes.back();
+
+					passData->seqIdx = passIdx;
+				}
+
+				nextPassIdx = std::max(nextPassIdx, passIdx) + 1;
+
+				passData->blendIsDefault &= !parseBlendState(passData->blendDesc, techniqueNode);
+				passData->rasterizerIsDefault &= !parseRasterizerState(passData->rasterizerDesc, techniqueNode);
+				passData->depthStencilIsDefault &= !parseDepthStencilState(passData->depthStencilDesc, techniqueNode);
+
+				passData->vertexCode = commonPassData.vertexCode + passData->vertexCode;
+				passData->fragmentCode = commonPassData.fragmentCode + passData->fragmentCode;
+				passData->geometryCode = commonPassData.geometryCode + passData->geometryCode;
+				passData->hullCode = commonPassData.hullCode + passData->hullCode;
+				passData->domainCode = commonPassData.domainCode + passData->domainCode;
+				passData->commonCode = commonPassData.commonCode + passData->commonCode;
+				
+				parsePass(passNode, codeBlocks, *passData);
+			}
 				break;
 			case OT_Renderer:
-				renderer = parseRenderer(removeQuotes(option->value.strValue));
+				techniqueData.renderer = parseRenderer(removeQuotes(option->value.strValue));
 				break;
 			case OT_Language:
-				parseLanguage(removeQuotes(option->value.strValue), renderAPI, language);
+				parseLanguage(removeQuotes(option->value.strValue), techniqueData.renderAPI, techniqueData.language);
 				break;
 			case OT_Code:
 				parseCodeBlock(option->value.nodePtr, codeBlocks, commonPassData);
 				break;
 			}
 		}
-
-		commonPassData.blendIsDefault = !parseBlendState(commonPassData.blendDesc, techniqueNode);
-		commonPassData.rasterizerIsDefault = !parseRasterizerState(commonPassData.rasterizerDesc, techniqueNode);
-		commonPassData.depthStencilIsDefault = !parseDepthStencilState(commonPassData.depthStencilDesc, techniqueNode);
-
-		UINT32 nextPassIdx = 0;
-		Map<UINT32, PassPtr, std::greater<UINT32>> passes;
-		for (auto& passNode : passNodes)
-		{
-			PassData passDataCopy = commonPassData;
-
-			UINT32 passIdx = nextPassIdx;
-			PassPtr pass = parsePass(passNode, codeBlocks, passDataCopy, renderAPI, language, passIdx);
-
-			if (pass != nullptr)
-				passes[passIdx] = pass;
-
-			nextPassIdx = std::max(nextPassIdx, passIdx) + 1;
-		}
-
-		Vector<PassPtr> orderedPasses;
-		for (auto& KVP : passes)
-			orderedPasses.push_back(KVP.second);
-
-		if (orderedPasses.size() > 0)
-			return Technique::create(renderAPI, renderer, orderedPasses);
-
-		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];
@@ -1536,93 +1196,8 @@ namespace BansheeEngine
 			return output;
 		}
 
-		// Merge all include ASTs
-		std::function<ParseState*(ParseState*, Vector<CodeBlock>&)> parseIncludes = 
-			[&](ParseState* parentParseState, Vector<CodeBlock>& parentCodeBlocks) -> ParseState*
-		{
-			ASTFXNode* node = parentParseState->rootNode;
-			Vector<tuple<ParseState*, Vector<CodeBlock>>> toMerge;
-
-			for (int i = 0; i < node->options->count; i++)
-			{
-				NodeOption* option = &node->options->entries[i];
-
-				if (option->type == OT_Include)
-				{
-					String includePath = removeQuotes(option->value.strValue);
-					HShaderInclude include = ShaderManager::instance().findInclude(includePath);
-
-					if (include != nullptr)
-						include.blockUntilLoaded();
-
-					if (include.isLoaded())
-					{
-						String includeSource = include->getString();
-
-						ParseState* includeParseState = parseStateCreate();
-						Vector<CodeBlock> includeCodeBlocks = parseCodeBlocks(includeSource);
-						parseFX(includeParseState, includeSource.c_str());
-
-						if (includeParseState->hasError > 0)
-						{
-							output.errorMessage = includeParseState->errorMessage;
-							output.errorLine = includeParseState->errorLine;
-							output.errorColumn = includeParseState->errorColumn;
-
-							parseStateDelete(includeParseState);
-							return nullptr;
-						}
-						else
-						{
-							ParseState* combinedIncludeParseState = parseIncludes(includeParseState, includeCodeBlocks);
-
-							toMerge.push_back(make_tuple(combinedIncludeParseState, includeCodeBlocks));
-						}
-					}
-					else
-					{
-						output.errorMessage = "Cannot find shader include file: " + includePath;
-						return nullptr;
-					}
-				}
-			}
-
-			UINT32 size = (UINT32)toMerge.size();
-			if (size > 0)
-			{
-				Vector<CodeBlock>& outputCodeBlocks = get<1>(toMerge[0]);
-				for (UINT32 i = 1; i < size; i++)
-				{
-					mergeAST(get<0>(toMerge[0]), get<0>(toMerge[i])->rootNode, (UINT32)outputCodeBlocks.size());
-
-					const Vector<CodeBlock>& curCodeBlocks = get<1>(toMerge[i]);
-					for (auto& codeBlock : curCodeBlocks)
-						outputCodeBlocks.push_back(codeBlock);
-
-					parseStateDelete(get<0>(toMerge[i]));
-				}
-
-				mergeAST(get<0>(toMerge[0]), node, (UINT32)outputCodeBlocks.size());
-				for (auto& codeBlock : parentCodeBlocks)
-					outputCodeBlocks.push_back(codeBlock);
-
-				parseStateDelete(parentParseState);
-
-				parentCodeBlocks = outputCodeBlocks;
-				return get<0>(toMerge[0]);
-			}
-			else
-				return parentParseState;
-		};
-
-		parseState = parseIncludes(parseState, codeBlocks);
-		if (parseState == nullptr) // Error
-			return output;
-
-		Vector<String> topLevelIncludes;
-
 		SHADER_DESC shaderDesc;
-		Vector<TechniquePtr> techniques;
+		Vector<pair<ASTFXNode*, TechniqueData>> techniqueData;
 
 		for (int i = 0; i < parseState->rootNode->options->count; i++)
 		{
@@ -1630,12 +1205,6 @@ namespace BansheeEngine
 
 			switch (option->type)
 			{
-			case OT_Include:
-			{
-				String includePath = removeQuotes(option->value.strValue);
-				topLevelIncludes.push_back(includePath);
-			}
-				break;
 			case OT_Separable:
 				shaderDesc.separablePasses = option->value.intValue > 1;
 				break;
@@ -1650,10 +1219,22 @@ namespace BansheeEngine
 				break;
 			case OT_Technique:
 			{
-				TechniquePtr technique = parseTechnique(option->value.nodePtr, codeBlocks);
-				if (technique != nullptr)
-					techniques.push_back(technique);
+				auto iterFind = std::find_if(techniqueData.begin(), techniqueData.end(), 
+					[&] (auto x)
+				{
+					return doTechniquesMatch(x.first, option->value.nodePtr);
+				});
+
+				TechniqueData* data = nullptr;
+				if (iterFind != techniqueData.end())
+					data = &iterFind->second;
+				else
+				{
+					techniqueData.push_back(std::make_pair(option->value.nodePtr, TechniqueData()));
+					data = &techniqueData.back().second;
+				}
 
+				parseTechnique(option->value.nodePtr, codeBlocks, *data);
 				break;
 			}
 			case OT_Parameters:
@@ -1665,10 +1246,94 @@ namespace BansheeEngine
 			}
 		}
 
+		Vector<TechniquePtr> techniques;
+		for(auto& entry : techniqueData)
+		{
+			const TechniqueData& techniqueData = entry.second;
+
+			Map<UINT32, PassPtr, std::greater<UINT32>> passes;
+			for (auto& passData : entry.second.passes)
+			{
+				PASS_DESC passDesc;
+
+				if (!passData.blendIsDefault)
+					passDesc.blendState = BlendState::create(passData.blendDesc);
+
+				if (!passData.rasterizerIsDefault)
+					passDesc.rasterizerState = RasterizerState::create(passData.rasterizerDesc);
+
+				if (!passData.depthStencilIsDefault)
+					passDesc.depthStencilState = DepthStencilState::create(passData.depthStencilDesc);
+
+				if (!passData.vertexCode.empty())
+				{
+					passDesc.vertexProgram = GpuProgram::create(passData.commonCode + passData.vertexCode, "main", 
+						techniqueData.language, GPT_VERTEX_PROGRAM, getProfile(techniqueData.renderAPI, GPT_VERTEX_PROGRAM));
+				}
+
+				if (!passData.fragmentCode.empty())
+				{
+					passDesc.fragmentProgram = GpuProgram::create(passData.commonCode + passData.fragmentCode, "main", 
+						techniqueData.language, GPT_FRAGMENT_PROGRAM, getProfile(techniqueData.renderAPI, GPT_FRAGMENT_PROGRAM));
+				}
+
+				if (!passData.geometryCode.empty())
+				{
+					passDesc.geometryProgram = GpuProgram::create(passData.commonCode + passData.geometryCode, "main", 
+						techniqueData.language, GPT_GEOMETRY_PROGRAM, getProfile(techniqueData.renderAPI, GPT_GEOMETRY_PROGRAM));
+				}
+
+				if (!passData.hullCode.empty())
+				{
+					passDesc.hullProgram = GpuProgram::create(passData.commonCode + passData.hullCode, "main", 
+						techniqueData.language, GPT_HULL_PROGRAM, getProfile(techniqueData.renderAPI, GPT_HULL_PROGRAM));
+				}
+
+				if (!passData.domainCode.empty())
+				{
+					passDesc.domainProgram = GpuProgram::create(passData.commonCode + passData.domainCode, "main", 
+						techniqueData.language, GPT_DOMAIN_PROGRAM, getProfile(techniqueData.renderAPI, GPT_DOMAIN_PROGRAM));
+				}
+
+				if (!passData.computeCode.empty())
+				{
+					passDesc.computeProgram = GpuProgram::create(passData.commonCode + passData.computeCode, "main", 
+						techniqueData.language, GPT_COMPUTE_PROGRAM, getProfile(techniqueData.renderAPI, GPT_COMPUTE_PROGRAM));
+				}
+
+				PassPtr pass = Pass::create(passDesc);
+				if (pass != nullptr)
+					passes[passData.seqIdx] = pass;
+			}
+
+			Vector<PassPtr> orderedPasses;
+			for (auto& KVP : passes)
+				orderedPasses.push_back(KVP.second);
+
+			if (orderedPasses.size() > 0)
+			{
+				TechniquePtr technique = Technique::create(techniqueData.renderAPI, techniqueData.renderer, orderedPasses);
+				techniques.push_back(technique);
+			}
+		}
+
+		Vector<String> includes;
+		IncludeLink* includeLink = parseState->includes;
+		while(includeLink != nullptr)
+		{
+			String includeFilename = includeLink->data->filename;
+
+			auto iterFind = std::find(includes.begin(), includes.end(), includeFilename);
+			if (iterFind == includes.end())
+				includes.push_back(includeFilename);
+
+			includeLink = includeLink->next;
+		}
+
 		parseStateDelete(parseState);
 
 		output.shader = Shader::_createPtr(name, shaderDesc, techniques);
-		output.shader->setIncludeFiles(topLevelIncludes);
+		output.shader->setIncludeFiles(includes);
 
 		return output;
 	}

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini