Переглянути джерело

added partial buffer support

Nicolas Cannasse 1 рік тому
батько
коміт
bde90dbaaa
11 змінених файлів з 189 додано та 15 видалено
  1. 6 0
      h3d/impl/GlDriver.hx
  2. 43 2
      h3d/scene/MeshBatch.hx
  3. 9 0
      hxd/BufferFormat.hx
  4. 8 1
      hxsl/Ast.hx
  5. 1 1
      hxsl/Checker.hx
  6. 7 1
      hxsl/Eval.hx
  7. 16 1
      hxsl/Flatten.hx
  8. 4 4
      hxsl/GlslOut.hx
  9. 3 1
      hxsl/MacroParser.hx
  10. 7 0
      hxsl/Macros.hx
  11. 85 4
      hxsl/SharedShader.hx

+ 6 - 0
h3d/impl/GlDriver.hx

@@ -421,6 +421,8 @@ class GlDriver extends Driver {
 					#end
 				case Uniform:
 					gl.getUniformBlockIndex(p.p,(shader.kind==Vertex?"vertex_":"")+"uniform_buffer"+i);
+				default:
+					throw "assert";
 				}
 			}];
 			var start = 0;
@@ -433,6 +435,8 @@ class GlDriver extends Driver {
 					#if (hl_ver >= version("1.15.0"))
 					gl.shaderStorageBlockBinding(p.p,s.buffers[i], i + start);
 					#end
+				default:
+					throw "assert";
 				}
 		}
 	}
@@ -603,6 +607,8 @@ class GlDriver extends Driver {
 						if ( !buf.buffers[i].flags.has(ReadWriteBuffer) )
 							throw "Buffer was allocated without ReadWriteBuffer flag";
 						gl.bindBufferBase(0x90D2 /*GL.SHADER STORAGE BUFFER*/, i + start, buf.buffers[i].vbuf);
+					default:
+						throw "assert";
 					}
 			}
 		case Textures:

+ 43 - 2
h3d/scene/MeshBatch.hx

