2
0

CmMeshHeap.cpp 18 KB

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