Przeglądaj źródła

added @perInstance for MeshBatch (close #948)

Nicolas Cannasse 4 lat temu
rodzic
commit
5622b5c4df
6 zmienionych plików z 156 dodań i 106 usunięć
  1. 5 78
      h3d/scene/MeshBatch.hx
  2. 1 1
      h3d/shader/ColorMult.hx
  3. 3 0
      hxsl/BatchShader.hx
  4. 138 18
      hxsl/Cache.hx
  5. 8 8
      hxsl/CacheFile.hx
  6. 1 1
      hxsl/Checker.hx

+ 5 - 78
h3d/scene/MeshBatch.hx

@@ -19,10 +19,6 @@ private class BatchData {
 
 }
 
-interface MeshBatchAccess {
-	var perInstance : Bool;
-}
-
 /**
 	h3d.scene.MeshBatch allows to draw multiple meshed in a single draw call.
 	See samples/MeshBatch.hx for an example.
@@ -35,17 +31,10 @@ class MeshBatch extends Mesh {
 	var indexCount : Int;
 	var modelViewID = hxsl.Globals.allocID("global.modelView");
 	var modelViewInverseID = hxsl.Globals.allocID("global.modelViewInverse");
-	var colorSave = new h3d.Vector();
-	var colorMult : h3d.shader.ColorMult;
 	var needUpload = false;
 
 	static var MAX_BUFFER_ELEMENTS = 4096;
 
-	/**
-		Tells if we can use material.color as a global multiply over each instance color (default: true)
-	**/
-	public var allowGlobalMaterialColor : Bool = true;
-
 	/**
 	 * 	If set, use this position in emitInstance() instead MeshBatch absolute position
 	**/
@@ -95,56 +84,13 @@ class MeshBatch extends Mesh {
 
 			var manager = cast(ctx,h3d.pass.Default).manager;
 			var shaders = p.getShadersRec();
-
-			// Keep only batched shader
-			var batchShaders : ShaderList = shaders;
-			var prev = null;
-			var cur = batchShaders;
-			while( cur != null ) {
-				if( hxd.impl.Api.isOfType(cur.s, MeshBatchAccess) ) {
-					var access : MeshBatchAccess = cast cur.s;
-					if( !access.perInstance ) {
-						if( prev != null )
-							prev.next = cur.next;
-						else
-							batchShaders = cur.next;
-						cur = cur.next;
-					}
-					else {
-						prev = cur;
-						cur = cur.next;
-					}
-				}
-				else {
-					prev = cur;
-					cur = cur.next;
-				}
-			}
-
-			var rt = manager.compileShaders(batchShaders, false);
-			var shader = manager.shaderCache.makeBatchShader(rt);
+			var rt = manager.compileShaders(shaders, false);
+			var shader = manager.shaderCache.makeBatchShader(rt, shaders);
 
 			var b = new BatchData();
-			b.paramsCount = rt.vertex.paramsSize + rt.fragment.paramsSize;
+			b.paramsCount = shader.paramsSize;
 			b.maxInstance = Std.int(MAX_BUFFER_ELEMENTS / b.paramsCount);
-			b.params = rt.fragment.params == null ? null : rt.fragment.params.clone();
-
-			var hd = b.params;
-			while( hd != null ) {
-				hd.pos += rt.vertex.paramsSize << 2;
-				hd = hd.next;
-			}
-
-			if( b.params == null )
-				b.params = rt.vertex.params;
-			else if( rt.vertex != null ) {
-				var vl = rt.vertex.params.clone();
-				var hd = vl;
-				while( vl.next != null ) vl = vl.next;
-				vl.next = b.params;
-				b.params = hd;
-			}
-
+			b.params = shader.params;
 			b.shader = shader;
 			b.pass = p;
 			b.shaders = [null/*link shader*/];
@@ -154,7 +100,7 @@ class MeshBatch extends Mesh {
 			b.next = dataPasses;
 			dataPasses = b;
 
-			var sl = batchShaders;
+			var sl = shaders;
 			while( sl != null ) {
 				b.shaders.push(sl.s);
 				sl = sl.next;
@@ -173,27 +119,10 @@ class MeshBatch extends Mesh {
 	}
 
 	public function begin( emitCountTip = -1, resizeDown = false ) {
-		colorSave.load(material.color);
-
 		curInstances = 0;
 		if( shadersChanged ) {
-			if( colorMult != null ) {
-				material.mainPass.removeShader(colorMult);
-				colorMult = null;
-			}
 			initShadersMapping();
 			shadersChanged = false;
-			if( allowGlobalMaterialColor ) {
-				if( colorMult == null ) {
-					colorMult = new h3d.shader.ColorMult();
-					material.mainPass.addShader(colorMult);
-				}
-			} else {
-				if( colorMult != null ) {
-					material.mainPass.removeShader(colorMult);
-					colorMult = null;
-				}
-			}
 		}
 
 		if( emitCountTip < 0 )
@@ -299,7 +228,6 @@ class MeshBatch extends Mesh {
 			syncData(p);
 			p = p.next;
 		}
-		if( allowGlobalMaterialColor ) material.color.load(colorSave);
 		curInstances++;
 	}
 
@@ -332,7 +260,6 @@ class MeshBatch extends Mesh {
 			p = p.next;
 		}
 		needUpload = false;
-		if( colorMult != null ) colorMult.color.load(material.color);
 	}
 
 	override function draw(ctx:RenderContext) {

+ 1 - 1
h3d/shader/ColorMult.hx

@@ -5,7 +5,7 @@ class ColorMult extends hxsl.Shader {
 	static var SRC = {
 		var pixelColor : Vec4;
 
-		@param var color : Vec4;
+		@perInstance @param var color : Vec4;
 		@param var amount : Float = 1.0;
 
 		function fragment() {

+ 3 - 0
hxsl/BatchShader.hx

@@ -7,4 +7,7 @@ class BatchShader extends hxsl.Shader {
 		@param var Batch_Buffer : Buffer<Vec4,Batch_Count>;
 	};
 
+	public var params : RuntimeShader.AllocParam;
+	public var paramsSize : Int;
+
 }

+ 138 - 18
hxsl/Cache.hx

@@ -18,7 +18,7 @@ class Cache {
 
 	var linkCache : SearchMap;
 	var linkShaders : Map<String, Shader>;
-	var batchShaders : Map<Int, SharedShader>;
+	var batchShaders : Map<Int, { shader : SharedShader, params : RuntimeShader.AllocParam, size : Int }>;
 	var byID : Map<String, RuntimeShader>;
 	public var constsToGlobal : Bool;
 
@@ -457,18 +457,29 @@ class Cache {
 		return c;
 	}
 
-	public function makeBatchShader( rt : RuntimeShader ) : BatchShader {
+	public function makeBatchShader( rt : RuntimeShader, shaders ) : BatchShader {
 		var sh = batchShaders.get(rt.id);
 		if( sh == null ) {
-			sh = createBatchShader(rt);
+			sh = createBatchShader(rt, shaders);
 			batchShaders.set(rt.id,sh);
 		}
 		var shader = std.Type.createEmptyInstance(BatchShader);
-		@:privateAccess shader.shader = sh;
+		@:privateAccess shader.shader = sh.shader;
+		shader.params = sh.params;
+		shader.paramsSize = sh.size;
 		return shader;
 	}
 
-	function createBatchShader( rt : RuntimeShader ) {
+	function isPerInstance(v:TVar) {
+		if( v.qualifiers == null )
+			return false;
+		for( q in v.qualifiers )
+			if( q.match(PerInstance(_) | PerObject) )
+				return true;
+		return false;
+	}
+
+	function createBatchShader( rt : RuntimeShader, shaders : hxsl.ShaderList ) : { shader : SharedShader, params : RuntimeShader.AllocParam, size : Int } {
 		var s = new hxsl.SharedShader("");
 		var id = rt.spec.signature.substr(0, 8);
 
@@ -497,7 +508,121 @@ class Cache {
 			funs : [],
 		};
 
-		var stride = rt.vertex.paramsSize + rt.fragment.paramsSize;
+		function getVarRec( v : TVar, name, kind ) {
+			if( v.kind == kind && v.name == name )
+				return v;
+			switch( v.type ) {
+			case TStruct(vl):
+				for( v in vl ) {
+					var v = getVarRec(v, name, kind);
+					if( v != null ) return v;
+				}
+			default:
+			}
+			return null;
+		}
+
+		function getVar( p : RuntimeShader.AllocParam ) {
+			var s = shaders;
+			if( p.perObjectGlobal != null ) {
+				var path = p.perObjectGlobal.path.split(".");
+				while( s != null ) {
+					for( v in @:privateAccess s.s.shader.data.vars ) {
+						if( v.name != path[0] ) continue;
+						var v = getVarRec(v, p.name, Global);
+						if( v != null ) return v;
+					}
+					s = s.next;
+				}
+			} else {
+				var i = p.instance - 1;
+				while( i > 0 ) {
+					i--;
+					s = s.next;
+				}
+				var name = p.name;
+				while( true ) {
+					for( v in @:privateAccess s.s.shader.data.vars ) {
+						var v = getVarRec(v, name, Param);
+						if( v != null ) return v;
+					}
+					var cc = name.charCodeAt(name.length - 1);
+					if( cc >= '0'.code && cc <= '9'.code ) name = name.substr(0,-1) else break;
+				}
+			}
+			throw "Var not found "+p.name;
+		}
+
+		var params = null;
+		var used = [];
+
+		function addParam(p:RuntimeShader.AllocParam) {
+			var size = switch( p.type ) {
+				case TMat4: 4 * 4;
+				case TVec(n,VFloat): n;
+				case TFloat: 1;
+				default: throw "Unsupported batch var type "+p.type;
+			}
+			var index;
+			if( size >= 4 ) {
+				index = used.length << 2;
+				for( i in 0...size>>2 )
+					used.push(15);
+			} else if( size == 1 ) {
+				var best = -1;
+				for( i in 0...used.length )
+					if( used[i] != 15 && (best < 0 || used[best] < used[i]) )
+						best = i;
+				if( best < 0 ) {
+					best = used.length;
+					used.push(0);
+				}
+				index = best << 2;
+				for( k in 0...4 ) {
+					var bit = 3 - k;
+					if( used[best] & (1 << bit) == 0 ) {
+						used[best] |= 1 << bit;
+						index += bit;
+						break;
+					}
+				}
+			} else {
+				var k = size == 2 ? 3 : 7;
+				var best = -1;
+				for( i in 0...used.length )
+					if( used[i] & k == 0 ) {
+						used[i] |= k;
+						best = i;
+						break;
+					}
+				if( best < 0 ) {
+					best = used.length;
+					used.push(k);
+				}
+				index = best << 2;
+			}
+			var p2 = new AllocParam(p.name, index, p.instance, p.index, p.type);
+			p2.perObjectGlobal = p.perObjectGlobal;
+			p2.next = params;
+			params = p2;
+		}
+
+		var p = rt.vertex.params;
+		while( p != null ) {
+			var v = getVar(p);
+			if( isPerInstance(v) )
+				addParam(p);
+			p = p.next;
+		}
+		var p = rt.fragment.params;
+		while( p != null ) {
+			var v = getVar(p);
+			if( isPerInstance(v) )
+				addParam(p);
+			p = p.next;
+		}
+
+
 		var parentVars = new Map();
 		var swiz = [[X],[Y],[Z],[W]];
 
@@ -505,7 +630,7 @@ class Cache {
 			return { e : TArray(ebuffer,{ e : TBinop(OpAdd,eoffset,{ e : TConst(CInt(index)), t : TInt, p : pos }), t : TInt, p : pos }), t : tvec4, p : pos };
 		}
 
-		function extractVar( v : AllocParam, offset : Int ) {
+		function extractVar( v : AllocParam ) {
 			var vreal : TVar = declVar(v.name, v.type, Local);
 			if( v.perObjectGlobal != null ) {
 				var path = v.perObjectGlobal.path.split(".");
@@ -528,7 +653,7 @@ class Cache {
 				}
 			}
 			s.data.vars.push(vreal);
-			var index = (v.pos>>2) + offset;
+			var index = (v.pos>>2);
 			var extract = switch( v.type ) {
 			case TMat4:
 				{ p : pos, t : v.type, e : TCall({ e : TGlobal(Mat4), t : TVoid, p : pos },[
@@ -551,21 +676,16 @@ class Cache {
 			case TFloat:
 				{ p : pos, t : v.type, e : TSwiz(readOffset(index),swiz[v.pos&3]) }
 			default:
-				throw "Unsupported batch var type "+v.type;
+				throw "assert";
 			}
 			return { p : pos, e : TBinop(OpAssign, { e : TVar(vreal), p : pos, t : v.type }, extract), t : TVoid };
 		}
 
 		var exprs = [];
-		var p = rt.vertex.params;
-		while( p != null ) {
-			exprs.push(extractVar(p,0));
-			p = p.next;
-		}
-
-		var p = rt.fragment.params;
+		var stride = used.length;
+		var p = params;
 		while( p != null ) {
-			exprs.push(extractVar(p,rt.vertex.paramsSize));
+			exprs.push(extractVar(p));
 			p = p.next;
 		}
 
@@ -587,7 +707,7 @@ class Cache {
 		s.consts = new SharedShader.ShaderConst(vcount,0,countBits);
 		s.consts.globalId = 0;
 
-		return s;
+		return { shader : s, params : params, size : stride };
 	}
 
 	static var INST : Cache;

+ 8 - 8
hxsl/CacheFile.hx

@@ -69,9 +69,9 @@ class CacheFile extends Cache {
 		return shader;
 	}
 
-	override function createBatchShader( rt ) {
-		var b = super.createBatchShader(rt);
-		batchers.push({ rt : rt, shader : b });
+	override function createBatchShader( rt, shaders ) {
+		var b = super.createBatchShader(rt, shaders);
+		batchers.push({ rt : rt, shader : b.shader });
 		return b;
 	}
 
@@ -247,7 +247,7 @@ class CacheFile extends Cache {
 
 		// recompile or load runtime shaders
 		runtimeShaders = [];
-		var rttMap = new Map<String,RuntimeShader>();
+		var rttMap = new Map<String,{ rt : RuntimeShader, shaders : hxsl.ShaderList }>();
 		if( recompileRT ) {
 
 			for( r in runtimes ) {
@@ -263,7 +263,7 @@ class CacheFile extends Cache {
 								r = null; // was modified
 								break;
 							}
-							var sh = makeBatchShader(rt);
+							var sh = makeBatchShader(rt.rt, rt.shaders);
 							i.shader = { version : null, shader : sh.shader };
 							batchMode = true;
 						}
@@ -284,7 +284,7 @@ class CacheFile extends Cache {
 				var rt2 = rttMap.get(r.specSign);
 				if( rt2 != null ) throw "assert";
 				runtimeShaders.push(rt);
-				rttMap.set(r.specSign, rt);
+				rttMap.set(r.specSign, { rt : rt, shaders : shaderList });
 			}
 
 		} else {
@@ -315,7 +315,7 @@ class CacheFile extends Cache {
 								r = null; // was modified
 								break;
 							}
-							var sh = makeBatchShader(rt);
+							var sh = makeBatchShader(rt.rt, rt.shaders);
 							i.shader = { version : null, shader : sh.shader };
 							r.batchMode = true;
 						}
@@ -337,7 +337,7 @@ class CacheFile extends Cache {
 				addToCache(r, shaderList);
 				reviveRuntime(r);
 				runtimeShaders.push(r);
-				rttMap.set(spec.specSign, r);
+				rttMap.set(spec.specSign, { rt : r, shaders : shaderList });
 			}
 
 		}

+ 1 - 1
hxsl/Checker.hx

@@ -788,7 +788,7 @@ class Checker {
 					}
 					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 PerInstance(_): if( tv.kind != Input ) error("@perInstance only allowed on input", pos);
+				case PerInstance(_): if( tv.kind != Input && tv.kind != Param && (tv.kind != Global || v.qualifiers.indexOf(PerObject) < 0) ) error("@perInstance only allowed on input/param", 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);