@@ -11,6 +11,7 @@ private class BatchData {
 	public var indexStart : Int;
 	public var instanceBuffers : Array<h3d.impl.InstanceBuffer>;
 	public var buffers : Array<h3d.Buffer> = [];
+	public var bufferFormat : hxd.BufferFormat;
 	public var data : hxd.FloatBuffer;
 	public var params : hxsl.RuntimeShader.AllocParam;
 	public var shader : hxsl.BatchShader;
@@ -136,7 +137,8 @@ class MeshBatch extends MultiMaterial {
 				b.indexStart = matInfo.start;
 				b.paramsCount = shader.paramsSize;
 				b.maxInstance = Std.int(MAX_BUFFER_ELEMENTS / b.paramsCount);
-				 if ( b.maxInstance <= 0 )
+				b.bufferFormat = hxd.BufferFormat.VEC4_DATA;
+				if( b.maxInstance <= 0 )
 					throw "Mesh batch shaders needs at least one perInstance parameter";
 				b.params = shader.params;
 				b.shader = shader;
@@ -146,6 +148,41 @@ class MeshBatch extends MultiMaterial {
 				p.dynamicParameters = true;
 				p.batchMode = true;
 
+				if( allowGpuUpdate ) {
+					var pl = [];
+					var p = b.params;
+					while( p != null ) {
+						pl.push(p);
+						p = p.next;
+					}
+					pl.sort(function(p1,p2) return p1.pos - p2.pos);
+					var fmt : Array<hxd.BufferFormat.BufferInput> = [];
+					var curPos = 0;
+					for( p in pl ) {
+						if( curPos != p.pos )
+							throw "Buffer has padding";
+						var name = p.name;
+						var prev = fmt.length;
+						switch( p.type ) {
+						case TMat3:
+							for( i in 0...3 )
+								fmt.push(new hxd.BufferFormat.BufferInput(name+"__m"+i,DVec3));
+						case TMat3x4:
+							for( i in 0...3 )
+								fmt.push(new hxd.BufferFormat.BufferInput(name+"__m"+i,DVec4));
+						case TMat4:
+							for( i in 0...4 )
+								fmt.push(new hxd.BufferFormat.BufferInput(name+"__m"+i,DVec4));
+						default:
+							var t = hxd.BufferFormat.InputFormat.fromHXSL(p.type);
+							fmt.push(new hxd.BufferFormat.BufferInput(p.name,t));
+						}
+						for( i in prev...fmt.length )
+							curPos += fmt[i].getBytesSize() >> 2;
+					}
+					b.bufferFormat = hxd.BufferFormat.make(fmt);
+				}
+
 				b.next = dataPasses;
 				dataPasses = b;
 
@@ -341,6 +378,10 @@ class MeshBatch extends MultiMaterial {
 	override function sync(ctx:RenderContext) {
 		super.sync(ctx);
 		if( instanceCount == 0 ) return;
+		flush();
+	}
+
+	public function flush() {
 		var p = dataPasses;
 		var alloc = hxd.impl.Allocator.get();
 		var psBytes = primitiveSubBytes;
@@ -355,7 +396,7 @@ class MeshBatch extends MultiMaterial {
 					count = p.maxInstance;
 				if( buf == null || buf.isDisposed() ) {
 					var bufferFlags : hxd.impl.Allocator.BufferFlags = allowGpuUpdate ? UniformReadWrite : UniformDynamic;
-					buf = alloc.allocBuffer(MAX_BUFFER_ELEMENTS,hxd.BufferFormat.VEC4_DATA,bufferFlags);
+					buf = alloc.allocBuffer(MAX_BUFFER_ELEMENTS,p.bufferFormat,bufferFlags);
 					p.buffers[index] = buf;
 					upload = true;
 				}

+ 9 - 0
hxd/BufferFormat.hx

@@ -371,6 +371,15 @@ class BufferFormat {
 	}
 
 	static var ALL_FORMATS = new Map<String,Array<BufferFormat>>();
+
+	public static function fromID( uid : Int ) {
+		for( fl in ALL_FORMATS )
+			for( f in fl )
+				if( f.uid == uid )
+					return f;
+		return null;
+	}
+
 	public static function make( inputs : Array<BufferInput> ) {
 		var names = [];
 		for( b in inputs )

+ 8 - 1
hxsl/Ast.hx

@@ -3,6 +3,8 @@ package hxsl;
 enum BufferKind {
 	Uniform;
 	RW;
+	Partial;
+	RWPartial;
 }
 
 enum TexDimension {
@@ -326,6 +328,7 @@ class Tools {
 
 	public static var SWIZ = Component.createAll();
 	public static var MAX_CHANNELS_BITS = 3;
+	public static var MAX_PARTIAL_MAPPINGS_BITS = 7;
 
 	public static function allocVarId() {
 		// in order to prevent compile time ids to conflict with runtime allocated ones
@@ -398,13 +401,15 @@ class Tools {
 				}
 		case TChannel(_):
 			return 3 + MAX_CHANNELS_BITS;
+		case TBuffer(_, _, Partial|RWPartial):
+			return MAX_PARTIAL_MAPPINGS_BITS;
 		default:
 		}
 		return 0;
 	}
 
 	public static function isConst( v : TVar ) {
-		if( v.type.match(TChannel(_)) )
+		if( v.type.match(TChannel(_)|TBuffer(_,_,Partial|RWPartial)) )
 			return true;
 		if( v.qualifiers != null )
 			for( q in v.qualifiers )
@@ -465,6 +470,8 @@ class Tools {
 			var prefix = switch( k ) {
 			case Uniform: "Buffer";
 			case RW: "RWBuffer";
+			case Partial: "PartialBuffer";
+			case RWPartial: "RWPartialBuffer";
 			};
 			prefix+" "+toString(t) + "[" + (switch( s ) { case SConst(i): "" + i; case SVar(v): v.name; } ) + "]";
 		case TBytes(n): "Bytes" + n;

+ 1 - 1
hxsl/Checker.hx

@@ -404,7 +404,7 @@ class Checker {
 				return;
 			case Local if( v.qualifiers == null || v.qualifiers.indexOf(Final) < 0 ):
 				return;
-			case Param, Local if( v.type.match(TBuffer(_,_,RW) | TRWTexture(_)) ):
+			case Param, Local if( v.type.match(TBuffer(_,_,RW|RWPartial) | TRWTexture(_)) ):
 				return;
 			default:
 			}

+ 7 - 1
hxsl/Eval.hx

@@ -261,6 +261,7 @@ class Eval {
 	}
 
 	function evalExpr( e : TExpr, isVal = true ) : TExpr {
+		var t = e.t;
 		var d : TExprDef = switch( e.e ) {
 		case TGlobal(_), TConst(_): e.e;
 		case TVar(v):
@@ -269,6 +270,7 @@ class Eval {
 				c;
 			else {
 				var v2 = mapVar(v);
+				t = v2.type;
 				TVar(v2);
 			}
 		case TVarDecl(v, init):
@@ -280,6 +282,10 @@ class Eval {
 			case [TArrayDecl(el),TConst(CInt(i))] if( i >= 0 && i < el.length ):
 				el[i].e;
 			default:
+				switch( e1.t ) {
+				case TArray(at, _), TBuffer(at,_,_): t = at;
+				default:
+				}
 				TArray(e1, e2);
 			}
 		case TSwiz(e, r):
@@ -546,7 +552,7 @@ class Eval {
 		case TField(e, name):
 			TField(evalExpr(e), name);
 		};
-		return { e : d, t : e.t, p : e.p }
+		return { e : d, t : t, p : e.p }
 	}
 
 }

+ 16 - 1
hxsl/Flatten.hx

@@ -163,8 +163,10 @@ class Flatten {
 			var expr = mapExpr(expr);
 			function read(pos, size) {
 				var idx = pos >> 2;
-				var sw = SWIZ.slice(pos&3,(pos&3) + size);
 				var arr : TExpr = optimize({ e : TArray(expr,{ e : TConst(CInt(idx)), p : e.p, t : TInt }), p : e.p, t : TVec(4,VFloat) });
+				if( size == 4 && pos & 3 == 0 )
+					return arr;
+				var sw = SWIZ.slice(pos&3,(pos&3) + size);
 				return { e : TSwiz(arr,sw), t : size == 1 ? TFloat : TVec(size,VFloat), p : e.p }
 			}
 			switch( e.t ) {
@@ -182,6 +184,19 @@ class Flatten {
 						read(pos + k, size - k)
 					]), t : e.t, p : e.p }
 				}
+			case TMat4:
+				{ e : TCall({ e : TGlobal(Mat4), p : e.p, t : TVoid },[
+					read(pos, 4),
+					read(pos + 4, 4),
+					read(pos + 8, 4),
+					read(pos + 12, 4),
+				]), t : e.t, p : e.p }
+			case TMat3x4:
+				{ e : TCall({ e : TGlobal(Mat3x4), p : e.p, t : TVoid },[
+					read(pos, 4),
+					read(pos + 4, 4),
+					read(pos + 8, 4),
+				]), t : e.t, p : e.p }
 			default:
 				throw "Unsupported type "+e.t.toString();
 			}

+ 4 - 4
hxsl/GlslOut.hx

@@ -225,8 +225,8 @@ class GlslOut {
 			add("]");
 		case TBuffer(t, size, kind):
 			switch( kind ) {
-			case Uniform:
-			case RW:
+			case Uniform, Partial:
+			case RW, RWPartial:
 				add("rw_");
 			}
 			add((isVertex ? "vertex_" : "") + "uniform_buffer"+(uniformBuffer++));
@@ -709,9 +709,9 @@ class GlslOut {
 			case TBuffer(_, _, kind):
 				add("layout(std140) ");
 				switch( kind ) {
-				case Uniform:
+				case Uniform, Partial:
 					add("uniform ");
-				case RW:
+				case RW, RWPartial:
 					add("buffer ");
 				}
 			case TArray(TRWTexture(_, _, chans), _):

+ 3 - 1
hxsl/MacroParser.hx

@@ -147,7 +147,7 @@ class MacroParser {
 			var t = getTexDim(name.substr(9), (dim,arr) -> TRWTexture(dim,arr,chans));
 			if( t != null )
 				return t;
-		case TPath( { pack : [], name : name = ("Array"|"Buffer"|"RWBuffer"), sub : null, params : [t, size] } ):
+		case TPath( { pack : [], name : name = ("Array"|"Buffer"|"RWBuffer"|"PartialBuffer"|"RWPartialBuffer"), sub : null, params : [t, size] } ):
 			var t = switch( t ) {
 			case TPType(t): parseType(t, pos);
 			default: null;
@@ -165,6 +165,8 @@ class MacroParser {
 				case "Array": TArray(t, size);
 				case "Buffer": TBuffer(t,size,Uniform);
 				case "RWBuffer": TBuffer(t,size,RW);
+				case "PartialBuffer": TBuffer(t,size,Partial);
+				case "RWPartialBuffer": TBuffer(t,size,RWPartial);
 				default: throw "assert";
 				}
 		case TAnonymous(fl):

+ 7 - 0
hxsl/Macros.hx

@@ -261,6 +261,13 @@ class Macros {
 					if( $p == null ) $psel = Unknown else if( $psel == Unknown ) $defFormat;
 					constBits |= ((globals.allocChannelID($p) << 3) | Type.enumIndex($psel)) << $v{ c.pos };
 				});
+			case TBuffer(_,_,Partial|RWPartial):
+				var psel = getPath(c.v,"Format");
+				exprs.push(macro {
+					if( $p == null ) throw "Partial buffer is not set";
+					if( $p.format.uid >>> hxsl.Ast.Tools.MAX_PARTIAL_MAPPINGS_BITS != 0 ) throw "Buffer format is out of range";
+					constBits |= $p.format.uid << $v{ c.pos };
+				});
 			default:
 				throw "assert";
 			}

+ 85 - 4
hxsl/SharedShader.hx

@@ -69,15 +69,96 @@ class SharedShader {
 		return if( i == null ) makeInstance(constBits) else i;
 	}
 
+	function makeBufferType( v : TVar, tbuf : hxsl.Ast.Type, fmt : hxd.BufferFormat ) : hxsl.Ast.Type {
+		var name = v.name;
+		switch( tbuf ) {
+		case TStruct(vl):
+			var inputs = [for( i in fmt.getInputs() ) i];
+			var vli : Array<TVar> = [];
+			var p = 0;
+			while( p < inputs.length ) {
+				var i = inputs[p++];
+				var name = i.name;
+				var t = switch( i.type ) {
+				case DVec2: TVec(2,VFloat);
+				case DVec3: TVec(3,VFloat);
+				case DVec4: TVec(4,VFloat);
+				case DFloat: TFloat;
+				case DBytes4: TBytes(4);
+				}
+				if( StringTools.endsWith(i.name,"__m0") ) {
+					var h = i.type.getSize();
+					var w = 2;
+					while( inputs[p+w-1] != null && StringTools.endsWith(inputs[p+w-1].name,"__m"+w) )
+						w++;
+					t = switch( [w,h] ) {
+					case [2,2]: TMat2;
+					case [3,3]: TMat3;
+					case [3,4]: TMat3x4;
+					case [4,4]: TMat4;
+					default: throw "Unsupported matrix format";
+					}
+					name = i.name.substr(0,-4);
+					p += w - 1;
+				}
+				vli.push({
+					id : Tools.allocVarId(),
+					name : name,
+					type : t,
+					kind : v.kind,
+					parent : v,
+				});
+			}
+			for( v in vl ) {
+				var found = false;
+				for( v2 in vli )
+					if( v.name == v2.name ) {
+						switch( [v.type, v2.type] ) {
+						case [TFloat, TFloat]:
+						case [TVec(a,VFloat), TVec(b,VFloat)] if( a <= b ):
+						default:
+							if( !v.type.equals(v2.type) )
+								throw "Buffer "+data.name+"."+v.name+":"+v.type.toString()+" should be "+v2.type.toString();
+						}
+						found = true;
+						break;
+					}
+				if( !found )
+					throw "Buffer is missing "+data.name+"."+v.name+":"+v.type.toString();
+			}
+			return TStruct(vli);
+		default:
+			throw "assert";
+		}
+	}
+
 	function makeInstance( constBits : Int )  {
 		var eval = new hxsl.Eval();
 		var c = consts;
 		while( c != null ) {
-			eval.setConstant(c.v, switch( c.v.type ) {
-			case TBool: CBool((constBits >>> c.pos) & 1 != 0);
-			case TInt, TChannel(_): CInt((constBits >>> c.pos) & ((1 << c.bits) - 1));
+			switch( c.v.type ) {
+			case TBool:
+				eval.setConstant(c.v, CBool((constBits >>> c.pos) & 1 != 0));
+			case TInt, TChannel(_):
+				eval.setConstant(c.v, CInt((constBits >>> c.pos) & ((1 << c.bits) - 1)));
+			case TBuffer(t,size,kind):
+				var bits = (constBits >>> c.pos) & ((1 << c.bits) - 1);
+				var fmt = hxd.BufferFormat.fromID(bits);
+				var v : TVar = {
+					id : c.v.id,
+					name : c.v.name,
+					kind : c.v.kind,
+					type : null,
+				};
+				var fullT = makeBufferType(v, t, fmt);
+				v.type = TBuffer(fullT, size, switch( kind ) {
+					case Partial: Uniform;
+					case RWPartial: RW;
+					default: throw "assert";
+				});
+				eval.varMap.set(c.v, v);
 			default: throw "assert";
-			});
+			}
 			c = c.next;
 		}
 		eval.inlineCalls = true;