MemoryManager.hx 8.1 KB

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