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; var vbuf : Driver.VertexBuffer; var freeList : FreeCell; var next : ManagedBuffer; #if debug var allocHead : Buffer; #end public function new( stride, size, ?flags : Array ) { this.flags = new haxe.EnumFlags(); if( flags != null ) for( f in flags ) this.flags.set(f); this.size = size; this.stride = stride; this.freeList = new FreeCell(0, size, null); #if !noEngine this.mem = h3d.Engine.getCurrent().mem; mem.allocManaged(this); #end } 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 readVertexBytes( start : Int, vertices : Int, data : haxe.io.Bytes, dataPos = 0 ) { mem.driver.readVertexBytes(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; }; #if debug @:privateAccess b.allocNext = allocHead; allocHead = b; #end return b; } public function getFreeVertices() { var m = 0; var l = freeList; while( l != null ) { m += l.count; l = l.next; } return m; } 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; }; #if debug @:privateAccess b.allocNext = allocHead; allocHead = b; #end 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 debug @:privateAccess { var cur = allocHead, prev : Buffer = null; while( cur != null ) { if( cur == b ) { if( prev == null ) allocHead = b.allocNext; else prev.allocNext = b.allocNext; break; } prev = cur; cur = cur.allocNext; } } #end if( freeList.count == size && !flags.has(Managed) ) dispose(); } public function dispose() { mem.freeManaged(this); } public inline function isDisposed() { return vbuf == null; } }