Browse Source

constant bits handling ok.

Nicolas Cannasse 11 năm trước cách đây
mục cha
commit
e7862849ca
15 tập tin đã thay đổi với 516 bổ sung56 xóa
  1. 24 0
      doc/Render.hx
  2. 2 0
      hxsl.hxml
  3. 1 1
      hxsl.hxproj
  4. 52 1
      hxsl/Ast.hx
  5. 36 12
      hxsl/Checker.hx
  6. 1 0
      hxsl/Eval.hx
  7. 31 0
      hxsl/Globals.hx
  8. 5 0
      hxsl/Linker.hx
  9. 8 3
      hxsl/MacroParser.hx
  10. 180 2
      hxsl/Macros.hx
  11. 2 1
      hxsl/Printer.hx
  12. 22 23
      hxsl/Shader.hx
  13. 97 0
      hxsl/SharedShader.hx
  14. 21 0
      hxsl/Types.hx
  15. 34 13
      test/Test.hx

+ 24 - 0
doc/Render.hx

@@ -1,3 +1,27 @@
+// shader setup:
+	
+if( globals.constModified || shader.constModified ) {
+	shader.syncGlobalConsts(globals);
+	// will get/build an instance based on globals + const bits
+	// the instance is the eval'd version of the shader and has an unique ID
+}
+instances.push(shader.instance);
+
+// get/build a linked shader based on the unique instance identifiers and outputs
+// make sure that it's unique (use md5(toString) to prevent dups)
+var linked = getLinkedVersion(instances, ["output.pos", "output.color"]);
+
+shaders.sort(sortByLinkedId)
+for( s in linked ) {
+	if( s != prevId ) {
+		selectShader(s);
+		uploadParams(s.buildGlobals(globals));
+	}
+	// handle params and per-object globals (modelView, modelViewInv)
+	uploadParams(s.buildParams(globals));
+	render();g
+}
+
 // CHECK HOW RENDER PASS ARE DONE
 // CHECK HOW RENDER PASS ARE DONE
 
 
 var passes : Map< String, Array<ObjectPass> > = collecObjectPasses();
 var passes : Map< String, Array<ObjectPass> > = collecObjectPasses();

+ 2 - 0
hxsl.hxml

@@ -5,3 +5,5 @@
 -swf-version 11.6
 -swf-version 11.6
 -main Test
 -main Test
 -dce full
 -dce full
+-lib h3d
+-cp .

+ 1 - 1
hxsl.hxproj

@@ -23,7 +23,7 @@
     <option flashStrict="True" />
     <option flashStrict="True" />
     <option mainClass="Test" />
     <option mainClass="Test" />
     <option enabledebug="False" />
     <option enabledebug="False" />
-    <option additional="-dce full" />
+    <option additional="-dce full&#xA;-lib h3d&#xA;-cp ." />
   </build>
   </build>
   <!-- haxelib libraries -->
   <!-- haxelib libraries -->
   <haxelib>
   <haxelib>

+ 52 - 1
hxsl/Ast.hx

@@ -67,9 +67,10 @@ enum VarKind {
 }
 }
 
 
 enum VarQualifier {
 enum VarQualifier {
-	Const;
+	Const( ?max : Int );
 	Private;
 	Private;
 	Nullable;
 	Nullable;
+	PerObject;
 	Name( n : String );
 	Name( n : String );
 }
 }
 
 
