فهرست منبع

merged memory manager changes

Nicolas Cannasse 11 سال پیش
والد
کامیت
1dab4c24fb

+ 5 - 5
h2d/Graphics.hx

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

+ 1 - 1
h2d/RenderContext.hx

@@ -63,7 +63,7 @@ class RenderContext {
 		if( bufPos == 0 ) return;
 		beforeDraw();
 		var nverts = Std.int(bufPos / stride);
-		var tmp = engine.mem.alloc(nverts, stride, 4);
+		var tmp = new h3d.Buffer(nverts, stride, [Quads, Dynamic]);
 		tmp.uploadVector(buffer, 0, nverts);
 		engine.renderQuadBuffer(tmp);
 		tmp.dispose();

+ 1 - 4
h2d/SpriteBatch.hx

@@ -183,10 +183,7 @@ class SpriteBatch extends Drawable {
 			}
 			e = e.next;
 		}
-		var stride = 8;
-		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, 8, [Dynamic, Quads]);
 		ctx.beginDrawObject(this, tile.getTexture());
 		ctx.engine.renderQuadBuffer(buffer);
 		buffer.dispose();

+ 10 - 1
h2d/Tile.hx

@@ -114,7 +114,9 @@ class Tile {
 		return t;
 	}
 	
-	
+	/**
+		Split horizontaly or verticaly the number of given frames
+	**/
 	public function split( frames : Int, vertical = false ) {
 		var tl = [];
 		if( vertical ) {
@@ -128,6 +130,13 @@ class Tile {
 		}
 		return tl;
 	}
+
+	/**
+		Split the tile into a list of tiles of Size x Size pixels.
+	**/
+	public function grid( size : Int, dx = 0, dy = 0 ) {
+		return [for( y in 0...Std.int(height / size) ) for( x in 0...Std.int(width / size) ) sub(x * size, y * size, size, size, dx, dy)];
+	}
 	
 	public function toString() {
 		return "Tile(" + x + "," + y + "," + width + "x" + height + (dx != 0 || dy != 0 ? "," + dx + ":" + dy:"") + ")";

+ 3 - 11
h2d/TileGroup.hx

@@ -19,15 +19,7 @@ private class TileLayerContent extends h3d.prim.Primitive {
 	}
 
 	override public function triCount() {
-		if( buffer == null )
-			return tmp.length >> 4;
-		var v = 0;
-		var b = buffer;
-		while( b != null ) {
-			v += b.nvert;
-			b = b.next;
-		}
-		return v >> 1;
+		return if( buffer == null ) tmp.length >> 4 else buffer.totalVertices() >> 1;
 	}
 
 	public inline function addColor( x : Int, y : Int, color : h3d.Vector, t : Tile ) {
@@ -78,7 +70,7 @@ private class TileLayerContent extends h3d.prim.Primitive {
 		tmp.push(0);
 		insertColor(color);
 	}
-
+	
 	inline function insertColor( c : Int ) {
 		tmp.push(((c >> 16) & 0xFF) / 255.);
 		tmp.push(((c >> 8) & 0xFF) / 255.);
@@ -134,7 +126,7 @@ private class TileLayerContent extends h3d.prim.Primitive {
 
 	override public function alloc(engine:h3d.Engine) {
 		if( tmp == null ) reset();
-		buffer = engine.mem.allocVector(tmp, 8, 4);
+		buffer = h3d.Buffer.ofFloats(tmp, 8, [Quads]);
 	}
 
 	public function doRender(engine, min, len) {

+ 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;
+	}
+}

+ 15 - 13
h3d/Engine.hx

@@ -94,28 +94,28 @@ class Engine {
 		driver.uploadShaderBuffers(buffers, which);
 	}
 	
-	function selectBuffer( buf : h3d.impl.MemoryManager.BigBuffer ) {
+	function selectBuffer( buf : h3d.impl.ManagedBuffer ) {
 		if( buf.isDisposed() )
 			return false;
-		driver.selectBuffer(buf.vbuf);
+		driver.selectBuffer(@:privateAccess buf.vbuf);
 		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);
 	}
 	
-	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);
 	}
 	
 	// 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() )
 			return;
 		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 >= ntri ) {
 					startTri -= ntri;
@@ -134,7 +134,7 @@ class Engine {
 					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
 				driver.draw(indexes.ibuf, pos * 3, ntri);
 				drawTriangles += ntri;
@@ -145,14 +145,14 @@ class Engine {
 	}
 	
 	// 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 )
 			throw "Buffer is split";
 		if( indexes.isDisposed() )
 			return;
 		var maxTri = Std.int(indexes.count / 3);
 		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
 			driver.draw(indexes.ibuf, startTri * 3, drawTri);
 			drawTriangles += drawTri;
@@ -160,7 +160,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);
 		if( maxTri <= 0 ) return;
 		driver.selectMultiBuffers(buffers);
@@ -189,8 +189,10 @@ class Engine {
 		}
 		if( disposed )
 			mem.onContextLost();
-		else
-			mem = new h3d.impl.MemoryManager(driver, 65400);
+		else {
+			mem = new h3d.impl.MemoryManager(driver);
+			mem.init();
+		}
 		hardware = driver.isHardware();
 		set_debug(debug);
 		set_fullScreen(fullScreen);

+ 12 - 6
h3d/impl/Indexes.hx → h3d/Indexes.hx

@@ -1,17 +1,17 @@
-package h3d.impl;
+package h3d;
 
 @:allow(h3d.impl.MemoryManager)
 @:allow(h3d.Engine)
 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;
 	
-	function new(mem, ibuf, count) {
-		this.mem = mem;
-		this.ibuf = ibuf;
+	public function new(count) {
+		this.mem = h3d.Engine.getCurrent().mem;
 		this.count = count;
+		mem.allocIndexes(this);
 	}
 	
 	public function isDisposed() {
@@ -26,5 +26,11 @@ class Indexes {
 		if( ibuf != null )
 			mem.deleteIndexes(this);
 	}
+	
+	public static function alloc( i : hxd.IndexBuffer ) {
+		var idx = new Indexes( i.length );
+		idx.upload(i, 0, i.length);
+		return idx;
+	}
 
 }

+ 37 - 2
h3d/col/Ray.hx

@@ -45,6 +45,31 @@ class Ray {
 			return new Point(px + lx * k, py + ly * k, pz + lz * k);
 		}
 	}
+	
+	public inline function collideFrustum( mvp : Matrix ) {
+		// transform the two ray points into the normalized frustum box
+		var a = new h3d.Vector(px, py, pz);
+		a.project(mvp);
+		var b = new h3d.Vector(px + lx, py + ly, pz + lz);
+		b.project(mvp);
+		// use collide on the frustum [-1,1] bounds
+		var lx = b.x - a.x;
+		var ly = b.y - a.y;
+		var lz = b.z - a.z;
+		
+		var dx = 1 / lx;
+		var dy = 1 / ly;
+		var dz = 1 / lz;
+		var t1 = (-1 - a.x) * dx;
+		var t2 = (1 - a.x) * dx;
+		var t3 = (-1 - a.y) * dy;
+		var t4 = (1 - a.y) * dy;
+		var t5 = (0 - a.z) * dz;
+		var t6 = (1 - a.z) * dz;
+		var tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)), Math.min(t5, t6));
+		var tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)), Math.max(t5, t6));
+		return !(tmax < 0 || tmin > tmax);
+	}
 
 	public inline function collide( b : Bounds ) : Bool {
 		var dx = 1 / lx;
@@ -70,7 +95,7 @@ class Ray {
 		}
 	}
 	
