MemoryManager.hx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. package h3d.impl;
  2. class MemoryManager {
  3. static inline var MAX_MEMORY = 4096 * (1024. * 1024.); // MB
  4. static inline var SIZE = 65532;
  5. static var ALL_FLAGS = Type.allEnums(Buffer.BufferFlag);
  6. @:allow(h3d)
  7. var driver : Driver;
  8. var buffers : Array<Buffer>;
  9. var textures : Array<h3d.mat.Texture>;
  10. var depths : Array<h3d.mat.Texture>;
  11. var triIndexes16 : Indexes;
  12. var quadIndexes16 : Indexes;
  13. var triIndexes32 : Indexes;
  14. var quadIndexes32 : Indexes;
  15. public var usedMemory(default, null) : Float = 0;
  16. public var texMemory(default, null) : Float = 0;
  17. public var autoDisposeCooldown : Int = 60;
  18. var lastAutoDispose = 0;
  19. public function new(driver) {
  20. this.driver = driver;
  21. }
  22. public function init() {
  23. textures = new Array();
  24. buffers = new Array();
  25. depths = new Array();
  26. initIndexes();
  27. }
  28. function initIndexes() {
  29. var indices = new hxd.IndexBuffer();
  30. for( i in 0...SIZE ) indices.push(i);
  31. triIndexes16 = h3d.Indexes.alloc(indices);
  32. var indices = new hxd.IndexBuffer();
  33. var p = 0;
  34. for( i in 0...Std.int(SIZE/6) ) {
  35. var k = i << 2;
  36. indices.push(k);
  37. indices.push(k + 1);
  38. indices.push(k + 2);
  39. indices.push(k + 2);
  40. indices.push(k + 1);
  41. indices.push(k + 3);
  42. }
  43. indices.push(SIZE);
  44. quadIndexes16 = h3d.Indexes.alloc(indices);
  45. }
  46. /**
  47. Call user-defined garbage function that will cleanup some unused allocated objects.
  48. Might be called several times if we need to allocate a lot of memory
  49. **/
  50. public dynamic function garbage() {
  51. }
  52. public function getTriIndexes( vertices : Int ) {
  53. if( vertices <= SIZE )
  54. return triIndexes16;
  55. if( triIndexes32 == null || triIndexes32.count < vertices ) {
  56. var sz = 1 << 17;
  57. while( sz < vertices ) sz <<= 1;
  58. var bytes = haxe.io.Bytes.alloc(sz << 2);
  59. for( i in 0...sz )
  60. bytes.setInt32(i<<2, i);
  61. if( triIndexes32 != null )
  62. triIndexes32.dispose();
  63. triIndexes32 = new h3d.Indexes(sz,true);
  64. triIndexes32.uploadBytes(bytes,0,sz);
  65. }
  66. return triIndexes32;
  67. }
  68. public function getQuadIndexes( vertices : Int ) {
  69. var nquads = ((vertices + 3) >> 2) * 6;
  70. if( nquads <= SIZE )
  71. return quadIndexes16;
  72. if( quadIndexes32 == null || quadIndexes32.count < vertices ) {
  73. var sz = 1 << 17;
  74. while( sz < nquads ) sz <<= 1;
  75. var bytes = haxe.io.Bytes.alloc(sz << 2);
  76. var p = 0;
  77. for( i in 0...Std.int(sz/6) ) {
  78. var k = i << 2;
  79. bytes.setInt32(p++ << 2, k);
  80. bytes.setInt32(p++ << 2, k + 1);
  81. bytes.setInt32(p++ << 2, k + 2);
  82. bytes.setInt32(p++ << 2, k + 2);
  83. bytes.setInt32(p++ << 2, k + 1);
  84. bytes.setInt32(p++ << 2, k + 3);
  85. }
  86. if( quadIndexes32 != null )
  87. quadIndexes32.dispose();
  88. quadIndexes32 = new h3d.Indexes(sz,true);
  89. quadIndexes32.uploadBytes(bytes,0,sz);
  90. }
  91. return quadIndexes32;
  92. }
  93. // ------------------------------------- BUFFERS ------------------------------------------
  94. function allocBuffer( b : Buffer ) {
  95. if( b.vbuf != null ) return;
  96. var mem = b.getMemSize();
  97. if( mem == 0 ) return;
  98. while( usedMemory + mem > MAX_MEMORY || (b.vbuf = driver.allocBuffer(b)) == null ) {
  99. if( driver.isDisposed() ) return;
  100. var size = usedMemory;
  101. garbage();
  102. if( usedMemory == size )
  103. throw "Memory full (" + Math.fceil(size / 1024) + " KB," + buffers.length + " buffers)";
  104. }
  105. usedMemory += mem;
  106. buffers.push(b);
  107. }
  108. function freeBuffer( b : Buffer ) {
  109. if( b.vbuf == null ) return;
  110. driver.disposeBuffer(b);
  111. b.vbuf = null;
  112. // in case it was allocated with a previous memory manager
  113. if( buffers.remove(b) )
  114. usedMemory -= b.getMemSize();
  115. }
  116. // ------------------------------------- TEXTURES ------------------------------------------
  117. function memSize( t : h3d.mat.Texture ) {
  118. if( t.flags.has(AsyncLoading) && t.flags.has(Loading) )
  119. return 4; // 1x1 pixel
  120. var size = hxd.Pixels.calcDataSize(t.width,t.height,t.format);
  121. if( t.mipLevels > 0 ) {
  122. for( i in 1...t.mipLevels ) {
  123. var w = t.width >> i; if( w == 0 ) w = 1;
  124. var h = t.height >> i; if( h == 0 ) h = 1;
  125. size += hxd.Pixels.calcDataSize(w,h,t.format);
  126. }
  127. }
  128. return size * t.layerCount;
  129. }
  130. public function cleanTextures( force = true ) {
  131. textures.sort(sortByLRU);
  132. for( t in textures ) {
  133. if( t.realloc == null || t.isDisposed() ) continue;
  134. if( (force || t.lastFrame < hxd.Timer.frameCount - 3600) && t.lastFrame != h3d.mat.Texture.PREVENT_AUTO_DISPOSE ) {
  135. t.dispose();
  136. return true;
  137. }
  138. }
  139. return false;
  140. }
  141. function sortByLRU( t1 : h3d.mat.Texture, t2 : h3d.mat.Texture ) {
  142. return t1.lastFrame - t2.lastFrame;
  143. }
  144. @:allow(h3d.mat.Texture.dispose)
  145. function deleteTexture( t : h3d.mat.Texture ) {
  146. if( !textures.remove(t) ) return;
  147. driver.disposeTexture(t);
  148. texMemory -= memSize(t);
  149. }
  150. @:allow(h3d.mat.Texture.alloc)
  151. function allocTexture( t : h3d.mat.Texture ) {
  152. while( true ) {
  153. var free = true;
  154. if ( hxd.Timer.frameCount > lastAutoDispose + autoDisposeCooldown ) {
  155. free = cleanTextures(false);
  156. lastAutoDispose = hxd.Timer.frameCount;
  157. }
  158. t.t = driver.allocTexture(t);
  159. if( t.t != null ) break;
  160. if( driver.isDisposed() ) return;
  161. while( cleanTextures(false) ) {} // clean all old textures
  162. if( !free && !cleanTextures(true) )
  163. throw "Maximum texture memory reached";
  164. }
  165. textures.push(t);
  166. texMemory += memSize(t);
  167. }
  168. @:allow(h3d.mat.Texture.alloc)
  169. function allocDepth( b : h3d.mat.Texture ) {
  170. while( true ) {
  171. var free = cleanTextures(false);
  172. b.t = driver.allocDepthBuffer(b);
  173. if( b.t != null ) break;
  174. if( driver.isDisposed() ) return;
  175. while( cleanTextures(false) ) {} // clean all old textures
  176. if( !free && !cleanTextures(true) )
  177. throw "Maximum texture memory reached";
  178. }
  179. depths.push(b);
  180. texMemory += b.width * b.height * 4;
  181. }
  182. @:allow(h3d.mat.Texture.dispose)
  183. function deleteDepth( b : h3d.mat.Texture ) {
  184. if( !depths.remove(b) ) return;
  185. driver.disposeDepthBuffer(b);
  186. texMemory -= b.width * b.height * 4;
  187. }
  188. // ------------------------------------- DISPOSE ------------------------------------------
  189. public function onContextLost() {
  190. dispose();
  191. initIndexes();
  192. }
  193. public function dispose() {
  194. if( triIndexes16 != null ) triIndexes16.dispose();
  195. if( quadIndexes16 != null ) quadIndexes16.dispose();
  196. if( triIndexes32 != null ) triIndexes32.dispose();
  197. if( quadIndexes32 != null ) quadIndexes32.dispose();
  198. triIndexes16 = null;
  199. quadIndexes16 = null;
  200. triIndexes32 = null;
  201. quadIndexes32 = null;
  202. for( t in textures.copy() )
  203. t.dispose();
  204. for( b in depths.copy() )
  205. b.dispose();
  206. for( b in buffers.copy() )
  207. b.dispose();
  208. buffers = [];
  209. textures = [];
  210. usedMemory = 0;
  211. texMemory = 0;
  212. }
  213. // ------------------------------------- STATS ------------------------------------------
  214. public function stats() {
  215. var total = 0.;
  216. for( b in buffers )
  217. total += b.getMemSize();
  218. return {
  219. bufferCount : buffers.length,
  220. bufferMemory : total,
  221. totalMemory : usedMemory + texMemory,
  222. textureCount : textures.length,
  223. textureMemory : texMemory,
  224. };
  225. }
  226. /**
  227. * Return statistics for currently allocated buffers and textures. Requires -D track-alloc compilation flag
  228. */
  229. @:access(h3d.Buffer)
  230. public function allocStats() : Array<{ position : String, count : Int, tex : Bool, size : Int, stacks : Array<{ stack : String, count : Int, size : Int }> }> {
  231. #if !track_alloc
  232. return [];
  233. #else
  234. var h = new Map();
  235. var all = [];
  236. inline function addStack( a : hxd.impl.AllocPos, stacks : Array<{ stack : String, count : Int, size : Int }>, size : Int ) {
  237. var stackStr = a.stack.join("\n");
  238. for( s in stacks )
  239. if( s.stack == stackStr ) {
  240. s.size += size;
  241. s.count++;
  242. stackStr = null;
  243. }
  244. if( stackStr != null )
  245. stacks.push({ stack : stackStr, count : 1, size : size });
  246. }
  247. for( t in textures ) {
  248. var key = "$"+t.allocPos.position;
  249. var inf = h.get(key);
  250. if( inf == null ) {
  251. inf = { position : t.allocPos.position, count : 0, size : 0, tex : true, stacks : [] };
  252. h.set(key, inf);
  253. all.push(inf);
  254. }
  255. inf.count++;
  256. var size = memSize(t);
  257. inf.size += size;
  258. addStack(t.allocPos, inf.stacks, size);
  259. }
  260. for( b in buffers ) {
  261. var key = b.allocPos == null ? "null" : b.allocPos.position;
  262. var inf = h.get(key);
  263. if( inf == null ) {
  264. inf = { position : key, count : 0, size : 0, tex : false, stacks : [] };
  265. h.set(key, inf);
  266. all.push(inf);
  267. }
  268. inf.count++;
  269. var size = b.vertices * b.format.stride * 4;
  270. inf.size += size;
  271. addStack(b.allocPos, inf.stacks, size);
  272. }
  273. all.sort(function(a, b) return b.size - a.size);
  274. return all;
  275. #end
  276. }
  277. }