CmMeshHeap.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. #include "CmMeshHeap.h"
  2. #include "CmCoreThread.h"
  3. #include "CmTransientMesh.h"
  4. #include "CmHardwareBufferManager.h"
  5. #include "CmVertexDataDesc.h"
  6. #include "CmVertexData.h"
  7. #include "CmIndexData.h"
  8. #include "CmMeshData.h"
  9. #include "CmMath.h"
  10. #include "CmEventQuery.h"
  11. namespace CamelotFramework
  12. {
  13. const float MeshHeap::GrowPercent = 1.5f;
  14. MeshHeap::MeshHeap(UINT32 numVertices, UINT32 numIndices,
  15. const VertexDataDescPtr& vertexDesc, IndexBuffer::IndexType indexType)
  16. :mNumVertices(numVertices), mNumIndices(numIndices), mNextFreeId(0),
  17. mIndexType(indexType), mVertexDesc(vertexDesc), mCPUIndexData(nullptr),
  18. mNextQueryId(0)
  19. {
  20. for(UINT32 i = 0; i <= mVertexDesc->getMaxStreamIdx(); i++)
  21. {
  22. mCPUVertexData.push_back(nullptr);
  23. }
  24. }
  25. MeshHeap::~MeshHeap()
  26. {
  27. }
  28. MeshHeapPtr MeshHeap::create(UINT32 numVertices, UINT32 numIndices,
  29. const VertexDataDescPtr& vertexDesc, IndexBuffer::IndexType indexType)
  30. {
  31. MeshHeap* meshHeap = new (cm_alloc<MeshHeap>()) MeshHeap(numVertices, numIndices, vertexDesc, indexType);
  32. MeshHeapPtr meshHeapPtr = cm_core_ptr<MeshHeap, GenAlloc>(meshHeap);
  33. meshHeapPtr->setThisPtr(meshHeapPtr);
  34. meshHeapPtr->initialize();
  35. return meshHeapPtr;
  36. }
  37. void MeshHeap::initialize_internal()
  38. {
  39. THROW_IF_NOT_CORE_THREAD;
  40. growVertexBuffer(mNumVertices);
  41. growIndexBuffer(mNumIndices);
  42. }
  43. void MeshHeap::destroy_internal()
  44. {
  45. THROW_IF_NOT_CORE_THREAD;
  46. CoreObject::destroy_internal();
  47. }
  48. TransientMeshPtr MeshHeap::alloc(const MeshDataPtr& meshData, DrawOperationType drawOp)
  49. {
  50. UINT32 meshIdx = mNextFreeId++;
  51. MeshHeapPtr thisPtr = std::static_pointer_cast<MeshHeap>(getThisPtr());
  52. TransientMesh* transientMesh = new (cm_alloc<TransientMesh>()) TransientMesh(thisPtr, meshIdx, meshData->getNumVertices(), meshData->getNumIndices(), drawOp);
  53. TransientMeshPtr transientMeshPtr = cm_core_ptr<TransientMesh, GenAlloc>(transientMesh);
  54. transientMeshPtr->setThisPtr(transientMeshPtr);
  55. transientMeshPtr->initialize();
  56. mMeshes[meshIdx] = transientMeshPtr;
  57. queueGpuCommand(getThisPtr(), boost::bind(&MeshHeap::allocInternal, this, meshIdx, meshData));
  58. return transientMeshPtr;
  59. }
  60. void MeshHeap::dealloc(const TransientMeshPtr& mesh)
  61. {
  62. auto iterFind = mMeshes.find(mesh->mId);
  63. if(iterFind == mMeshes.end())
  64. return;
  65. mMeshes.erase(iterFind);
  66. queueGpuCommand(getThisPtr(), boost::bind(&MeshHeap::deallocInternal, this, mesh->mId));
  67. }
  68. void MeshHeap::allocInternal(UINT32 meshId, const MeshDataPtr& meshData)
  69. {
  70. // Find free vertex chunk and grow if needed
  71. UINT32 smallestVertFit = 0;
  72. UINT32 smallestVertFitIdx = 0;
  73. while(smallestVertFit == 0)
  74. {
  75. UINT32 curIdx = 0;
  76. for(auto& chunkIdx : mFreeVertChunks)
  77. {
  78. ChunkData& chunk = mVertChunks[chunkIdx];
  79. if(chunk.size >= meshData->getNumVertices() && (chunk.size < smallestVertFit || smallestVertFit == 0))
  80. {
  81. smallestVertFit = chunk.size;
  82. smallestVertFitIdx = curIdx;
  83. }
  84. curIdx++;
  85. }
  86. if(smallestVertFit > 0)
  87. break;
  88. UINT32 newNumVertices = mNumVertices;
  89. while(newNumVertices < (mNumVertices + meshData->getNumVertices()))
  90. {
  91. newNumVertices = Math::RoundToInt(newNumVertices * GrowPercent);
  92. }
  93. growVertexBuffer(newNumVertices);
  94. }
  95. // Find free index chunk and grow if needed
  96. UINT32 smallestIdxFit = 0;
  97. UINT32 smallestIdxFitIdx = 0;
  98. while(smallestIdxFit == 0)
  99. {
  100. UINT32 curIdx = 0;
  101. for(auto& chunkIdx : mFreeIdxChunks)
  102. {
  103. ChunkData& chunk = mIdxChunks[chunkIdx];
  104. if(chunk.size >= meshData->getNumIndices() && (chunk.size < smallestIdxFit || smallestIdxFit == 0))
  105. {
  106. smallestIdxFit = chunk.size;
  107. smallestIdxFitIdx = curIdx;
  108. }
  109. curIdx++;
  110. }
  111. if(smallestIdxFit > 0)
  112. break;
  113. UINT32 newNumIndices = mNumIndices;
  114. while(newNumIndices < (mNumIndices + meshData->getNumIndices()))
  115. {
  116. newNumIndices = Math::RoundToInt(newNumIndices * GrowPercent);
  117. }
  118. growIndexBuffer(newNumIndices);
  119. }
  120. UINT32 freeVertChunkIdx = 0;
  121. UINT32 freeIdxChunkIdx = 0;
  122. auto freeVertIter = mFreeVertChunks.begin();
  123. freeVertChunkIdx = (*freeVertIter);
  124. for(UINT32 i = 0; i < smallestVertFitIdx; i++)
  125. {
  126. freeVertIter++;
  127. freeVertChunkIdx = (*freeVertIter);
  128. }
  129. mFreeVertChunks.erase(freeVertIter);
  130. auto freeIdxIter = mFreeIdxChunks.begin();
  131. freeIdxChunkIdx = (*freeIdxIter);
  132. for(UINT32 i = 0; i < smallestIdxFitIdx; i++)
  133. {
  134. freeIdxIter++;
  135. freeIdxChunkIdx = (*freeIdxIter);
  136. }
  137. mFreeIdxChunks.erase(freeIdxIter);
  138. ChunkData& vertChunk = mVertChunks[freeVertChunkIdx];
  139. ChunkData& idxChunk = mIdxChunks[freeIdxChunkIdx];
  140. UINT32 remainingNumVerts = vertChunk.size - meshData->getNumVertices();
  141. UINT32 remainingNumIdx = idxChunk.size - meshData->getNumIndices();
  142. if(remainingNumVerts > 0)
  143. {
  144. if(!mEmptyVertChunks.empty())
  145. {
  146. UINT32 emptyChunkIdx = mEmptyVertChunks.top();
  147. ChunkData& emptyChunk = mVertChunks[emptyChunkIdx];
  148. mEmptyVertChunks.pop();
  149. emptyChunk.start = vertChunk.start + meshData->getNumVertices();
  150. emptyChunk.size = remainingNumVerts;
  151. }
  152. else
  153. {
  154. ChunkData newChunk;
  155. newChunk.size = remainingNumVerts;
  156. newChunk.start = vertChunk.start + meshData->getNumVertices();
  157. mVertChunks.push_back(newChunk);
  158. mFreeVertChunks.push_back((UINT32)(mVertChunks.size() - 1));
  159. }
  160. }
  161. if(remainingNumIdx > 0)
  162. {
  163. if(!mEmptyIdxChunks.empty())
  164. {
  165. UINT32 emptyChunkIdx = mEmptyIdxChunks.top();
  166. ChunkData& emptyChunk = mIdxChunks[emptyChunkIdx];
  167. mEmptyIdxChunks.pop();
  168. emptyChunk.start = idxChunk.start + meshData->getNumIndices();
  169. emptyChunk.size = remainingNumIdx;
  170. }
  171. else
  172. {
  173. ChunkData newChunk;
  174. newChunk.size = remainingNumIdx;
  175. newChunk.start = idxChunk.start + meshData->getNumIndices();
  176. mIdxChunks.push_back(newChunk);
  177. mFreeIdxChunks.push_back((UINT32)(mIdxChunks.size() - 1));
  178. }
  179. }
  180. vertChunk.size = meshData->getNumVertices();
  181. idxChunk.size = meshData->getNumIndices();
  182. AllocatedData newAllocData;
  183. newAllocData.vertChunkIdx = freeVertChunkIdx;
  184. newAllocData.idxChunkIdx = freeIdxChunkIdx;
  185. newAllocData.useFlags = UseFlags::GPUFree;
  186. newAllocData.eventQueryIdx = createEventQuery();
  187. mMeshAllocData[meshId] = newAllocData;
  188. // Actually copy data
  189. for(UINT32 i = 0; i <= mVertexDesc->getMaxStreamIdx(); i++)
  190. {
  191. if(!mVertexDesc->hasStream(i))
  192. continue;
  193. UINT32 vertSize = mVertexData->vertexDeclaration->getVertexSize(i);
  194. VertexBufferPtr vertexBuffer = mVertexData->getBuffer(i);
  195. UINT8* vertDest = mCPUVertexData[i] + vertChunk.start * vertSize;
  196. memcpy(vertDest, meshData->getStreamData(i), vertChunk.start * vertSize);
  197. if(vertexBuffer->vertexColorReqRGBFlip())
  198. {
  199. UINT32 vertexStride = mVertexDesc->getVertexStride(i);
  200. for(INT32 semanticIdx = 0; semanticIdx < VertexBuffer::MAX_SEMANTIC_IDX; semanticIdx++)
  201. {
  202. if(!mVertexDesc->hasElement(VES_COLOR, semanticIdx, i))
  203. continue;
  204. UINT8* colorData = vertDest + mVertexDesc->getElementOffsetFromStream(VES_COLOR, semanticIdx, i);
  205. for(UINT32 j = 0; j < mVertexData->vertexCount; j++)
  206. {
  207. UINT32* curColor = (UINT32*)colorData;
  208. (*curColor) = ((*curColor) & 0xFF00FF00) | ((*curColor >> 16) & 0x000000FF) | ((*curColor << 16) & 0x00FF0000);
  209. colorData += vertexStride;
  210. }
  211. }
  212. }
  213. vertexBuffer->writeData(vertChunk.start * vertSize, vertChunk.size * vertSize, vertDest, false);
  214. }
  215. IndexBufferPtr indexBuffer = mIndexData->indexBuffer;
  216. UINT32 idxSize = indexBuffer->getIndexSize();
  217. UINT8* idxDest = mCPUIndexData + idxChunk.start * idxSize;
  218. memcpy(idxDest, meshData->getIndexData(), idxChunk.start * idxSize);
  219. indexBuffer->writeData(idxChunk.start * idxSize, idxChunk.size * idxSize, idxDest, false);
  220. }
  221. void MeshHeap::deallocInternal(UINT32 meshId)
  222. {
  223. auto findIter = mMeshAllocData.find(meshId);
  224. assert(findIter != mMeshAllocData.end());
  225. AllocatedData& allocData = findIter->second;
  226. if(allocData.useFlags == UseFlags::GPUFree)
  227. {
  228. allocData.useFlags = UseFlags::Free;
  229. freeEventQuery(allocData.eventQueryIdx);
  230. mFreeVertChunks.push_back(allocData.vertChunkIdx);
  231. mFreeIdxChunks.push_back(allocData.idxChunkIdx);
  232. mergeWithNearbyChunks(allocData.vertChunkIdx, allocData.idxChunkIdx);
  233. mMeshAllocData.erase(findIter);
  234. }
  235. else if(allocData.useFlags == UseFlags::Used)
  236. allocData.useFlags = UseFlags::CPUFree;
  237. }
  238. void MeshHeap::growVertexBuffer(UINT32 numVertices)
  239. {
  240. mNumVertices = numVertices;
  241. mVertexData = std::shared_ptr<VertexData>(cm_new<VertexData, PoolAlloc>());
  242. mVertexData->vertexCount = mNumVertices;
  243. mVertexData->vertexDeclaration = mVertexDesc->createDeclaration();
  244. // Create buffers and copy data
  245. for(UINT32 i = 0; i <= mVertexDesc->getMaxStreamIdx(); i++)
  246. {
  247. if(!mVertexDesc->hasStream(i))
  248. continue;
  249. UINT32 vertSize = mVertexData->vertexDeclaration->getVertexSize(i);
  250. VertexBufferPtr vertexBuffer = HardwareBufferManager::instance().createVertexBuffer(
  251. vertSize, mVertexData->vertexCount, GBU_DYNAMIC);
  252. mVertexData->setBuffer(i, vertexBuffer);
  253. // Copy all data to the new buffer
  254. UINT8* oldBuffer = mCPUVertexData[i];
  255. UINT8* buffer = (UINT8*)cm_alloc(vertSize * numVertices);
  256. UINT32 destOffset = 0;
  257. if(oldBuffer != nullptr)
  258. {
  259. for(auto& allocData : mMeshAllocData)
  260. {
  261. ChunkData& oldChunk = mVertChunks[allocData.second.vertChunkIdx];
  262. UINT8* oldData = oldBuffer + oldChunk.start * vertSize;
  263. memcpy(buffer + destOffset * vertSize, oldData, oldChunk.size * vertSize);
  264. destOffset += oldChunk.size;
  265. }
  266. cm_free(oldBuffer);
  267. }
  268. vertexBuffer->writeData(0, destOffset * vertSize, buffer, false);
  269. mCPUVertexData[i] = buffer;
  270. }
  271. // Reorder chunks
  272. UINT32 destOffset = 0;
  273. Vector<ChunkData>::type newVertChunks;
  274. List<UINT32>::type freeVertChunks;
  275. for(auto& allocData : mMeshAllocData)
  276. {
  277. ChunkData& oldChunk = mVertChunks[allocData.second.vertChunkIdx];
  278. ChunkData newChunk;
  279. newChunk.start = destOffset;
  280. newChunk.size = oldChunk.size;
  281. allocData.second.vertChunkIdx = (UINT32)newVertChunks.size();
  282. newVertChunks.push_back(newChunk);
  283. destOffset += oldChunk.size;
  284. }
  285. // Add free chunk
  286. if(destOffset != mNumVertices)
  287. {
  288. ChunkData newChunk;
  289. newChunk.start = destOffset;
  290. newChunk.size = mNumVertices - destOffset;
  291. newVertChunks.push_back(newChunk);
  292. freeVertChunks.push_back((UINT32)(newVertChunks.size() - 1));
  293. }
  294. mVertChunks = newVertChunks;
  295. mFreeVertChunks = freeVertChunks;
  296. while(!mEmptyVertChunks.empty())
  297. mEmptyVertChunks.pop();
  298. }
  299. void MeshHeap::growIndexBuffer(UINT32 numIndices)
  300. {
  301. mNumIndices = numIndices;
  302. mIndexData = std::shared_ptr<IndexData>(cm_new<IndexData, PoolAlloc>());
  303. mIndexData->indexCount = mNumIndices;
  304. mIndexData->indexBuffer = HardwareBufferManager::instance().createIndexBuffer(
  305. mIndexType, mIndexData->indexCount, GBU_DYNAMIC);
  306. // Copy all data to the new buffer
  307. UINT32 idxSize = mIndexData->indexBuffer->getIndexSize();
  308. UINT8* oldBuffer = mCPUIndexData;
  309. UINT8* buffer = (UINT8*)cm_alloc(idxSize * numIndices);
  310. UINT32 destOffset = 0;
  311. if(oldBuffer != nullptr)
  312. {
  313. for(auto& allocData : mMeshAllocData)
  314. {
  315. ChunkData& oldChunk = mIdxChunks[allocData.second.idxChunkIdx];
  316. UINT8* oldData = oldBuffer + oldChunk.start * idxSize;
  317. memcpy(buffer + destOffset * idxSize, oldData, oldChunk.size * idxSize);
  318. destOffset += oldChunk.size;
  319. }
  320. cm_free(oldBuffer);
  321. }
  322. mIndexData->indexBuffer->writeData(0, destOffset * idxSize, buffer, false);
  323. mCPUIndexData = buffer;
  324. // Reorder chunks
  325. destOffset = 0;
  326. Vector<ChunkData>::type newIdxChunks;
  327. List<UINT32>::type freeIdxChunks;
  328. for(auto& allocData : mMeshAllocData)
  329. {
  330. ChunkData& oldChunk = mIdxChunks[allocData.second.idxChunkIdx];
  331. ChunkData newChunk;
  332. newChunk.start = destOffset;
  333. newChunk.size = oldChunk.size;
  334. allocData.second.idxChunkIdx = (UINT32)newIdxChunks.size();
  335. newIdxChunks.push_back(newChunk);
  336. destOffset += oldChunk.size;
  337. }
  338. // Add free chunk
  339. if(destOffset != mNumIndices)
  340. {
  341. ChunkData newChunk;
  342. newChunk.start = destOffset;
  343. newChunk.size = mNumIndices - destOffset;
  344. newIdxChunks.push_back(newChunk);
  345. freeIdxChunks.push_back((UINT32)(newIdxChunks.size() - 1));
  346. }
  347. mIdxChunks = newIdxChunks;
  348. mFreeIdxChunks = freeIdxChunks;
  349. while(!mEmptyIdxChunks.empty())
  350. mEmptyIdxChunks.pop();
  351. }
  352. UINT32 MeshHeap::createEventQuery()
  353. {
  354. UINT32 idx = 0;
  355. if(mFreeEventQueries.size() > 0)
  356. {
  357. idx = mFreeEventQueries.top();
  358. mFreeEventQueries.pop();
  359. }
  360. else
  361. {
  362. QueryData newQuery;
  363. newQuery.query = EventQuery::create();
  364. newQuery.queryId = 0;
  365. mEventQueries.push_back(newQuery);
  366. idx = (UINT32)(mEventQueries.size() - 1);
  367. }
  368. return idx;
  369. }
  370. void MeshHeap::freeEventQuery(UINT32 idx)
  371. {
  372. mEventQueries[idx].queryId = 0;
  373. mFreeEventQueries.push(idx);
  374. }
  375. std::shared_ptr<VertexData> MeshHeap::getVertexData() const
  376. {
  377. return mVertexData;
  378. }
  379. std::shared_ptr<IndexData> MeshHeap::getIndexData() const
  380. {
  381. return mIndexData;
  382. }
  383. UINT32 MeshHeap::getVertexOffset(UINT32 meshId) const
  384. {
  385. auto findIter = mMeshAllocData.find(meshId);
  386. assert(findIter != mMeshAllocData.end());
  387. UINT32 chunkIdx = findIter->second.vertChunkIdx;
  388. return mVertChunks[chunkIdx].start;
  389. }
  390. UINT32 MeshHeap::getIndexOffset(UINT32 meshId) const
  391. {
  392. auto findIter = mMeshAllocData.find(meshId);
  393. assert(findIter != mMeshAllocData.end());
  394. UINT32 chunkIdx = findIter->second.idxChunkIdx;
  395. return mIdxChunks[chunkIdx].start;
  396. }
  397. void MeshHeap::notifyUsedOnGPU(UINT32 meshId)
  398. {
  399. auto findIter = mMeshAllocData.find(meshId);
  400. assert(findIter != mMeshAllocData.end());
  401. AllocatedData& allocData = findIter->second;
  402. assert(allocData.useFlags != UseFlags::Free);
  403. if(allocData.useFlags == UseFlags::GPUFree)
  404. allocData.useFlags = UseFlags::Used;
  405. QueryData& queryData = mEventQueries[allocData.eventQueryIdx];
  406. queryData.queryId = mNextQueryId++;
  407. queryData.query->onTriggered.connect(boost::bind(&MeshHeap::queryTriggered, this, meshId, queryData.queryId));
  408. queryData.query->begin();
  409. }
  410. void MeshHeap::queryTriggered(UINT32 meshId, UINT32 queryId)
  411. {
  412. auto findIter = mMeshAllocData.find(meshId);
  413. assert(findIter != mMeshAllocData.end());
  414. AllocatedData& allocData = findIter->second;
  415. // If query ids don't match then it means there either a more recent query or
  416. // the buffer was discarded and we are not interested in query result
  417. QueryData& queryData = mEventQueries[allocData.eventQueryIdx];
  418. if(queryId == queryData.queryId)
  419. {
  420. assert(allocData.useFlags != UseFlags::Free && allocData.useFlags != UseFlags::GPUFree);
  421. if(allocData.useFlags == UseFlags::CPUFree)
  422. {
  423. allocData.useFlags = UseFlags::Free;
  424. freeEventQuery(allocData.eventQueryIdx);
  425. mFreeVertChunks.push_back(allocData.vertChunkIdx);
  426. mFreeIdxChunks.push_back(allocData.idxChunkIdx);
  427. mergeWithNearbyChunks(allocData.vertChunkIdx, allocData.idxChunkIdx);
  428. mMeshAllocData.erase(findIter);
  429. }
  430. else
  431. allocData.useFlags = UseFlags::GPUFree;
  432. }
  433. }
  434. void MeshHeap::mergeWithNearbyChunks(UINT32 chunkVertIdx, UINT32 chunkIdxIdx)
  435. {
  436. // Merge vertex chunks
  437. ChunkData& vertChunk = mVertChunks[chunkVertIdx];
  438. for(auto& freeChunkIdx : mFreeVertChunks)
  439. {
  440. if(chunkVertIdx == freeChunkIdx)
  441. continue;
  442. ChunkData& curChunk = mVertChunks[freeChunkIdx];
  443. bool merged = false;
  444. if(curChunk.start == (vertChunk.start + vertChunk.size))
  445. {
  446. vertChunk.size += curChunk.size;
  447. merged = true;
  448. }
  449. if((curChunk.start + curChunk.size) == vertChunk.start)
  450. {
  451. vertChunk.start = curChunk.start;
  452. vertChunk.size += curChunk.size;
  453. merged = true;
  454. }
  455. if(merged)
  456. {
  457. // We can't remove the chunk since that would break the indexing scheme, so
  458. // mark it as empty and set size to 0. It will be reused when needed.
  459. curChunk.start = 0;
  460. curChunk.size = 0;
  461. mEmptyVertChunks.push(freeChunkIdx);
  462. }
  463. }
  464. // Merge index chunks
  465. ChunkData& idxChunk = mIdxChunks[chunkIdxIdx];
  466. for(auto& freeChunkIdx : mFreeIdxChunks)
  467. {
  468. if(chunkIdxIdx == freeChunkIdx)
  469. continue;
  470. ChunkData& curChunk = mIdxChunks[freeChunkIdx];
  471. bool merged = false;
  472. if(curChunk.start == (idxChunk.start + idxChunk.size))
  473. {
  474. idxChunk.size += curChunk.size;
  475. merged = true;
  476. }
  477. if((curChunk.start + curChunk.size) == idxChunk.start)
  478. {
  479. idxChunk.start = curChunk.start;
  480. idxChunk.size += curChunk.size;
  481. merged = true;
  482. }
  483. if(merged)
  484. {
  485. // We can't remove the chunk since that would break the indexing scheme, so
  486. // mark it as empty and set size to 0. It will be reused when needed.
  487. curChunk.start = 0;
  488. curChunk.size = 0;
  489. mEmptyIdxChunks.push(freeChunkIdx);
  490. }
  491. }
  492. }
  493. }