-	public static function fromPoints( p1 : Point, p2 : Point ) {
+	public static inline function fromPoints( p1 : Point, p2 : Point ) {
 		var r = new Ray();
 		r.px = p1.x;
 		r.py = p1.y;
@@ -80,6 +105,16 @@ class Ray {
 		r.lz = p2.z - p1.z;
 		return r;
 	}
-	
+
+	public static inline function fromValues( x, y, z, dx, dy, dz ) {
+		var r = new Ray();
+		r.px = x;
+		r.py = y;
+		r.pz = z;
+		r.lx = dx;
+		r.ly = dy;
+		r.lz = dz;
+		return r;
+	}
 	
 }

+ 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
 	**/
 	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 - 1
h3d/impl/Driver.hx

@@ -105,7 +105,7 @@ class Driver {
 		return null;
 	}
 
-	public function allocVertex( count : Int, stride : Int ) : VertexBuffer {
+	public function allocVertex( m : ManagedBuffer ) : VertexBuffer {
 		return null;
 	}
 	

+ 6 - 6
h3d/impl/GlDriver.hx

@@ -322,12 +322,12 @@ class GlDriver extends Driver {
 		return tt;
 	}
 	
-	override function allocVertex( count : Int, stride : Int ) : VertexBuffer {
+	override function allocVertex( m : ManagedBuffer ) : VertexBuffer {
 		var b = gl.createBuffer();
 		#if js
 		gl.bindBuffer(GL.ARRAY_BUFFER, b);
-		if( count * stride == 0 ) throw "assert";
-		gl.bufferData(GL.ARRAY_BUFFER, count * stride * 4, GL.STATIC_DRAW);
+		if( m.size * m.stride == 0 ) throw "assert";
+		gl.bufferData(GL.ARRAY_BUFFER, m.size * m.stride * 4, m.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
 		gl.bindBuffer(GL.ARRAY_BUFFER, null);
 		#else
 		var tmp = new Uint8Array(count * stride * 4);
@@ -335,7 +335,7 @@ class GlDriver extends Driver {
 		gl.bufferData(GL.ARRAY_BUFFER, tmp, GL.STATIC_DRAW);
 		gl.bindBuffer(GL.ARRAY_BUFFER, null);
 		#end
-		return { b : b, stride : stride };
+		return { b : b, stride : m.stride };
 	}
 	
 	override function allocIndexes( count : Int ) : IndexBuffer {
@@ -429,8 +429,8 @@ class GlDriver extends Driver {
 	
 	override function selectMultiBuffers( buffers : Buffer.BufferOffset ) {
 		for( a in curProgram.attribs ) {
-			gl.bindBuffer(GL.ARRAY_BUFFER, buffers.b.b.vbuf.b);
-			gl.vertexAttribPointer(a.index, a.size, a.type, false, buffers.b.b.stride * 4, buffers.offset * 4);
+			gl.bindBuffer(GL.ARRAY_BUFFER, @:privateAccess buffers.buffer.buffer.vbuf.b);
+			gl.vertexAttribPointer(a.index, a.size, a.type, false, buffers.buffer.buffer.stride * 4, buffers.offset * 4);
 			buffers = buffers.next;
 		}
 	}

+ 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;
+	}
+
+}

+ 205 - 319
h3d/impl/MemoryManager.hx

@@ -1,94 +1,15 @@
 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 {
 
 	static inline var MAX_MEMORY = 250 << 20; // MB
 	static inline var MAX_BUFFERS = 4096;
+	static inline var SIZE = 65533;
+	static var ALL_FLAGS = Type.allEnums(Buffer.BufferFlag);
 
 	@:allow(h3d)
 	var driver : Driver;
-	var buffers : Array<BigBuffer>;
+	var buffers : Array<ManagedBuffer>;
 	var indexes : Array<Indexes>;
 	var textures : Array<h3d.mat.Texture>;
 	
@@ -97,27 +18,26 @@ class MemoryManager {
 	public var usedMemory(default, null) : Int = 0;
 	public var texMemory(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.allocSize = allocSize;
-
+	}
+	
+	public function init() {
 		indexes = new Array();
 		textures = new Array();
 		buffers = new Array();
-		
 		initIndexes();
 	}
 	
 	function initIndexes() {
 		var indices = new hxd.IndexBuffer();
-		for( i in 0...allocSize ) indices.push(i);
-		triIndexes = allocIndex(indices);
+		for( i in 0...SIZE ) indices.push(i);
+		triIndexes = h3d.Indexes.alloc(indices);
 
 		var indices = new hxd.IndexBuffer();
 		var p = 0;
-		for( i in 0...allocSize >> 2 ) {
+		for( i in 0...SIZE >> 2 ) {
 			var k = i << 2;
 			indices.push(k);
 			indices.push(k + 1);
@@ -126,7 +46,8 @@ class MemoryManager {
 			indices.push(k + 1);
 			indices.push(k + 3);
 		}
-		quadIndexes = allocIndex(indices);
+		indices.push(SIZE);
+		quadIndexes = h3d.Indexes.alloc(indices);
 	}
 
 	/**
@@ -136,17 +57,17 @@ class MemoryManager {
 	public dynamic function garbage() {
 	}
 
+	// ------------------------------------- BUFFERS ------------------------------------------
+	
 	/**
 		Clean empty (unused) buffers
 	**/
-	public function cleanBuffers() {
+	public function cleanManagedBuffers() {
 		for( i in 0...buffers.length ) {
-			var b = buffers[i], prev : BigBuffer = null;
+			var b = buffers[i], prev : ManagedBuffer = null;
 			while( b != null ) {
-				if( b.free.count == b.size ) {
+				if( b.freeList.count == b.size ) {
 					b.dispose();
-					bufferCount--;
-					usedMemory -= b.size * b.stride * 4;
 					if( prev == null )
 						buffers[i] = b.next;
 					else
@@ -157,73 +78,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;
 			}
+			b.vertices = total;
 		}
-		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);
-		}
-		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)
 	function deleteIndexes( i : Indexes ) {
 		indexes.remove(i);
 		driver.disposeIndexes(i.ibuf);
@@ -231,6 +187,16 @@ class MemoryManager {
 		usedMemory -= i.count * 2;
 	}
 	
+	@:allow(h3d.Indexes)
+	function allocIndexes( i : Indexes ) {
+		i.ibuf = driver.allocIndexes(i.count);
+		indexes.push(i);
+		usedMemory += i.count * 2;
+	}
+
+	
+	// ------------------------------------- TEXTURES ------------------------------------------
+	
 	function bpp( t : h3d.mat.Texture ) {
 		return 4;
 	}
@@ -272,39 +238,45 @@ class MemoryManager {
 		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;
 		for( b in buffers ) {
 			var b = b;
 			while( b != null ) {
-				var free = b.free;
+				var free = b.freeList;
 				while( free != null ) {
 					size += free.count * b.stride * 4;
 					free = free.next;
@@ -314,160 +286,74 @@ class MemoryManager {
 		}
 		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 ) {
-					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
-		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;
-	}
 
 }

+ 33 - 19
h3d/impl/Stage3dDriver.hx

@@ -6,25 +6,24 @@ import h3d.impl.Driver;
 @:allow(h3d.impl.Stage3dDriver)
 class VertexWrapper {
 	var vbuf : flash.display3D.VertexBuffer3D;
-	var stride : Int;
 	var written : Bool;
-	var b : MemoryManager.BigBuffer;
+	var b : ManagedBuffer;
 	
-	function new(vbuf, stride) {
+	function new(vbuf, b) {
 		this.vbuf = vbuf;
-		this.stride = stride;
+		this.b = b;
 	}
-	
+		
 	function finalize( driver : Stage3dDriver ) {
 		if( written ) return;
 		written = true;
 		// 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 ) {
 			if( f.count > 0 ) {
 				var mem : UInt = f.count * b.stride * 4;
 				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;
 		}
@@ -151,17 +150,17 @@ class Stage3dDriver extends Driver {
 		t.dispose();
 	}
 	
-	override function allocVertex( count : Int, stride : Int ) : VertexBuffer {
+	override function allocVertex( buf : ManagedBuffer ) : VertexBuffer {
 		var v;
 		try {
-			v = ctx.createVertexBuffer(count, stride);
+			v = ctx.createVertexBuffer(buf.size, buf.stride);
 		} catch( e : flash.errors.Error ) {
 			// too many resources / out of memory
 			if( e.errorID == 3691 )
 				return null;
 			throw e;
 		}
-		return new VertexWrapper(v, stride);
+		return new VertexWrapper(v, buf);
 	}
 
 	override function allocIndexes( count : Int ) : IndexBuffer {
@@ -184,7 +183,7 @@ class Stage3dDriver extends Driver {
 			throw "Unsupported texture flag";
 		try {
 			if( t.flags.has(IsRectangle) ) {
-				if( t.flags.has(Cubic) || t.flags.has(MipMapped) || t.flags.has(Target) )
+				if( t.flags.has(Cubic) || t.flags.has(MipMapped) )
 					throw "Not power of two texture is not supported with these flags";
 				#if !flash11_8
 				throw "Support for rectangle texture requires Flash 11.8+ compilation";
@@ -249,7 +248,7 @@ class Stage3dDriver extends Driver {
 	
 	override function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
 		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 ) {
@@ -345,8 +344,8 @@ class Stage3dDriver extends Driver {
 			return;
 		curBuffer = v;
 		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 )
 			v.finalize(this);
 		var pos = 0, offset = 0;
@@ -385,11 +384,11 @@ class Stage3dDriver extends Driver {
 			var b = buffers;
 			while( offset < curShader.stride ) {
 				var size = bits & 7;
-				if( b.b.next != null )
+				if( b.buffer.next != null )
 					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;
 				offset += size == 0 ? 1 : size;
 				bits >>= 3;
@@ -402,9 +401,24 @@ class Stage3dDriver extends Driver {
 			curBuffer = null;
 		}
 	}
+
+	function debugDraw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
+		try {
+			ctx.drawTriangles(ibuf, startIndex, ntriangles);
+		} catch( e : flash.errors.Error ) {
+			// this error should not happen, but sometime does in debug mode (?)
+			if( e.errorID != 3605 )
+				throw e;
+		}
+	}
 	
 	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
-		if( enableDraw ) ctx.drawTriangles(ibuf, startIndex, ntriangles);
+		if( enableDraw ) {
+			if( ctx.enableErrorChecking )
+				debugDraw(ibuf, startIndex, ntriangles);
+			else
+				ctx.drawTriangles(ibuf, startIndex, ntriangles);
+		}
 	}
 
 	override function setRenderZone( x : Int, y : Int, width : Int, height : Int ) {

+ 11 - 2
h3d/mat/Texture.hx

@@ -112,10 +112,13 @@ class Texture {
 
 		this.width = width;
 		this.height = height;
-		alloc();
+		
+		if( !flags.has(NoAlloc) )
+			alloc();
 	}
 
 	public function clear( color : Int ) {
+		alloc();
 		var p = hxd.Pixels.alloc(width, height, BGRA);
 		var k = 0;
 		var b = color & 0xFF, g = (color >> 8) & 0xFF, r = (color >> 16) & 0xFF, a = color >>> 24;
@@ -130,16 +133,22 @@ class Texture {
 	}
 	
 	public function uploadBitmap( bmp : hxd.BitmapData, mipLevel = 0, side = 0 ) {
+		alloc();
 		mem.driver.uploadTextureBitmap(this, bmp, mipLevel, side);
 	}
 
 	public function uploadPixels( pixels : hxd.Pixels, mipLevel = 0, side = 0 ) {
+		alloc();
 		mem.driver.uploadTexturePixels(this, pixels, mipLevel, side);
 	}
 
 	public function dispose() {
-		if( t != null )
+		if( t != null ) {
 			mem.deleteTexture(this);
+			#if debug
+			this.allocPos.customParams = ["#DISPOSED"];
+			#end
+		}
 	}
 	
 	public static function fromBitmap( bmp : hxd.BitmapData, ?allocPos : h3d.impl.AllocPos ) {

+ 1 - 3
h3d/parts/Emitter.hx

@@ -558,9 +558,7 @@ class Emitter extends h3d.scene.Object implements Randomized {
 		}
 		var stride = 10;
 		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);
 		
 		/*

+ 10 - 10
h3d/prim/FBXModel.hx

@@ -1,6 +1,6 @@
 package h3d.prim;
 using h3d.fbx.Data;
-import h3d.impl.Buffer.BufferOffset;
+import h3d.Buffer.BufferOffset;
 import h3d.col.Point;
 
 class FBXModel extends MeshPrimitive {
@@ -10,7 +10,7 @@ class FBXModel extends MeshPrimitive {
 	public var multiMaterial : Bool;
 	var bounds : h3d.col.Bounds;
 	var curMaterial : Int;
-	var groupIndexes : Array<h3d.impl.Indexes>;
+	var groupIndexes : Array<Indexes>;
 
 	public function new(g) {
 		this.geom = g;
@@ -183,28 +183,28 @@ class FBXModel extends MeshPrimitive {
 			pos++;
 		}
 		
-		addBuffer("position", 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("position", 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 ) {
 			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);
 			addBuffer("weights", skinBuf, 0);
 			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 = h3d.Indexes.alloc(idx);
 		if( mats != null ) {
 			groupIndexes = [];
 			for( i in midx )
-				groupIndexes.push(i == null ? null : engine.mem.allocIndex(i));
+				groupIndexes.push(i == null ? null : h3d.Indexes.alloc(i));
 		}
 		if( sidx != null ) {
 			groupIndexes = [];
 			for( i in sidx )
-				groupIndexes.push(i == null ? null : engine.mem.allocIndex(i));
+				groupIndexes.push(i == null ? null : h3d.Indexes.alloc(i));
 		}
 	}
 	

+ 2 - 2
h3d/prim/MeshPrimitive.hx

@@ -2,7 +2,7 @@ package h3d.prim;
 
 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 ) {
 		return null;
@@ -22,7 +22,7 @@ class MeshPrimitive extends Primitive {
 		var id = hash(name);
 		var old = bufferCache.get(id);
 		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() {

+ 1 - 1
h3d/prim/Plan2D.hx

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

+ 2 - 2
h3d/prim/Polygon.hx

@@ -56,10 +56,10 @@ class Polygon extends Primitive {
 				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 )
-			indexes = engine.mem.allocIndex(idx);
+			indexes = h3d.Indexes.alloc(idx);
 	}
 
 

+ 3 - 11
h3d/prim/Primitive.hx

@@ -2,19 +2,11 @@ package h3d.prim;
 
 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() {
-		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 {

+ 1 - 1
h3d/prim/Quads.hx

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

+ 2 - 2
h3d/prim/RawPrimitive.hx

@@ -3,9 +3,9 @@ package h3d.prim;
 class RawPrimitive extends Primitive {
 
 	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 )
-			indexes = engine.mem.allocIndex(ibuf);
+			indexes = h3d.Indexes.alloc(ibuf);
 	}
 
 }

+ 1 - 1
h3d/scene/Scene.hx

@@ -121,7 +121,7 @@ class Scene extends Object implements h3d.IDrawable {
 				p = p.next;
 			}
 		}
-		ctx.passes = passes[0].pass;
+		if( passes.length > 0 ) ctx.passes = passes[0].pass;
 		ctx.done();
 		for( p in postPasses )
 			p.render(engine);

+ 2 - 2
hxd/Stage.hx

@@ -302,11 +302,11 @@ class Stage {
 	}
 
 	function get_mouseX() {
-		return Math.round(curMouseX - canvasPos.left);
+		return Math.round((curMouseX - canvasPos.left) * js.Browser.window.devicePixelRatio);
 	}
 
 	function get_mouseY() {
-		return Math.round(curMouseY - canvasPos.top);
+		return Math.round((curMouseY - canvasPos.top) * js.Browser.window.devicePixelRatio);
 	}
 	
 	function get_mouseLock() {