Forráskód Böngészése

refactor: add format for buffer and primitive, allow runtime remap to shader (not only based on stride)

Nicolas Cannasse 2 éve
szülő
commit
167e365bfe

+ 3 - 3
h2d/Graphics.hx

@@ -87,13 +87,13 @@ private class GraphicsContent extends h3d.prim.Primitive {
 	override function alloc( engine : h3d.Engine ) {
 	override function alloc( engine : h3d.Engine ) {
 		if (index.length <= 0) return ;
 		if (index.length <= 0) return ;
 		var alloc = Allocator.get();
 		var alloc = Allocator.get();
-		buffer = alloc.ofFloats(tmp, 8, RawFormat);
+		buffer = alloc.ofFloats(tmp, hxd.BufferFormat.H2D);
 		#if track_alloc
 		#if track_alloc
 		@:privateAccess buffer.allocPos = allocPos;
 		@:privateAccess buffer.allocPos = allocPos;
 		#end
 		#end
 		indexes = alloc.ofIndexes(index);
 		indexes = alloc.ofIndexes(index);
 		for( b in buffers ) {
 		for( b in buffers ) {
-			if( b.vbuf == null || b.vbuf.isDisposed() ) b.vbuf = alloc.ofFloats(b.buf, 8, RawFormat);
+			if( b.vbuf == null || b.vbuf.isDisposed() ) b.vbuf = alloc.ofFloats(b.buf, hxd.BufferFormat.H2D);
 			if( b.ibuf == null || b.ibuf.isDisposed() ) b.ibuf = alloc.ofIndexes(b.idx);
 			if( b.ibuf == null || b.ibuf.isDisposed() ) b.ibuf = alloc.ofIndexes(b.idx);
 		}
 		}
 		bufferDirty = false;
 		bufferDirty = false;
@@ -114,7 +114,7 @@ private class GraphicsContent extends h3d.prim.Primitive {
 			var allocator = Allocator.get();
 			var allocator = Allocator.get();
 			if ( bufferDirty ) {
 			if ( bufferDirty ) {
 				allocator.disposeBuffer(buffer);
 				allocator.disposeBuffer(buffer);
-				buffer = allocator.ofFloats(tmp, 8, RawFormat);
+				buffer = allocator.ofFloats(tmp, hxd.BufferFormat.H2D);
 				bufferDirty = false;
 				bufferDirty = false;
 			}
 			}
 			if ( indexDirty ) {
 			if ( indexDirty ) {

+ 2 - 2
h2d/RenderContext.hx

@@ -580,7 +580,7 @@ class RenderContext extends h3d.impl.RenderContext {
 		if( bufPos == 0 ) return;
 		if( bufPos == 0 ) return;
 		beforeDraw();
 		beforeDraw();
 		var nverts = Std.int(bufPos / stride);
 		var nverts = Std.int(bufPos / stride);
-		var tmp = new h3d.Buffer(nverts, stride, [Dynamic,RawFormat]);
+		var tmp = new h3d.Buffer(nverts, hxd.BufferFormat.XY_UV_RGBA, [Dynamic]);
 		tmp.uploadVector(buffer, 0, nverts);
 		tmp.uploadVector(buffer, 0, nverts);
 		engine.renderQuadBuffer(tmp);
 		engine.renderQuadBuffer(tmp);
 		tmp.dispose();
 		tmp.dispose();
@@ -750,7 +750,7 @@ class RenderContext extends h3d.impl.RenderContext {
 		baseShader.uvPos.set(tile.u, tile.v, tile.u2 - tile.u, tile.v2 - tile.v);
 		baseShader.uvPos.set(tile.u, tile.v, tile.u2 - tile.u, tile.v2 - tile.v);
 		beforeDraw();
 		beforeDraw();
 		if( fixedBuffer == null || fixedBuffer.isDisposed() ) {
 		if( fixedBuffer == null || fixedBuffer.isDisposed() ) {
-			fixedBuffer = new h3d.Buffer(4, 8, [RawFormat]);
+			fixedBuffer = new h3d.Buffer(4, hxd.BufferFormat.H2D);
 			var k = new hxd.FloatBuffer();
 			var k = new hxd.FloatBuffer();
 			for( v in [0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] )
 			for( v in [0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] )
 				k.push(v);
 				k.push(v);

+ 1 - 1
h2d/SpriteBatch.hx

@@ -438,7 +438,7 @@ class SpriteBatch extends Drawable {
 		}
 		}
 		empty = bufferVertices == 0;
 		empty = bufferVertices == 0;
 		if( bufferVertices > 0 )
 		if( bufferVertices > 0 )
-			buffer = h3d.Buffer.ofSubFloats(tmpBuf, 8, bufferVertices, [Dynamic, RawFormat]);
+			buffer = h3d.Buffer.ofSubFloats(tmpBuf, bufferVertices, hxd.BufferFormat.H2D, [Dynamic]);
 	}
 	}
 
 
 	override function draw( ctx : RenderContext ) {
 	override function draw( ctx : RenderContext ) {

+ 2 - 2
h2d/TileGroup.hx

@@ -511,8 +511,8 @@ class TileLayerContent extends h3d.prim.Primitive {
 		if( tmp == null ) clear();
 		if( tmp == null ) clear();
 		if( tmp.length > 0 ) {
 		if( tmp.length > 0 ) {
 			buffer = tmp.length < useAllocatorLimit
 			buffer = tmp.length < useAllocatorLimit
-				? hxd.impl.Allocator.get().ofFloats(tmp, 8, RawFormat)
-				: h3d.Buffer.ofFloats(tmp, 8, [RawFormat]);
+				? hxd.impl.Allocator.get().ofFloats(tmp, hxd.BufferFormat.H2D)
+				: h3d.Buffer.ofFloats(tmp, hxd.BufferFormat.H2D);
 		}
 		}
 	}
 	}
 
 

+ 12 - 12
h3d/Buffer.hx

@@ -5,10 +5,6 @@ enum BufferFlag {
 		Indicate that the buffer content will be often modified.
 		Indicate that the buffer content will be often modified.
 	**/
 	**/
 	Dynamic;
 	Dynamic;
-	/**
-		Directly map the buffer content to the shader inputs, without assuming [pos:vec3,normal:vec3,uv:vec2] default prefix.
-	**/
-	RawFormat;
 	/**
 	/**
 		Used internaly
 		Used internaly
 	**/
 	**/
@@ -31,13 +27,13 @@ class Buffer {
 	var mem : h3d.impl.MemoryManager;
 	var mem : h3d.impl.MemoryManager;
 	var vbuf : h3d.impl.Driver.GPUBuffer;
 	var vbuf : h3d.impl.Driver.GPUBuffer;
 	public var vertices(default,null) : Int;
 	public var vertices(default,null) : Int;
-	public var stride(default,null) : Int;
+	public var format(default,null) : hxd.BufferFormat;
 	public var flags(default, null) : haxe.EnumFlags<BufferFlag>;
 	public var flags(default, null) : haxe.EnumFlags<BufferFlag>;
 
 
-	public function new(vertices, stride, ?flags : Array<BufferFlag> ) {
+	public function new(vertices, format : hxd.BufferFormat, ?flags : Array<BufferFlag> ) {
 		id = GUID++;
 		id = GUID++;
 		this.vertices = vertices;
 		this.vertices = vertices;
-		this.stride = stride;
+		this.format = format;
 		this.flags = new haxe.EnumFlags();
 		this.flags = new haxe.EnumFlags();
 		#if track_alloc
 		#if track_alloc
 		this.allocPos = new hxd.impl.AllocPos();
 		this.allocPos = new hxd.impl.AllocPos();
@@ -49,6 +45,10 @@ class Buffer {
 			@:privateAccess h3d.Engine.getCurrent().mem.allocBuffer(this);
 			@:privateAccess h3d.Engine.getCurrent().mem.allocBuffer(this);
 	}
 	}
 
 
+	public inline function getMemSize() {
+		return vertices * (format.stride << 2);
+	}
+
 	public inline function isDisposed() {
 	public inline function isDisposed() {
 		return vbuf == null;
 		return vbuf == null;
 	}
 	}
@@ -78,15 +78,15 @@ class Buffer {
 		mem.driver.readBufferBytes(vbuf, startVertice, vertices, bytes, bytesPosition);
 		mem.driver.readBufferBytes(vbuf, startVertice, vertices, bytes, bytesPosition);
 	}
 	}
 
 
-	public static function ofFloats( v : hxd.FloatBuffer, stride : Int, ?flags ) {
-		var nvert = Std.int(v.length / stride);
-		var b = new Buffer(nvert, stride, flags);
+	public static function ofFloats( v : hxd.FloatBuffer, format : hxd.BufferFormat, ?flags ) {
+		var nvert = Std.int(v.length / format.stride);
+		var b = new Buffer(nvert, format, flags);
 		b.uploadVector(v, 0, nvert);
 		b.uploadVector(v, 0, nvert);
 		return b;
 		return b;
 	}
 	}
 
 
-	public static function ofSubFloats( v : hxd.FloatBuffer, stride : Int, vertices : Int, ?flags ) {
-		var b = new Buffer(vertices, stride, flags);
+	public static function ofSubFloats( v : hxd.FloatBuffer, vertices : Int, format : hxd.BufferFormat, ?flags ) {
+		var b = new Buffer(vertices, format, flags);
 		b.uploadVector(v, 0, vertices);
 		b.uploadVector(v, 0, vertices);
 		return b;
 		return b;
 	}
 	}

+ 17 - 16
h3d/impl/DirectXDriver.hx

@@ -41,6 +41,7 @@ private class CompiledShader {
 	public var layout : Layout;
 	public var layout : Layout;
 	public var inputs : InputNames;
 	public var inputs : InputNames;
 	public var offsets : Array<Int>;
 	public var offsets : Array<Int>;
+	public var format : hxd.BufferFormat;
 	public function new() {
 	public function new() {
 	}
 	}
 }
 }
@@ -316,11 +317,11 @@ class DirectXDriver extends h3d.impl.Driver {
 	}
 	}
 
 
 	override function allocBuffer(b:Buffer):GPUBuffer {
 	override function allocBuffer(b:Buffer):GPUBuffer {
-		var size = b.vertices * b.stride * 4;
+		var size = b.getMemSize();
 		var uniform = b.flags.has(UniformBuffer);
 		var uniform = b.flags.has(UniformBuffer);
 		var res = uniform ? dx.Driver.createBuffer(size, Dynamic, ConstantBuffer, CpuWrite, None, 0, null) : dx.Driver.createBuffer(size, Default, VertexBuffer, None, None, 0, null);
 		var res = uniform ? dx.Driver.createBuffer(size, Dynamic, ConstantBuffer, CpuWrite, None, 0, null) : dx.Driver.createBuffer(size, Default, VertexBuffer, None, None, 0, null);
 		if( res == null ) return null;
 		if( res == null ) return null;
-		return { res : res, count : b.vertices, stride : b.stride, uniform : uniform };
+		return { res : res, count : b.vertices, stride : b.format.stride, uniform : uniform };
 	}
 	}
 
 
 	override function allocIndexes( count : Int, is32 : Bool ) : IndexBuffer {
 	override function allocIndexes( count : Int, is32 : Bool ) : IndexBuffer {
@@ -1099,7 +1100,7 @@ class DirectXDriver extends h3d.impl.Driver {
 			s.fragment = fragment.s;
 			s.fragment = fragment.s;
 			s.offsets = [];
 			s.offsets = [];
 
 
-			var layout = [], offset = 0;
+			var layout = [], offset = 0, format : Array<hxd.BufferFormat.BufferInput> = [];
 			for( v in shader.vertex.data.vars )
 			for( v in shader.vertex.data.vars )
 				if( v.kind == Input ) {
 				if( v.kind == Input ) {
 					var e = new LayoutElement();
 					var e = new LayoutElement();
@@ -1130,20 +1131,15 @@ class DirectXDriver extends h3d.impl.Driver {
 					layout.push(e);
 					layout.push(e);
 					s.offsets.push(offset);
 					s.offsets.push(offset);
 					inputs.push(v.name);
 					inputs.push(v.name);
-
-					var size = switch( v.type ) {
-					case TVec(n, _): n;
-					case TBytes(n): n;
-					case TFloat: 1;
-					default: throw "assert " + v.type;
-					}
-
-					offset += size;
+					var t = hxd.BufferFormat.InputFormat.fromHXSL(v.type);
+					format.push({ name : v.name, type : t });
+					offset += t.getSize();
 				}
 				}
 
 
 			var n = new hl.NativeArray(layout.length);
 			var n = new hl.NativeArray(layout.length);
 			for( i in 0...layout.length )
 			for( i in 0...layout.length )
 				n[i] = layout[i];
 				n[i] = layout[i];
+			s.format = hxd.BufferFormat.make(format);
 			s.inputs = InputNames.get(inputs);
 			s.inputs = InputNames.get(inputs);
 			s.layout = Driver.createInputLayout(n, vertex.bytes, vertex.bytes.length);
 			s.layout = Driver.createInputLayout(n, vertex.bytes, vertex.bytes.length);
 			if( s.layout == null )
 			if( s.layout == null )
@@ -1171,11 +1167,16 @@ class DirectXDriver extends h3d.impl.Driver {
 		if( hasDeviceError ) return;
 		if( hasDeviceError ) return;
 		var vbuf = @:privateAccess buffer.vbuf;
 		var vbuf = @:privateAccess buffer.vbuf;
 		var start = -1, max = -1, position = 0;
 		var start = -1, max = -1, position = 0;
+		var bufOffsets;
+		if( buffer.format == currentShader.format || currentShader.format.isSubSet(buffer.format) )
+			bufOffsets = currentShader.offsets;
+		else
+			bufOffsets = buffer.format.getMatchingOffsets(currentShader.format);
 		for( i in 0...currentShader.inputs.names.length ) {
 		for( i in 0...currentShader.inputs.names.length ) {
-			if( currentVBuffers[i] != vbuf.res || offsets[i] != currentShader.offsets[i] << 2 ) {
+			if( currentVBuffers[i] != vbuf.res || offsets[i] != bufOffsets[i] << 2 ) {
 				currentVBuffers[i] = vbuf.res;
 				currentVBuffers[i] = vbuf.res;
-				strides[i] = buffer.stride << 2;
-				offsets[i] = currentShader.offsets[i] << 2;
+				strides[i] = buffer.format.stride << 2;
+				offsets[i] = bufOffsets[i] << 2;
 				if( start < 0 ) start = i;
 				if( start < 0 ) start = i;
 				max = i;
 				max = i;
 			}
 			}
@@ -1193,7 +1194,7 @@ class DirectXDriver extends h3d.impl.Driver {
 			if( currentVBuffers[index] != vbuf.res || offsets[index] != bl.offset << 2 ) {
 			if( currentVBuffers[index] != vbuf.res || offsets[index] != bl.offset << 2 ) {
 				currentVBuffers[index] = vbuf.res;
 				currentVBuffers[index] = vbuf.res;
 				offsets[index] = bl.offset << 2;
 				offsets[index] = bl.offset << 2;
-				strides[index] = bl.buffer.stride << 2;
+				strides[index] = bl.buffer.format.stride << 2;
 				if( start < 0 ) start = index;
 				if( start < 0 ) start = index;
 				max = index;
 				max = index;
 			}
 			}

+ 31 - 47
h3d/impl/GlDriver.hx

@@ -68,7 +68,7 @@ private class CompiledProgram {
 	public var p : Program;
 	public var p : Program;
 	public var vertex : CompiledShader;
 	public var vertex : CompiledShader;
 	public var fragment : CompiledShader;
 	public var fragment : CompiledShader;
-	public var stride : Int;
+	public var format : hxd.BufferFormat;
 	public var inputs : InputNames;
 	public var inputs : InputNames;
 	public var attribs : Array<CompiledAttribute>;
 	public var attribs : Array<CompiledAttribute>;
 	public var hasAttribIndex : Array<Bool>;
 	public var hasAttribIndex : Array<Bool>;
@@ -413,27 +413,31 @@ class GlDriver extends Driver {
 			var attribNames = [];
 			var attribNames = [];
 			p.attribs = [];
 			p.attribs = [];
 			p.hasAttribIndex = [];
 			p.hasAttribIndex = [];
-			p.stride = 0;
+			var format : Array<hxd.BufferFormat.BufferInput> = [];
+			var stride = 0;
 			for( v in shader.vertex.data.vars )
 			for( v in shader.vertex.data.vars )
 				switch( v.kind ) {
 				switch( v.kind ) {
 				case Input:
 				case Input:
-					var t = GL.FLOAT;
-					var size = switch( v.type ) {
-					case TVec(n, _): n;
-					case TBytes(n): t = GL.BYTE; n;
-					case TFloat: 1;
-					default: throw "assert " + v.type;
-					}
+					var t = hxd.BufferFormat.InputFormat.fromHXSL(v.type);
 					var index = gl.getAttribLocation(p.p, glout.varNames.exists(v.id) ? glout.varNames.get(v.id) : v.name);
 					var index = gl.getAttribLocation(p.p, glout.varNames.exists(v.id) ? glout.varNames.get(v.id) : v.name);
 					if( index < 0 ) {
 					if( index < 0 ) {
-						p.stride += size;
+						stride += t.getSize();
 						continue;
 						continue;
 					}
 					}
 					var a = new CompiledAttribute();
 					var a = new CompiledAttribute();
-					a.type = t;
-					a.size = size;
+					a.type = GL.FLOAT;
 					a.index = index;
 					a.index = index;
-					a.offset = p.stride;
+					a.size = t.getSize();
+					a.offset = stride;
+					stride += a.size;
+
+					switch( v.type ) {
+					case TBytes(n):
+						a.type = GL.BYTE;
+						a.size = n;
+					default:
+					}
+
 					a.divisor = 0;
 					a.divisor = 0;
 					if( v.qualifiers != null ) {
 					if( v.qualifiers != null ) {
 						for( q in v.qualifiers )
 						for( q in v.qualifiers )
@@ -445,9 +449,10 @@ class GlDriver extends Driver {
 					p.attribs.push(a);
 					p.attribs.push(a);
 					p.hasAttribIndex[a.index] = true;
 					p.hasAttribIndex[a.index] = true;
 					attribNames.push(v.name);
 					attribNames.push(v.name);
-					p.stride += size;
+					format.push({ name : v.name, type : t });
 				default:
 				default:
 				}
 				}
+			p.format = hxd.BufferFormat.make(format);
 			p.inputs = InputNames.get(attribNames);
 			p.inputs = InputNames.get(attribNames);
 			programs.set(shader.id, p);
 			programs.set(shader.id, p);
 		}
 		}
@@ -1008,13 +1013,13 @@ class GlDriver extends Driver {
 		discardError();
 		discardError();
 		var vb = gl.createBuffer();
 		var vb = gl.createBuffer();
 		gl.bindBuffer(GL.ARRAY_BUFFER, vb);
 		gl.bindBuffer(GL.ARRAY_BUFFER, vb);
-		if( b.vertices * b.stride == 0 ) throw "assert";
+		if( b.vertices * b.format.stride == 0 ) throw "assert";
 		#if js
 		#if js
-		gl.bufferData(GL.ARRAY_BUFFER, b.vertices * b.stride * 4, b.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
+		gl.bufferData(GL.ARRAY_BUFFER, b.getMemSize(), b.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
 		#elseif hl
 		#elseif hl
-		gl.bufferDataSize(GL.ARRAY_BUFFER, b.vertices * b.stride * 4, b.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
+		gl.bufferDataSize(GL.ARRAY_BUFFER, b.getMemSize(), b.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
 		#else
 		#else
-		var tmp = new Uint8Array(b.vertices * b.stride * 4);
+		var tmp = new Uint8Array(b.getMemSize());
 		gl.bufferData(GL.ARRAY_BUFFER, tmp, b.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
 		gl.bufferData(GL.ARRAY_BUFFER, tmp, b.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
 		#end
 		#end
 		var outOfMem = outOfMemoryCheck && gl.getError() == GL.OUT_OF_MEMORY;
 		var outOfMem = outOfMemoryCheck && gl.getError() == GL.OUT_OF_MEMORY;
@@ -1023,7 +1028,7 @@ class GlDriver extends Driver {
 			gl.deleteBuffer(vb);
 			gl.deleteBuffer(vb);
 			return null;
 			return null;
 		}
 		}
-		return { b : vb, stride : b.stride #if multidriver, driver : this #end };
+		return { b : vb, stride : b.format.stride #if multidriver, driver : this #end };
 	}
 	}
 
 
 	override function allocIndexes( count : Int, is32 : Bool ) : IndexBuffer {
 	override function allocIndexes( count : Int, is32 : Bool ) : IndexBuffer {
@@ -1268,7 +1273,6 @@ class GlDriver extends Driver {
 	}
 	}
 
 
 	override function selectBuffer( b : h3d.Buffer ) {
 	override function selectBuffer( b : h3d.Buffer ) {
-
 		if( b == curBuffer )
 		if( b == curBuffer )
 			return;
 			return;
 
 
@@ -1277,41 +1281,21 @@ class GlDriver extends Driver {
 		curBuffer = b;
 		curBuffer = b;
 
 
 		var m = @:privateAccess b.vbuf;
 		var m = @:privateAccess b.vbuf;
-		if( m.stride < curShader.stride )
-			throw "Buffer stride (" + m.stride + ") and shader stride (" + curShader.stride + ") mismatch";
-
 		#if multidriver
 		#if multidriver
 		if( m.driver != this )
 		if( m.driver != this )
 			throw "Invalid buffer context";
 			throw "Invalid buffer context";
 		#end
 		#end
 		gl.bindBuffer(GL.ARRAY_BUFFER, m.b);
 		gl.bindBuffer(GL.ARRAY_BUFFER, m.b);
-
-		if( b.flags.has(RawFormat) ) {
+		var strideBytes = m.stride * 4;
+		if( b.format == curShader.format || curShader.format.isSubSet(b.format) ) {
 			for( a in curShader.attribs ) {
 			for( a in curShader.attribs ) {
-				var pos = a.offset;
-				gl.vertexAttribPointer(a.index, a.size, a.type, false, m.stride * 4, pos * 4);
+				gl.vertexAttribPointer(a.index, a.size, a.type, false, strideBytes, a.offset * 4);
 				updateDivisor(a);
 				updateDivisor(a);
 			}
 			}
 		} else {
 		} else {
-			var offset = 8;
-			for( i in 0...curShader.attribs.length ) {
-				var a = curShader.attribs[i];
-				var pos;
-				switch( curShader.inputs.names[i] ) {
-				case "position":
-					pos = 0;
-				case "normal":
-					if( m.stride < 6 ) throw "Buffer is missing NORMAL data, set it to RAW format ?" #if track_alloc + @:privateAccess v.allocPos #end;
-					pos = 3;
-				case "uv":
-					if( m.stride < 8 ) throw "Buffer is missing UV data, set it to RAW format ?" #if track_alloc + @:privateAccess v.allocPos #end;
-					pos = 6;
-				case s:
-					pos = offset;
-					offset += a.size;
-					if( offset > m.stride ) throw "Buffer is missing '"+s+"' data, set it to RAW format ?" #if track_alloc + @:privateAccess v.allocPos #end;
-				}
-				gl.vertexAttribPointer(a.index, a.size, a.type, false, m.stride * 4, pos * 4);
+			var offsets = b.format.getMatchingOffsets(curShader.format);
+			for( i => a in curShader.attribs ) {
+				gl.vertexAttribPointer(a.index, a.size, a.type, false, strideBytes, offsets[i] * 4);
 				updateDivisor(a);
 				updateDivisor(a);
 			}
 			}
 		}
 		}
@@ -1320,7 +1304,7 @@ class GlDriver extends Driver {
 	override function selectMultiBuffers( buffers : Buffer.BufferOffset ) {
 	override function selectMultiBuffers( buffers : Buffer.BufferOffset ) {
 		for( a in curShader.attribs ) {
 		for( a in curShader.attribs ) {
 			gl.bindBuffer(GL.ARRAY_BUFFER, @:privateAccess buffers.buffer.vbuf.b);
 			gl.bindBuffer(GL.ARRAY_BUFFER, @:privateAccess buffers.buffer.vbuf.b);
-			gl.vertexAttribPointer(a.index, a.size, a.type, false, buffers.buffer.stride * 4, buffers.offset * 4);
+			gl.vertexAttribPointer(a.index, a.size, a.type, false, buffers.buffer.format.stride * 4, buffers.offset * 4);
 			updateDivisor(a);
 			updateDivisor(a);
 			buffers = buffers.next;
 			buffers = buffers.next;
 		}
 		}

+ 1 - 1
h3d/impl/LogDriver.hx

@@ -307,7 +307,7 @@ class LogDriver extends Driver {
 	}
 	}
 
 
 	override function allocBuffer( b : Buffer ) : GPUBuffer {
 	override function allocBuffer( b : Buffer ) : GPUBuffer {
-		log('AllocBuffer count=${b.vertices} stride=${b.stride}');
+		log('AllocBuffer count=${b.vertices} format=${b.format}');
 		return d.allocBuffer(b);
 		return d.allocBuffer(b);
 	}
 	}
 
 

+ 3 - 3
h3d/impl/MemoryManager.hx

@@ -81,7 +81,7 @@ class MemoryManager {
 	function allocBuffer( b : Buffer ) {
 	function allocBuffer( b : Buffer ) {
 		if( b.vbuf != null ) return;
 		if( b.vbuf != null ) return;
 
 
-		var mem = b.vertices * b.stride * 4;
+		var mem = b.getMemSize();
 
 
 		if( mem == 0 ) return;
 		if( mem == 0 ) return;
 
 
@@ -107,7 +107,7 @@ class MemoryManager {
 		driver.disposeBuffer(b.vbuf);
 		driver.disposeBuffer(b.vbuf);
 		b.vbuf = null;
 		b.vbuf = null;
 		b.mem = null;
 		b.mem = null;
-		usedMemory -= b.vertices * b.stride * 4;
+		usedMemory -= b.getMemSize();
 		buffers.remove(b);
 		buffers.remove(b);
 	}
 	}
 
 
@@ -243,7 +243,7 @@ class MemoryManager {
 	public function stats() {
 	public function stats() {
 		var total = 0.;
 		var total = 0.;
 		for( b in buffers )
 		for( b in buffers )
-			total += b.stride * b.vertices * 4;
+			total += b.getMemSize();
 		return {
 		return {
 			bufferCount : buffers.length,
 			bufferCount : buffers.length,
 			bufferMemory : total,
 			bufferMemory : total,

+ 11 - 3
h3d/parts/GpuParticles.hx

@@ -452,6 +452,15 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 
 
 	static inline var VERSION = 2;
 	static inline var VERSION = 2;
 	static inline var STRIDE = 14;
 	static inline var STRIDE = 14;
+	static var PFORMAT = hxd.BufferFormat.make([
+		{ name : "position", type : DVec3 },
+		{ name : "normal", type : DVec3 },
+		{ name : "uv", type : DVec2 },
+		{ name : "time", type : DFloat },
+		{ name : "life", type : DFloat },
+		{ name : "init", type : DVec2 },
+		{ name : "delta", type : DVec2 },
+	]);
 
 
 	var groups : Array<GpuPartGroup>;
 	var groups : Array<GpuPartGroup>;
 	var primitiveBuffers : Array<hxd.FloatBuffer>;
 	var primitiveBuffers : Array<hxd.FloatBuffer>;
@@ -685,11 +694,10 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 			primitiveBuffers.resize(groups.length);
 			primitiveBuffers.resize(groups.length);
 
 
 		for( gid in 0...groups.length ) {
 		for( gid in 0...groups.length ) {
-			if( primitiveBuffers[gid] == null ||  primitiveBuffers[gid].length > STRIDE * partCount * 4 )
+			if( primitiveBuffers[gid] == null || primitiveBuffers[gid].length > STRIDE * partCount * 4 )
 				primitiveBuffers[gid] = new hxd.FloatBuffer();
 				primitiveBuffers[gid] = new hxd.FloatBuffer();
 			primitiveBuffers[gid].grow(STRIDE * groups[gid].nparts * 4);
 			primitiveBuffers[gid].grow(STRIDE * groups[gid].nparts * 4);
-			primitives[gid] = new h3d.prim.RawPrimitive( { vbuf : primitiveBuffers[gid], stride : STRIDE, quads : true, bounds:bounds }, true);
-			primitives[gid].buffer.flags.set(RawFormat);
+			primitives[gid] = new h3d.prim.RawPrimitive( { vbuf : primitiveBuffers[gid], format : PFORMAT, bounds:bounds }, true);
 		}
 		}
 
 
 		if( hasLoop ) {
 		if( hasLoop ) {

+ 18 - 1
h3d/parts/Particles.hx

@@ -365,7 +365,21 @@ class Particles extends h3d.scene.Mesh {
 		var stride = 10;
 		var stride = 10;
 		if( hasColor ) stride += 4;
 		if( hasColor ) stride += 4;
 		var count = Std.int(pos/stride);
 		var count = Std.int(pos/stride);
-		var buffer = hxd.impl.Allocator.get().ofSubFloats(tmp, stride, count,RawFormat);
+		var format = PART_FMT;
+		if( format == null ) {
+			format = PART_FMT = hxd.BufferFormat.make([
+				{ name : "position", type : DVec3 },
+				{ name : "rpos", type : DVec2 },
+				{ name : "rotation", type : DFloat },
+				{ name : "size", type : DVec2 },
+				{ name : "uv", type : DVec2 },
+			]);
+		}
+		if( hasColor ) {
+			format = PART_FMT_COLOR;
+			if( format == null ) format = PART_FMT_COLOR = PART_FMT.append("color", DVec4);
+		}
+		var buffer = hxd.impl.Allocator.get().ofSubFloats(tmp, count, format);
 		if( pshader.is3D )
 		if( pshader.is3D )
 			pshader.size.set(globalSize, globalSize);
 			pshader.size.set(globalSize, globalSize);
 		else
 		else
@@ -375,4 +389,7 @@ class Particles extends h3d.scene.Mesh {
 		buffer.dispose();
 		buffer.dispose();
 	}
 	}
 
 
+	static var PART_FMT : hxd.BufferFormat;
+	static var PART_FMT_COLOR : hxd.BufferFormat;
+
 }
 }

+ 1 - 1
h3d/pass/Border.hx

@@ -42,7 +42,7 @@ class Border extends ScreenFx<BorderShader> {
 		add(width-size, height);
 		add(width-size, height);
 		add(width, height);
 		add(width, height);
 
 
-		this.primitive = new h3d.prim.RawPrimitive({ vbuf : bbuf, stride : 2, quads : true }, true);
+		this.primitive = new h3d.prim.RawPrimitive({ vbuf : bbuf, format : hxd.BufferFormat.make([{ name : "position", type : DVec2 }]) }, true);
 		shader.color.set(1,1,1,1);
 		shader.color.set(1,1,1,1);
 	}
 	}
 
 

+ 16 - 20
h3d/prim/BigPrimitive.hx

@@ -6,8 +6,7 @@ package h3d.prim;
 **/
 **/
 class BigPrimitive extends Primitive {
 class BigPrimitive extends Primitive {
 
 
-	var isRaw : Bool;
-	var stride : Int;
+	public var format(default,null) : hxd.BufferFormat;
 	var buffers : Array<Buffer>;
 	var buffers : Array<Buffer>;
 	var allIndexes : Array<Indexes>;
 	var allIndexes : Array<Indexes>;
 	var tmpBuf : hxd.FloatBuffer;
 	var tmpBuf : hxd.FloatBuffer;
@@ -29,14 +28,13 @@ class BigPrimitive extends Primitive {
 	static var PREV_BUFFER : hxd.FloatBuffer;
 	static var PREV_BUFFER : hxd.FloatBuffer;
 	static var PREV_INDEX : hxd.IndexBuffer;
 	static var PREV_INDEX : hxd.IndexBuffer;
 
 
-	public function new(stride, isRaw=false, ?alloc) {
-		this.isRaw = isRaw;
+	public function new(format, ?alloc) {
+		this.format = format;
 		buffers = [];
 		buffers = [];
 		allIndexes = [];
 		allIndexes = [];
 		bounds = new h3d.col.Bounds();
 		bounds = new h3d.col.Bounds();
 		this.allocator = alloc;
 		this.allocator = alloc;
-		this.stride = stride;
-		if( stride < 3 ) throw "Minimum stride = 3";
+		if( format.stride < 3 ) throw "Minimum stride = 3";
 		#if track_alloc
 		#if track_alloc
 		allocPos = new hxd.impl.AllocPos();
 		allocPos = new hxd.impl.AllocPos();
 		#end
 		#end
@@ -47,7 +45,7 @@ class BigPrimitive extends Primitive {
 		The count value is the number of vertexes you will add, it will automatically flush() if it doesn't fit into the current buffer.
 		The count value is the number of vertexes you will add, it will automatically flush() if it doesn't fit into the current buffer.
 	**/
 	**/
 	public function begin(vcount,icount) {
 	public function begin(vcount,icount) {
-		startIndex = Std.int(bufPos / stride);
+		startIndex = Std.int(bufPos / format.stride);
 		if( startIndex + vcount >= 65535 ) {
 		if( startIndex + vcount >= 65535 ) {
 			if( vcount >= 65535 ) throw "Too many vertices in begin()";
 			if( vcount >= 65535 ) throw "Too many vertices in begin()";
 			flush();
 			flush();
@@ -59,10 +57,10 @@ class BigPrimitive extends Primitive {
 			else
 			else
 				PREV_BUFFER = null;
 				PREV_BUFFER = null;
 			if( isStatic )
 			if( isStatic )
-				tmpBuf.grow(65535 * stride);
+				tmpBuf.grow(65535 * format.stride);
 		}
 		}
 		if( !isStatic )
 		if( !isStatic )
-			tmpBuf.grow(vcount * stride + bufPos);
+			tmpBuf.grow(vcount * format.stride + bufPos);
 		if( tmpIdx == null ) {
 		if( tmpIdx == null ) {
 			tmpIdx = PREV_INDEX;
 			tmpIdx = PREV_INDEX;
 			if( tmpIdx == null )
 			if( tmpIdx == null )
@@ -112,7 +110,7 @@ class BigPrimitive extends Primitive {
 		var count = 0;
 		var count = 0;
 		for( b in buffers )
 		for( b in buffers )
 			count += b.vertices;
 			count += b.vertices;
-		count += Std.int(bufPos / stride);
+		count += Std.int(bufPos / format.stride);
 		return count;
 		return count;
 	}
 	}
 
 
@@ -126,11 +124,9 @@ class BigPrimitive extends Primitive {
 				flushing = true;
 				flushing = true;
 				var b : h3d.Buffer;
 				var b : h3d.Buffer;
 				if(allocator != null)
 				if(allocator != null)
-					b = allocator.ofSubFloats(tmpBuf, stride, Std.int(bufPos / stride), isRaw ? RawFormat : Dynamic);
-				else {
-					b = h3d.Buffer.ofSubFloats(tmpBuf, stride, Std.int(bufPos / stride));
-					if( isRaw ) b.flags.set(RawFormat);
-				}
+					b = allocator.ofSubFloats(tmpBuf, Std.int(bufPos / format.stride), format);
+				else
+					b = h3d.Buffer.ofSubFloats(tmpBuf, Std.int(bufPos / format.stride), format);
 
 
 				buffers.push(b);
 				buffers.push(b);
 				var idx = if(allocator != null)
 				var idx = if(allocator != null)
@@ -194,7 +190,7 @@ class BigPrimitive extends Primitive {
 		See addSub for complete documentation.
 		See addSub for complete documentation.
 	**/
 	**/
 	public function add( buf : hxd.FloatBuffer, idx : hxd.IndexBuffer, dx : Float = 0. , dy : Float = 0., dz : Float = 0., rotation = 0., scale = 1., stride = -1 ) {
 	public function add( buf : hxd.FloatBuffer, idx : hxd.IndexBuffer, dx : Float = 0. , dy : Float = 0., dz : Float = 0., rotation = 0., scale = 1., stride = -1 ) {
-		return addSub(buf, idx, 0, 0, Std.int(buf.length / (stride < 0 ? this.stride : stride)), Std.int(idx.length / 3), dx, dy, dz, rotation, scale, stride);
+		return addSub(buf, idx, 0, 0, Std.int(buf.length / (stride < 0 ? format.stride : stride)), Std.int(idx.length / 3), dx, dy, dz, rotation, scale, stride);
 	}
 	}
 	/**
 	/**
 		Adds a buffer to the primitive, with custom position,scale,rotation.
 		Adds a buffer to the primitive, with custom position,scale,rotation.
@@ -206,8 +202,8 @@ class BigPrimitive extends Primitive {
 	**/
 	**/
 	@:noDebug
 	@:noDebug
 	public function addSub( buf : hxd.FloatBuffer, idx : hxd.IndexBuffer, startVert, startTri, nvert, triCount, dx : Float = 0. , dy : Float = 0., dz : Float = 0., rotation = 0., scale = 1., stride = -1, deltaU = 0., deltaV = 0., color = 1., mat : h3d.Matrix = null) {
 	public function addSub( buf : hxd.FloatBuffer, idx : hxd.IndexBuffer, startVert, startTri, nvert, triCount, dx : Float = 0. , dy : Float = 0., dz : Float = 0., rotation = 0., scale = 1., stride = -1, deltaU = 0., deltaV = 0., color = 1., mat : h3d.Matrix = null) {
-		if( stride < 0 ) stride = this.stride;
-		if( stride < this.stride ) throw "only stride >= " + this.stride+" allowed";
+		if( stride < 0 ) stride = format.stride;
+		if( stride < format.stride ) throw "only stride >= " + format.stride+" allowed";
 		begin(nvert, triCount*3);
 		begin(nvert, triCount*3);
 		var start = startIndex;
 		var start = startIndex;
 		var cr = Math.cos(rotation);
 		var cr = Math.cos(rotation);
@@ -246,7 +242,8 @@ class BigPrimitive extends Primitive {
 				bounds.addPos(vx, vy, vz);
 				bounds.addPos(vx, vy, vz);
 			}
 			}
 
 
-			if(this.stride >= 6) {
+			var stride = format.stride;
+			if(stride >= 6) {
 				var nx = buf[p++];
 				var nx = buf[p++];
 				var ny = buf[p++];
 				var ny = buf[p++];
 				var nz = buf[p++];
 				var nz = buf[p++];
@@ -268,7 +265,6 @@ class BigPrimitive extends Primitive {
 				}
 				}
 			}
 			}
 
 
-			var stride = this.stride;
 			if( hasTangents ) {
 			if( hasTangents ) {
 				var tx = buf[p++];
 				var tx = buf[p++];
 				var ty = buf[p++];
 				var ty = buf[p++];

+ 5 - 5
h3d/prim/DynamicPrimitive.hx

@@ -6,7 +6,7 @@ class DynamicPrimitive extends Primitive {
 	var ibuf : hxd.IndexBuffer;
 	var ibuf : hxd.IndexBuffer;
 	var vsize : Int;
 	var vsize : Int;
 	var isize : Int;
 	var isize : Int;
-	var stride : Int;
+	var format : hxd.BufferFormat;
 
 
 	/** Minimum number of elements in vertex buffer **/
 	/** Minimum number of elements in vertex buffer **/
 	public var minVSize = 0;
 	public var minVSize = 0;
@@ -15,8 +15,8 @@ class DynamicPrimitive extends Primitive {
 
 
 	public var bounds = new h3d.col.Bounds();
 	public var bounds = new h3d.col.Bounds();
 
 
-	public function new( stride : Int ) {
-		this.stride = stride;
+	public function new( format ) {
+		this.format = format;
 	}
 	}
 
 
 	override function getBounds() {
 	override function getBounds() {
@@ -24,7 +24,7 @@ class DynamicPrimitive extends Primitive {
 	}
 	}
 
 
 	public function getBuffer( vertices : Int ) {
 	public function getBuffer( vertices : Int ) {
-		if( vbuf == null ) vbuf = hxd.impl.Allocator.get().allocFloats(vertices * stride) else vbuf.grow(vertices * stride);
+		if( vbuf == null ) vbuf = hxd.impl.Allocator.get().allocFloats(vertices * format.stride) else vbuf.grow(vertices * format.stride);
 		vsize = vertices;
 		vsize = vertices;
 		return vbuf;
 		return vbuf;
 	}
 	}
@@ -59,7 +59,7 @@ class DynamicPrimitive extends Primitive {
 		}
 		}
 
 
 		if( buffer == null )
 		if( buffer == null )
-			buffer = alloc.allocBuffer(hxd.Math.imax(minVSize, vsize), stride, Dynamic);
+			buffer = alloc.allocBuffer(hxd.Math.imax(minVSize, vsize), format, Dynamic);
 		if( indexes == null )
 		if( indexes == null )
 			indexes = alloc.allocIndexBuffer(hxd.Math.imax(minISize, isize));
 			indexes = alloc.allocIndexBuffer(hxd.Math.imax(minISize, isize));
 
 

+ 4 - 4
h3d/prim/HMDModel.hx

@@ -59,7 +59,7 @@ class HMDModel extends MeshPrimitive {
 
 
 	override function alloc(engine:h3d.Engine) {
 	override function alloc(engine:h3d.Engine) {
 		dispose();
 		dispose();
-		buffer = new h3d.Buffer(data.vertexCount, data.vertexFormat.stride);
+		buffer = new h3d.Buffer(data.vertexCount, data.vertexFormat);
 
 
 		var entry = lib.resource.entry;
 		var entry = lib.resource.entry;
 
 
@@ -97,7 +97,7 @@ class HMDModel extends MeshPrimitive {
 		var alias = bufferAliases.get(name);
 		var alias = bufferAliases.get(name);
 		var buffer = bufferCache.get(hxsl.Globals.allocID(alias.realName));
 		var buffer = bufferCache.get(hxsl.Globals.allocID(alias.realName));
 		if( buffer == null ) throw "Buffer " + alias.realName+" not found for alias " + name;
 		if( buffer == null ) throw "Buffer " + alias.realName+" not found for alias " + name;
-		if( buffer.offset + alias.offset > buffer.buffer.stride ) throw "Alias " + name+" for buffer " + alias.realName+" outside stride";
+		if( buffer.offset + alias.offset > buffer.buffer.format.stride ) throw "Alias " + name+" for buffer " + alias.realName+" outside stride";
 		addBuffer(name, buffer.buffer, buffer.offset + alias.offset);
 		addBuffer(name, buffer.buffer, buffer.offset + alias.offset);
 	}
 	}
 
 
@@ -157,7 +157,7 @@ class HMDModel extends MeshPrimitive {
 			v[k++] = n.y;
 			v[k++] = n.y;
 			v[k++] = n.z;
 			v[k++] = n.z;
 		}
 		}
-		var buf = h3d.Buffer.ofFloats(v, 3);
+		var buf = h3d.Buffer.ofFloats(v, hxd.BufferFormat.make([{ name : "normal", type : DVec3 }]));
 		addBuffer(name, buf, 0);
 		addBuffer(name, buf, 0);
 		normalsRecomputed = name;
 		normalsRecomputed = name;
 	}
 	}
@@ -199,7 +199,7 @@ class HMDModel extends MeshPrimitive {
 			v[k++] = t.y;
 			v[k++] = t.y;
 			v[k++] = t.z;
 			v[k++] = t.z;
 		}
 		}
-		var buf = h3d.Buffer.ofFloats(v, 3);
+		var buf = h3d.Buffer.ofFloats(v, hxd.BufferFormat.make([{ name : "tangent", type : DVec3 }]));
 		addBuffer("tangent", buf, 0);
 		addBuffer("tangent", buf, 0);
 	}
 	}
 
 

+ 1 - 1
h3d/prim/Plane2D.hx

@@ -35,7 +35,7 @@ class Plane2D extends Primitive {
 		v.push( 1);
 		v.push( 1);
 		v.push( 0);
 		v.push( 0);
 
 
-		buffer = h3d.Buffer.ofFloats(v, 4, [RawFormat]);
+		buffer = h3d.Buffer.ofFloats(v, hxd.BufferFormat.XY_UV);
 	}
 	}
 
 
 	override function render(engine:h3d.Engine) {
 	override function render(engine:h3d.Engine) {

+ 15 - 28
h3d/prim/Polygon.hx

@@ -29,29 +29,15 @@ class Polygon extends MeshPrimitive {
 	override function alloc( engine : h3d.Engine ) {
 	override function alloc( engine : h3d.Engine ) {
 		dispose();
 		dispose();
 
 
-		var size = 3;
-		var names = ["position"];
-		var positions = [0];
-		if( normals != null ) {
-			names.push("normal");
-			positions.push(size);
-			size += 3;
-		}
-		if( tangents != null ) {
-			names.push("tangent");
-			positions.push(size);
-			size += 3;
-		}
-		if( uvs != null ) {
-			names.push("uv");
-			positions.push(size);
-			size += 2;
-		}
-		if( colors != null ) {
-			names.push("color");
-			positions.push(size);
-			size += 3;
-		}
+		var format = hxd.BufferFormat.POS3D;
+		if( normals != null )
+			format = format.append("normal", DVec3);
+		if( tangents != null )
+			format = format.append("tangent", DVec3);
+		if( uvs != null )
+			format = format.append("uv", DVec2);
+		if( colors != null )
+			format = format.append("color", DVec3);
 
 
 		var buf = new hxd.FloatBuffer();
 		var buf = new hxd.FloatBuffer();
 		for( k in 0...points.length ) {
 		for( k in 0...points.length ) {
@@ -83,12 +69,13 @@ class Polygon extends MeshPrimitive {
 				buf.push(c.z);
 				buf.push(c.z);
 			}
 			}
 		}
 		}
-		var flags : Array<h3d.Buffer.BufferFlag> = [];
-		if( normals == null || tangents != null ) flags.push(RawFormat);
-		buffer = h3d.Buffer.ofFloats(buf, size, flags);
+		buffer = h3d.Buffer.ofFloats(buf, format);
 
 
-		for( i in 0...names.length )
-			addBuffer(names[i], buffer, positions[i]);
+		var position = 0;
+		for( i in format.getInputs() ) {
+			addBuffer(i.name, buffer, position);
+			position += i.type.getSize();
+		}
 
 
 		if( idx != null )
 		if( idx != null )
 			indexes = h3d.Indexes.alloc(idx);
 			indexes = h3d.Indexes.alloc(idx);

+ 9 - 6
h3d/prim/Quads.hx

@@ -94,12 +94,15 @@ class Quads extends Primitive {
 				v.push(t.v);
 				v.push(t.v);
 			}
 			}
 		}
 		}
-		var size = 3;
-		if( normals != null ) size += 3;
-		if( uvs != null ) size += 2;
-		var flags : Array<h3d.Buffer.BufferFlag> = [];
-		if( normals == null ) flags.push(RawFormat);
-		buffer = h3d.Buffer.ofFloats(v, size, flags);
+		var format = if( normals != null && uvs != null )
+			hxd.BufferFormat.POS3D_NORMAL_UV
+		else if( normals != null )
+			hxd.BufferFormat.POS3D_NORMAL
+		else if( uvs != null )
+			hxd.BufferFormat.POS3D_UV
+		else
+			hxd.BufferFormat.POS3D;
+		buffer = h3d.Buffer.ofFloats(v, format);
 	}
 	}
 
 
 	public function addNormals() {
 	public function addNormals() {

+ 3 - 5
h3d/prim/RawPrimitive.hx

@@ -5,9 +5,9 @@ class RawPrimitive extends Primitive {
 	var vcount : Int;
 	var vcount : Int;
 	var tcount : Int;
 	var tcount : Int;
 	var bounds : h3d.col.Bounds;
 	var bounds : h3d.col.Bounds;
-	public var onContextLost : Void -> { vbuf : hxd.FloatBuffer, stride : Int, ?ibuf : hxd.IndexBuffer };
+	public var onContextLost : Void -> { vbuf : hxd.FloatBuffer, format : hxd.BufferFormat, ?ibuf : hxd.IndexBuffer };
 
 
-	public function new( inf : { vbuf : hxd.FloatBuffer, stride : Int, ?ibuf : hxd.IndexBuffer, ?quads : Bool, ?bounds : h3d.col.Bounds }, persist = false ) {
+	public function new( inf : { vbuf : hxd.FloatBuffer, format : hxd.BufferFormat, ?ibuf : hxd.IndexBuffer, ?bounds : h3d.col.Bounds }, persist = false ) {
 		onContextLost = function() return inf;
 		onContextLost = function() return inf;
 		this.bounds = inf.bounds;
 		this.bounds = inf.bounds;
 		alloc(null);
 		alloc(null);
@@ -17,9 +17,7 @@ class RawPrimitive extends Primitive {
 	override function alloc( engine : h3d.Engine ) {
 	override function alloc( engine : h3d.Engine ) {
 		if( onContextLost == null ) throw "Cannot realloc " + this;
 		if( onContextLost == null ) throw "Cannot realloc " + this;
 		var inf = onContextLost();
 		var inf = onContextLost();
-		var flags : Array<h3d.Buffer.BufferFlag> = [];
-		if( inf.stride < 8 ) flags.push(RawFormat);
-		buffer = h3d.Buffer.ofFloats(inf.vbuf, inf.stride, flags);
+		buffer = h3d.Buffer.ofFloats(inf.vbuf, inf.format);
 		vcount = buffer.vertices;
 		vcount = buffer.vertices;
 		tcount = inf.ibuf != null ? Std.int(inf.ibuf.length / 3) : Std.int(vcount/3);
 		tcount = inf.ibuf != null ? Std.int(inf.ibuf.length / 3) : Std.int(vcount/3);
 		if( inf.ibuf != null )
 		if( inf.ibuf != null )

+ 1 - 1
h3d/scene/Graphics.hx

@@ -40,7 +40,7 @@ class Graphics extends Mesh {
 	public var is3D(default, set) : Bool;
 	public var is3D(default, set) : Bool;
 
 
 	public function new(?parent) {
 	public function new(?parent) {
-		bprim = new h3d.prim.BigPrimitive(12);
+		bprim = new h3d.prim.BigPrimitive(hxd.BufferFormat.POS3D_NORMAL_UV_RGBA);
 		bprim.isStatic = false;
 		bprim.isStatic = false;
 		super(bprim, null, parent);
 		super(bprim, null, parent);
 		tmpPoints = [];
 		tmpPoints = [];

+ 5 - 2
h3d/scene/MeshBatch.hx

@@ -327,6 +327,9 @@ class MeshBatch extends MultiMaterial {
 		}
 		}
 	}
 	}
 
 
+	static var VEC4_FMT = hxd.BufferFormat.make([{ name : "data", type : DVec4 }]);
+	static var SINGLE_FLOAT_FMT = hxd.BufferFormat.make([{ name : "data", type : DFloat }]);
+
 	override function sync(ctx:RenderContext) {
 	override function sync(ctx:RenderContext) {
 		super.sync(ctx);
 		super.sync(ctx);
 		if( instanceCount == 0 ) return;
 		if( instanceCount == 0 ) return;
@@ -343,7 +346,7 @@ class MeshBatch extends MultiMaterial {
 				if( count > p.maxInstance )
 				if( count > p.maxInstance )
 					count = p.maxInstance;
 					count = p.maxInstance;
 				if( buf == null || buf.isDisposed() ) {
 				if( buf == null || buf.isDisposed() ) {
-					buf = alloc.allocBuffer(MAX_BUFFER_ELEMENTS,4,UniformDynamic);
+					buf = alloc.allocBuffer(MAX_BUFFER_ELEMENTS,VEC4_FMT,UniformDynamic);
 					p.buffers[index] = buf;
 					p.buffers[index] = buf;
 					upload = true;
 					upload = true;
 				}
 				}
@@ -376,7 +379,7 @@ class MeshBatch extends MultiMaterial {
 				var tmp = haxe.io.Bytes.alloc(4 * instanceCount);
 				var tmp = haxe.io.Bytes.alloc(4 * instanceCount);
 				for( i in 0...instanceCount )
 				for( i in 0...instanceCount )
 					tmp.setFloat(i<<2, i);
 					tmp.setFloat(i<<2, i);
-				offsets = new h3d.Buffer(instanceCount, 1);
+				offsets = new h3d.Buffer(instanceCount, SINGLE_FLOAT_FMT);
 				offsets.uploadBytes(tmp,0,instanceCount);
 				offsets.uploadBytes(tmp,0,instanceCount);
 				@:privateAccess prim.addBuffer("Batch_Start", offsets);
 				@:privateAccess prim.addBuffer("Batch_Start", offsets);
 			}
 			}

+ 1 - 1
h3d/scene/Trail.hx

@@ -30,7 +30,7 @@ class Trail extends Mesh {
 	var pending = new TrailElement(); // tmp
 	var pending = new TrailElement(); // tmp
 
 
 	public function new(?parent) {
 	public function new(?parent) {
-		dprim = new h3d.prim.DynamicPrimitive(8);
+		dprim = new h3d.prim.DynamicPrimitive(hxd.BufferFormat.POS3D_NORMAL_UV);
 		super(dprim, null, parent);
 		super(dprim, null, parent);
 		material.props = getMaterialProps();
 		material.props = getMaterialProps();
 		material.mainPass.dynamicParameters = true;
 		material.mainPass.dynamicParameters = true;

+ 1 - 1
h3d/scene/World.hx

@@ -533,7 +533,7 @@ class World extends Object {
 			for( g in model.geometries ) {
 			for( g in model.geometries ) {
 				var b = c.buffers.get(g.m.bits);
 				var b = c.buffers.get(g.m.bits);
 				if( b == null ) {
 				if( b == null ) {
-					var bp = new h3d.prim.BigPrimitive(getFormat(model).stride, true);
+					var bp = new h3d.prim.BigPrimitive(getFormat(model));
 					bp.hasTangents = enableNormalMaps;
 					bp.hasTangents = enableNormalMaps;
 					b = new h3d.scene.Mesh(bp, c.root);
 					b = new h3d.scene.Mesh(bp, c.root);
 					b.name = g.m.name;
 					b.name = g.m.name;

+ 1 - 1
h3d/scene/pbr/LightBuffer.hx

@@ -36,7 +36,7 @@ class LightBuffer {
 		size += MAX_SPOT_LIGHT * SPOT_LIGHT_INFO_SIZE;
 		size += MAX_SPOT_LIGHT * SPOT_LIGHT_INFO_SIZE;
 		size = hxd.Math.imax(1, size); // Avoid empty buffer
 		size = hxd.Math.imax(1, size); // Avoid empty buffer
 		lightInfos = new hxd.FloatBuffer(size * stride);
 		lightInfos = new hxd.FloatBuffer(size * stride);
-		defaultForwardShader.lightInfos = new h3d.Buffer(size, stride, [UniformBuffer, Dynamic]);
+		defaultForwardShader.lightInfos = new h3d.Buffer(size, hxd.BufferFormat.make([{ name : "uniformData", type : DVec4 }]), [UniformBuffer, Dynamic]);
 		defaultForwardShader.BUFFER_SIZE = size;
 		defaultForwardShader.BUFFER_SIZE = size;
 		defaultForwardShader.dirLightStride = DIR_LIGHT_INFO_SIZE * MAX_DIR_LIGHT;
 		defaultForwardShader.dirLightStride = DIR_LIGHT_INFO_SIZE * MAX_DIR_LIGHT;
 		defaultForwardShader.pointLightStride = POINT_LIGHT_INFO_SIZE * MAX_POINT_LIGHT;
 		defaultForwardShader.pointLightStride = POINT_LIGHT_INFO_SIZE * MAX_POINT_LIGHT;

+ 4 - 3
h3d/shader/ParticleShader.hx

@@ -15,7 +15,8 @@ class ParticleShader extends hxsl.Shader {
 
 
 		@input var input : {
 		@input var input : {
 			var position : Vec3;
 			var position : Vec3;
-			var normal : Vec3;
+			var rpos : Vec2;
+			var rot : Float;
 			var size : Vec2;
 			var size : Vec2;
 			var uv : Vec2;
 			var uv : Vec2;
 		};
 		};
@@ -36,8 +37,8 @@ class ParticleShader extends hxsl.Shader {
 		}
 		}
 
 
 		function vertex() {
 		function vertex() {
-			var rpos = input.normal.xy;
-			var rot = input.normal.z;
+			var rpos = input.rpos;
+			var rot = input.rot;
 			var cr = rot.cos();
 			var cr = rot.cos();
 			var sr = rot.sin();
 			var sr = rot.sin();
 			var pos = input.size * rpos;
 			var pos = input.size * rpos;

+ 42 - 0
hxd/BufferFormat.hx

@@ -63,6 +63,7 @@ class BufferFormat {
 	public var uid : Int;
 	public var uid : Int;
 	public var stride(default,null) : Int;
 	public var stride(default,null) : Int;
 	var inputs : Array<BufferInput>;
 	var inputs : Array<BufferInput>;
+	var offsets : Map<Int, Array<Int>>;
 
 
 	function new( inputs : Array<BufferInput> ) {
 	function new( inputs : Array<BufferInput> ) {
 		uid = _UID++;
 		uid = _UID++;
@@ -91,6 +92,42 @@ class BufferFormat {
 		return make(inputs);
 		return make(inputs);
 	}
 	}
 
 
+	public function isSubSet( fmt : BufferFormat ) {
+		if( fmt == this )
+			return true;
+		if( inputs.length >= fmt.inputs.length )
+			return false;
+		for( i in 0...inputs.length ) {
+			var i1 = inputs[i];
+			var i2 = fmt.inputs[i];
+			if( i1.name != i2.name || i1.type != i2.type )
+				return false;
+		}
+		return true;
+	}
+
+	public function getMatchingOffsets( target : BufferFormat ) {
+		var offs = offsets == null ? null : offsets.get(target.uid);
+		if( offs != null )
+			return offs;
+		offs = [];
+		for( i in target.inputs ) {
+			var v = 0;
+			for( i2 in inputs ) {
+				if( i2.name == i.name ) {
+					offs.push(v);
+					v = -1;
+					break;
+				}
+				v += i2.type.getSize();
+			}
+			if( v >= 0 ) throw "Missing buffer input '"+i.name+"'";
+		}
+		if( offsets == null ) offsets = new Map();
+		offsets.set(target.uid, offs);
+		return offs;
+	}
+
 	public inline function getInputs() {
 	public inline function getInputs() {
 		return inputs.iterator();
 		return inputs.iterator();
 	}
 	}
@@ -105,6 +142,7 @@ class BufferFormat {
 	public static var POS3D_NORMAL(get,null) : BufferFormat;
 	public static var POS3D_NORMAL(get,null) : BufferFormat;
 	public static var POS3D_UV(get,null) : BufferFormat;
 	public static var POS3D_UV(get,null) : BufferFormat;
 	public static var POS3D_NORMAL_UV(get,null) : BufferFormat;
 	public static var POS3D_NORMAL_UV(get,null) : BufferFormat;
+	public static var POS3D_NORMAL_UV_RGBA(get,null) : BufferFormat;
 
 
 	static inline function get_H2D() return XY_UV_RGBA;
 	static inline function get_H2D() return XY_UV_RGBA;
 	static function get_XY_UV_RGBA() {
 	static function get_XY_UV_RGBA() {
@@ -127,6 +165,10 @@ class BufferFormat {
 		if( POS3D_NORMAL_UV == null ) POS3D_NORMAL_UV = make([{ name : "position", type : DVec3 },{ name : "normal", type : DVec3 },{ name : "uv", type : DVec2 }]);
 		if( POS3D_NORMAL_UV == null ) POS3D_NORMAL_UV = make([{ name : "position", type : DVec3 },{ name : "normal", type : DVec3 },{ name : "uv", type : DVec2 }]);
 		return POS3D_NORMAL_UV;
 		return POS3D_NORMAL_UV;
 	}
 	}
+	static function get_POS3D_NORMAL_UV_RGBA() {
+		if( POS3D_NORMAL_UV_RGBA == null ) POS3D_NORMAL_UV_RGBA = POS3D_NORMAL_UV.append("color",DVec4);
+		return POS3D_NORMAL_UV_RGBA;
+	}
 	static function get_POS3D_UV() {
 	static function get_POS3D_UV() {
 		if( POS3D_UV == null ) POS3D_UV = make([{ name : "position", type : DVec3 },{ name : "uv", type : DVec2 }]);
 		if( POS3D_UV == null ) POS3D_UV = make([{ name : "position", type : DVec3 },{ name : "uv", type : DVec2 }]);
 		return POS3D_UV;
 		return POS3D_UV;

+ 12 - 12
hxd/impl/Allocator.hx

@@ -2,8 +2,8 @@ package hxd.impl;
 
 
 enum abstract BufferFlags(Int) {
 enum abstract BufferFlags(Int) {
 	public var Dynamic = 0;
 	public var Dynamic = 0;
-	public var UniformDynamic = 1;
-	public var RawFormat = 2;
+	public var Static = 1;
+	public var UniformDynamic = 2;
 	public inline function toInt() : Int {
 	public inline function toInt() : Int {
 		return this;
 		return this;
 	}
 	}
@@ -16,22 +16,22 @@ class Allocator {
 
 
 	// GPU
 	// GPU
 
 
-	public function allocBuffer( vertices : Int, stride : Int, flags : BufferFlags ) : h3d.Buffer {
-		return new h3d.Buffer(vertices, stride,
+	public function allocBuffer( vertices : Int, format, flags : BufferFlags = Dynamic ) : h3d.Buffer {
+		return new h3d.Buffer(vertices, format,
 			switch( flags ) {
 			switch( flags ) {
-				case Dynamic: [Dynamic];
-				case UniformDynamic: [UniformBuffer,Dynamic];
-				case RawFormat: [RawFormat];
+			case Static: null;
+			case Dynamic: [Dynamic];
+			case UniformDynamic: [UniformBuffer,Dynamic];
 			});
 			});
 	}
 	}
 
 
-	public function ofFloats( v : hxd.FloatBuffer, stride : Int, flags : BufferFlags ) {
-		var nvert = Std.int(v.length / stride);
-		return ofSubFloats(v, stride, nvert, flags);
+	public function ofFloats( v : hxd.FloatBuffer, format : hxd.BufferFormat, flags : BufferFlags = Dynamic ) {
+		var nvert = Std.int(v.length / format.stride);
+		return ofSubFloats(v, nvert, format, flags);
 	}
 	}
 
 
-	public function ofSubFloats( v : hxd.FloatBuffer, stride : Int, vertices : Int, flags : BufferFlags ) {
-		var b = allocBuffer(vertices, stride, flags);
+	public function ofSubFloats( v : hxd.FloatBuffer, vertices : Int, format, flags : BufferFlags = Dynamic ) {
+		var b = allocBuffer(vertices, format, flags);
 		b.uploadVector(v, 0, vertices);
 		b.uploadVector(v, 0, vertices);
 		return b;
 		return b;
 	}
 	}

+ 5 - 5
hxd/impl/CacheAllocator.hx

@@ -50,24 +50,24 @@ class CacheAllocator extends Allocator {
 	**/
 	**/
 	public var maxKeepTime = 60.;
 	public var maxKeepTime = 60.;
 
 
-	override function allocBuffer(vertices:Int, stride:Int, flags:BufferFlags):h3d.Buffer {
+	override function allocBuffer(vertices:Int, format:hxd.BufferFormat, flags:BufferFlags=Dynamic):h3d.Buffer {
 		if( vertices >= 65536 ) throw "assert";
 		if( vertices >= 65536 ) throw "assert";
 		checkFrame();
 		checkFrame();
-		var id = flags.toInt() | (stride << 3) | (vertices << 16);
+		var id = flags.toInt() | (format.uid << 3) | (vertices << 16);
 		var c = buffers.get(id);
 		var c = buffers.get(id);
 		if( c != null ) {
 		if( c != null ) {
 			var b = c.get();
 			var b = c.get();
 			if( b != null ) return b;
 			if( b != null ) return b;
 		}
 		}
 		checkGC();
 		checkGC();
-		return super.allocBuffer(vertices,stride,flags);
+		return super.allocBuffer(vertices,format,flags);
 	}
 	}
 
 
 	override function disposeBuffer(b:h3d.Buffer) {
 	override function disposeBuffer(b:h3d.Buffer) {
 		if( b.isDisposed() ) return;
 		if( b.isDisposed() ) return;
 		var f = b.flags;
 		var f = b.flags;
-		var flags = f.has(RawFormat) ? RawFormat : (f.has(UniformBuffer) ? UniformDynamic : Dynamic);
-		var id = flags.toInt() | (b.stride << 3) | (b.vertices << 16);
+		var flags = f.has(UniformBuffer) ? UniformDynamic : (f.has(Dynamic) ? Dynamic : Static);
+		var id = flags.toInt() | (b.format.uid << 3) | (b.vertices << 16);
 		var c = buffers.get(id);
 		var c = buffers.get(id);
 		if( c == null ) {
 		if( c == null ) {
 			c = new Cache(function(b:h3d.Buffer) b.dispose());
 			c = new Cache(function(b:h3d.Buffer) b.dispose());