123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- package h3d.impl;
- class MemoryManager {
- static inline var MAX_MEMORY = 4096 * (1024. * 1024.); // MB
- static inline var MAX_BUFFERS = 65536;
- static inline var SIZE = 65532;
- static var ALL_FLAGS = Type.allEnums(Buffer.BufferFlag);
- @:allow(h3d)
- var driver : Driver;
- var buffers : Array<Buffer>;
- var indexes : Array<Indexes>;
- var textures : Array<h3d.mat.Texture>;
- var depths : Array<h3d.mat.Texture>;
- var triIndexes16 : Indexes;
- var quadIndexes16 : Indexes;
- var triIndexes32 : Indexes;
- var quadIndexes32 : Indexes;
- public var usedMemory(default, null) : Float = 0;
- public var texMemory(default, null) : Float = 0;
- public function new(driver) {
- this.driver = driver;
- }
- public function init() {
- indexes = new Array();
- textures = new Array();
- buffers = new Array();
- depths = new Array();
- initIndexes();
- }
- function initIndexes() {
- var indices = new hxd.IndexBuffer();
- for( i in 0...SIZE ) indices.push(i);
- triIndexes16 = h3d.Indexes.alloc(indices);
- var indices = new hxd.IndexBuffer();
- var p = 0;
- for( i in 0...Std.int(SIZE/6) ) {
- var k = i << 2;
- indices.push(k);
- indices.push(k + 1);
- indices.push(k + 2);
- indices.push(k + 2);
- indices.push(k + 1);
- indices.push(k + 3);
- }
- indices.push(SIZE);
- quadIndexes16 = h3d.Indexes.alloc(indices);
- }
- /**
- Call user-defined garbage function that will cleanup some unused allocated objects.
- Might be called several times if we need to allocate a lot of memory
- **/
- public dynamic function garbage() {
- }
- public function getTriIndexes( vertices : Int ) {
- if( vertices <= SIZE )
- return triIndexes16;
- if( triIndexes32 == null || triIndexes32.count < vertices ) {
- var sz = 1 << 17;
- while( sz < vertices ) sz <<= 1;
- var bytes = haxe.io.Bytes.alloc(sz << 2);
- for( i in 0...sz )
- bytes.setInt32(i<<2, i);
- if( triIndexes32 != null )
- triIndexes32.dispose();
- triIndexes32 = new h3d.Indexes(sz,true);
- triIndexes32.uploadBytes(bytes,0,sz);
- }
- return triIndexes32;
- }
- public function getQuadIndexes( vertices : Int ) {
- var nquads = ((vertices + 3) >> 2) * 6;
- if( nquads <= SIZE )
- return quadIndexes16;
- if( quadIndexes32 == null || quadIndexes32.count < vertices ) {
- var sz = 1 << 17;
- while( sz < nquads ) sz <<= 1;
- var bytes = haxe.io.Bytes.alloc(sz << 2);
- var p = 0;
- for( i in 0...Std.int(sz/6) ) {
- var k = i << 2;
- bytes.setInt32(p++ << 2, k);
- bytes.setInt32(p++ << 2, k + 1);
- bytes.setInt32(p++ << 2, k + 2);
- bytes.setInt32(p++ << 2, k + 2);
- bytes.setInt32(p++ << 2, k + 1);
- bytes.setInt32(p++ << 2, k + 3);
- }
- if( quadIndexes32 != null )
- quadIndexes32.dispose();
- quadIndexes32 = new h3d.Indexes(sz,true);
- quadIndexes32.uploadBytes(bytes,0,sz);
- }
- return quadIndexes32;
- }
- // ------------------------------------- BUFFERS ------------------------------------------
- function allocBuffer( b : Buffer ) {
- if( b.vbuf != null ) return;
- var mem = b.getMemSize();
- if( mem == 0 ) return;
- while( usedMemory + mem > MAX_MEMORY || buffers.length >= MAX_BUFFERS || (b.vbuf = driver.allocBuffer(b)) == null ) {
- if( driver.isDisposed() ) return;
- var size = usedMemory;
- garbage();
- if( usedMemory == size ) {
- if( buffers.length >= MAX_BUFFERS )
- throw "Too many buffers";
- throw "Memory full (" + Math.fceil(size / 1024) + " KB," + buffers.length + " buffers)";
- }
- }
- usedMemory += mem;
- buffers.push(b);
- }
- function freeBuffer( b : Buffer ) {
- if( b.vbuf == null ) return;
- driver.disposeBuffer(b);
- b.vbuf = null;
- // in case it was allocated with a previous memory manager
- if( buffers.remove(b) )
- usedMemory -= b.getMemSize();
- }
- // ------------------------------------- INDEXES ------------------------------------------
- @:allow(h3d.Indexes)
- function deleteIndexes( i : Indexes ) {
- indexes.remove(i);
- driver.disposeIndexes(i.ibuf);
- i.ibuf = null;
- usedMemory -= i.count * (i.is32 ? 4 : 2);
- }
- @:allow(h3d.Indexes)
- function allocIndexes( i : Indexes ) {
- i.ibuf = driver.allocIndexes(i.count,i.is32);
- indexes.push(i);
- usedMemory += i.count * (i.is32 ? 4 : 2);
- }
- // ------------------------------------- TEXTURES ------------------------------------------
- function memSize( t : h3d.mat.Texture ) {
- if( t.flags.has(AsyncLoading) && t.flags.has(Loading) )
- return 4; // 1x1 pixel
- var size = hxd.Pixels.calcDataSize(t.width,t.height,t.format);
- if( t.mipLevels > 0 ) {
- for( i in 1...t.mipLevels ) {
- var w = t.width >> i; if( w == 0 ) w = 1;
- var h = t.height >> i; if( h == 0 ) h = 1;
- size += hxd.Pixels.calcDataSize(w,h,t.format);
- }
- }
- return size * t.layerCount;
- }
- public function cleanTextures( force = true ) {
- textures.sort(sortByLRU);
- for( t in textures ) {
- if( t.realloc == null || t.isDisposed() ) continue;
- if( (force || t.lastFrame < hxd.Timer.frameCount - 3600) && t.lastFrame != h3d.mat.Texture.PREVENT_AUTO_DISPOSE ) {
- t.dispose();
- return true;
- }
- }
- return false;
- }
- function sortByLRU( t1 : h3d.mat.Texture, t2 : h3d.mat.Texture ) {
- return t1.lastFrame - t2.lastFrame;
- }
- @:allow(h3d.mat.Texture.dispose)
- function deleteTexture( t : h3d.mat.Texture ) {
- if( !textures.remove(t) ) return;
- driver.disposeTexture(t);
- texMemory -= memSize(t);
- }
- @:allow(h3d.mat.Texture.alloc)
- function allocTexture( t : h3d.mat.Texture ) {
- while( true ) {
- var free = cleanTextures(false);
- t.t = driver.allocTexture(t);
- if( t.t != null ) break;
- if( driver.isDisposed() ) return;
- while( cleanTextures(false) ) {} // clean all old textures
- if( !free && !cleanTextures(true) )
- throw "Maximum texture memory reached";
- }
- textures.push(t);
- texMemory += memSize(t);
- }
- @:allow(h3d.mat.Texture.alloc)
- function allocDepth( b : h3d.mat.Texture ) {
- while( true ) {
- var free = cleanTextures(false);
- b.t = driver.allocDepthBuffer(b);
- if( b.t != null ) break;
- if( driver.isDisposed() ) return;
- while( cleanTextures(false) ) {} // clean all old textures
- if( !free && !cleanTextures(true) )
- throw "Maximum texture memory reached";
- }
- depths.push(b);
- texMemory += b.width * b.height * 4;
- }
- @:allow(h3d.mat.Texture.dispose)
- function deleteDepth( b : h3d.mat.Texture ) {
- if( !depths.remove(b) ) return;
- driver.disposeDepthBuffer(b);
- texMemory -= b.width * b.height * 4;
- }
- // ------------------------------------- DISPOSE ------------------------------------------
- public function onContextLost() {
- dispose();
- initIndexes();
- }
- public function dispose() {
- if( triIndexes16 != null ) triIndexes16.dispose();
- if( quadIndexes16 != null ) quadIndexes16.dispose();
- if( triIndexes32 != null ) triIndexes32.dispose();
- if( quadIndexes32 != null ) quadIndexes32.dispose();
- triIndexes16 = null;
- quadIndexes16 = null;
- triIndexes32 = null;
- quadIndexes32 = null;
- for( t in textures.copy() )
- t.dispose();
- for( b in depths.copy() )
- b.dispose();
- for( b in buffers.copy() )
- b.dispose();
- for( i in indexes.copy() )
- i.dispose();
- buffers = [];
- indexes = [];
- textures = [];
- usedMemory = 0;
- texMemory = 0;
- }
- // ------------------------------------- STATS ------------------------------------------
- public function stats() {
- var total = 0.;
- for( b in buffers )
- total += b.getMemSize();
- return {
- bufferCount : buffers.length,
- bufferMemory : total,
- totalMemory : usedMemory + texMemory,
- textureCount : textures.length,
- textureMemory : texMemory,
- };
- }
- /**
- * Return statistics for currently allocated buffers and textures. Requires -D track-alloc compilation flag
- */
- @:access(h3d.Buffer)
- public function allocStats() : Array<{ position : String, count : Int, tex : Bool, size : Int, stacks : Array<{ stack : String, count : Int, size : Int }> }> {
- #if !track_alloc
- return [];
- #else
- var h = new Map();
- var all = [];
- inline function addStack( a : hxd.impl.AllocPos, stacks : Array<{ stack : String, count : Int, size : Int }>, size : Int ) {
- var stackStr = a.stack.join("\n");
- for( s in stacks )
- if( s.stack == stackStr ) {
- s.size += size;
- s.count++;
- stackStr = null;
- }
- if( stackStr != null )
- stacks.push({ stack : stackStr, count : 1, size : size });
- }
- for( t in textures ) {
- var key = "$"+t.allocPos.position;
- var inf = h.get(key);
- if( inf == null ) {
- inf = { position : t.allocPos.position, count : 0, size : 0, tex : true, stacks : [] };
- h.set(key, inf);
- all.push(inf);
- }
- inf.count++;
- var size = memSize(t);
- inf.size += size;
- addStack(t.allocPos, inf.stacks, size);
- }
- for( b in buffers ) {
- var key = b.allocPos == null ? "null" : b.allocPos.position;
- var inf = h.get(key);
- if( inf == null ) {
- inf = { position : key, count : 0, size : 0, tex : false, stacks : [] };
- h.set(key, inf);
- all.push(inf);
- }
- inf.count++;
- var size = b.vertices * b.format.stride * 4;
- inf.size += size;
- addStack(b.allocPos, inf.stacks, size);
- }
- all.sort(function(a, b) return b.size - a.size);
- return all;
- #end
- }
- }
|