MemoryManager.hx 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. package h3d.impl;
  2. class MemoryManager {
  3. static inline var MAX_MEMORY = 4096 * (1024. * 1024.); // MB
  4. static inline var MAX_BUFFERS = 65536;
  5. static inline var SIZE = 65532;
  6. static var ALL_FLAGS = Type.allEnums(Buffer.BufferFlag);
  7. @:allow(h3d)
  8. var driver : Driver;
  9. var buffers : Array<Buffer>;
  10. var indexes : Array<Indexes>;
  11. var textures : Array<h3d.mat.Texture>;
  12. var depths : Array<h3d.mat.Texture>;
  13. var triIndexes16 : Indexes;
  14. var quadIndexes16 : Indexes;
  15. var triIndexes32 : Indexes;
  16. var quadIndexes32 : Indexes;
  17. public var usedMemory(default, null) : Float = 0;
  18. public var texMemory(default, null) : Float = 0;
  19. public function new(driver) {
  20. this.driver = driver;
  21. }
  22. public function init() {
  23. indexes = new Array();
  24. textures = new Array();
  25. buffers = new Array();
  26. depths = new Array();
  27. initIndexes();
  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 || buffers.length >= MAX_BUFFERS || (b.vbuf = driver.allocBuffer(b)) == null ) {
  100. if( driver.isDisposed() ) return;
  101. var size = usedMemory;
  102. garbage();
  103. if( usedMemory == size ) {
  104. if( buffers.length >= MAX_BUFFERS )
  105. throw "Too many buffers";
  106. throw "Memory full (" + Math.fceil(size / 1024) + " KB," + buffers.length + " buffers)";
  107. }
  108. }
  109. usedMemory += mem;
  110. buffers.push(b);
  111. }
  112. function freeBuffer( b : Buffer ) {
  113. if( b.vbuf == null ) return;
  114. driver.disposeBuffer(b);
  115. b.vbuf = null;
  116. // in case it was allocated with a previous memory manager
  117. if( buffers.remove(b) )
  118. usedMemory -= b.getMemSize();
  119. }
  120. // ------------------------------------- INDEXES ------------------------------------------
  121. @:allow(h3d.Indexes)
  122. function deleteIndexes( i : Indexes ) {
  123. indexes.remove(i);
  124. driver.disposeIndexes(i.ibuf);
  125. i.ibuf = null;
  126. usedMemory -= i.count * (i.is32 ? 4 : 2);
  127. }
  128. @:allow(h3d.Indexes)
  129. function allocIndexes( i : Indexes ) {
  130. i.ibuf = driver.allocIndexes(i.count,i.is32);
  131. indexes.push(i);
  132. usedMemory += i.count * (i.is32 ? 4 : 2);
  133. }
  134. // ------------------------------------- TEXTURES ------------------------------------------
  135. function memSize( t : h3d.mat.Texture ) {
  136. if( t.flags.has(AsyncLoading) && t.flags.has(Loading) )
  137. return 4; // 1x1 pixel
  138. var size = hxd.Pixels.calcDataSize(t.width,t.height,t.format);
  139. if( t.mipLevels > 0 ) {
  140. for( i in 1...t.mipLevels ) {
  141. var w = t.width >> i; if( w == 0 ) w = 1;
  142. var h = t.height >> i; if( h == 0 ) h = 1;
  143. size += hxd.Pixels.calcDataSize(w,h,t.format);
  144. }
  145. }
  146. return size * t.layerCount;
  147. }
  148. public function cleanTextures( force = true ) {
  149. textures.sort(sortByLRU);
  150. for( t in textures ) {
  151. if( t.realloc == null || t.isDisposed() ) continue;
  152. if( (force || t.lastFrame < hxd.Timer.frameCount - 3600) && t.lastFrame != h3d.mat.Texture.PREVENT_AUTO_DISPOSE ) {
  153. t.dispose();
  154. return true;
  155. }
  156. }
  157. return false;
  158. }
  159. function sortByLRU( t1 : h3d.mat.Texture, t2 : h3d.mat.Texture ) {
  160. return t1.lastFrame - t2.lastFrame;
  161. }
  162. @:allow(h3d.mat.Texture.dispose)
  163. function deleteTexture( t : h3d.mat.Texture ) {
  164. if( !textures.remove(t) ) return;
  165. driver.disposeTexture(t);
  166. texMemory -= memSize(t);
  167. }
  168. @:allow(h3d.mat.Texture.alloc)
  169. function allocTexture( t : h3d.mat.Texture ) {
  170. while( true ) {
  171. var free = cleanTextures(false);
  172. t.t = driver.allocTexture(t);
  173. if( t.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. textures.push(t);
  180. texMemory += memSize(t);
  181. }
  182. @:allow(h3d.mat.Texture.alloc)
  183. function allocDepth( b : h3d.mat.Texture ) {
  184. while( true ) {
  185. var free = cleanTextures(false);
  186. b.t = driver.allocDepthBuffer(b);
  187. if( b.t != null ) break;
  188. if( driver.isDisposed() ) return;
  189. while( cleanTextures(false) ) {} // clean all old textures
  190. if( !free && !cleanTextures(true) )
  191. throw "Maximum texture memory reached";
  192. }
  193. depths.push(b);
  194. texMemory += b.width * b.height * 4;
  195. }
  196. @:allow(h3d.mat.Texture.dispose)
  197. function deleteDepth( b : h3d.mat.Texture ) {
  198. if( !depths.remove(b) ) return;
  199. driver.disposeDepthBuffer(b);
  200. texMemory -= b.width * b.height * 4;
  201. }
  202. // ------------------------------------- DISPOSE ------------------------------------------
  203. public function onContextLost() {
  204. dispose();
  205. initIndexes();
  206. }
  207. public function dispose() {
  208. if( triIndexes16 != null ) triIndexes16.dispose();
  209. if( quadIndexes16 != null ) quadIndexes16.dispose();
  210. if( triIndexes32 != null ) triIndexes32.dispose();
  211. if( quadIndexes32 != null ) quadIndexes32.dispose();
  212. triIndexes16 = null;
  213. quadIndexes16 = null;
  214. triIndexes32 = null;
  215. quadIndexes32 = null;
  216. for( t in textures.copy() )
  217. t.dispose();
  218. for( b in depths.copy() )
  219. b.dispose();
  220. for( b in buffers.copy() )
  221. b.dispose();
  222. for( i in indexes.copy() )
  223. i.dispose();
  224. buffers = [];
  225. indexes = [];
  226. textures = [];
  227. usedMemory = 0;
  228. texMemory = 0;
  229. }
  230. // ------------------------------------- STATS ------------------------------------------
  231. public function stats() {
  232. var total = 0.;
  233. for( b in buffers )
  234. total += b.getMemSize();
  235. return {
  236. bufferCount : buffers.length,
  237. bufferMemory : total,
  238. totalMemory : usedMemory + texMemory,
  239. textureCount : textures.length,
  240. textureMemory : texMemory,
  241. };
  242. }
  243. /**
  244. * Return statistics for currently allocated buffers and textures. Requires -D track-alloc compilation flag
  245. */
  246. @:access(h3d.Buffer)
  247. public function allocStats() : Array<{ position : String, count : Int, tex : Bool, size : Int, stacks : Array<{ stack : String, count : Int, size : Int }> }> {
  248. #if !track_alloc
  249. return [];
  250. #else
  251. var h = new Map();
  252. var all = [];
  253. inline function addStack( a : hxd.impl.AllocPos, stacks : Array<{ stack : String, count : Int, size : Int }>, size : Int ) {
  254. var stackStr = a.stack.join("\n");
  255. for( s in stacks )
  256. if( s.stack == stackStr ) {
  257. s.size += size;
  258. s.count++;
  259. stackStr = null;
  260. }
  261. if( stackStr != null )
  262. stacks.push({ stack : stackStr, count : 1, size : size });
  263. }
  264. for( t in textures ) {
  265. var key = "$"+t.allocPos.position;
  266. var inf = h.get(key);
  267. if( inf == null ) {
  268. inf = { position : t.allocPos.position, count : 0, size : 0, tex : true, stacks : [] };
  269. h.set(key, inf);
  270. all.push(inf);
  271. }
  272. inf.count++;
  273. var size = memSize(t);
  274. inf.size += size;
  275. addStack(t.allocPos, inf.stacks, size);
  276. }
  277. for( b in buffers ) {
  278. var key = b.allocPos == null ? "null" : b.allocPos.position;
  279. var inf = h.get(key);
  280. if( inf == null ) {
  281. inf = { position : key, count : 0, size : 0, tex : false, stacks : [] };
  282. h.set(key, inf);
  283. all.push(inf);
  284. }
  285. inf.count++;
  286. var size = b.vertices * b.format.stride * 4;
  287. inf.size += size;
  288. addStack(b.allocPos, inf.stacks, size);
  289. }
  290. all.sort(function(a, b) return b.size - a.size);
  291. return all;
  292. #end
  293. }
  294. }