@@ -117,6 +118,7 @@ enum ExprDef {
 }
 }
 
 
 typedef TVar = {
 typedef TVar = {
+	var id : Int;
 	var name : String;
 	var name : String;
 	var type : Type;
 	var type : Type;
 	var kind : VarKind;
 	var kind : VarKind;
@@ -228,6 +230,55 @@ typedef ShaderData = {
 
 
 class Tools {
 class Tools {
 	
 	
+	static var UID = 0;
+	
+	public static function allocVarId() {
+		return ++UID;
+	}
+	
+	public static function getName( v : TVar ) {
+		if( v.qualifiers == null )
+			return v.name;
+		for( q in v.qualifiers )
+			switch( q ) {
+			case Name(n): return n;
+			default:
+			}
+		return v.name;
+	}
+	
+	public static function getConstBits( v : TVar ) {
+		switch( v.type ) {
+		case TBool:
+			return 1;
+		case TInt:
+			for( q in v.qualifiers )
+				switch( q ) {
+				case Const(n):
+					if( n != null ) {
+						var bits = 0;
+						while( n >= 1 << bits )
+							bits++;
+						return bits;
+					}
+					return 8;
+				default:
+				}
+		default:
+		}
+		return 0;
+	}
+	
+	public static function isConst( v : TVar ) {
+		if( v.qualifiers != null )
+			for( q in v.qualifiers )
+				switch( q ) {
+				case Const(_): return true;
+				default:
+				}
+		return false;
+	}
+
 	public static function isStruct( v : TVar ) {
 	public static function isStruct( v : TVar ) {
 		return switch( v.type ) { case TStruct(_): true; default: false; }
 		return switch( v.type ) { case TStruct(_): true; default: false; }
 	}
 	}

+ 36 - 12
hxsl/Checker.hx

@@ -83,7 +83,7 @@ class Checker {
 				if( a.kind == null ) a.kind = Local;
 				if( a.kind == null ) a.kind = Local;
 				if( a.kind != Local ) error("Argument should be local", pos);
 				if( a.kind != Local ) error("Argument should be local", pos);
 				if( a.qualifiers.length != 0 ) error("No qualifier allowed for argument", pos);
 				if( a.qualifiers.length != 0 ) error("No qualifier allowed for argument", pos);
-				{ name : a.name, kind : Local, type : a.type };
+				{ id : 0, name : a.name, kind : Local, type : a.type };
 			}];
 			}];
 			if( args.length != 0 )
 			if( args.length != 0 )
 				switch( f.name ) {
 				switch( f.name ) {
@@ -92,6 +92,7 @@ class Checker {
 				default:
 				default:
 				}
 				}
 			var fv : TVar = {
 			var fv : TVar = {
+				id : 0,
 				name : f.name,
 				name : f.name,
 				kind : Function,
 				kind : Function,
 				type : TFun([{ args : [for( a in args ) { type : a.type, name : a.name }], ret : f.ret == null ? TVoid : f.ret }]),
 				type : TFun([{ args : [for( a in args ) { type : a.type, name : a.name }], ret : f.ret == null ? TVoid : f.ret }]),
@@ -380,6 +381,7 @@ class Checker {
 			switch( loop.t ) {
 			switch( loop.t ) {
 			case TArray(t, _):
 			case TArray(t, _):
 				var v : TVar = {
 				var v : TVar = {
+					id : 0,
 					name : v,
 					name : v,
 					type : t,
 					type : t,
 					kind : Local,
 					kind : Local,
@@ -435,7 +437,14 @@ class Checker {
 			funs.push({ f : f, p : e.pos });
 			funs.push({ f : f, p : e.pos });
 		case EVars(vl):
 		case EVars(vl):
 			for( v in vl ) {
 			for( v in vl ) {
-				if( v.kind == null ) v.kind = Var;
+				if( v.kind == null ) {
+					v.kind = Var;
+					for( q in v.qualifiers )
+						switch( q ) {
+						case Const(_): v.kind = Param;
+						default:
+						}
+				}
 				if( v.expr != null ) error("Cannot initialize variable declaration", v.expr.pos);
 				if( v.expr != null ) error("Cannot initialize variable declaration", v.expr.pos);
 				if( v.type == null ) error("Type required for variable declaration", e.pos);
 				if( v.type == null ) error("Type required for variable declaration", e.pos);
 				if( vars.exists(v.name) ) error("Duplicate var decl '" + v.name + "'", e.pos);
 				if( vars.exists(v.name) ) error("Duplicate var decl '" + v.name + "'", e.pos);
@@ -446,14 +455,35 @@ class Checker {
 		}
 		}
 	}
 	}
 	
 	
-	function makeVar( v : VarDecl, pos : Position ) {
+	function makeVar( v : VarDecl, pos : Position, ?parent : TVar ) {
 		var tv : TVar = {
 		var tv : TVar = {
+			id : 0,
 			name : v.name,
 			name : v.name,
 			kind : v.kind,
 			kind : v.kind,
 			type : v.type,
 			type : v.type,
 		};
 		};
-		if( v.qualifiers.length > 0 )
+		if( parent != null )
+			tv.parent = parent;
+		if( tv.kind == null ) {
+			if( parent == null )
+				tv.kind = Var;
+			else
+				tv.kind = parent.kind;
+		}
+		if( v.qualifiers.length > 0 ) {
 			tv.qualifiers = v.qualifiers;
 			tv.qualifiers = v.qualifiers;
+			for( q in v.qualifiers )
+				switch( q ) {
+				case Private: if( tv.kind != Var ) error("@private only allowed on varying", pos);
+				case Const(_):
+					if( tv.kind != Global && tv.kind != Param ) error("@const only allowed on parameter or global", pos);
+				case PerObject: if( tv.kind != Global ) error("@perObject only allowed on global", pos);
+				case Nullable: if( tv.kind != Param ) error("@nullable only allowed on parameter or global", pos);
+				case Name(_):
+					if( parent != null ) error("Cannot have an explicit name for a structure variable", pos);
+					if( tv.kind != Global ) error("Explicit name is only allowed for global var", pos);
+				}
+		}
 		if( tv.type != null )
 		if( tv.type != null )
 			tv.type = makeVarType(tv.type, tv, pos);
 			tv.type = makeVarType(tv.type, tv, pos);
 		return tv;
 		return tv;
@@ -462,20 +492,14 @@ class Checker {
 	function makeVarType( t : Type, parent : TVar, pos : Position ) {
 	function makeVarType( t : Type, parent : TVar, pos : Position ) {
 		switch( t ) {
 		switch( t ) {
 		case TStruct(vl):
 		case TStruct(vl):
-			var vl = vl.copy();
-			for( v in vl ) {
-				v.parent = parent;
-				if( v.kind == null ) v.kind = parent.kind;
-				v.type = makeVarType(v.type, v, pos);
-			}
-			return TStruct(vl);
+			return TStruct([for( v in vl ) makeVar({ type : v.type, qualifiers : v.qualifiers, name : v.name, kind : v.kind, expr : null },pos,parent)]);
 		case TArray(t, size):
 		case TArray(t, size):
 			var s = switch( size ) {
 			var s = switch( size ) {
 			case SConst(_): size;
 			case SConst(_): size;
 			case SVar(v):
 			case SVar(v):
 				var v2 = vars.get(v.name);
 				var v2 = vars.get(v.name);
 				if( v2 == null ) error("Array size variable not found", pos);
 				if( v2 == null ) error("Array size variable not found", pos);
-				if( !v2.hasQualifier(Const) ) error("Array size variable should be a constant", pos);
+				if( !v2.isConst() ) error("Array size variable should be a constant", pos);
 				SVar(v2);
 				SVar(v2);
 			}
 			}
 			return TArray(makeVarType(t,parent,pos), s);
 			return TArray(makeVarType(t,parent,pos), s);

+ 1 - 0
hxsl/Eval.hx

@@ -30,6 +30,7 @@ class Eval {
 		if( v2 != null )
 		if( v2 != null )
 			return v2;
 			return v2;
 		v2 = {
 		v2 = {
+			id : v.id == 0 ? Tools.allocVarId() : v.id,
 			name : v.name,
 			name : v.name,
 			type : v.type,
 			type : v.type,
 			kind : v.kind,
 			kind : v.kind,

+ 31 - 0
hxsl/Globals.hx

@@ -2,7 +2,38 @@ package hxsl;
 
 
 class Globals {
 class Globals {
 
 
+	var map : Map<Int,Dynamic>;
+	
 	public function new() {
 	public function new() {
+		map = new Map<Int,Dynamic>();
+	}
+	
+	public function set( path : String, v : Dynamic ) {
+		map.set(allocID(path), v);
+	}
+
+	public function get( path : String) : Dynamic {
+		return map.get(allocID(path));
+	}
+
+	public inline function fastSet( id : Int, v : Dynamic ) {
+		map.set(id, v);
+	}
+
+	public inline function fastGet( id : Int ) : Dynamic {
+		return map.get(id);
+	}
+
+	static var ALL = [];
+	static var MAP = new Map();
+	public static function allocID( path : String ) {
+		var id = MAP.get(path);
+		if( id == null ) {
+			id = ALL.length;
+			ALL.push(path);
+			MAP.set(path, id);
+		}
+		return id;
 	}
 	}
 	
 	
 }
 }

+ 5 - 0
hxsl/Linker.hx

@@ -50,6 +50,7 @@ class Linker {
 	var allFuns : Map<Int,TFunction>;
 	var allFuns : Map<Int,TFunction>;
 	var curShader : ShaderInfos;
 	var curShader : ShaderInfos;
 	var shaders : Array<ShaderInfos>;
 	var shaders : Array<ShaderInfos>;
+	var varIdMap : Map<Int,Int>;
 	
 	
 	public function new() {
 	public function new() {
 	}
 	}
@@ -123,10 +124,12 @@ class Linker {
 			} else {
 			} else {
 				mergeVar(key, v, v2.v, p);
 				mergeVar(key, v, v2.v, p);
 				v2.merged.push(v);
 				v2.merged.push(v);
+				varIdMap.set(v.id, v2.id);
 				return v2;
 				return v2;
 			}
 			}
 		}
 		}
 		var v2 : TVar = {
 		var v2 : TVar = {
+			id : v.id,
 			name : v.name,
 			name : v.name,
 			type : v.type,
 			type : v.type,
 			kind : v.kind,
 			kind : v.kind,
@@ -247,6 +250,7 @@ class Linker {
 	
 	
 	public function link( shadersData : Array<ShaderData>, outVars : Array<String> ) : ShaderData {
 	public function link( shadersData : Array<ShaderData>, outVars : Array<String> ) : ShaderData {
 		varMap = new Map();
 		varMap = new Map();
+		varIdMap = new Map();
 		allVars = new Array();
 		allVars = new Array();
 		allFuns = new Map();
 		allFuns = new Map();
 		shaders = [];
 		shaders = [];
@@ -343,6 +347,7 @@ class Linker {
 		// build resulting shader functions
 		// build resulting shader functions
 		function build(name, a:Array<ShaderInfos> ) : TFunction {
 		function build(name, a:Array<ShaderInfos> ) : TFunction {
 			var v : TVar = {
 			var v : TVar = {
+				id : Tools.allocVarId(),
 				name : name,
 				name : name,
 				type : TFun([ { ret : TVoid, args : [] } ]),
 				type : TFun([ { ret : TVoid, args : [] } ]),
 				kind : Function,
 				kind : Function,

+ 8 - 3
hxsl/MacroParser.hx

@@ -16,6 +16,9 @@ class MacroParser {
 		case []:
 		case []:
 		case [ { expr : EConst(CString(n)), pos : pos } ] if( m.name == "var" || m.name == "global" || m.name == "input" ):
 		case [ { expr : EConst(CString(n)), pos : pos } ] if( m.name == "var" || m.name == "global" || m.name == "input" ):
 			v.qualifiers.push(Name(n));
 			v.qualifiers.push(Name(n));
+		case [ { expr : EConst(CInt(n)), pos : pos } ] if( m.name == "const" ):
+			v.qualifiers.push(Const(Std.parseInt(n)));
+			return;
 		default:
 		default:
 			error("Invalid meta parameter", m.pos);
 			error("Invalid meta parameter", m.pos);
 		}
 		}
@@ -29,11 +32,13 @@ class MacroParser {
 		case "input":
 		case "input":
 			if( v.kind == null ) v.kind = Input else error("Duplicate type qualifier", m.pos);
 			if( v.kind == null ) v.kind = Input else error("Duplicate type qualifier", m.pos);
 		case "const":
 		case "const":
-			v.qualifiers.push(Const);
+			v.qualifiers.push(Const());
 		case "private":
 		case "private":
 			v.qualifiers.push(Private);
 			v.qualifiers.push(Private);
 		case "nullable":
 		case "nullable":
 			v.qualifiers.push(Nullable);
 			v.qualifiers.push(Nullable);
+		case "perObject":
+			v.qualifiers.push(PerObject);
 		default:
 		default:
 			error("Unsupported qualifier " + m.name, m.pos);
 			error("Unsupported qualifier " + m.name, m.pos);
 		}
 		}
@@ -69,7 +74,7 @@ class MacroParser {
 			}
 			}
 			var size : Ast.SizeDecl = switch( size ) {
 			var size : Ast.SizeDecl = switch( size ) {
 			case TPExpr({ expr : EConst(CInt(v)) }): SConst(Std.parseInt(v));
 			case TPExpr({ expr : EConst(CInt(v)) }): SConst(Std.parseInt(v));
-			case TPType(TPath( { pack : [], name : name, sub : null, params : [] } )): SVar( { type : null, name : name, kind : null } );
+			case TPType(TPath( { pack : [], name : name, sub : null, params : [] } )): SVar( { id : 0, type : null, name : name, kind : null } );
 			default: null;
 			default: null;
 			}
 			}
 			if( t != null && size != null )
 			if( t != null && size != null )
@@ -88,7 +93,7 @@ class MacroParser {
 					};
 					};
 					for( m in f.meta )
 					for( m in f.meta )
 						applyMeta(m,v);
 						applyMeta(m,v);
-					{ name : v.name, type : v.type, kind : v.kind, qualifiers : v.qualifiers };
+					{ id : 0, name : v.name, type : v.type, kind : v.kind, qualifiers : v.qualifiers };
 				default:
 				default:
 					error("Only variables are allowed in structures", f.pos);
 					error("Only variables are allowed in structures", f.pos);
 				}
 				}

+ 180 - 2
hxsl/Macros.hx

@@ -1,24 +1,202 @@
 package hxsl;
 package hxsl;
 import haxe.macro.Context;
 import haxe.macro.Context;
+import haxe.macro.Expr;
+using hxsl.Ast;
 
 
 class Macros {
 class Macros {
 
 
+	static function makeType( t : Type ) : ComplexType {
+		return switch( t ) {
+		case TVoid: macro : Void;
+		case TVec(_, t):
+			switch( t ) {
+			case VFloat:
+				macro : hxsl.Types.Vec;
+			case VInt:
+				macro : hxsl.Types.IVec;
+			case VBool:
+				macro : hxsl.Types.BVec;
+			}
+		case TStruct(vl):
+			TAnonymous([for( v in vl ) { pos : Context.currentPos(), name : v.name, kind : FVar(makeType(v.type)) } ]);
+		case TSampler2D:
+			macro : hxsl.Types.Sampler2D;
+		case TSamplerCube:
+			macro : hxsl.Types.SamplerCube;
+		case TMat3, TMat3x4, TMat4:
+			macro : hxsl.Types.Matrix;
+		case TString:
+			macro : String;
+		case TInt:
+			macro : Int;
+		case TFloat:
+			macro : Float;
+		case TBool:
+			macro : Bool;
+		case TArray(t, _):
+			var t = makeType(t);
+			macro : Array<$t>;
+		case TFun(_):
+			throw "assert";
+		}
+	}
+	
+	static function makeDef( t : Type, pos : Position ) : haxe.macro.Expr {
+		return switch( t ) {
+		case TFloat, TInt: macro 0;
+		case TVec(_, t):
+			switch( t ) {
+			case VFloat:
+				macro new hxsl.Types.Vec();
+			case VInt:
+				macro new hxsl.Types.IVec();
+			case VBool:
+				macro new hxsl.Types.BVec();
+			}
+		case TStruct(vl):
+			{ expr : EObjectDecl([for( v in vl ) { field : v.name, expr : makeDef(v.type, pos) } ]), pos : pos };
+		case TArray(_):
+			macro new Array();
+		default:
+			null;
+		}
+	}
+	
+	static function getConsts( v : TVar, p : Position, consts : Array<{ pos : Int, bits : Int, v : TVar }> ) {
+		switch( v.type ) {
+		case TStruct(vl):
+			for( v in vl )
+				getConsts(v, p, consts);
+		default:
+			if( v.isConst() ) {
+				var bits = v.getConstBits();
+				if( bits == 0 )
+					Error.t("Unsupported @const type "+v.type.toString(), p);
+				var prev = consts[consts.length - 1];
+				var pos = prev == null ? 0 : prev.pos + prev.bits;
+				if( pos + bits > 32 )
+					Error.t("Too many constants for this shader, reduce int size by specifiying @const(max-value)", p);
+				consts.push({ pos : pos, bits : bits, v : v });
+			}
+		}
+	}
+	
+	static function buildFields( shader : ShaderData, pos : Position ) {
+		var fields = new Array<Field>();
+		var globals = [], consts = [];
+		for( v in shader.vars ) {
+			var cpos = consts.length;
+			getConsts(v, pos, consts);
+			switch( v.kind ) {
+			case Param:
+				var t = makeType(v.type);
+				var f : Field = {
+					name : v.name,
+					pos : pos,
+					kind : FProp("get","set", t, makeDef(v.type,pos)),
+					access : [APublic],
+					meta : [{ name : ":isVar", pos : pos }],
+				};
+				var fget : Field = {
+					name : "get_" + v.name,
+					pos : pos,
+					kind : FFun( {
+						ret : t,
+						args : [],
+						expr : if( consts.length == cpos || (consts.length == cpos+1 && consts[cpos].v == v) )
+							macro return $i{ v.name };
+						else
+							macro {
+								constModified = true;
+								return $i{ v.name };
+							},
+					}),
+					access : [AInline],
+				};
+				var fset : Field = {
+					name : "set_" + v.name,
+					pos : pos,
+					kind : FFun( {
+						ret : t,
+						args : [ { name : "_v", type : t } ],
+						expr : if( consts.length == cpos )
+							macro return $i{ v.name } = _v;
+						else
+							macro {
+								constModified = true;
+								return $i{ v.name } = _v;
+							}
+					}),
+					access : [AInline],
+				};
+				fields.push(f);
+				fields.push(fget);
+				fields.push(fset);
+			case Global:
+				globals.push(v);
+			default:
+			}
+		}
+		// updateConstants
+		var exprs = [];
+		function getPath(v:TVar) {
+			if( v.parent == null )
+				return { expr : haxe.macro.Expr.ExprDef.EConst(CIdent(v.name)), pos : pos };
+			return { expr : haxe.macro.Expr.ExprDef.EField(getPath(v.parent), v.name), pos : pos };
+		}
+		for( c in consts ) {
+			if( c.v.kind == Global ) continue;
+			var p = getPath(c.v);
+			switch( c.v.type ) {
+			case TInt:
+				exprs.push(macro {
+					var v : Int = $p;
+					if( v >>> $v{ c.bits } != 0 ) throw $v{ c.v.name } +" is out of range " + v + ">" + $v{ (1 << c.bits) - 1 };
+					constBits |= v << $v{ c.pos };
+				});
+			case TBool:
+				exprs.push(macro if( $p ) constBits |= 1 << $v{ c.pos } );
+			default:
+				throw "assert";
+			}
+		}
+		fields.push( {
+			name : "updateConstants",
+			pos : pos,
+			kind : FFun({
+				ret : macro : Void,
+				args : [ { name : "globals", type : macro : hxsl.Globals } ],
+				expr : macro {
+					constBits = 0;
+					{$a{ exprs }};
+					super.updateConstants(globals);
+				},
+			}),
+			access : [AOverride],
+		});
+		return fields;
+	}
+	
 	public static function buildShader() {
 	public static function buildShader() {
 		var fields = Context.getBuildFields();
 		var fields = Context.getBuildFields();
 		for( f in fields )
 		for( f in fields )
 			if( f.name == "SRC" ) {
 			if( f.name == "SRC" ) {
 				switch( f.kind ) {
 				switch( f.kind ) {
 				case FVar(_, expr) if( expr != null ):
 				case FVar(_, expr) if( expr != null ):
+					var pos = expr.pos;
+					if( !Lambda.has(f.access, AStatic) ) f.access.push(AStatic);
 					try {
 					try {
 						var shader = new MacroParser().parseExpr(expr);
 						var shader = new MacroParser().parseExpr(expr);
 						var name = Std.string(Context.getLocalClass());
 						var name = Std.string(Context.getLocalClass());
 						var shader = new Checker().check(name,shader);
 						var shader = new Checker().check(name,shader);
 						var str = Serializer.run(shader);
 						var str = Serializer.run(shader);
-						f.kind = FVar(null, { expr : EConst(CString(str)), pos : expr.pos } );
+						f.kind = FVar(null, { expr : EConst(CString(str)), pos : pos } );
 						f.meta.push({
 						f.meta.push({
 							name : ":keep",
 							name : ":keep",
-							pos : expr.pos,
+							pos : pos,
 						});
 						});
+						for( f in buildFields(shader,pos) )
+							fields.push(f);
 					} catch( e : Ast.Error ) {
 					} catch( e : Ast.Error ) {
 						fields.remove(f);
 						fields.remove(f);
 						Context.error(e.msg, e.pos);
 						Context.error(e.msg, e.pos);

+ 2 - 1
hxsl/Printer.hx

@@ -49,9 +49,10 @@ class Printer {
 		if( v.qualifiers != null ) {
 		if( v.qualifiers != null ) {
 			for( q in v.qualifiers )
 			for( q in v.qualifiers )
 				add("@" + (switch( q ) {
 				add("@" + (switch( q ) {
-				case Const: "const";
+				case Const(max): "const" + (max == null ? "" : "("+max+")");
 				case Private: "private";
 				case Private: "private";
 				case Nullable: "nullable";
 				case Nullable: "nullable";
+				case PerObject: "perObject";
 				case Name(n): "name('" + n + "')";
 				case Name(n): "name('" + n + "')";
 				}) + " ");
 				}) + " ");
 		}
 		}

+ 22 - 23
hxsl/Shader.hx

@@ -4,39 +4,38 @@ using hxsl.Ast;
 @:autoBuild(hxsl.Macros.buildShader())
 @:autoBuild(hxsl.Macros.buildShader())
 class Shader {
 class Shader {
 	
 	
-	var shader : Ast.ShaderData;
+	var shader : SharedShader;
+	var instance : SharedShader.ShaderInstance;
+	var constBits : Int;
+	var constModified : Bool;
 	
 	
 	public function new() {
 	public function new() {
 		var cl : Dynamic = std.Type.getClass(this);
 		var cl : Dynamic = std.Type.getClass(this);
 		shader = cl.SHADER;
 		shader = cl.SHADER;
+		constModified = true;
 		if( shader == null ) {
 		if( shader == null ) {
-			shader = haxe.Unserializer.run(cl.SRC);
+			shader = new SharedShader(cl.SRC);
 			cl.SHADER = shader;
 			cl.SHADER = shader;
 		}
 		}
 	}
 	}
 	
 	
-	function initConst( v : Ast.TVar, e : hxsl.Eval ) {
-		switch( v.type ) {
-		case TStruct(vl):
-			for( v in vl )
-				initConst(v, e);
-		default:
-			if( v.hasQualifier(Const) ) {
-				var value = switch( v.type ) {
-				case TBool: CBool(false);
-				case TInt: CInt(0);
-				default: throw "Unsupported const " + v.type.toString();
-				};
-				e.setConstant(v, value);
+	public function updateConstants( globals : Globals ) {
+		for( c in shader.consts )
+			if( c.globalId > 0 ) {
+				var v : Dynamic = globals.fastGet(c.globalId);
+				switch( c.v.type ) {
+				case TInt:
+					var v : Int = v;
+					if( v >>> c.bits != 0 ) throw "Constant " + c.v.name + " is outside range (" + v + " > " + ((1 << c.bits) - 1) + ")";
+					constBits |= v << c.pos;
+				case TBool:
+					var v : Bool = v;
+					if( v ) constBits |= 1 << c.pos;
+				default:
+					throw "assert";
+				}
 			}
 			}
-		}
-	}
-	
-	public function compile() {
-		var e = new hxsl.Eval();
-		for( v in shader.vars )
-			initConst(v, e);
-		return e.eval(shader);
+		instance = shader.getInstance(constBits);
 	}
 	}
 	
 	
 	public function setup( globals : Globals ) {
 	public function setup( globals : Globals ) {

+ 97 - 0
hxsl/SharedShader.hx

@@ -0,0 +1,97 @@
+package hxsl;
+using hxsl.Ast;
+
+class ShaderInstance {
+	public var id : Int;
+	public var shader : ShaderData;
+	public function new(shader) {
+		id = Tools.allocVarId();
+		this.shader = shader;
+	}
+}
+
+class ShaderGlobal {
+	public var v : TVar;
+	public var globalId : Int;
+	public function new(v, gid) {
+		this.v = v;
+		this.globalId = gid;
+	}
+}
+
+class ShaderConst {
+	public var v : TVar;
+	public var pos : Int;
+	public var bits : Int;
+	public var globalId : Int;
+	public function new(v, pos, bits) {
+		this.v = v;
+		this.pos = pos;
+		this.bits = bits;
+	}
+}
+
+class SharedShader {
+	
+	public var data : ShaderData;
+	public var globals : Array<ShaderGlobal>;
+	public var consts : Array<ShaderConst>;
+	var instanceCache : Map<Int,ShaderInstance>;
+	
+	public function new(src:String) {
+		instanceCache = new Map();
+		data = haxe.Unserializer.run(src);
+		consts = [];
+		globals = [];
+		for( v in data.vars )
+			browseVar(v);
+		if( consts.length == 0 )
+			instanceCache.set(0, new ShaderInstance(data));
+	}
+	
+	public function getInstance( constBits : Int ) {
+		var i = instanceCache.get(constBits);
+		if( i != null )
+			return i;
+		var e = new hxsl.Eval();
+		for( c in consts )
+			e.setConstant(c.v, switch( c.v.type ) {
+			case TBool: CBool((constBits >>> c.pos) & 1 != 0);
+			case TInt: CInt((constBits >>> c.pos) & ((1 << c.bits) - 1));
+			default: throw "assert";
+			});
+		i = new ShaderInstance(e.eval(data));
+		instanceCache.set(constBits, i);
+		return i;
+	}
+	
+	function browseVar( v : TVar, ?path : String ) {
+		v.id = Tools.allocVarId();
+		if( path == null )
+			path = v.getName();
+		else
+			path += "." + v.name;
+		switch( v.type ) {
+		case TStruct(vl):
+			for( vs in vl )
+				browseVar(vs,path);
+		default:
+			var globalId = 0;
+			if( v.kind == Global ) {
+				globalId = Globals.allocID(path);
+				globals.push(new ShaderGlobal(v,globalId));
+			}
+			if( !v.isConst() )
+				return;
+			var bits = v.getConstBits();
+			if( bits > 0 ) {
+				var prev = consts[consts.length - 1];
+				var pos = prev == null ? 0 : prev.pos + prev.bits;
+				var c = new ShaderConst(v, pos, bits);
+				c.globalId = globalId;
+				consts.push(c);
+			}
+		}
+	}
+
+}

+ 21 - 0
hxsl/Types.hx

@@ -0,0 +1,21 @@
+package hxsl;
+
+#if h3d
+
+typedef Vec = h3d.Vector;
+typedef IVec = Array<Int>;
+typedef BVec = Array<Bool>;
+typedef Matrix = h3d.Matrix;
+typedef Sampler2D = /*h3d.mat.Texture*/ Dynamic;
+typedef SamplerCube = /*h3d.mat.Texture*/ Dynamic;
+
+#else
+
+typedef Vec = Array<Float>;
+typedef IVec = Array<Int>;
+typedef BVec = Array<Bool>;
+typedef Matrix = Array<Float>;
+typedef Sampler2D = Dynamic;
+typedef SamplerCube = Dynamic;
+
+#end

+ 34 - 13
test/Test.hx

@@ -16,8 +16,8 @@ class Proto extends hxsl.Shader {
 
 
 	@global var global : {
 	@global var global : {
 		var time : Float;
 		var time : Float;
-		var modelView : Mat4;
-		var modelViewInverse : Mat4;
+		@perObject var modelView : Mat4;
+		@perObject var modelViewInverse : Mat4;
 		// ... other available globals in BasePass
 		// ... other available globals in BasePass
 	};
 	};
 	
 	
@@ -58,10 +58,15 @@ class Proto extends hxsl.Shader {
 	}
 	}
 
 
 	};
 	};
+	
+	public function new() {
+		super();
+		color.set(1, 1, 1);
+	}
 
 
 }
 }
 
 
-// ----------------------------------------------------------------------------------------------------------------
+// -------------------buildGlobals()---------------------------------------------------------------------------------------------
 
 
 class VertexColor extends hxsl.Shader {
 class VertexColor extends hxsl.Shader {
 static var SRC = {
 static var SRC = {
@@ -131,13 +136,13 @@ static var SRC = {
 class LightSystem extends hxsl.Shader {
 class LightSystem extends hxsl.Shader {
 static var SRC = {
 static var SRC = {
 
 
-	@global("light.ndirs") @const var NDirLights : Int;
-	@global("light.npoints") @const var NPointLights : Int;
+	@global("light.ndirs") @const(16) var NDirLights : Int;
+	@global("light.npoints") @const(64) var NPointLights : Int;
 	
 	
 	@global var light : {
 	@global var light : {
 		@const var perPixel : Bool;
 		@const var perPixel : Bool;
-		@const var ndirs : Int;
-		@const var npoints : Int;
+		@const(16) var ndirs : Int;
+		@const(64) var npoints : Int;
 		var ambient : Vec3;
 		var ambient : Vec3;
 		var dirs : Array<{ dir : Vec3, color : Vec3 }, NDirLights>;
 		var dirs : Array<{ dir : Vec3, color : Vec3 }, NDirLights>;
 		var points : Array<{ pos : Vec3, color : Vec3, att : Vec3 }, NPointLights>;
 		var points : Array<{ pos : Vec3, color : Vec3, att : Vec3 }, NPointLights>;
@@ -233,7 +238,14 @@ static var SRC = {
 		transformedNormal = skinMatrixes[input.indexes.x].mat3() * (n * input.weights.x) + skinMatrixes[input.indexes.y].mat3() * (n * input.weights.y) + skinMatrixes[input.indexes.z].mat3() * (n * input.weights.z);
 		transformedNormal = skinMatrixes[input.indexes.x].mat3() * (n * input.weights.x) + skinMatrixes[input.indexes.y].mat3() * (n * input.weights.y) + skinMatrixes[input.indexes.z].mat3() * (n * input.weights.z);
 	}
 	}
 
 
-}}
+}
+
+	public function new() {
+		super();
+		MaxBones = 34;
+	}
+
+}
 
 
 // ----------------------------------------------------------------------------------------------------------------
 // ----------------------------------------------------------------------------------------------------------------
 
 
@@ -327,14 +339,23 @@ static var SRC = {
 
 
 class Test {
 class Test {
 	
 	
+	@:access(hxsl)
 	static function main() {
 	static function main() {
 		var shaders = [
 		var shaders = [
-			new Proto().compile(),
-			new Texture().compile(),
-			new AnimatedUV().compile(),
-//			new Outline().compile(),
+			new Proto(),
+			{ var t = new Texture(); t.killAlpha = true; t; },
+			new AnimatedUV(),
+		//	new Outline(),
 		];
 		];
-		var s = new hxsl.Linker().link(shaders, ["output.position", "output.color"]);
+		
+		
+		
+		var globals = new hxsl.Globals();
+		
+		var instances = [for( s in shaders ) { s.updateConstants(globals); s.instance.shader; }];
+		
+		
+		var s = new hxsl.Linker().link(instances, ["output.position", "output.color"]);
 		trace("\n"+hxsl.Printer.shaderToString(s));
 		trace("\n"+hxsl.Printer.shaderToString(s));
 	}
 	}