Ver código fonte

Merge branch 'master' of https://github.com/HeapsIO/heaps

borisrp 8 meses atrás
pai
commit
a288900bef
13 arquivos alterados com 395 adições e 119 exclusões
  1. 8 8
      h2d/domkit/Style.hx
  2. 23 3
      h3d/scene/World.hx
  3. 35 0
      hxd/fs/SourceLoader.hx
  4. 7 0
      hxd/res/AnimGraph.hx
  5. 4 0
      hxd/res/Any.hx
  6. 2 1
      hxd/res/Config.hx
  7. 19 24
      hxsl/Cache.hx
  8. 123 64
      hxsl/Dce.hx
  9. 35 0
      hxsl/Debug.hx
  10. 1 8
      hxsl/Linker.hx
  11. 15 6
      hxsl/Macros.hx
  12. 2 2
      hxsl/Shader.hx
  13. 121 3
      hxsl/SharedShader.hx

+ 8 - 8
h2d/domkit/Style.hx

@@ -525,12 +525,6 @@ class Style extends domkit.CssStyle {
 			var valueLines = [];
 			var files: Array<SourceFile> = [];
 			var lineDigits = 0;
-			var resourcePath = "";
-			if (onInspectHyperlink != null && resources.length > 0 && resources[0].entry is hxd.fs.LocalFileSystem.LocalEntry) {
-				var entry = Std.downcast(resources[0].entry, hxd.fs.LocalFileSystem.LocalEntry);
-				var idx = @:privateAccess entry.file.lastIndexOf("/");
-				if (idx >= 0) resourcePath = @:privateAccess entry.file.substr(0, idx);
-			}
 
 			for( i in 0...dom.currentSet.length ) {
 				if( dom.currentRuleStyles == null || dom.currentRuleStyles[i] == null )
@@ -569,15 +563,21 @@ class Style extends domkit.CssStyle {
 					v = vs.value;
 					var f = find(files, f -> f.name == vs.pos.file);
 					if (f != null) {
+						var res = find(resources, r -> r.name == f.name);
+						var entry = Std.downcast(res?.entry, hxd.fs.LocalFileSystem.LocalEntry);
+						var resDir = "";
+						if (entry != null && @:privateAccess entry.file.lastIndexOf("/") > 0) {
+							resDir = @:privateAccess entry.file.substr(0, entry.file.lastIndexOf("/"));
+						}
 						var pos = getPos(f, vs.pos.pmin);
 						var s = "" + pos.line;
 						if (pos.file == null)
 							lStr = '<font color="#707070">$s</font>';
 						else {
 							var posStr = '${pos.file}:$s';
-							if (onInspectHyperlink != null && resourcePath != null) {
+							if (onInspectHyperlink != null && resDir != null) {
 								var colStr = pos.col >= 0 ? (":" + pos.col) : "";
-								posStr = '<a href="$resourcePath/$posStr$colStr">$posStr</a>';
+								posStr = '<a href="$resDir/$posStr$colStr">$posStr</a>';
 							}
 							lStr = '<font color="#707070">$posStr</font>';
 						}

+ 23 - 3
h3d/scene/World.hx

@@ -211,6 +211,21 @@ class World extends Object {
 	*/
 	public var specularInAlpha = false;
 
+	public var wrap(default, set) : h3d.mat.Data.Wrap = Clamp;
+	public function set_wrap(v : h3d.mat.Data.Wrap) {
+		wrap = v;
+		inline function bigTextureWrap(t : h3d.mat.BigTexture) {
+			if ( t != null && t.tex != null )
+				t.tex.wrap = wrap;
+		}
+		for ( b in bigTextures ) {
+			bigTextureWrap(b.diffuse);
+			bigTextureWrap(b.normal);
+			bigTextureWrap(b.spec);
+		}
+		return wrap;
+	}
+
 	var bigTextureSize = 2048;
 	var defaultDiffuseBG = 0;
 	var defaultNormalBG = 0x8080FF;
@@ -321,6 +336,7 @@ class World extends Object {
 		}
 		if( t == null ) {
 			var b = new h3d.mat.BigTexture(bigTextures.length, bigTextureSize, defaultDiffuseBG);
+			b.tex.wrap = wrap;
 			btex = { diffuse : b, spec : null, normal : null };
 			bigTextures.unshift( btex );
 			t = b.add(rt);
@@ -331,7 +347,7 @@ class World extends Object {
 			if(res != null) {
 				var size = res.getSize();
 				if(size.width != t.width || size.height != t.height)
-					throw 'Texture ${res.entry.path} has different size from diffuse (${size.width}x${size.height})';
+					throw 'Texture ${res.entry.path} has different size ${size.width}x${size.height} from diffuse ${t.width}x${t.height}';
 			}
 		}
 
@@ -345,8 +361,10 @@ class World extends Object {
 					specTex = t;
 				}
 			} else {
-				if( btex.spec == null )
+				if( btex.spec == null ) {
 					btex.spec = new h3d.mat.BigTexture(-1, bigTextureSize, defaultSpecularBG);
+					btex.spec.tex.wrap = wrap;
+				}
 				if( res != null )
 					specTex = btex.spec.add(res);
 				else
@@ -358,8 +376,10 @@ class World extends Object {
 		if( enableNormalMaps ) {
 			var res = resolveNormalMap(texturePath, mat);
 			checkSize(res);
-			if( btex.normal == null )
+			if( btex.normal == null ) {
 				btex.normal = new h3d.mat.BigTexture(-1, bigTextureSize, defaultNormalBG);
+				btex.normal.tex.wrap = wrap;
+			}
 			if( res != null )
 				normalMap = btex.normal.add(res);
 			else

+ 35 - 0
hxd/fs/SourceLoader.hx

@@ -0,0 +1,35 @@
+package hxd.fs;
+
+class SourceLoader {
+
+	static var RELOAD_LFS : Array<hxd.fs.FileSystem> = [];
+	#if sys
+	public static function addLivePath( path : String ) {
+		RELOAD_LFS.push(new hxd.fs.LocalFileSystem(path,""));
+	}
+	public static function addLivePathHaxelib( libs : Array<String> ) {
+		var p = new sys.io.Process("haxelib",["path"].concat(libs));
+		var out = p.stdout.readAll().toString().split("\r\n").join("\n").split("\n");
+		p.exitCode();
+		for( line in out ) {
+			if( line.charCodeAt(0) == "-".code ) continue;
+			addLivePath(line);
+		}
+	}
+	public static function initLivePaths() {
+		addLivePath(".");
+		addLivePathHaxelib(["heaps" #if hide,"hide"#end]);
+	}
+	#end
+
+	public static function isActive() {
+		return RELOAD_LFS.length > 0;
+	}
+
+	public static function resolve( path : String ) {
+		for( fs in RELOAD_LFS )
+			try return fs.get(path) catch( e : hxd.res.NotFound ) {};
+		return null;
+	}
+
+}

+ 7 - 0
hxd/res/AnimGraph.hx

@@ -0,0 +1,7 @@
+package hxd.res;
+
+#if hide
+typedef AnimGraph = hrt.animgraph.Resource;
+#else
+typedef AnimGraph = hxd.res.Resource;
+#end

+ 4 - 0
hxd/res/Any.hx

@@ -55,6 +55,10 @@ class Any extends Resource {
 		return loader.loadCache(entry.path, hxd.res.Prefab);
 	}
 
+	public function toAnimGraph() {
+		return loader.loadCache(entry.path, hxd.res.AnimGraph);
+	}
+
 	public function to<T:hxd.res.Resource>( c : Class<T> ) : T {
 		return loader.loadCache(entry.path, c);
 	}

+ 2 - 1
hxd/res/Config.hx

@@ -27,7 +27,8 @@ class Config {
 		"grd" => "hxd.res.Gradients",
 		#if hide
 		"prefab,fx,fx2d,l3d" => "hxd.res.Prefab",
-		"world" => "hxd.res.World"
+		"world" => "hxd.res.World",
+		"animgraph" => "hxd.res.AnimGraph",
 		#end
 	];
 

+ 19 - 24
hxsl/Cache.hx

@@ -55,11 +55,6 @@ class SearchMap {
 
 class Cache {
 
-	#if shader_debug_dump
-	public static var DEBUG_IDS = false;
-	public static var TRACE = true;
-	#end
-
 	var linkCache : SearchMap;
 	var linkShaders : Map<String, Shader>;
 	var batchShaders : Map<RuntimeShader, { shader : SharedShader, params : RuntimeShader.AllocParam, size : Int }>;
@@ -256,7 +251,7 @@ class Cache {
 			dbg.writeString("----- DATAS ----\n\n");
 			for( s in shaderDatas ) {
 				dbg.writeString("\t\t**** " + s.inst.shader.name + (s.p == 0 ? "" : " P="+s.p)+ " *****\n");
-				dbg.writeString(Printer.shaderToString(s.inst.shader,DEBUG_IDS)+"\n\n");
+				dbg.writeString(Printer.shaderToString(s.inst.shader,Debug.VAR_IDS)+"\n\n");
 			}
 		}
 		//TRACE = shaderId == 0;
@@ -289,17 +284,17 @@ class Cache {
 				checkRec(v);
 		}
 
-		#if debug
-		Printer.check(s,[for( s in shaderDatas ) s.inst.shader]);
-		#end
-
 		#if shader_debug_dump
 		if( dbg != null ) {
 			dbg.writeString("----- LINK ----\n\n");
-			dbg.writeString(Printer.shaderToString(s,DEBUG_IDS)+"\n\n");
+			dbg.writeString(Printer.shaderToString(s,Debug.VAR_IDS)+"\n\n");
 		}
 		#end
 
+		#if debug
+		Printer.check(s,[for( s in shaderDatas ) s.inst.shader]);
+		#end
+
 		var prev = s;
 		var splitter = new hxsl.Splitter();
 		var sl = try splitter.split(s, mode == Batch ) catch( e : Error ) { e.msg += "\n\nin\n\n"+Printer.shaderToString(s); throw e; };
@@ -318,35 +313,35 @@ class Cache {
 			}
 
 
-		#if debug
-		for( s in sl )
-			Printer.check(s,[prev]);
-		#end
-
 		#if shader_debug_dump
 		if( dbg != null ) {
 			dbg.writeString("----- SPLIT ----\n\n");
 			for( s in sl )
-				dbg.writeString(Printer.shaderToString(s, DEBUG_IDS) + "\n\n");
+				dbg.writeString(Printer.shaderToString(s, Debug.VAR_IDS) + "\n\n");
 		}
 		#end
 
-		var prev = sl;
-		var sl = new hxsl.Dce().dce(sl);
-
 		#if debug
-		for( i => s in sl )
-			Printer.check(s,[prev[i]]);
+		for( s in sl )
+			Printer.check(s,[prev]);
 		#end
 
+		var prev = sl;
+		var sl = new hxsl.Dce().dce(sl);
+
 		#if shader_debug_dump
 		if( dbg != null ) {
 			dbg.writeString("----- DCE ----\n\n");
 			for( s in sl )
-				dbg.writeString(Printer.shaderToString(s, DEBUG_IDS) + "\n\n");
+				dbg.writeString(Printer.shaderToString(s, Debug.VAR_IDS) + "\n\n");
 		}
 		#end
 
+		#if debug
+		for( i => s in sl )
+			Printer.check(s,[prev[i]]);
+		#end
+
 		var r = buildRuntimeShader(sl, paramVars);
 		r.mode = mode;
 
@@ -354,7 +349,7 @@ class Cache {
 		if( dbg != null ) {
 			dbg.writeString("----- FLATTEN ----\n\n");
 			for( s in r.getShaders() )
-				dbg.writeString(Printer.shaderToString(s.data, DEBUG_IDS) + "\n\n");
+				dbg.writeString(Printer.shaderToString(s.data, Debug.VAR_IDS) + "\n\n");
 		}
 		#end
 

+ 123 - 64
hxsl/Dce.hx

@@ -1,25 +1,62 @@
 package hxsl;
 using hxsl.Ast;
+import hxsl.Debug.trace in debug;
 
 private class Exit {
 	public function new() {
 	}
 }
 
+private class VarRel {
+	public var v : VarDeps;
+	public var fields : Int = 0;
+	public function new(v) {
+		this.v = v;
+	}
+}
+
 private class VarDeps {
 	public var v : TVar;
-	public var keep : Bool;
-	public var used : Bool;
-	public var deps : Map<Int,VarDeps>;
-	public var adeps : Array<VarDeps>;
+	public var keep : Int = 0;
+	public var used : Int = 0;
+	public var deps : Map<Int,VarRel>;
+	public var adeps : Array<VarRel>;
 	public function new(v) {
 		this.v = v;
-		used = false;
 		deps = new Map();
 		adeps = [];
 	}
 }
 
+private class WriteTo {
+	public var vars : Array<VarDeps>;
+	public var bits : Array<Int>;
+	public function new() {
+		vars = [];
+		bits = [];
+	}
+	public function push(v,b) {
+		vars.push(v);
+		bits.push(b);
+	}
+	public function pop() {
+		vars.pop();
+		bits.pop();
+	}
+	public function append( v: VarDeps, b : Int ) {
+		for( i => v2 in vars )
+			if( v2 == v ) {
+				bits[i] |= b;
+				return;
+			}
+		push(v,b);
+	}
+	public function appendTo( w : WriteTo ) {
+		for( i => v in vars )
+			w.append(v, bits[i]);
+	}
+}
+
 class Dce {
 
 	var used : Map<Int,VarDeps>;
@@ -29,13 +66,6 @@ class Dce {
 	public function new() {
 	}
 
-	inline function debug( msg : String, ?pos : haxe.PosInfos ) {
-		#if shader_debug_dump
-		if( Cache.TRACE )
-			haxe.Log.trace(msg, pos);
-		#end
-	}
-
 	public function dce( shaders : Array<ShaderData> ) {
 		// collect vars dependencies
 		used = new Map();
@@ -48,14 +78,14 @@ class Dce {
 				if( v.kind == Input )
 					inputs.push(i);
 				if( v.kind == Output || v.type.match(TBuffer(_,_,RW) | TRWTexture(_)) )
-					i.keep = true;
+					i.keep = 15;
 			}
 		}
 
 		// collect dependencies
 		for( s in shaders ) {
 			for( f in s.funs )
-				check(f.expr, [], []);
+				check(f.expr, new WriteTo(), new WriteTo());
 		}
 
 		var outExprs = [];
@@ -64,13 +94,13 @@ class Dce {
 			debug("DCE LOOP");
 
 			for( v in used )
-				if( v.keep )
-					markRec(v);
+				if( v.keep != 0 )
+					markRec(v, v.keep);
 
-			while( inputs.length > 1 && !inputs[inputs.length - 1].used )
+			while( inputs.length > 1 && inputs[inputs.length - 1].used == 0 )
 				inputs.pop();
 			for( v in inputs )
-				markRec(v);
+				markRec(v, 15);
 
 			outExprs = [];
 			for( s in shaders ) {
@@ -91,7 +121,7 @@ class Dce {
 		}
 
 		for( v in used ) {
-			if( v.used ) continue;
+			if( v.used != 0 ) continue;
 			if( v.v.kind == VarKind.Input) continue;
 			for( s in shaders )
 				s.vars.remove(v.v);
@@ -109,82 +139,109 @@ class Dce {
 		return vd;
 	}
 
-	function markRec( v : VarDeps ) {
-		if( v.used ) return;
-		debug(v.v.name+" is used");
-		v.used = true;
-		for( d in v.adeps )
-			markRec(d);
+	function varName( v : TVar, bits = 15 ) {
+		return Debug.varName(v, bits);
 	}
 
-	function link( v : TVar, writeTo : Array<VarDeps> ) {
+	function markRec( v : VarDeps, bits : Int ) {
+		if( v.used & bits == bits ) return;
+		bits &= ~v.used;
+		debug(varName(v.v,bits)+" is used");
+		v.used |= bits;
+		for( d in v.adeps ) {
+			var mask = makeFieldsBits(15, bits);
+			if( d.fields & mask != 0 )
+				markRec(d.v, extractFieldsBits(d.fields,bits));
+		}
+	}
+
+	function extractFieldsBits( fields : Int, write : Int ) {
+		return ((write & 1 == 0 ? 0 : fields) | (write & 2 == 0 ? 0 : fields>>4) | (write & 4 == 0 ? 0 : fields>>8) | (write & 8 == 0 ? 0 : fields>>12)) & 15;
+	}
+
+	function makeFieldsBits( read : Int, write : Int ) {
+		return read * ((write & 1) + ((write & 2) << 3) + ((write & 4) << 6) + ((write & 8) << 9));
+	}
+
+	function link( v : TVar, writeTo : WriteTo, bits = 15 ) {
 		var vd = get(v);
-		for( w in writeTo ) {
+		for( i => w in writeTo.vars ) {
 			if( w == null ) {
 				// mark for conditional
-				if( !vd.keep ) {
-					debug("Force keep "+vd.v.name);
-					vd.keep = true;
+				if( vd.keep == 0 ) {
+					debug("Force keep "+varName(vd.v));
+					vd.keep = 15;
 					markAsKeep = true;
 				}
 				continue;
 			}
-			if( !w.deps.exists(v.id) ) {
-				debug(w.v.name+" depends on "+vd.v.name);
-				w.deps.set(v.id, vd);
-				w.adeps.push(vd);
+			var d = w.deps.get(v.id);
+			if( d == null ) {
+				d = new VarRel(vd);
+				w.deps.set(v.id, d);
+				w.adeps.push(d);
+			}
+			var fields = makeFieldsBits(bits, writeTo.bits[i]);
+			if( d.fields & fields != fields ) {
+				d.fields |= fields;
+				debug(varName(w.v,writeTo.bits[i])+" depends on "+varName(vd.v,bits));
 			}
 		}
 	}
 
-	function check( e : TExpr, writeTo : Array<VarDeps>, isAffected : Array<VarDeps> ) : Void {
+	function swizBits( sw : Array<Component> ) {
+		var b = 0;
+		for( c in sw )
+			b |= 1 << c.getIndex();
+		return b;
+	}
+
+	function check( e : TExpr, writeTo : WriteTo, isAffected : WriteTo ) : Void {
 		switch( e.e ) {
 		case TVar(v):
 			link(v, writeTo);
-		case TBinop(OpAssign | OpAssignOp(_), { e : (TVar(v) | TSwiz( { e : TVar(v) }, _)) }, e):
+		case TSwiz({ e : TVar(v) }, swiz):
+			link(v, writeTo, swizBits(swiz));
+		case TBinop(OpAssign | OpAssignOp(_), { e : TVar(v) }, e):
 			var v = get(v);
-			writeTo.push(v);
+			writeTo.push(v,15);
 			check(e, writeTo, isAffected);
 			writeTo.pop();
-			if( isAffected.indexOf(v) < 0 )
-				isAffected.push(v);
+			isAffected.append(v,15);
+		case TBinop(OpAssign | OpAssignOp(_), { e : TSwiz({ e : TVar(v) },swiz) }, e):
+			var v = get(v);
+			var bits = swizBits(swiz);
+			writeTo.push(v,bits);
+			check(e, writeTo, isAffected);
+			writeTo.pop();
+			isAffected.append(v,bits);
 		case TBinop(OpAssign | OpAssignOp(_), { e : (TArray({ e: TVar(v) }, i) | TSwiz({ e : TArray({ e : TVar(v) }, i) },_) | TField({e : TArray({ e : TVar(v) }, i) }, _)) }, e):
 			var v = get(v);
-			writeTo.push(v);
+			writeTo.push(v,15);
 			check(i, writeTo, isAffected);
 			check(e, writeTo, isAffected);
 			writeTo.pop();
-			if( isAffected.indexOf(v) < 0 )
-				isAffected.push(v);
+			isAffected.append(v,15);
 		case TBlock(el):
-			var noWrite = [];
+			var noWrite = new WriteTo();
 			for( i in 0...el.length )
 				check(el[i],i < el.length - 1 ? noWrite : writeTo, isAffected);
 		case TVarDecl(v, init) if( init != null ):
-			writeTo.push(get(v));
+			writeTo.push(get(v),15);
 			check(init, writeTo, isAffected);
 			writeTo.pop();
 		case TIf(e, eif, eelse):
-			var affect = [];
+			var affect = new WriteTo();
 			check(eif, writeTo, affect);
 			if( eelse != null ) check(eelse, writeTo, affect);
-			var len = affect.length;
-			for( v in writeTo )
-				if( affect.indexOf(v) < 0 )
-					affect.push(v);
+			affect.appendTo(isAffected);
+			writeTo.appendTo(affect);
 			check(e, affect, isAffected);
-			for( i in 0...len ) {
-				var v = affect[i];
-				if( isAffected.indexOf(v) < 0 )
-					isAffected.push(v);
-			}
 		case TFor(v, it, loop):
-			var affect = [];
+			var affect = new WriteTo();
 			check(loop, writeTo, affect);
+			affect.appendTo(isAffected);
 			check(it, affect, isAffected);
-			for( v in affect )
-				if( isAffected.indexOf(v) < 0 )
-					isAffected.push(v);
 		case TCall({ e : TGlobal(ChannelRead) }, [{ e : TVar(c) }, uv, { e : TConst(CInt(cid)) }]):
 			check(uv, writeTo, isAffected);
 			if( channelVars[cid] == null ) {
@@ -204,12 +261,11 @@ class Dce {
 			}
 		case TCall({ e : TGlobal(ImageStore)}, [{ e : TVar(v) }, uv, val]):
 			var v = get(v);
-			writeTo.push(v);
+			writeTo.push(v,15);
 			check(uv, writeTo, isAffected);
 			check(val, writeTo, isAffected);
 			writeTo.pop();
-			if( isAffected.indexOf(v) < 0 )
-				isAffected.push(v);
+			isAffected.append(v,15);
 		default:
 			e.iter(check.bind(_, writeTo, isAffected));
 		}
@@ -219,8 +275,9 @@ class Dce {
 		// found a branch with side effect left, this condition vars needs to be kept
 		switch( e.e ) {
 		case TIf(cond, _, _):
-			var writeTo = [null];
-			check(cond, writeTo, []);
+			var writeTo = new WriteTo();
+			writeTo.append(null,0);
+			check(cond, writeTo, new WriteTo());
 		default:
 		}
 		e.iter(checkBranches);
@@ -239,7 +296,9 @@ class Dce {
 				count++;
 			}
 			return { e : TBlock(out), p : e.p, t : e.t };
-		case TVarDecl(v,_) | TBinop(OpAssign | OpAssignOp(_), { e : (TVar(v) | TSwiz( { e : TVar(v) }, _) | TArray( { e : TVar(v) }, _)) }, _) if( !get(v).used ):
+		case TVarDecl(v,_) | TBinop(OpAssign | OpAssignOp(_), { e : (TVar(v) | TSwiz( { e : TVar(v) }, _) | TArray( { e : TVar(v) }, _)) }, _) if( get(v).used == 0 ):
+			return { e : TConst(CNull), t : e.t, p : e.p };
+		case TBinop(OpAssign | OpAssignOp(_), { e : TSwiz( { e : TVar(v) }, swiz) }, _) if( get(v).used & swizBits(swiz) == 0 ):
 			return { e : TConst(CNull), t : e.t, p : e.p };
 		case TCall({ e : TGlobal(ChannelRead) }, [_, uv, { e : TConst(CInt(cid)) }]):
 			var c = channelVars[cid];

+ 35 - 0
hxsl/Debug.hx

@@ -0,0 +1,35 @@
+package hxsl;
+
+class Debug {
+
+	public static var VAR_IDS = #if shader_debug_var_ids true #else false #end;
+	public static var TRACE = #if shader_debug_dump true #else false #end;
+
+	public static macro function trace(str) {
+		return macro if( hxsl.Debug.TRACE ) trace($str);
+	}
+
+	public static function varName( v : Ast.TVar, swizBits = 15 ) {
+		var name = v.name;
+		if( swizBits != 15 ) name += swizStr(swizBits);
+		return VAR_IDS ? v.name+"@"+v.id : v.name;
+	}
+
+	static function swizStr( bits : Int ) {
+		var str = ".";
+		if( bits & 1 != 0 ) str += "x";
+		if( bits & 2 != 0 ) str += "y";
+		if( bits & 4 != 0 ) str += "z";
+		if( bits & 8 != 0 ) str += "w";
+		return str;
+	}
+
+	public static macro function traceDepth(str) {
+		return macro if( hxsl.Debug.TRACE ) {
+			var msg = $str;
+			for( i in 0...debugDepth ) msg = "    " + msg;
+			trace(msg);
+		};
+	}
+
+}

+ 1 - 8
hxsl/Linker.hx

@@ -1,5 +1,6 @@
 package hxsl;
 using hxsl.Ast;
+import hxsl.Debug.traceDepth in debug;
 
 private class AllocatedVar {
 	public var id : Int;
@@ -62,14 +63,6 @@ class Linker {
 		this.mode = mode;
 	}
 
-	inline function debug( msg : String, ?pos : haxe.PosInfos ) {
-		#if shader_debug_dump
-		if( Cache.TRACE ) {
-			for( i in 0...debugDepth ) msg = "    " + msg; haxe.Log.trace(msg, pos);
-		}
-		#end
-	}
-
 	function error( msg : String, p : Position ) : Dynamic {
 		return Error.t(msg, p);
 	}

+ 15 - 6
hxsl/Macros.hx

@@ -403,11 +403,12 @@ class Macros {
 				case FVar(_, expr) if( expr != null ):
 					var pos = expr.pos;
 					if( !Lambda.has(f.access, AStatic) ) f.access.push(AStatic);
-					Context.getLocalClass().get().meta.add(":src", [expr], pos);
+					var cl = Context.getLocalClass();
+					var c = cl.get();
+					c.meta.add(":src", [expr], pos);
 					try {
 						var shader = new MacroParser().parseExpr(expr);
-						var c = Context.getLocalClass();
-						var csup = c.get().superClass;
+						var csup = c.superClass;
 						var supFields = new Map();
 						// add auto extends
 						do {
@@ -427,13 +428,12 @@ class Macros {
 							supFields.remove("clone");
 							csup = tsup.superClass;
 						} while( true);
-						var name = Std.string(c);
-						var check = new Checker();
+						var className = Std.string(cl);						var check = new Checker();
 						check.loadShader = loadShader;
 						check.warning = function(msg,pos) {
 							haxe.macro.Context.warning(msg, pos);
 						};
-						var shader = check.check(name, shader);
+						var shader = check.check(className, shader);
 						//Printer.check(shader);
 						var str = Context.defined("display") ? "" : Serializer.run(shader);
 						f.kind = FVar(null, { expr : EConst(CString(str)), pos : pos } );
@@ -444,6 +444,15 @@ class Macros {
 						for( f in buildFields(shader, check.inits, pos) )
 							if( !supFields.exists(f.name) )
 								fields.push(f);
+
+						fields.push( {
+							name : "_MODULE",
+							kind : FVar(null, macro $v{c.module}),
+							pos : pos,
+							access : [AStatic],
+							meta : [{ name : ":keep", pos : pos }],
+						});
+
 					} catch( e : Ast.Error ) {
 						fields.remove(f);
 						Context.error(e.msg, e.pos);

+ 2 - 2
hxsl/Shader.hx

@@ -28,7 +28,7 @@ class Shader {
 				throw std.Type.getClassName(cl) + " has no shader source";
 			shader = curClass._SHADER;
 			if( shader == null ) {
-				shader = new SharedShader(curClass.SRC);
+				shader = new SharedShader(curClass.SRC,curClass._MODULE);
 				curClass._SHADER = shader;
 			}
 		}
@@ -50,7 +50,7 @@ class Shader {
 		throw "assert"; // will be subclassed in sub shaders
 		return 0.;
 	}
-	
+
 	public function setParamIndexValue( index : Int, val : Dynamic ) {
 		throw "assert"; // will be subclassed in sub shaders
 	}

+ 121 - 3
hxsl/SharedShader.hx

@@ -36,26 +36,32 @@ class ShaderConst {
 }
 
 class SharedShader {
-
 	public static var UNROLL_LOOPS = false;
+	static var SHADER_RESOLVE : Map<String, SharedShader> = [];
 
 	public var data : ShaderData;
 	public var globals : Array<ShaderGlobal>;
 	public var consts : ShaderConst;
 	var instanceCache : Map<Int,ShaderInstance>;
 	var paramsCount : Int;
+	var file : hxd.fs.FileEntry;
+	var module : String;
 
-	public function new(src:String) {
+	public function new(src:String,?module:String) {
 		instanceCache = new Map();
 		consts = null;
 		globals = [];
 		if( src == "" )
 			return;
+		this.module = module;
 		data = new hxsl.Serializer().unserialize(src);
 		for( v in data.vars )
 			initVarId(v);
 		data = compactMem(data);
 		initialize();
+		#if !macro
+		initLiveReload();
+		#end
 	}
 
 	function initialize() {
@@ -177,7 +183,7 @@ class SharedShader {
 				}
 			default:
 			}
-		} 
+		}
 		eval.inlineCalls = true;
 		eval.unrollLoops = UNROLL_LOOPS;
 		var edata = eval.eval(data);
@@ -257,6 +263,118 @@ class SharedShader {
 		}
 	}
 
+	#if !macro
+
+	function initLiveReload() {
+		if( module == null )
+			return;
+		if( !hxd.fs.SourceLoader.isActive() )
+			return;
+		SHADER_RESOLVE.set(data.name, this);
+		var path = module.split(".").join("/")+".hx";
+		file = hxd.fs.SourceLoader.resolve(path);
+		if( file != null )
+			file.watch(onFileReload);
+	}
+
+	function onFileReload() {
+		// reload all shaders within the same file
+		for( sh in SHADER_RESOLVE )
+			if( sh.file == file )
+				sh.reloadShader();
+	}
+
+	function reloadShader() {
+		try {
+			var expr = loadShader(file, data.name);
+			if( expr == null )
+				return;
+			var checker = new hxsl.Checker();
+			checker.loadShader = function(name) {
+				var sh = SHADER_RESOLVE.get(name);
+				if( sh == null )
+					throw "Could not resolve shader "+name;
+				if( sh.file == null )
+					throw "Shader "+name+" can't be live reload because of missing live path";
+				return loadShader(sh.file, sh.data.name);
+			};
+			var data = checker.check(data.name,expr);
+			applyChanges(data);
+		} catch( e : hxsl.Ast.Error ) {
+			var line = file.getText().substr(0,e.pos.min).split("\n").length;
+			#if sys
+			Sys.println(e.pos.file+":"+line+": "+e.msg);
+			#else
+			haxe.Log.trace(e.msg,{ methodName: null, className: null, fileName : e.pos.file, lineNumber : line });
+			#end
+			return;
+		}
+	}
+
+	static function mergeVars( vl : Array<TVar>, vl2 : Array<TVar> ) {
+		if( vl.length != vl2.length )
+			return false;
+		for( i => v in vl ) {
+			var v2 = vl2[i];
+			if( v.name != v2.name )
+				return false;
+			v2.id = v.id; // copy ids
+			switch( [v.type, v2.type] ) {
+			case [TStruct(vl),TStruct(vl2)]:
+				if( vl.length != vl2.length ) return false;
+				if( !mergeVars(vl, vl2) )
+					return false;
+			default:
+			}
+		}
+		return true;
+	}
+
+	function applyChanges( data2 : ShaderData ) {
+		if( !mergeVars(data.vars, data2.vars) )
+			return false;
+		data = compactMem(data2);
+		instanceCache = new Map();
+		return true;
+	}
+
+	static function loadShader( fs : hxd.fs.FileEntry, name : String ) : Ast.Expr {
+		var text = fs.getText();
+		#if !hscript
+		throw "Shader live reload requires --library hscript";
+		return null;
+		#else
+		var parser = new hscript.Parser();
+		var m = try parser.parseModule(text,fs.path) catch( e : hscript.Expr.Error ) {
+			Sys.println(e.toString());
+			return null;
+		}
+		var clName = name.split(".").pop();
+		var found = null;
+		for( def in m )
+			switch( def ) {
+			case DClass(c) if( c.name == clName ):
+				for( f in c.fields )
+					if( f.name == "SRC" ) {
+						switch( f.kind ) {
+						case KVar(v): found = v.expr;
+						default:
+						}
+						break;
+					}
+			default:
+			}
+		// no matching class with SRC found in this module
+		if( found == null )
+			return null;
+		var expr = new hscript.Macro({ file : fs.path, min : 0, max : 0 }).convert(found);
+		var expr = new hxsl.MacroParser().parseExpr(expr);
+		return expr;
+		#end
+	}
+
+	#end
+
 	public static function compactMem<T>( mem : T ) {
 		#if (hl && heaps_compact_mem)
 		mem = hl.Api.compact(mem, null, 0, null);