浏览代码

entirely reviewed vertex buffer handling

ncannasse 11 年之前
父节点
当前提交
91785f8214

+ 3 - 3
h2d/Graphics.hx

@@ -25,7 +25,7 @@ private class GraphicsContent extends h3d.prim.Primitive {
 	var tmp : hxd.FloatBuffer;
 	var tmp : hxd.FloatBuffer;
 	var index : hxd.IndexBuffer;
 	var index : hxd.IndexBuffer;
 	
 	
-	var buffers : Array<{ buf : hxd.FloatBuffer, vbuf : h3d.impl.Buffer, idx : hxd.IndexBuffer, ibuf : h3d.impl.Indexes }>;
+	var buffers : Array<{ buf : hxd.FloatBuffer, vbuf : h3d.Buffer, idx : hxd.IndexBuffer, ibuf : h3d.Indexes }>;
 	
 	
 	public function new() {
 	public function new() {
 		buffers = [];
 		buffers = [];
@@ -59,10 +59,10 @@ 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 ;
-		buffer = engine.mem.allocVector(tmp, 8, 0);
+		buffer = h3d.Buffer.ofFloats(tmp, 8);
 		indexes = engine.mem.allocIndex(index);
 		indexes = engine.mem.allocIndex(index);
 		for( b in buffers ) {
 		for( b in buffers ) {
-			if( b.vbuf == null || b.vbuf.isDisposed() ) b.vbuf = engine.mem.allocVector(b.buf, 8, 0);
+			if( b.vbuf == null || b.vbuf.isDisposed() ) b.vbuf = h3d.Buffer.ofFloats(b.buf, 8);
 			if( b.ibuf == null || b.ibuf.isDisposed() ) b.ibuf = engine.mem.allocIndex(b.idx);
 			if( b.ibuf == null || b.ibuf.isDisposed() ) b.ibuf = engine.mem.allocIndex(b.idx);
 		}
 		}
 	}
 	}

+ 1 - 4
h2d/SpriteBatch.hx

@@ -149,10 +149,7 @@ class SpriteBatch extends Drawable {
 			}
 			}
 			e = e.next;
 			e = e.next;
 		}
 		}
-		var stride = 5;
-		var nverts = Std.int(pos / stride);
-		var buffer = ctx.engine.mem.alloc(nverts, stride, 4);
-		buffer.uploadVector(tmpBuf, 0, nverts);
+		var buffer = h3d.Buffer.ofFloats(tmpBuf, 5, [Dynamic, Quads]);
 		setupShader(ctx.engine, tile, 0);
 		setupShader(ctx.engine, tile, 0);
 		ctx.engine.renderQuadBuffer(buffer);
 		ctx.engine.renderQuadBuffer(buffer);
 		buffer.dispose();
 		buffer.dispose();

+ 2 - 2
h2d/TileColorGroup.hx

