MemoryManager.hx 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. package h3d.impl;
  2. class MemoryManager {
  3. static inline var MAX_MEMORY = #if flash 250 #else 4096 #end * (1024. * 1024.); // MB
  4. static inline var MAX_BUFFERS = #if flash 4096 #else 1 << 16 #end;
  5. static inline var SIZE = 65533;
  6. static var ALL_FLAGS = Type.allEnums(Buffer.BufferFlag);
  7. @:allow(h3d)
  8. var driver : Driver;
  9. var buffers : Array<ManagedBuffer>;
  10. var indexes : Array<Indexes>;
  11. var textures : Array<h3d.mat.Texture>;
  12. public var triIndexes(default,null) : Indexes;
  13. public var quadIndexes(default,null) : Indexes;
  14. public var usedMemory(default, null) : Float = 0;
  15. public var texMemory(default, null) : Float = 0;
  16. public var bufferCount(default,null) : Int = 0;
  17. public function new(driver) {
  18. this.driver = driver;
  19. }
  20. public function init() {
  21. indexes = new Array();
  22. textures = new Array();
  23. buffers = new Array();
  24. initIndexes();
  25. }
  26. function initIndexes() {
  27. var indices = new hxd.IndexBuffer();
  28. for( i in 0...SIZE ) indices.push(i);
  29. triIndexes = h3d.Indexes.alloc(indices);
  30. var indices = new hxd.IndexBuffer();
  31. var p = 0;
  32. for( i in 0...SIZE >> 2 ) {
  33. var k = i << 2;
  34. indices.push(k);
  35. indices.push(k + 1);
  36. indices.push(k + 2);
  37. indices.push(k + 2);
  38. indices.push(k + 1);
  39. indices.push(k + 3);
  40. }
  41. indices.push(SIZE);
  42. quadIndexes = h3d.Indexes.alloc(indices);
  43. }
  44. /**
  45. Call user-defined garbage function that will cleanup some unused allocated objects.
  46. Might be called several times if we need to allocate a lot of memory
  47. **/
  48. public dynamic function garbage() {
  49. }
  50. // ------------------------------------- BUFFERS ------------------------------------------
  51. /**
  52. Clean empty (unused) buffers
  53. **/
  54. public function cleanManagedBuffers() {
  55. for( i in 1...buffers.length ) {
  56. var b = buffers[i], prev : ManagedBuffer = null;
  57. while( b != null ) {
  58. if( b.freeList.count == b.size ) {
  59. b.dispose();
  60. if( prev == null )
  61. buffers[i] = b.next;
  62. else
  63. prev.next = b.next;
  64. } else
  65. prev = b;
  66. b = b.next;
  67. }
  68. }
  69. }
  70. @:allow(h3d.impl.ManagedBuffer)
  71. function allocManaged( m : ManagedBuffer ) {
  72. if( m.vbuf != null ) return;
  73. var mem = m.size * m.stride * 4;
  74. if( mem == 0 ) return;
  75. while( usedMemory + mem > MAX_MEMORY || bufferCount >= MAX_BUFFERS || (m.vbuf = driver.allocVertexes(m)) == null ) {
  76. var size = usedMemory - freeMemorySize();
  77. garbage();
  78. cleanManagedBuffers();
  79. if( usedMemory - freeMemorySize() == size ) {
  80. if( bufferCount >= MAX_BUFFERS )
  81. throw "Too many buffers";
  82. throw "Memory full (" + Math.fceil(size / 1024) + " KB," + bufferCount + " buffers)";
  83. }
  84. }
  85. usedMemory += mem;
  86. bufferCount++;
  87. }
  88. @:allow(h3d.impl.ManagedBuffer)
  89. function freeManaged( m : ManagedBuffer ) {
  90. if( m.vbuf == null ) return;
  91. driver.disposeVertexes(m.vbuf);
  92. m.vbuf = null;
  93. usedMemory -= m.size * m.stride * 4;
  94. bufferCount--;
  95. #if debug
  96. if( !m.flags.has(Managed) ) {
  97. var c = buffers[0], prev : ManagedBuffer = null;
  98. while( c != null ) {
  99. if( c == m ) {
  100. if( prev == null ) buffers[0] = m.next else prev.next = m.next;
  101. break;
  102. }
  103. prev = c;
  104. c = c.next;
  105. }
  106. }
  107. #end
  108. }
  109. @:allow(h3d.Buffer)
  110. @:access(h3d.Buffer)
  111. function allocBuffer( b : Buffer, stride : Int ) {
  112. // split big buffers
  113. var max = b.flags.has(Quads) ? 65532 : b.flags.has(Triangles) ? 65533 : 65534;
  114. if( b.vertices > max ) {
  115. if( max == 65534 )
  116. throw "Cannot split buffer with "+b.vertices+" vertices if it's not Quads/Triangles";
  117. var rem = b.vertices - max;
  118. b.vertices = max;
  119. // make sure to alloc in order
  120. allocBuffer(b, stride);
  121. var n = b;
  122. while( n.next != null ) n = n.next;
  123. var flags = [];
  124. for( f in ALL_FLAGS )
  125. if( b.flags.has(f) )
  126. flags.push(f);
  127. n.next = new Buffer(rem, stride, flags);
  128. return;
  129. }
  130. if( !b.flags.has(Managed) ) {
  131. var m = new ManagedBuffer(stride, b.vertices);
  132. #if debug
  133. m.next = buffers[0];
  134. buffers[0] = m;
  135. #end
  136. if( !m.allocBuffer(b) ) throw "assert";
  137. return;
  138. }
  139. // look into one of the managed buffers
  140. var m = buffers[stride], prev = null;
  141. while( m != null ) {
  142. if( m.allocBuffer(b) )
  143. return;
  144. prev = m;
  145. m = m.next;
  146. }
  147. // if quad/triangles, we are allowed to split it
  148. var align = b.flags.has(Triangles) ? 3 : b.flags.has(Quads) ? 4 : 0;
  149. if( m == null && align > 0 ) {
  150. var total = b.vertices;
  151. var size = total;
  152. while( size > 2048 ) {
  153. m = buffers[stride];
  154. size >>= 1;
  155. size -= size % align;
  156. b.vertices = size;
  157. while( m != null ) {
  158. if( m.allocBuffer(b) ) {
  159. var flags = [];
  160. for( f in ALL_FLAGS )
  161. if( b.flags.has(f) )
  162. flags.push(f);
  163. b.next = new Buffer(total - size, stride, flags);
  164. return;
  165. }
  166. m = m.next;
  167. }
  168. }
  169. b.vertices = total;
  170. }
  171. // alloc a new managed buffer
  172. m = new ManagedBuffer(stride, SIZE, [Managed]);
  173. if( prev == null )
  174. buffers[stride] = m;
  175. else
  176. prev.next = m;
  177. if( !m.allocBuffer(b) ) throw "assert";
  178. }
  179. // ------------------------------------- INDEXES ------------------------------------------
  180. @:allow(h3d.Indexes)
  181. function deleteIndexes( i : Indexes ) {
  182. indexes.remove(i);
  183. driver.disposeIndexes(i.ibuf);
  184. i.ibuf = null;
  185. usedMemory -= i.count * 2;
  186. }
  187. @:allow(h3d.Indexes)
  188. function allocIndexes( i : Indexes ) {
  189. i.ibuf = driver.allocIndexes(i.count);
  190. indexes.push(i);
  191. usedMemory += i.count * 2;
  192. }
  193. // ------------------------------------- TEXTURES ------------------------------------------
  194. function bpp( t : h3d.mat.Texture ) {
  195. return 4;
  196. }
  197. public function cleanTextures( force = true ) {
  198. textures.sort(sortByLRU);
  199. for( t in textures ) {
  200. if( t.realloc == null ) continue;
  201. if( force || t.lastFrame < h3d.Engine.getCurrent().frameCount - 3600 ) {
  202. t.dispose();
  203. return true;
  204. }
  205. }
  206. return false;
  207. }
  208. function sortByLRU( t1 : h3d.mat.Texture, t2 : h3d.mat.Texture ) {
  209. return t1.lastFrame - t2.lastFrame;
  210. }
  211. @:allow(h3d.mat.Texture.dispose)
  212. function deleteTexture( t : h3d.mat.Texture ) {
  213. textures.remove(t);
  214. driver.disposeTexture(t);
  215. texMemory -= t.width * t.height * bpp(t);
  216. }
  217. @:allow(h3d.mat.Texture.alloc)
  218. function allocTexture( t : h3d.mat.Texture ) {
  219. var free = cleanTextures(false);
  220. t.t = driver.allocTexture(t);
  221. if( t.t == null ) {
  222. if( !cleanTextures(true) ) throw "Maximum texture memory reached";
  223. allocTexture(t);
  224. return;
  225. }
  226. textures.push(t);
  227. texMemory += t.width * t.height * bpp(t);
  228. }
  229. // ------------------------------------- DISPOSE ------------------------------------------
  230. public function onContextLost() {
  231. dispose();
  232. initIndexes();
  233. }
  234. public function dispose() {
  235. triIndexes.dispose();
  236. quadIndexes.dispose();
  237. triIndexes = null;
  238. quadIndexes = null;
  239. for( t in textures.copy() )
  240. t.dispose();
  241. for( b in buffers.copy() ) {
  242. var b = b;
  243. while( b != null ) {
  244. b.dispose();
  245. b = b.next;
  246. }
  247. }
  248. for( i in indexes.copy() )
  249. i.dispose();
  250. buffers = [];
  251. indexes = [];
  252. textures = [];
  253. bufferCount = 0;
  254. usedMemory = 0;
  255. texMemory = 0;
  256. }
  257. // ------------------------------------- STATS ------------------------------------------
  258. function freeMemorySize() {
  259. var size = 0;
  260. for( b in buffers ) {
  261. var b = b;
  262. while( b != null ) {
  263. var free = b.freeList;
  264. while( free != null ) {
  265. size += free.count * b.stride * 4;
  266. free = free.next;
  267. }
  268. b = b.next;
  269. }
  270. }
  271. return size;
  272. }
  273. public function stats() {
  274. var total = 0, free = 0, count = 0;
  275. for( b in buffers ) {
  276. var b = b;
  277. while( b != null ) {
  278. total += b.stride * b.size * 4;
  279. var f = b.freeList;
  280. while( f != null ) {
  281. free += f.count * b.stride * 4;
  282. f = f.next;
  283. }
  284. count++;
  285. b = b.next;
  286. }
  287. }
  288. return {
  289. bufferCount : bufferCount,
  290. freeManagedMemory : free,
  291. managedMemory : total,
  292. totalMemory : usedMemory + texMemory,
  293. textureCount : textures.length,
  294. textureMemory : texMemory,
  295. };
  296. }
  297. @:access(h3d.Buffer)
  298. public function allocStats() : Array<{ file : String, line : Int, count : Int, tex : Bool, size : Int }> {
  299. #if !debug
  300. return [];
  301. #else
  302. var h = new Map();
  303. var all = [];
  304. for( t in textures ) {
  305. var key = "$"+t.allocPos.fileName + ":" + t.allocPos.lineNumber;
  306. var inf = h.get(key);
  307. if( inf == null ) {
  308. inf = { file : t.allocPos.fileName, line : t.allocPos.lineNumber, count : 0, size : 0, tex : true };
  309. h.set(key, inf);
  310. all.push(inf);
  311. }
  312. inf.count++;
  313. inf.size += t.width * t.height * bpp(t);
  314. }
  315. for( buf in buffers ) {
  316. var buf = buf;
  317. while( buf != null ) {
  318. var b = buf.allocHead;
  319. while( b != null ) {
  320. var key = b.allocPos.fileName + ":" + b.allocPos.lineNumber;
  321. var inf = h.get(key);
  322. if( inf == null ) {
  323. inf = { file : b.allocPos.fileName, line : b.allocPos.lineNumber, count : 0, size : 0, tex : false };
  324. h.set(key, inf);
  325. all.push(inf);
  326. }
  327. inf.count++;
  328. inf.size += b.vertices * b.buffer.stride * 4;
  329. b = b.allocNext;
  330. }
  331. buf = buf.next;
  332. }
  333. }
  334. all.sort(function(a, b) return a.size == b.size ? a.line - b.line : b.size - a.size);
  335. return all;
  336. #end
  337. }
  338. }