@@ -20,7 +20,7 @@ private class TileLayerContent extends h3d.prim.Primitive {
 		var v = 0;
 		var v = 0;
 		var b = buffer;
 		var b = buffer;
 		while( b != null ) {
 		while( b != null ) {
-			v += b.nvert;
+			v += b.vertices;
 			b = b.next;
 			b = b.next;
 		}
 		}
 		return v >> 1;
 		return v >> 1;
@@ -126,7 +126,7 @@ private class TileLayerContent extends h3d.prim.Primitive {
 
 
 	override public function alloc(engine:h3d.Engine) {
 	override public function alloc(engine:h3d.Engine) {
 		if( tmp == null ) reset();
 		if( tmp == null ) reset();
-		buffer = engine.mem.allocVector(tmp, 8, 4);
+		buffer = h3d.Buffer.ofFloats(tmp, 8, [Quads]);
 	}
 	}
 
 
 	public function doRender(engine, min, len) {
 	public function doRender(engine, min, len) {

+ 2 - 10
h2d/TileGroup.hx

@@ -42,20 +42,12 @@ private class TileLayerContent extends h3d.prim.Primitive {
 	}
 	}
 	
 	
 	override public function triCount() {
 	override public function triCount() {
-		if( buffer == null )
-			return tmp.length >> 3;
-		var v = 0;
-		var b = buffer;
-		while( b != null ) {
-			v += b.nvert;
-			b = b.next;
-		}
-		return v >> 1;
+		return if( buffer == null ) tmp.length >> 3 else buffer.totalVertices() >> 1;
 	}
 	}
 	
 	
 	override public function alloc(engine:h3d.Engine) {
 	override public function alloc(engine:h3d.Engine) {
 		if( tmp == null ) reset();
 		if( tmp == null ) reset();
-		buffer = engine.mem.allocVector(tmp, 4, 4);
+		buffer = h3d.Buffer.ofFloats(tmp, 4, [Quads]);
 	}
 	}
 
 
 	public function doRender(engine, min, len) {
 	public function doRender(engine, min, len) {

+ 4 - 3
h2d/Tools.hx

@@ -10,7 +10,7 @@ private class CoreObjects  {
 	public var tmpColor : h3d.Vector;
 	public var tmpColor : h3d.Vector;
 	public var tmpMatrix : h3d.Matrix;
 	public var tmpMatrix : h3d.Matrix;
 	public var tmpMaterial : h3d.mat.Material;
 	public var tmpMaterial : h3d.mat.Material;
-	public var planBuffer : h3d.impl.Buffer;
+	public var planBuffer : h3d.Buffer;
 	public var nullTile : Tile;
 	public var nullTile : Tile;
 	
 	
 	var emptyTexture : h3d.mat.Texture;
 	var emptyTexture : h3d.mat.Texture;
@@ -35,7 +35,8 @@ private class CoreObjects  {
 			vector.push(pt[1]);
 			vector.push(pt[1]);
 		}
 		}
 		
 		
-		planBuffer = h3d.Engine.getCurrent().mem.allocVector(vector, 4, 4);
+		planBuffer = new h3d.Buffer(4, 4, [Quads]);
+		planBuffer.uploadVector(vector, 0, 4);
 		nullTile = new Tile(null, 0, 0, 5, 5);
 		nullTile = new Tile(null, 0, 0, 5, 5);
 	}
 	}
 	
 	
@@ -61,7 +62,7 @@ class Tools {
 		var c = CORE;
 		var c = CORE;
 		if( c == null ) return;
 		if( c == null ) return;
 		// if we have lost our context
 		// if we have lost our context
-		if( c.planBuffer.b.isDisposed() )
+		if( c.planBuffer.isDisposed() )
 			CORE = null;
 			CORE = null;
 	}
 	}
 	
 	

+ 128 - 0
h3d/Buffer.hx

@@ -0,0 +1,128 @@
+package h3d;
+
+enum BufferFlag {
+	/**
+		Indicate that the buffer content will be often modified.
+	**/
+	Dynamic;
+	/**
+		The buffer contains only triangles. Imply Managed. Make sure the position is aligned on 3 vertices multiples.
+	**/
+	Triangles;
+	/**
+		The buffer contains only quads. Imply Managed. Make sure the position is aligned on 4 vertices multiples.
+	**/
+	Quads;
+	/**
+		Will allocate the memory as part of an shared buffer pool, preventing a lot of small GPU buffers to be allocated.
+	**/
+	Managed;
+	/**
+		Used internaly
+	**/
+	NoAlloc;
+}
+
+class Buffer {
+
+	public var buffer(default,null) : h3d.impl.ManagedBuffer;
+	public var position(default,null) : Int;
+	public var vertices(default,null) : Int;
+	public var next(default,null) : Buffer;
+	public var flags(default, null) : haxe.EnumFlags<BufferFlag>;
+	
+	public function new(vertices, stride, ?flags : Array<BufferFlag>) {
+		this.vertices = vertices;
+		this.flags = new haxe.EnumFlags();
+		if( flags != null )
+			for( f in flags )
+				this.flags.set(f);
+		if( this.flags.has(Quads) || this.flags.has(Triangles) )
+			this.flags.set(Managed);
+		if( !this.flags.has(NoAlloc) )
+			h3d.Engine.getCurrent().mem.allocBuffer(this, stride);
+	}
+
+	public function isDisposed() {
+		return buffer == null || buffer.isDisposed();
+	}
+	
+	public function dispose() {
+		if( buffer != null ) {
+			buffer.freeBuffer(this);
+			buffer = null;
+			if( next != null ) next.dispose();
+		}
+	}
+	
+	/**
+		Returns the total number of vertices including the potential next buffers if it is split.
+	**/
+	public function totalVertices() {
+		var count = 0;
+		var b = this;
+		while( b != null ) {
+			count += b.vertices;
+			b = b.next;
+		}
+		return count;
+	}
+	
+	public function uploadVector( buf : hxd.FloatBuffer, bufPos : Int, vertices : Int ) {
+		var cur = this;
+		while( vertices > 0 ) {
+			if( cur == null ) throw "Too many vertices";
+			var count = vertices > cur.vertices ? cur.vertices : vertices;
+			cur.buffer.uploadVertexBuffer(cur.position, count, buf, bufPos);
+			bufPos += count * buffer.stride;
+			vertices -= count;
+			cur = cur.next;
+		}
+	}
+	
+	public function uploadBytes( data : haxe.io.Bytes, dataPos : Int, vertices : Int ) {
+		var cur = this;
+		while( vertices > 0 ) {
+			if( cur == null ) throw "Too many vertices";
+			var count = vertices > cur.vertices ? cur.vertices : vertices;
+			cur.buffer.uploadVertexBytes(cur.position, count, data, dataPos);
+			dataPos += count * buffer.stride * 4;
+			vertices -= count;
+			cur = cur.next;
+		}
+	}
+	
+	public static function ofFloats( v : hxd.FloatBuffer, stride : Int, ?flags ) {
+		var nvert = Std.int(v.length / stride);
+		var b = new Buffer(nvert, stride, flags);
+		b.uploadVector(v, 0, nvert);
+		return b;
+	}
+	
+}
+
+class BufferOffset {
+	public var id : Int;
+	public var buffer : Buffer;
+	public var offset : Int;
+	
+	/*
+		This is used to return a list of BufferOffset without allocating an array
+	*/
+	public var next : BufferOffset;
+	
+	static var UID = 0;
+	
+	public function new(buffer, offset) {
+		this.id = UID++;
+		this.buffer = buffer;
+		this.offset = offset;
+	}
+	public function dispose() {
+		if( buffer != null ) {
+			buffer.dispose();
+			buffer = null;
+		}
+		next = null;
+	}
+}

+ 12 - 12
h3d/Engine.hx

@@ -99,28 +99,28 @@ class Engine {
 		selectShader(m.shader);
 		selectShader(m.shader);
 	}
 	}
 
 
-	function selectBuffer( buf : h3d.impl.MemoryManager.BigBuffer ) {
+	function selectBuffer( buf : h3d.impl.ManagedBuffer ) {
 		if( buf.isDisposed() )
 		if( buf.isDisposed() )
 			return false;
 			return false;
-		driver.selectBuffer(buf.vbuf);
+		driver.selectBuffer(@:privateAccess buf.vbuf);
 		return true;
 		return true;
 	}
 	}
 
 
-	public inline function renderTriBuffer( b : h3d.impl.Buffer, start = 0, max = -1 ) {
+	public inline function renderTriBuffer( b : Buffer, start = 0, max = -1 ) {
 		return renderBuffer(b, mem.triIndexes, 3, start, max);
 		return renderBuffer(b, mem.triIndexes, 3, start, max);
 	}
 	}
 	
 	
-	public inline function renderQuadBuffer( b : h3d.impl.Buffer, start = 0, max = -1 ) {
+	public inline function renderQuadBuffer( b : Buffer, start = 0, max = -1 ) {
 		return renderBuffer(b, mem.quadIndexes, 2, start, max);
 		return renderBuffer(b, mem.quadIndexes, 2, start, max);
 	}
 	}
 	
 	
 	// we use preallocated indexes so all the triangles are stored inside our buffers
 	// we use preallocated indexes so all the triangles are stored inside our buffers
-	function renderBuffer( b : h3d.impl.Buffer, indexes : h3d.impl.Indexes, vertPerTri : Int, startTri = 0, drawTri = -1 ) {
+	function renderBuffer( b : Buffer, indexes : Indexes, vertPerTri : Int, startTri = 0, drawTri = -1 ) {
 		if( indexes.isDisposed() )
 		if( indexes.isDisposed() )
 			return;
 			return;
 		do {
 		do {
-			var ntri = Std.int(b.nvert / vertPerTri);
-			var pos = Std.int(b.pos / vertPerTri);
+			var ntri = Std.int(b.vertices / vertPerTri);
+			var pos = Std.int(b.position / vertPerTri);
 			if( startTri > 0 ) {
 			if( startTri > 0 ) {
 				if( startTri >= ntri ) {
 				if( startTri >= ntri ) {
 					startTri -= ntri;
 					startTri -= ntri;
@@ -139,7 +139,7 @@ class Engine {
 					drawTri = 0;
 					drawTri = 0;
 				}
 				}
 			}
 			}
-			if( ntri > 0 && selectBuffer(b.b) ) {
+			if( ntri > 0 && selectBuffer(b.buffer) ) {
 				// *3 because it's the position in indexes which are always by 3
 				// *3 because it's the position in indexes which are always by 3
 				driver.draw(indexes.ibuf, pos * 3, ntri);
 				driver.draw(indexes.ibuf, pos * 3, ntri);
 				drawTriangles += ntri;
 				drawTriangles += ntri;
@@ -150,14 +150,14 @@ class Engine {
 	}
 	}
 	
 	
 	// we use custom indexes, so the number of triangles is the number of indexes/3
 	// we use custom indexes, so the number of triangles is the number of indexes/3
-	public function renderIndexed( b : h3d.impl.Buffer, indexes : h3d.impl.Indexes, startTri = 0, drawTri = -1 ) {
+	public function renderIndexed( b : Buffer, indexes : Indexes, startTri = 0, drawTri = -1 ) {
 		if( b.next != null )
 		if( b.next != null )
 			throw "Buffer is split";
 			throw "Buffer is split";
 		if( indexes.isDisposed() )
 		if( indexes.isDisposed() )
 			return;
 			return;
 		var maxTri = Std.int(indexes.count / 3);
 		var maxTri = Std.int(indexes.count / 3);
 		if( drawTri < 0 ) drawTri = maxTri - startTri;
 		if( drawTri < 0 ) drawTri = maxTri - startTri;
-		if( drawTri > 0 && selectBuffer(b.b) ) {
+		if( drawTri > 0 && selectBuffer(b.buffer) ) {
 			// *3 because it's the position in indexes which are always by 3
 			// *3 because it's the position in indexes which are always by 3
 			driver.draw(indexes.ibuf, startTri * 3, drawTri);
 			driver.draw(indexes.ibuf, startTri * 3, drawTri);
 			drawTriangles += drawTri;
 			drawTriangles += drawTri;
@@ -165,7 +165,7 @@ class Engine {
 		}
 		}
 	}
 	}
 	
 	
-	public function renderMultiBuffers( buffers : h3d.impl.Buffer.BufferOffset, indexes : h3d.impl.Indexes, startTri = 0, drawTri = -1 ) {
+	public function renderMultiBuffers( buffers : Buffer.BufferOffset, indexes : Indexes, startTri = 0, drawTri = -1 ) {
 		var maxTri = Std.int(indexes.count / 3);
 		var maxTri = Std.int(indexes.count / 3);
 		if( maxTri <= 0 ) return;
 		if( maxTri <= 0 ) return;
 		driver.selectMultiBuffers(buffers);
 		driver.selectMultiBuffers(buffers);
@@ -194,7 +194,7 @@ class Engine {
 		if( disposed )
 		if( disposed )
 			mem.onContextLost();
 			mem.onContextLost();
 		else
 		else
-			mem = new h3d.impl.MemoryManager(driver, 65400);
+			mem = new h3d.impl.MemoryManager(driver);
 		hardware = driver.isHardware();
 		hardware = driver.isHardware();
 		set_debug(debug);
 		set_debug(debug);
 		set_fullScreen(fullScreen);
 		set_fullScreen(fullScreen);

+ 3 - 3
h3d/impl/Indexes.hx → h3d/Indexes.hx

@@ -1,11 +1,11 @@
-package h3d.impl;
+package h3d;
 
 
 @:allow(h3d.impl.MemoryManager)
 @:allow(h3d.impl.MemoryManager)
 @:allow(h3d.Engine)
 @:allow(h3d.Engine)
 class Indexes {
 class Indexes {
 
 
-	var mem : MemoryManager;
-	var ibuf : Driver.IndexBuffer;
+	var mem : h3d.impl.MemoryManager;
+	var ibuf : h3d.impl.Driver.IndexBuffer;
 	public var count(default,null) : Int;
 	public var count(default,null) : Int;
 	
 	
 	function new(mem, ibuf, count) {
 	function new(mem, ibuf, count) {

+ 1 - 1
h3d/fbx/Geometry.hx

@@ -101,7 +101,7 @@ class Geometry {
 	}
 	}
 
 
 	/**
 	/**
-		Decode polygon informations into triangle indexes and vertexes indexes.
+		Decode polygon informations into triangle indexes and vertices indexes.
 		Returns vidx, which is the list of vertices indexes and iout which is the index buffer for the full vertex model
 		Returns vidx, which is the list of vertices indexes and iout which is the index buffer for the full vertex model
 	**/
 	**/
 	public function getIndexes() {
 	public function getIndexes() {

+ 0 - 91
h3d/impl/Buffer.hx

@@ -1,91 +0,0 @@
-package h3d.impl;
-
-class Buffer {
-
-	public var b : MemoryManager.BigBuffer;
-	public var pos : Int;
-	public var nvert : Int;
-	public var next : Buffer;
-	#if debug
-	public var allocPos : AllocPos;
-	public var allocNext : Buffer;
-	public var allocPrev : Buffer;
-	#end
-	
-	public function new(b, pos, nvert) {
-		this.b = b;
-		this.pos = pos;
-		this.nvert = nvert;
-	}
-
-	public function isDisposed() {
-		return b == null || b.isDisposed();
-	}
-	
-	public function dispose() {
-		if( b != null ) {
-			b.freeCursor(pos, nvert);
-			#if debug
-			if( allocNext != null )
-				allocNext.allocPrev = allocPrev;
-			if( allocPrev != null )
-				allocPrev.allocNext = allocNext;
-			if( b.allocHead == this )
-				b.allocHead = allocNext;
-			#end
-			b = null;
-			if( next != null ) next.dispose();
-		}
-	}
-	
-	public function uploadVector( data : hxd.FloatBuffer, dataPos : Int, nverts : Int ) {
-		var cur = this;
-		while( nverts > 0 ) {
-			if( cur == null ) throw "Too many vertexes";
-			var count = nverts > cur.nvert ? cur.nvert : nverts;
-			cur.b.mem.driver.uploadVertexBuffer(cur.b.vbuf, cur.pos, count, data, dataPos);
-			dataPos += count * b.stride;
-			nverts -= count;
-			cur = cur.next;
-		}
-	}
-	
-	public function uploadBytes( data : haxe.io.Bytes, dataPos : Int, nverts : Int ) {
-		var cur = this;
-		while( nverts > 0 ) {
-			if( cur == null ) throw "Too many vertexes";
-			var count = nverts > cur.nvert ? cur.nvert : nverts;
-			cur.b.mem.driver.uploadVertexBytes(cur.b.vbuf, cur.pos, count, data, dataPos);
-			dataPos += count * b.stride * 4;
-			nverts -= count;
-			cur = cur.next;
-		}
-	}
-	
-}
-
-class BufferOffset {
-	public var id : Int;
-	public var b : Buffer;
-	public var offset : Int;
-	
-	/*
-		This is used to return a list of BufferOffset without allocating an array
-	*/
-	public var next : BufferOffset;
-	
-	static var UID = 0;
-	
-	public function new(b, offset) {
-		this.id = UID++;
-		this.b = b;
-		this.offset = offset;
-	}
-	public function dispose() {
-		if( b != null ) {
-			b.dispose();
-			b = null;
-		}
-		next = null;
-	}
-}

+ 1 - 15
h3d/impl/Driver.hx

@@ -1,22 +1,8 @@
 package h3d.impl;
 package h3d.impl;
 
 
-#if flash
 typedef IndexBuffer = flash.display3D.IndexBuffer3D;
 typedef IndexBuffer = flash.display3D.IndexBuffer3D;
 typedef VertexBuffer = Stage3dDriver.VertexWrapper;
 typedef VertexBuffer = Stage3dDriver.VertexWrapper;
 typedef Texture = flash.display3D.textures.TextureBase;
 typedef Texture = flash.display3D.textures.TextureBase;
-#elseif js
-typedef IndexBuffer = js.html.webgl.Buffer;
-typedef VertexBuffer = { b : js.html.webgl.Buffer, stride : Int };
-typedef Texture = js.html.webgl.Texture;
-#elseif cpp
-typedef IndexBuffer = openfl.gl.GLBuffer;
-typedef VertexBuffer = { b : openfl.gl.GLBuffer, stride : Int };
-typedef Texture = openfl.gl.GLTexture;
-#else
-typedef IndexBuffer = Int;
-typedef VertexBuffer = Int;
-typedef Texture = Int;
-#end
 
 
 class Driver {
 class Driver {
 	
 	
@@ -94,7 +80,7 @@ class Driver {
 		return null;
 		return null;
 	}
 	}
 
 
-	public function allocVertex( count : Int, stride : Int ) : VertexBuffer {
+	public function allocVertex( m : ManagedBuffer ) : VertexBuffer {
 		return null;
 		return null;
 	}
 	}
 	
 	

+ 139 - 0
h3d/impl/ManagedBuffer.hx

@@ -0,0 +1,139 @@
+package h3d.impl;
+
+@:allow(h3d)
+private class FreeCell {
+	var pos : Int;
+	var count : Int;
+	var next : FreeCell;
+	function new(pos,count,next) {
+		this.pos = pos;
+		this.count = count;
+		this.next = next;
+	}
+}
+
+@:allow(h3d.impl.MemoryManager)
+class ManagedBuffer {
+
+	var mem : MemoryManager;
+	public var stride(default,null) : Int;
+	public var size(default,null) : Int;
+	public var flags(default, null) : haxe.EnumFlags<Buffer.BufferFlag>;
+	
+	var vbuf : Driver.VertexBuffer;
+	var freeList : FreeCell;
+	var next : ManagedBuffer;
+	
+	public function new( stride, size, ?flags : Array<Buffer.BufferFlag> ) {
+		this.flags = new haxe.EnumFlags();
+		if( flags != null )
+			for( f in flags )
+				this.flags.set(f);
+		this.mem = h3d.Engine.getCurrent().mem;
+		this.size = size;
+		this.stride = stride;
+		this.freeList = new FreeCell(0, size, null);
+		mem.allocManaged(this);
+	}
+
+	public function uploadVertexBuffer( start : Int, vertices : Int, buf : hxd.FloatBuffer, bufPos = 0 ) {
+		mem.driver.uploadVertexBuffer(vbuf, start, vertices, buf, bufPos);
+	}
+
+	public function uploadVertexBytes( start : Int, vertices : Int, data : haxe.io.Bytes, dataPos = 0 ) {
+		mem.driver.uploadVertexBytes(vbuf, start, vertices, data, dataPos);
+	}
+	
+	public function alloc(vertices,align) {
+		var p = allocPosition(vertices, align);
+		if( p < 0 )
+			return null;
+		var b = new Buffer(vertices, stride, [NoAlloc]);
+		@:privateAccess {
+			b.position = p;
+			b.buffer = this;
+		};
+		return b;
+	}
+	
+	function allocPosition( nvert : Int, align : Int ) {
+		var free = freeList;
+		while( free != null ) {
+			if( free.count >= nvert ) {
+				var d = (align - (free.pos % align)) % align;
+				if( d == 0 )
+					break;
+				// insert some padding
+				if( free.count >= nvert + d ) {
+					free.next = new FreeCell(free.pos + d, free.count - d, free.next);
+					free.count = d;
+					free = free.next;
+					break;
+				}
+			}
+			free = free.next;
+		}
+		if( free == null )
+			return -1;
+		var pos = free.pos;
+		free.pos += nvert;
+		free.count -= nvert;
+		return pos;
+	}
+	
+	function allocBuffer( b : Buffer ) {
+		var align = b.flags.has(Quads) ? 4 : (b.flags.has(Triangles) ? 3 : 1);
+		var p = allocPosition(b.vertices, align);
+		if( p < 0 ) return false;
+		@:privateAccess {
+			b.position = p;
+			b.buffer = this;
+		};
+		return true;
+	}
+	
+	@:allow(h3d.Buffer.dispose)
+	function freeBuffer( b : Buffer ) {
+		var prev : FreeCell = null;
+		var f = freeList;
+		var nvert = b.vertices;
+		var end = b.position + nvert;
+		while( f != null ) {
+			if( f.pos == end ) {
+				f.pos -= nvert;
+				f.count += nvert;
+				if( prev != null && prev.pos + prev.count == f.pos ) {
+					prev.count += f.count;
+					prev.next = f.next;
+				}
+				nvert = 0;
+				break;
+			}
+			if( f.pos > end ) {
+				if( prev != null && prev.pos + prev.count == b.position )
+					prev.count += nvert;
+				else {
+					var n = new FreeCell(b.position, nvert, f);
+					if( prev == null ) freeList = n else prev.next = n;
+				}
+				nvert = 0;
+				break;
+			}
+			prev = f;
+			f = f.next;
+		}
+		if( nvert != 0 )
+			throw "assert";
+		if( freeList.count == size && !flags.has(Managed) )
+			dispose();
+	}
+
+	public function dispose() {
+		mem.freeManaged(this);
+	}
+	
+	public inline function isDisposed() {
+		return vbuf == null;
+	}
+
+}

+ 203 - 317
h3d/impl/MemoryManager.hx

@@ -1,94 +1,15 @@
 package h3d.impl;
 package h3d.impl;
 
 
-#if flash
-private typedef WeakMap<K,T> = haxe.ds.WeakMap<K,T>;
-#else
-private typedef WeakMap<K,T> = haxe.ds.ObjectMap<K,T>;
-#end
-
-@:allow(h3d)
-class FreeCell {
-	var pos : Int;
-	var count : Int;
-	var next : FreeCell;
-	function new(pos,count,next) {
-		this.pos = pos;
-		this.count = count;
-		this.next = next;
-	}
-}
-
-@:allow(h3d)
-class BigBuffer {
-
-	var mem : MemoryManager;
-	var stride : Int;
-	var size : Int;
-	
-	var vbuf : Driver.VertexBuffer;
-	var free : FreeCell;
-	var next : BigBuffer;
-	#if debug
-	public var allocHead : Buffer;
-	#end
-	
-	function new(mem, v, stride, size) {
-		this.mem = mem;
-		this.size = size;
-		this.stride = stride;
-		this.vbuf = v;
-		this.free = new FreeCell(0,size,null);
-	}
-
-	function freeCursor( pos:Int, nvect:Int ) {
-		var prev : FreeCell = null;
-		var f = free;
-		var end = pos + nvect;
-		while( f != null ) {
-			if( f.pos == end ) {
-				f.pos -= nvect;
-				f.count += nvect;
-				if( prev != null && prev.pos + prev.count == f.pos ) {
-					prev.count += f.count;
-					prev.next = f.next;
-				}
-				return;
-			}
-			if( f.pos > end ) {
-				if( prev != null && prev.pos + prev.count == pos )
-					prev.count += nvect;
-				else {
-					var n = new FreeCell(pos, nvect, f);
-					if( prev == null ) free = n else prev.next = n;
-				}
-				return;
-			}
-			prev = f;
-			f = f.next;
-		}
-		if( nvect != 0 )
-			throw "assert";
-	}
-
-	function dispose() {
-		mem.driver.disposeVertex(vbuf);
-		vbuf = null;
-	}
-	
-	inline function isDisposed() {
-		return vbuf == null;
-	}
-
-}
-
 class MemoryManager {
 class MemoryManager {
 
 
 	static inline var MAX_MEMORY = 250 << 20; // MB
 	static inline var MAX_MEMORY = 250 << 20; // MB
 	static inline var MAX_BUFFERS = 4096;
 	static inline var MAX_BUFFERS = 4096;
+	static inline var SIZE = 65533;
+	static var ALL_FLAGS = Type.allEnums(Buffer.BufferFlag);
 
 
 	@:allow(h3d)
 	@:allow(h3d)
 	var driver : Driver;
 	var driver : Driver;
-	var buffers : Array<BigBuffer>;
+	var buffers : Array<ManagedBuffer>;
 	var indexes : Array<Indexes>;
 	var indexes : Array<Indexes>;
 	var textures : Array<h3d.mat.Texture>;
 	var textures : Array<h3d.mat.Texture>;
 	
 	
@@ -97,27 +18,23 @@ class MemoryManager {
 	public var usedMemory(default, null) : Int = 0;
 	public var usedMemory(default, null) : Int = 0;
 	public var texMemory(default, null) : Int = 0;
 	public var texMemory(default, null) : Int = 0;
 	public var bufferCount(default,null) : Int = 0;
 	public var bufferCount(default,null) : Int = 0;
-	public var allocSize(default,null) : Int;
 
 
-	public function new(driver,allocSize) {
+	public function new(driver) {
 		this.driver = driver;
 		this.driver = driver;
-		this.allocSize = allocSize;
-
 		indexes = new Array();
 		indexes = new Array();
 		textures = new Array();
 		textures = new Array();
 		buffers = new Array();
 		buffers = new Array();
-		
 		initIndexes();
 		initIndexes();
 	}
 	}
 	
 	
 	function initIndexes() {
 	function initIndexes() {
 		var indices = new hxd.IndexBuffer();
 		var indices = new hxd.IndexBuffer();
-		for( i in 0...allocSize ) indices.push(i);
+		for( i in 0...SIZE ) indices.push(i);
 		triIndexes = allocIndex(indices);
 		triIndexes = allocIndex(indices);
 
 
 		var indices = new hxd.IndexBuffer();
 		var indices = new hxd.IndexBuffer();
 		var p = 0;
 		var p = 0;
-		for( i in 0...allocSize >> 2 ) {
+		for( i in 0...SIZE >> 2 ) {
 			var k = i << 2;
 			var k = i << 2;
 			indices.push(k);
 			indices.push(k);
 			indices.push(k + 1);
 			indices.push(k + 1);
@@ -126,6 +43,7 @@ class MemoryManager {
 			indices.push(k + 1);
 			indices.push(k + 1);
 			indices.push(k + 3);
 			indices.push(k + 3);
 		}
 		}
+		indices.push(SIZE);
 		quadIndexes = allocIndex(indices);
 		quadIndexes = allocIndex(indices);
 	}
 	}
 
 
@@ -136,17 +54,17 @@ class MemoryManager {
 	public dynamic function garbage() {
 	public dynamic function garbage() {
 	}
 	}
 
 
+	// ------------------------------------- BUFFERS ------------------------------------------
+	
 	/**
 	/**
 		Clean empty (unused) buffers
 		Clean empty (unused) buffers
 	**/
 	**/
-	public function cleanBuffers() {
+	public function cleanManagedBuffers() {
 		for( i in 0...buffers.length ) {
 		for( i in 0...buffers.length ) {
-			var b = buffers[i], prev : BigBuffer = null;
+			var b = buffers[i], prev : ManagedBuffer = null;
 			while( b != null ) {
 			while( b != null ) {
-				if( b.free.count == b.size ) {
+				if( b.freeList.count == b.size ) {
 					b.dispose();
 					b.dispose();
-					bufferCount--;
-					usedMemory -= b.size * b.stride * 4;
 					if( prev == null )
 					if( prev == null )
 						buffers[i] = b.next;
 						buffers[i] = b.next;
 					else
 					else
@@ -157,73 +75,108 @@ class MemoryManager {
 			}
 			}
 		}
 		}
 	}
 	}
+	
+	@:allow(h3d.impl.ManagedBuffer)
+	function allocManaged( m : ManagedBuffer ) {
+		if( m.vbuf != null ) return;
 
 
-	public function stats() {
-		var total = 0, free = 0, count = 0;
-		for( b in buffers ) {
-			var b = b;
-			while( b != null ) {
-				total += b.stride * b.size * 4;
-				var f = b.free;
-				while( f != null ) {
-					free += f.count * b.stride * 4;
-					f = f.next;
-				}
-				count++;
-				b = b.next;
+		var mem = m.size * m.stride * 4;
+		while( usedMemory + mem > MAX_MEMORY || bufferCount >= MAX_BUFFERS || (m.vbuf = driver.allocVertex(m)) == null ) {
+			var size = usedMemory - freeMemorySize();
+			garbage();
+			cleanManagedBuffers();
+			if( usedMemory - freeMemorySize() == size ) {
+				if( bufferCount >= MAX_BUFFERS )
+					throw "Too many buffer";
+				throw "Memory full";
 			}
 			}
 		}
 		}
-		return {
-			bufferCount : count,
-			freeMemory : free,
-			totalMemory : total,
-			textureCount : textures.length,
-			textureMemory : texMemory,
-		};
+		usedMemory += mem;
+		bufferCount++;
 	}
 	}
 
 
-	public function allocStats() : Array<{ file : String, line : Int, count : Int, tex : Bool, size : Int }> {
-		#if !debug
-		return [];
-		#else
-		var h = new Map();
-		var all = [];
-		for( buf in buffers ) {
-			var buf = buf;
-			while( buf != null ) {
-				var b = buf.allocHead;
-				while( b != null ) {
-					var key = b.allocPos.fileName + ":" + b.allocPos.lineNumber;
-					var inf = h.get(key);
-					if( inf == null ) {
-						inf = { file : b.allocPos.fileName, line : b.allocPos.lineNumber, count : 0, size : 0, tex : false };
-						h.set(key, inf);
-						all.push(inf);
+	@:allow(h3d.impl.ManagedBuffer)
+	function freeManaged( m : ManagedBuffer ) {
+		if( m.vbuf == null ) return;
+		driver.disposeVertex(m.vbuf);
+		m.vbuf = null;
+		usedMemory -= m.size * m.stride * 4;
+		bufferCount--;
+	}
+	
+	@:allow(h3d.Buffer)
+	@:access(h3d.Buffer)
+	function allocBuffer( b : Buffer, stride : Int ) {
+		// split big buffers
+		var max = b.flags.has(Quads) ? 65532 : b.flags.has(Triangles) ? 65533 : 65534;
+		if( b.vertices > max ) {
+			if( max == 65534 )
+				throw "Cannot split buffer with "+b.vertices+" vertices if it's not Quads/Triangles";
+			var rem = b.vertices - max;
+			b.vertices = max;
+			// make sure to alloc in order
+			allocBuffer(b, stride);
+			var flags = [];
+			for( f in ALL_FLAGS )
+				if( b.flags.has(f) )
+					flags.push(f);
+			b.next = new Buffer(rem, stride, flags);
+			return;
+		}
+		
+		if( !b.flags.has(Managed) ) {
+			var m = new ManagedBuffer(stride, b.vertices);
+			m.allocBuffer(b);
+			return;
+		}
+		
+		// look into one of the managed buffers
+		var m = buffers[stride], prev = null;
+		while( m != null ) {
+			if( m.allocBuffer(b) )
+				return;
+			prev = m;
+			m = m.next;
+		}
+
+		// if quad/triangles, we are allowed to split it
+		var align = b.flags.has(Triangles) ? 3 : b.flags.has(Quads) ? 4 : 0;
+		if( m == null && align > 0 ) {
+			var total = b.vertices;
+			var size = total;
+			while( size > 2048 ) {
+				m = buffers[stride];
+				size >>= 1;
+				size -= size % align;
+				b.vertices = size;
+				while( m != null ) {
+					if( m.allocBuffer(b) ) {
+						var flags = [];
+						for( f in ALL_FLAGS )
+							if( b.flags.has(f) )
+								flags.push(f);
+						b.next = new Buffer(total - size, stride, flags);
+						return;
 					}
 					}
-					inf.count++;
-					inf.size += b.nvert * b.b.stride * 4;
-					b = b.allocNext;
+					m = m.next;
 				}
 				}
-				buf = buf.next;
-			}
-		}
-		for( t in textures ) {
-			var key = "$"+t.allocPos.fileName + ":" + t.allocPos.lineNumber;
-			var inf = h.get(key);
-			if( inf == null ) {
-				inf = { file : t.allocPos.fileName, line : t.allocPos.lineNumber, count : 0, size : 0, tex : true };
-				h.set(key, inf);
-				all.push(inf);
 			}
 			}
-			inf.count++;
-			inf.size += t.width * t.height * bpp(t);
+			b.vertices = total;
 		}
 		}
-		all.sort(function(a, b) return a.size == b.size ? a.line - b.line : b.size - a.size);
-		return all;
-		#end
+		
+		// alloc a new managed buffer
+		m = new ManagedBuffer(stride, SIZE, [Managed]);
+		if( prev == null )
+			buffers[stride] = m;
+		else
+			prev.next = m;
+	
+		if( !m.allocBuffer(b) ) throw "assert";
 	}
 	}
+
+	// ------------------------------------- INDEXES ------------------------------------------
 	
 	
-	@:allow(h3d.impl.Indexes.dispose)
+	@:allow(h3d.Indexes.dispose)
 	function deleteIndexes( i : Indexes ) {
 	function deleteIndexes( i : Indexes ) {
 		indexes.remove(i);
 		indexes.remove(i);
 		driver.disposeIndexes(i.ibuf);
 		driver.disposeIndexes(i.ibuf);
@@ -231,6 +184,19 @@ class MemoryManager {
 		usedMemory -= i.count * 2;
 		usedMemory -= i.count * 2;
 	}
 	}
 	
 	
+	public function allocIndex( indices : hxd.IndexBuffer, pos = 0, count = -1 ) {
+		if( count < 0 ) count = indices.length;
+		var ibuf = driver.allocIndexes(count);
+		var idx = new Indexes(this, ibuf, count);
+		idx.upload(indices, 0, count);
+		indexes.push(idx);
+		usedMemory += idx.count * 2;
+		return idx;
+	}
+
+	
+	// ------------------------------------- TEXTURES ------------------------------------------
+	
 	function bpp( t : h3d.mat.Texture ) {
 	function bpp( t : h3d.mat.Texture ) {
 		return 4;
 		return 4;
 	}
 	}
@@ -272,39 +238,45 @@ class MemoryManager {
 		texMemory += t.width * t.height * bpp(t);
 		texMemory += t.width * t.height * bpp(t);
 	}
 	}
 	
 	
-	public function allocIndex( indices : hxd.IndexBuffer, pos = 0, count = -1 ) {
-		if( count < 0 ) count = indices.length;
-		var ibuf = driver.allocIndexes(count);
-		var idx = new Indexes(this, ibuf, count);
-		idx.upload(indices, 0, count);
-		indexes.push(idx);
-		usedMemory += idx.count * 2;
-		return idx;
-	}
-
-	public function allocBytes( bytes : haxe.io.Bytes, stride : Int, align, ?allocPos : AllocPos ) {
-		var count = Std.int(bytes.length / (stride * 4));
-		var b = alloc(count, stride, align, allocPos);
-		b.uploadBytes(bytes, 0, count);
-		return b;
+	// ------------------------------------- DISPOSE ------------------------------------------
+	
+	public function onContextLost() {
+		dispose();
+		initIndexes();
 	}
 	}
 
 
-	public function allocVector( v : hxd.FloatBuffer, stride, align, ?allocPos : AllocPos ) {
-		var nvert = Std.int(v.length / stride);
-		var b = alloc(nvert, stride, align, allocPos);
-		b.uploadVector(v, 0, nvert);
-		return b;
+	public function dispose() {
+		triIndexes.dispose();
+		quadIndexes.dispose();
+		triIndexes = null;
+		quadIndexes = null;
+		for( t in textures.copy() )
+			t.dispose();
+		for( b in buffers.copy() ) {
+			var b = b;
+			while( b != null ) {
+				b.dispose();
+				b = b.next;
+			}
+		}
+		for( i in indexes.copy() )
+			i.dispose();
+		buffers = [];
+		indexes = [];
+		textures = [];
+		bufferCount = 0;
+		usedMemory = 0;
+		texMemory = 0;
 	}
 	}
+	
+	// ------------------------------------- STATS ------------------------------------------
 
 
-	/**
-		The amount of free buffers memory
-	 **/
-	function freeMemory() {
+	function freeMemorySize() {
 		var size = 0;
 		var size = 0;
 		for( b in buffers ) {
 		for( b in buffers ) {
 			var b = b;
 			var b = b;
 			while( b != null ) {
 			while( b != null ) {
-				var free = b.free;
+				var free = b.freeList;
 				while( free != null ) {
 				while( free != null ) {
 					size += free.count * b.stride * 4;
 					size += free.count * b.stride * 4;
 					free = free.next;
 					free = free.next;
@@ -314,160 +286,74 @@ class MemoryManager {
 		}
 		}
 		return size;
 		return size;
 	}
 	}
-
-	/**
-		Allocate a vertex buffer.
-		Align represent the number of vertex that represent a single primitive : 3 for triangles, 4 for quads
-		You can use 0 to allocate your own buffer but in that case you can't use pre-allocated indexes/quadIndexes
-	 **/
-	public function alloc( nvect : Int, stride, align, ?allocPos : AllocPos ) {
-		var b = buffers[stride], free = null;
-		if( nvect == 0 && align == 0 )
-			align = 3;
-		while( b != null ) {
-			free = b.free;
-			while( free != null ) {
-				if( free.count >= nvect ) {
-					// align 0 must be on first index
-					if( align == 0 ) {
-						if( free.pos != 0 )
-							free = null;
-						break;
-					} else {
-						// we can't alloc into a smaller buffer because we might use preallocated indexes
-						if( b.size != allocSize ) {
-							free = null;
-							break;
-						}
-						var d = (align - (free.pos % align)) % align;
-						if( d == 0 )
-							break;
-							
-						// insert some padding
-						if( free.count >= nvect + d ) {
-							free.next = new FreeCell(free.pos + d, free.count - d, free.next);
-							free.count = d;
-							free = free.next;
-							break;
-						}
-					}
-					break;
+	
+	public function stats() {
+		var total = 0, free = 0, count = 0;
+		for( b in buffers ) {
+			var b = b;
+			while( b != null ) {
+				total += b.stride * b.size * 4;
+				var f = b.freeList;
+				while( f != null ) {
+					free += f.count * b.stride * 4;
+					f = f.next;
 				}
 				}
-				free = free.next;
+				count++;
+				b = b.next;
 			}
 			}
-			if( free != null ) break;
-			b = b.next;
 		}
 		}
-		// try splitting big groups
-		if( b == null && align > 0 ) {
-			var size = nvect;
-			while( size > 1000 ) {
-				b = buffers[stride];
-				size >>= 1;
-				size -= size % align;
+		return {
+			bufferCount : bufferCount,
+			freeManagedMemory : free,
+			managedMemory : total,
+			totalVertexMemory : usedMemory,
+			textureCount : textures.length,
+			textureMemory : texMemory,
+		};
+	}
+
+	public function allocStats() : Array<{ file : String, line : Int, count : Int, tex : Bool, size : Int }> {
+		#if !debug
+		return [];
+		#else
+		var h = new Map();
+		var all = [];
+		/*
+		for( buf in buffers ) {
+			var buf = buf;
+			while( buf != null ) {
+				var b = buf.allocHead;
 				while( b != null ) {
 				while( b != null ) {
-					free = b.free;
-					// skip not aligned buffers
-					if( b.size != allocSize )
-						free = null;
-					while( free != null ) {
-						if( free.count >= size ) {
-							// check alignment
-							var d = (align - (free.pos % align)) % align;
-							if( d == 0 )
-								break;
-							// insert some padding
-							if( free.count >= size + d ) {
-								free.next = new FreeCell(free.pos + d, free.count - d, free.next);
-								free.count = d;
-								free = free.next;
-								break;
-							}
-						}
-						free = free.next;
+					var key = b.allocPos.fileName + ":" + b.allocPos.lineNumber;
+					var inf = h.get(key);
+					if( inf == null ) {
+						inf = { file : b.allocPos.fileName, line : b.allocPos.lineNumber, count : 0, size : 0, tex : false };
+						h.set(key, inf);
+						all.push(inf);
 					}
 					}
-					if( free != null ) break;
-					b = b.next;
+					inf.count++;
+					inf.size += b.nvert * b.b.stride * 4;
+					b = b.allocNext;
 				}
 				}
-				if( b != null ) break;
+				buf = buf.next;
 			}
 			}
 		}
 		}
-		// buffer not found : allocate a new one
-		if( b == null ) {
-			var size;
-			if( align == 0 ) {
-				size = nvect;
-				if( size > 0xFFFF ) throw "Too many vertex to allocate "+size;
-			} else
-				size = allocSize; // group allocations together to minimize buffer count
-			var mem = size * stride * 4, v = null;
-			if( usedMemory + mem > MAX_MEMORY || bufferCount >= MAX_BUFFERS || (v = driver.allocVertex(size,stride)) == null ) {
-				var size = usedMemory - freeMemory();
-				garbage();
-				cleanBuffers();
-				if( usedMemory - freeMemory() == size ) {
-					if( bufferCount >= MAX_BUFFERS )
-						throw "Too many buffer";
-					throw "Memory full";
-				}
-				return alloc(nvect, stride, align, allocPos);
+		*/
+		for( t in textures ) {
+			var key = "$"+t.allocPos.fileName + ":" + t.allocPos.lineNumber;
+			var inf = h.get(key);
+			if( inf == null ) {
+				inf = { file : t.allocPos.fileName, line : t.allocPos.lineNumber, count : 0, size : 0, tex : true };
+				h.set(key, inf);
+				all.push(inf);
 			}
 			}
-			usedMemory += mem;
-			bufferCount++;
-			b = new BigBuffer(this, v, stride, size);
-			#if flash
-			untyped v.b = b;
-			#end
-			b.next = buffers[stride];
-			buffers[stride] = b;
-			free = b.free;
+			inf.count++;
+			inf.size += t.width * t.height * bpp(t);
 		}
 		}
-		// always alloc multiples of 4 (prevent quad split)
-		var alloc = nvect > free.count ? free.count - (free.count%align) : nvect;
-		var fpos = free.pos;
-		free.pos += alloc;
-		free.count -= alloc;
-		var b = new Buffer(b, fpos, alloc);
-		nvect -= alloc;
-		#if debug
-		var head = b.b.allocHead;
-		b.allocPos = allocPos;
-		b.allocNext = head;
-		if( head != null ) head.allocPrev = b;
-		b.b.allocHead = b;
+		all.sort(function(a, b) return a.size == b.size ? a.line - b.line : b.size - a.size);
+		return all;
 		#end
 		#end
-		if( nvect > 0 )
-			b.next = this.alloc(nvect, stride, align #if debug, allocPos #end);
-		return b;
 	}
 	}
 
 
-	public function onContextLost() {
-		dispose();
-		initIndexes();
-	}
-
-	public function dispose() {
-		triIndexes.dispose();
-		quadIndexes.dispose();
-		triIndexes = null;
-		quadIndexes = null;
-		for( t in textures.copy() )
-			t.dispose();
-		for( b in buffers.copy() ) {
-			var b = b;
-			while( b != null ) {
-				b.dispose();
-				b = b.next;
-			}
-		}
-		for( i in indexes.copy() )
-			i.dispose();
-		buffers = [];
-		indexes = [];
-		textures = [];
-		bufferCount = 0;
-		usedMemory = 0;
-		texMemory = 0;
-	}
 
 
 }
 }

+ 16 - 17
h3d/impl/Stage3dDriver.hx

@@ -6,25 +6,24 @@ import h3d.impl.Driver;
 @:allow(h3d.impl.Stage3dDriver)
 @:allow(h3d.impl.Stage3dDriver)
 class VertexWrapper {
 class VertexWrapper {
 	var vbuf : flash.display3D.VertexBuffer3D;
 	var vbuf : flash.display3D.VertexBuffer3D;
-	var stride : Int;
 	var written : Bool;
 	var written : Bool;
-	var b : MemoryManager.BigBuffer;
+	var b : ManagedBuffer;
 	
 	
-	function new(vbuf, stride) {
+	function new(vbuf, b) {
 		this.vbuf = vbuf;
 		this.vbuf = vbuf;
-		this.stride = stride;
+		this.b = b;
 	}
 	}
-	
+		
 	function finalize( driver : Stage3dDriver ) {
 	function finalize( driver : Stage3dDriver ) {
 		if( written ) return;
 		if( written ) return;
 		written = true;
 		written = true;
 		// fill all the free positions that were unwritten with zeroes (necessary for flash)
 		// fill all the free positions that were unwritten with zeroes (necessary for flash)
-		var f = b.free;
+		var f = @:privateAccess b.freeList;
 		while( f != null ) {
 		while( f != null ) {
 			if( f.count > 0 ) {
 			if( f.count > 0 ) {
 				var mem : UInt = f.count * b.stride * 4;
 				var mem : UInt = f.count * b.stride * 4;
 				if( driver.empty.length < mem ) driver.empty.length = mem;
 				if( driver.empty.length < mem ) driver.empty.length = mem;
-				driver.uploadVertexBytes(b.vbuf, f.pos, f.count, haxe.io.Bytes.ofData(driver.empty), 0);
+				driver.uploadVertexBytes(@:privateAccess b.vbuf, f.pos, f.count, haxe.io.Bytes.ofData(driver.empty), 0);
 			}
 			}
 			f = f.next;
 			f = f.next;
 		}
 		}
@@ -151,17 +150,17 @@ class Stage3dDriver extends Driver {
 		t.dispose();
 		t.dispose();
 	}
 	}
 	
 	
-	override function allocVertex( count : Int, stride : Int ) : VertexBuffer {
+	override function allocVertex( buf : ManagedBuffer ) : VertexBuffer {
 		var v;
 		var v;
 		try {
 		try {
-			v = ctx.createVertexBuffer(count, stride);
+			v = ctx.createVertexBuffer(buf.size, buf.stride);
 		} catch( e : flash.errors.Error ) {
 		} catch( e : flash.errors.Error ) {
 			// too many resources / out of memory
 			// too many resources / out of memory
 			if( e.errorID == 3691 )
 			if( e.errorID == 3691 )
 				return null;
 				return null;
 			throw e;
 			throw e;
 		}
 		}
-		return new VertexWrapper(v, stride);
+		return new VertexWrapper(v, buf);
 	}
 	}
 
 
 	override function allocIndexes( count : Int ) : IndexBuffer {
 	override function allocIndexes( count : Int ) : IndexBuffer {
@@ -249,7 +248,7 @@ class Stage3dDriver extends Driver {
 	
 	
 	override function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
 	override function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
 		var data = buf.getNative();
 		var data = buf.getNative();
-		v.vbuf.uploadFromVector( bufPos == 0 ? data : data.slice(bufPos, vertexCount * v.stride + bufPos), startVertex, vertexCount );
+		v.vbuf.uploadFromVector( bufPos == 0 ? data : data.slice(bufPos, vertexCount * v.b.stride + bufPos), startVertex, vertexCount );
 	}
 	}
 
 
 	override function uploadVertexBytes( v : VertexBuffer, startVertex : Int, vertexCount : Int, bytes : haxe.io.Bytes, bufPos : Int ) {
 	override function uploadVertexBytes( v : VertexBuffer, startVertex : Int, vertexCount : Int, bytes : haxe.io.Bytes, bufPos : Int ) {
@@ -345,8 +344,8 @@ class Stage3dDriver extends Driver {
 			return;
 			return;
 		curBuffer = v;
 		curBuffer = v;
 		curMultiBuffer[0] = -1;
 		curMultiBuffer[0] = -1;
-		if( v.stride < curShader.stride )
-			throw "Buffer stride (" + v.stride + ") and shader stride (" + curShader.stride + ") mismatch";
+		if( v.b.stride < curShader.stride )
+			throw "Buffer stride (" + v.b.stride + ") and shader stride (" + curShader.stride + ") mismatch";
 		if( !v.written )
 		if( !v.written )
 			v.finalize(this);
 			v.finalize(this);
 		var pos = 0, offset = 0;
 		var pos = 0, offset = 0;
@@ -385,11 +384,11 @@ class Stage3dDriver extends Driver {
 			var b = buffers;
 			var b = buffers;
 			while( offset < curShader.stride ) {
 			while( offset < curShader.stride ) {
 				var size = bits & 7;
 				var size = bits & 7;
-				if( b.b.next != null )
+				if( b.buffer.next != null )
 					throw "Buffer is split";
 					throw "Buffer is split";
-				if( !b.b.b.vbuf.written )
-					b.b.b.vbuf.finalize(this);
-				ctx.setVertexBufferAt(pos, b.b.b.vbuf.vbuf, b.offset, FORMAT[size]);
+				var vbuf = @:privateAccess b.buffer.buffer.vbuf;
+				if( !vbuf.written ) vbuf.finalize(this);
+				ctx.setVertexBufferAt(pos, vbuf.vbuf, b.offset, FORMAT[size]);
 				curMultiBuffer[pos] = b.id;
 				curMultiBuffer[pos] = b.id;
 				offset += size == 0 ? 1 : size;
 				offset += size == 0 ? 1 : size;
 				bits >>= 3;
 				bits >>= 3;

+ 1 - 3
h3d/parts/Emitter.hx

@@ -558,9 +558,7 @@ class Emitter extends h3d.scene.Object implements Randomized {
 		}
 		}
 		var stride = 10;
 		var stride = 10;
 		if( hasColor ) stride += 4;
 		if( hasColor ) stride += 4;
-		var nverts = Std.int(pos / stride);
-		var buffer = ctx.engine.mem.alloc(nverts, stride, 4);
-		buffer.uploadVector(tmpBuf, 0, nverts);
+		var buffer = h3d.Buffer.ofFloats(tmpBuf, stride, [Quads, Dynamic]);
 		var size = eval(state.globalSize);
 		var size = eval(state.globalSize);
 		
 		
 		material.pshader.mpos = state.emitLocal ? this.absPos : h3d.Matrix.I();
 		material.pshader.mpos = state.emitLocal ? this.absPos : h3d.Matrix.I();

+ 7 - 7
h3d/prim/FBXModel.hx

@@ -1,6 +1,6 @@
 package h3d.prim;
 package h3d.prim;
 using h3d.fbx.Data;
 using h3d.fbx.Data;
-import h3d.impl.Buffer.BufferOffset;
+import h3d.Buffer.BufferOffset;
 import h3d.col.Point;
 import h3d.col.Point;
 
 
 class FBXModel extends MeshPrimitive {
 class FBXModel extends MeshPrimitive {
@@ -10,7 +10,7 @@ class FBXModel extends MeshPrimitive {
 	public var multiMaterial : Bool;
 	public var multiMaterial : Bool;
 	var bounds : h3d.col.Bounds;
 	var bounds : h3d.col.Bounds;
 	var curMaterial : Int;
 	var curMaterial : Int;
-	var groupIndexes : Array<h3d.impl.Indexes>;
+	var groupIndexes : Array<Indexes>;
 
 
 	public function new(g) {
 	public function new(g) {
 		this.geom = g;
 		this.geom = g;
@@ -183,17 +183,17 @@ class FBXModel extends MeshPrimitive {
 			pos++;
 			pos++;
 		}
 		}
 		
 		
-		addBuffer("pos", engine.mem.allocVector(pbuf, 3, 0));
-		if( nbuf != null ) addBuffer("normal", engine.mem.allocVector(nbuf, 3, 0));
-		if( tbuf != null ) addBuffer("uv", engine.mem.allocVector(tbuf, 2, 0));
+		addBuffer("pos", h3d.Buffer.ofFloats(pbuf, 3));
+		if( nbuf != null ) addBuffer("normal", h3d.Buffer.ofFloats(nbuf, 3));
+		if( tbuf != null ) addBuffer("uv", h3d.Buffer.ofFloats(tbuf, 2));
 		if( sbuf != null ) {
 		if( sbuf != null ) {
 			var nverts = Std.int(sbuf.length / ((skin.bonesPerVertex + 1) * 4));
 			var nverts = Std.int(sbuf.length / ((skin.bonesPerVertex + 1) * 4));
-			var skinBuf = engine.mem.alloc(nverts, skin.bonesPerVertex + 1, 0);
+			var skinBuf = new h3d.Buffer(nverts, skin.bonesPerVertex + 1);
 			skinBuf.uploadBytes(sbuf.getBytes(), 0, nverts);
 			skinBuf.uploadBytes(sbuf.getBytes(), 0, nverts);
 			addBuffer("weights", skinBuf, 0);
 			addBuffer("weights", skinBuf, 0);
 			addBuffer("indexes", skinBuf, skin.bonesPerVertex);
 			addBuffer("indexes", skinBuf, skin.bonesPerVertex);
 		}
 		}
-		if( cbuf != null ) addBuffer("color", engine.mem.allocVector(cbuf, 3, 0));
+		if( cbuf != null ) addBuffer("color", h3d.Buffer.ofFloats(cbuf, 3));
 		
 		
 		indexes = engine.mem.allocIndex(idx);
 		indexes = engine.mem.allocIndex(idx);
 		if( mats != null ) {
 		if( mats != null ) {

+ 2 - 2
h3d/prim/MeshPrimitive.hx

@@ -2,7 +2,7 @@ package h3d.prim;
 
 
 class MeshPrimitive extends Primitive {
 class MeshPrimitive extends Primitive {
 		
 		
-	var bufferCache : Map<Int,h3d.impl.Buffer.BufferOffset>;
+	var bufferCache : Map<Int,h3d.Buffer.BufferOffset>;
 	
 	
 	function allocBuffer( engine : h3d.Engine, name : String ) {
 	function allocBuffer( engine : h3d.Engine, name : String ) {
 		return null;
 		return null;
@@ -22,7 +22,7 @@ class MeshPrimitive extends Primitive {
 		var id = hash(name);
 		var id = hash(name);
 		var old = bufferCache.get(id);
 		var old = bufferCache.get(id);
 		if( old != null ) old.dispose();
 		if( old != null ) old.dispose();
-		bufferCache.set(id, new h3d.impl.Buffer.BufferOffset(buf, offset));
+		bufferCache.set(id, new h3d.Buffer.BufferOffset(buf, offset));
 	}
 	}
 
 
 	override public function dispose() {
 	override public function dispose() {

+ 1 - 1
h3d/prim/Plan2D.hx

@@ -27,7 +27,7 @@ class Plan2D extends Primitive {
 		v.push( 1);
 		v.push( 1);
 		v.push( 0);
 		v.push( 0);
 		
 		
-		buffer = engine.mem.allocVector(v, 4, 4);
+		buffer = h3d.Buffer.ofFloats(v, 4, [Quads]);
 	}
 	}
 	
 	
 	override function render(engine:h3d.Engine) {
 	override function render(engine:h3d.Engine) {

+ 1 - 1
h3d/prim/Plan3D.hx

@@ -73,7 +73,7 @@ class Plan3D extends Primitive {
 		v.push( 0);
 		v.push( 0);
 		v.push( 1);
 		v.push( 1);
 		
 		
-		buffer = engine.mem.allocVector(v, 8, 4);
+		buffer = h3d.Buffer.ofFloats(v, 8, [Quads]);
 	}
 	}
 	
 	
 	override function render(engine:h3d.Engine) {
 	override function render(engine:h3d.Engine) {

+ 1 - 1
h3d/prim/Polygon.hx

@@ -56,7 +56,7 @@ class Polygon extends Primitive {
 				buf.push(c.z);
 				buf.push(c.z);
 			}
 			}
 		}
 		}
-		buffer = engine.mem.allocVector(buf, size, idx == null ? 3 : 0);
+		buffer = h3d.Buffer.ofFloats(buf, size, idx == null ? [Triangles] : null);
 		
 		
 		if( idx != null )
 		if( idx != null )
 			indexes = engine.mem.allocIndex(idx);
 			indexes = engine.mem.allocIndex(idx);

+ 3 - 11
h3d/prim/Primitive.hx

@@ -2,19 +2,11 @@ package h3d.prim;
 
 
 class Primitive {
 class Primitive {
 	
 	
-	public var buffer : h3d.impl.Buffer;
-	public var indexes : h3d.impl.Indexes;
+	public var buffer : Buffer;
+	public var indexes : Indexes;
 	
 	
 	public function triCount() {
 	public function triCount() {
-		if( indexes != null )
-			return Std.int(indexes.count / 3);
-		var count = 0;
-		var b = buffer;
-		while( b != null ) {
-			count += Std.int(b.nvert / 3);
-			b = b.next;
-		}
-		return count;
+		return if( indexes != null ) Std.int(indexes.count / 3) else if( buffer == null ) 0 else Std.int(buffer.totalVertices() / 3);
 	}
 	}
 	
 	
 	public function getBounds() : h3d.col.Bounds {
 	public function getBounds() : h3d.col.Bounds {

+ 1 - 1
h3d/prim/Quads.hx

@@ -62,7 +62,7 @@ class Quads extends Primitive {
 		var size = 3;
 		var size = 3;
 		if( normals != null ) size += 3;
 		if( normals != null ) size += 3;
 		if( uvs != null ) size += 2;
 		if( uvs != null ) size += 2;
-		buffer = engine.mem.allocVector(v,size, 4);
+		buffer = h3d.Buffer.ofFloats(v, size, [Quads]);
 	}
 	}
 	
 	
 	public function getPoints() {
 	public function getPoints() {

+ 1 - 1
h3d/prim/RawPrimitive.hx

@@ -3,7 +3,7 @@ package h3d.prim;
 class RawPrimitive extends Primitive {
 class RawPrimitive extends Primitive {
 
 
 	public function new( engine : h3d.Engine, vbuf : hxd.FloatBuffer, stride : Int, ?ibuf : hxd.IndexBuffer ) {
 	public function new( engine : h3d.Engine, vbuf : hxd.FloatBuffer, stride : Int, ?ibuf : hxd.IndexBuffer ) {
-		buffer = engine.mem.allocVector(vbuf, stride, ibuf == null ? 3 : 0);
+		buffer = h3d.Buffer.ofFloats(vbuf, stride, ibuf == null ? [Triangles] : null);
 		if( ibuf != null )
 		if( ibuf != null )
 			indexes = engine.mem.allocIndex(ibuf);
 			indexes = engine.mem.allocIndex(ibuf);
 	}
 	}

+ 1 - 1
h3d/scene/Image.hx

@@ -64,7 +64,7 @@ class Image extends Object {
 		tmp.push(tile.v2);
 		tmp.push(tile.v2);
 		
 		
 		cast(material.shader,ImageShader).tex = tile.getTexture();
 		cast(material.shader,ImageShader).tex = tile.getTexture();
-		var b = ctx.engine.mem.allocVector(tmp, 4, 4);
+		var b = h3d.Buffer.ofFloats(tmp,4,[Quads,Dynamic]);
 		ctx.engine.selectMaterial(material);
 		ctx.engine.selectMaterial(material);
 		ctx.engine.renderQuadBuffer(b);
 		ctx.engine.renderQuadBuffer(b);
 		b.dispose();
 		b.dispose();

+ 1 - 3
h3d/scene/Particles.hx

@@ -142,9 +142,7 @@ class Particles extends Object {
 		if( hasFrame ) stride++;
 		if( hasFrame ) stride++;
 		if( hasRotation ) stride++;
 		if( hasRotation ) stride++;
 		if( hasSize ) stride++;
 		if( hasSize ) stride++;
-		var nverts = Std.int(pos / stride);
-		var buffer = ctx.engine.mem.alloc(nverts, stride, 4);
-		buffer.uploadVector(tmpBuf, 0, nverts);
+		var buffer = h3d.Buffer.ofFloats(tmpBuf, stride, [Quads, Dynamic]);
 		var size = partSize;
 		var size = partSize;
 		ctx.localPos = this.absPos;
 		ctx.localPos = this.absPos;
 		material.setup(ctx);
 		material.setup(ctx);