1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Mesh/BsMeshHeap.h"
4#include "CoreThread/BsCoreThread.h"
5#include "Mesh/BsTransientMesh.h"
6#include "Managers/BsHardwareBufferManager.h"
7#include "RenderAPI/BsVertexDataDesc.h"
8#include "RenderAPI/BsVertexData.h"
9#include "Mesh/BsMeshData.h"
10#include "Math/BsMath.h"
11#include "RenderAPI/BsEventQuery.h"
12#include "RenderAPI/BsRenderAPI.h"
13
14namespace bs
15{
16 MeshHeap::MeshHeap(UINT32 numVertices, UINT32 numIndices,
17 const SPtr<VertexDataDesc>& vertexDesc, IndexType indexType)
18 :mNumVertices(numVertices), mNumIndices(numIndices), mVertexDesc(vertexDesc), mIndexType(indexType), mNextFreeId(0)
19 {
20 }
21
22 SPtr<MeshHeap> MeshHeap::create(UINT32 numVertices, UINT32 numIndices, const SPtr<VertexDataDesc>& vertexDesc,
23 IndexType indexType)
24 {
25 MeshHeap* meshHeap = new (bs_alloc<MeshHeap>()) MeshHeap(numVertices, numIndices, vertexDesc, indexType);
26 SPtr<MeshHeap> meshHeapPtr = bs_core_ptr<MeshHeap>(meshHeap);
27
28 meshHeapPtr->_setThisPtr(meshHeapPtr);
29 meshHeapPtr->initialize();
30
31 return meshHeapPtr;
32 }
33
34 SPtr<TransientMesh> MeshHeap::alloc(const SPtr<MeshData>& meshData, DrawOperationType drawOp)
35 {
36 UINT32 meshIdx = mNextFreeId++;
37
38 SPtr<MeshHeap> thisPtr = std::static_pointer_cast<MeshHeap>(getThisPtr());
39 TransientMesh* transientMesh = new (bs_alloc<TransientMesh>()) TransientMesh(thisPtr, meshIdx,
40 meshData->getNumVertices(), meshData->getNumIndices(), drawOp);
41 SPtr<TransientMesh> transientMeshPtr = bs_core_ptr<TransientMesh>(transientMesh);
42
43 transientMeshPtr->_setThisPtr(transientMeshPtr);
44 transientMeshPtr->initialize();
45
46 mMeshes[meshIdx] = transientMeshPtr;
47
48 queueGpuCommand(getCore(), std::bind(&ct::MeshHeap::alloc, getCore().get(), transientMeshPtr->getCore(), meshData));
49
50 return transientMeshPtr;
51 }
52
53 void MeshHeap::dealloc(const SPtr<TransientMesh>& mesh)
54 {
55 auto iterFind = mMeshes.find(mesh->mId);
56 if(iterFind == mMeshes.end())
57 return;
58
59 mesh->markAsDestroyed();
60 mMeshes.erase(iterFind);
61
62 queueGpuCommand(getCore(), std::bind(&ct::MeshHeap::dealloc, getCore().get(), mesh->getCore()));
63 }
64
65 SPtr<ct::MeshHeap> MeshHeap::getCore() const
66 {
67 return std::static_pointer_cast<ct::MeshHeap>(mCoreSpecific);
68 }
69
70 SPtr<ct::CoreObject> MeshHeap::createCore() const
71 {
72 ct::MeshHeap* obj = new (bs_alloc<ct::MeshHeap>()) ct::MeshHeap(mNumVertices, mNumIndices,
73 mVertexDesc, mIndexType, GDF_DEFAULT);
74
75 SPtr<ct::MeshHeap> corePtr = bs_shared_ptr<ct::MeshHeap>(obj);
76 obj->_setThisPtr(corePtr);
77
78 return corePtr;
79 }
80
81 namespace ct
82 {
83 const float MeshHeap::GrowPercent = 1.5f;
84
85 MeshHeap::MeshHeap(UINT32 numVertices, UINT32 numIndices,
86 const SPtr<VertexDataDesc>& vertexDesc, IndexType indexType, GpuDeviceFlags deviceMask)
87 : mNumVertices(numVertices), mNumIndices(numIndices), mCPUIndexData(nullptr), mVertexDesc(vertexDesc)
88 , mIndexType(indexType), mDeviceMask(deviceMask), mNextQueryId(0)
89 {
90 for (UINT32 i = 0; i <= mVertexDesc->getMaxStreamIdx(); i++)
91 {
92 mCPUVertexData.push_back(nullptr);
93 }
94 }
95
96 MeshHeap::~MeshHeap()
97 {
98 THROW_IF_NOT_CORE_THREAD;
99
100 for (auto& cpuVertBuffer : mCPUVertexData)
101 bs_free(cpuVertBuffer);
102
103 if (mCPUIndexData != nullptr)
104 bs_free(mCPUIndexData);
105
106 mVertexData = nullptr;
107 mIndexBuffer = nullptr;
108 mVertexDesc = nullptr;
109 }
110
111 void MeshHeap::initialize()
112 {
113 THROW_IF_NOT_CORE_THREAD;
114
115 growVertexBuffer(mNumVertices);
116 growIndexBuffer(mNumIndices);
117
118 CoreObject::initialize();
119 }
120
121 void MeshHeap::alloc(SPtr<TransientMesh> mesh, const SPtr<MeshData>& meshData)
122 {
123 // Find free vertex chunk and grow if needed
124 UINT32 smallestVertFit = 0;
125 UINT32 smallestVertFitIdx = 0;
126
127 while (smallestVertFit == 0)
128 {
129 UINT32 curIdx = 0;
130 for (auto& chunkIdx : mFreeVertChunks)
131 {
132 ChunkData& chunk = mVertChunks[chunkIdx];
133
134 if (chunk.size >= meshData->getNumVertices() && (chunk.size < smallestVertFit || smallestVertFit == 0))
135 {
136 smallestVertFit = chunk.size;
137 smallestVertFitIdx = curIdx;
138 }
139
140 curIdx++;
141 }
142
143 if (smallestVertFit > 0)
144 break;
145
146 UINT32 newNumVertices = mNumVertices;
147 while (newNumVertices < (mNumVertices + meshData->getNumVertices()))
148 {
149 newNumVertices = Math::roundToInt(newNumVertices * GrowPercent);
150 }
151
152 growVertexBuffer(newNumVertices);
153 }
154
155 // Find free index chunk and grow if needed
156 UINT32 smallestIdxFit = 0;
157 UINT32 smallestIdxFitIdx = 0;
158
159 while (smallestIdxFit == 0)
160 {
161 UINT32 curIdx = 0;
162 for (auto& chunkIdx : mFreeIdxChunks)
163 {
164 ChunkData& chunk = mIdxChunks[chunkIdx];
165
166 if (chunk.size >= meshData->getNumIndices() && (chunk.size < smallestIdxFit || smallestIdxFit == 0))
167 {
168 smallestIdxFit = chunk.size;
169 smallestIdxFitIdx = curIdx;
170 }
171
172 curIdx++;
173 }
174
175 if (smallestIdxFit > 0)
176 break;
177
178 UINT32 newNumIndices = mNumIndices;
179 while (newNumIndices < (mNumIndices + meshData->getNumIndices()))
180 {
181 newNumIndices = Math::roundToInt(newNumIndices * GrowPercent);
182 }
183
184 growIndexBuffer(newNumIndices);
185 }
186
187 UINT32 freeVertChunkIdx = 0;
188 UINT32 freeIdxChunkIdx = 0;
189
190 auto freeVertIter = mFreeVertChunks.begin();
191 freeVertChunkIdx = (*freeVertIter);
192 for (UINT32 i = 0; i < smallestVertFitIdx; i++)
193 {
194 freeVertIter++;
195 freeVertChunkIdx = (*freeVertIter);
196 }
197
198 mFreeVertChunks.erase(freeVertIter);
199
200 auto freeIdxIter = mFreeIdxChunks.begin();
201 freeIdxChunkIdx = (*freeIdxIter);
202 for (UINT32 i = 0; i < smallestIdxFitIdx; i++)
203 {
204 freeIdxIter++;
205 freeIdxChunkIdx = (*freeIdxIter);
206 }
207
208 mFreeIdxChunks.erase(freeIdxIter);
209
210 ChunkData& vertChunk = mVertChunks[freeVertChunkIdx];
211 ChunkData& idxChunk = mIdxChunks[freeIdxChunkIdx];
212
213 UINT32 vertChunkStart = vertChunk.start;
214 UINT32 idxChunkStart = idxChunk.start;
215
216 UINT32 remainingNumVerts = vertChunk.size - meshData->getNumVertices();
217 UINT32 remainingNumIdx = idxChunk.size - meshData->getNumIndices();
218
219 vertChunk.size = meshData->getNumVertices();
220 idxChunk.size = meshData->getNumIndices();
221
222 if (remainingNumVerts > 0)
223 {
224 if (!mEmptyVertChunks.empty())
225 {
226 UINT32 emptyChunkIdx = mEmptyVertChunks.top();
227 ChunkData& emptyChunk = mVertChunks[emptyChunkIdx];
228 mEmptyVertChunks.pop();
229
230 emptyChunk.start = vertChunkStart + meshData->getNumVertices();
231 emptyChunk.size = remainingNumVerts;
232 }
233 else
234 {
235 ChunkData newChunk;
236 newChunk.size = remainingNumVerts;
237 newChunk.start = vertChunkStart + meshData->getNumVertices();
238
239 mVertChunks.push_back(newChunk);
240 mFreeVertChunks.push_back((UINT32)(mVertChunks.size() - 1));
241 }
242 }
243
244 if (remainingNumIdx > 0)
245 {
246 if (!mEmptyIdxChunks.empty())
247 {
248 UINT32 emptyChunkIdx = mEmptyIdxChunks.top();
249 ChunkData& emptyChunk = mIdxChunks[emptyChunkIdx];
250 mEmptyIdxChunks.pop();
251
252 emptyChunk.start = idxChunkStart + meshData->getNumIndices();
253 emptyChunk.size = remainingNumIdx;
254 }
255 else
256 {
257 ChunkData newChunk;
258 newChunk.size = remainingNumIdx;
259 newChunk.start = idxChunkStart + meshData->getNumIndices();
260
261 mIdxChunks.push_back(newChunk);
262 mFreeIdxChunks.push_back((UINT32)(mIdxChunks.size() - 1));
263 }
264 }
265
266 AllocatedData newAllocData;
267 newAllocData.vertChunkIdx = freeVertChunkIdx;
268 newAllocData.idxChunkIdx = freeIdxChunkIdx;
269 newAllocData.useFlags = UseFlags::GPUFree;
270 newAllocData.eventQueryIdx = createEventQuery();
271 newAllocData.mesh = mesh;
272
273 mMeshAllocData[mesh->getMeshHeapId()] = newAllocData;
274
275 // Actually copy data
276 for (UINT32 i = 0; i <= mVertexDesc->getMaxStreamIdx(); i++)
277 {
278 if (!mVertexDesc->hasStream(i))
279 continue;
280
281 if (!meshData->getVertexDesc()->hasStream(i))
282 continue;
283
284 // Ensure vertex sizes match
285 UINT32 vertSize = mVertexData->vertexDeclaration->getProperties().getVertexSize(i);
286 UINT32 otherVertSize = meshData->getVertexDesc()->getVertexStride(i);
287 if (otherVertSize != vertSize)
288 {
289 BS_EXCEPT(InvalidParametersException, "Provided vertex size for stream " + toString(i) + " doesn't match meshes vertex size. Needed: " +
290 toString(vertSize) + ". Got: " + toString(otherVertSize));
291 }
292
293 SPtr<VertexBuffer> vertexBuffer = mVertexData->getBuffer(i);
294
295 UINT8* vertDest = mCPUVertexData[i] + vertChunkStart * vertSize;
296 memcpy(vertDest, meshData->getStreamData(i), meshData->getNumVertices() * vertSize);
297
298 vertexBuffer->writeData(vertChunkStart * vertSize, meshData->getNumVertices() * vertSize, vertDest, BTW_NO_OVERWRITE);
299 }
300
301 const IndexBufferProperties& ibProps = mIndexBuffer->getProperties();
302
303 UINT32 idxSize = ibProps.getIndexSize();
304
305 // Ensure index sizes match
306 if (meshData->getIndexElementSize() != idxSize)
307 {
308 BS_EXCEPT(InvalidParametersException, "Provided index size doesn't match meshes index size. Needed: " +
309 toString(idxSize) + ". Got: " + toString(meshData->getIndexElementSize()));
310 }
311
312 UINT8* idxDest = mCPUIndexData + idxChunkStart * idxSize;
313 memcpy(idxDest, meshData->getIndexData(), meshData->getNumIndices() * idxSize);
314 mIndexBuffer->writeData(idxChunkStart * idxSize, meshData->getNumIndices() * idxSize, idxDest, BTW_NO_OVERWRITE);
315 }
316
317 void MeshHeap::dealloc(SPtr<TransientMesh> mesh)
318 {
319 auto findIter = mMeshAllocData.find(mesh->getMeshHeapId());
320 assert(findIter != mMeshAllocData.end());
321
322 AllocatedData& allocData = findIter->second;
323 if (allocData.useFlags == UseFlags::GPUFree)
324 {
325 allocData.useFlags = UseFlags::Free;
326 freeEventQuery(allocData.eventQueryIdx);
327
328 mFreeVertChunks.push_back(allocData.vertChunkIdx);
329 mFreeIdxChunks.push_back(allocData.idxChunkIdx);
330
331 mergeWithNearbyChunks(allocData.vertChunkIdx, allocData.idxChunkIdx);
332
333 mMeshAllocData.erase(findIter);
334 }
335 else if (allocData.useFlags == UseFlags::Used)
336 allocData.useFlags = UseFlags::CPUFree;
337 }
338
339 void MeshHeap::growVertexBuffer(UINT32 numVertices)
340 {
341 mNumVertices = numVertices;
342 mVertexData = SPtr<VertexData>(bs_new<VertexData>());
343
344 mVertexData->vertexCount = mNumVertices;
345 mVertexData->vertexDeclaration = VertexDeclaration::create(mVertexDesc, mDeviceMask);
346
347 // Create buffers and copy data
348 for (UINT32 i = 0; i <= mVertexDesc->getMaxStreamIdx(); i++)
349 {
350 if (!mVertexDesc->hasStream(i))
351 continue;
352
353 UINT32 vertSize = mVertexData->vertexDeclaration->getProperties().getVertexSize(i);
354
355 VERTEX_BUFFER_DESC desc;
356 desc.vertexSize = vertSize;
357 desc.numVerts = mVertexData->vertexCount;
358 desc.usage = GBU_DYNAMIC;
359
360 SPtr<VertexBuffer> vertexBuffer = VertexBuffer::create(desc, mDeviceMask);
361 mVertexData->setBuffer(i, vertexBuffer);
362
363 // Copy all data to the new buffer
364 UINT8* oldBuffer = mCPUVertexData[i];
365 UINT8* buffer = (UINT8*)bs_alloc(vertSize * numVertices);
366
367 UINT32 destOffset = 0;
368 if (oldBuffer != nullptr)
369 {
370 for (auto& allocData : mMeshAllocData)
371 {
372 ChunkData& oldChunk = mVertChunks[allocData.second.vertChunkIdx];
373
374 UINT8* oldData = oldBuffer + oldChunk.start * vertSize;
375 memcpy(buffer + destOffset * vertSize, oldData, oldChunk.size * vertSize);
376
377 destOffset += oldChunk.size;
378 }
379
380 bs_free(oldBuffer);
381 }
382
383 if (destOffset > 0)
384 vertexBuffer->writeData(0, destOffset * vertSize, buffer, BTW_NO_OVERWRITE);
385
386 mCPUVertexData[i] = buffer;
387 }
388
389 // Reorder chunks
390 UINT32 destOffset = 0;
391 Vector<ChunkData> newVertChunks;
392 List<UINT32> freeVertChunks;
393
394 for (auto& allocData : mMeshAllocData)
395 {
396 ChunkData& oldChunk = mVertChunks[allocData.second.vertChunkIdx];
397
398 ChunkData newChunk;
399 newChunk.start = destOffset;
400 newChunk.size = oldChunk.size;
401
402 allocData.second.vertChunkIdx = (UINT32)newVertChunks.size();
403 newVertChunks.push_back(newChunk);
404
405 destOffset += oldChunk.size;
406 }
407
408 // Add free chunk
409 if (destOffset != mNumVertices)
410 {
411 ChunkData newChunk;
412 newChunk.start = destOffset;
413 newChunk.size = mNumVertices - destOffset;
414
415 newVertChunks.push_back(newChunk);
416 freeVertChunks.push_back((UINT32)(newVertChunks.size() - 1));
417 }
418
419 mVertChunks = newVertChunks;
420 mFreeVertChunks = freeVertChunks;
421
422 while (!mEmptyVertChunks.empty())
423 mEmptyVertChunks.pop();
424 }
425
426 void MeshHeap::growIndexBuffer(UINT32 numIndices)
427 {
428 mNumIndices = numIndices;
429
430 INDEX_BUFFER_DESC ibDesc;
431 ibDesc.indexType = mIndexType;
432 ibDesc.numIndices = mNumIndices;
433 ibDesc.usage = GBU_DYNAMIC;
434
435 mIndexBuffer = IndexBuffer::create(ibDesc, mDeviceMask);
436
437 const IndexBufferProperties& ibProps = mIndexBuffer->getProperties();
438
439 // Copy all data to the new buffer
440 UINT32 idxSize = ibProps.getIndexSize();
441
442 UINT8* oldBuffer = mCPUIndexData;
443 UINT8* buffer = (UINT8*)bs_alloc(idxSize * numIndices);
444
445 UINT32 destOffset = 0;
446 if (oldBuffer != nullptr)
447 {
448 for (auto& allocData : mMeshAllocData)
449 {
450 ChunkData& oldChunk = mIdxChunks[allocData.second.idxChunkIdx];
451
452 UINT8* oldData = oldBuffer + oldChunk.start * idxSize;
453 memcpy(buffer + destOffset * idxSize, oldData, oldChunk.size * idxSize);
454
455 destOffset += oldChunk.size;
456 }
457
458 bs_free(oldBuffer);
459 }
460
461 if (destOffset > 0)
462 mIndexBuffer->writeData(0, destOffset * idxSize, buffer, BTW_NO_OVERWRITE);
463
464 mCPUIndexData = buffer;
465
466 // Reorder chunks
467 destOffset = 0;
468 Vector<ChunkData> newIdxChunks;
469 List<UINT32> freeIdxChunks;
470
471 for (auto& allocData : mMeshAllocData)
472 {
473 ChunkData& oldChunk = mIdxChunks[allocData.second.idxChunkIdx];
474
475 ChunkData newChunk;
476 newChunk.start = destOffset;
477 newChunk.size = oldChunk.size;
478
479 allocData.second.idxChunkIdx = (UINT32)newIdxChunks.size();
480 newIdxChunks.push_back(newChunk);
481
482 destOffset += oldChunk.size;
483 }
484
485 // Add free chunk
486 if (destOffset != mNumIndices)
487 {
488 ChunkData newChunk;
489 newChunk.start = destOffset;
490 newChunk.size = mNumIndices - destOffset;
491
492 newIdxChunks.push_back(newChunk);
493 freeIdxChunks.push_back((UINT32)(newIdxChunks.size() - 1));
494 }
495
496 mIdxChunks = newIdxChunks;
497 mFreeIdxChunks = freeIdxChunks;
498
499 while (!mEmptyIdxChunks.empty())
500 mEmptyIdxChunks.pop();
501 }
502
503 UINT32 MeshHeap::createEventQuery()
504 {
505 UINT32 idx = 0;
506 if (mFreeEventQueries.size() > 0)
507 {
508 idx = mFreeEventQueries.top();
509 mFreeEventQueries.pop();
510 }
511 else
512 {
513 QueryData newQuery;
514 newQuery.query = EventQuery::create();
515 newQuery.queryId = 0;
516
517 mEventQueries.push_back(newQuery);
518 idx = (UINT32)(mEventQueries.size() - 1);
519 }
520
521 return idx;
522 }
523
524 void MeshHeap::freeEventQuery(UINT32 idx)
525 {
526 mEventQueries[idx].query->onTriggered.clear();
527 mEventQueries[idx].queryId = 0;
528 mFreeEventQueries.push(idx);
529 }
530
531 SPtr<VertexData> MeshHeap::getVertexData() const
532 {
533 return mVertexData;
534 }
535
536 SPtr<IndexBuffer> MeshHeap::getIndexBuffer() const
537 {
538 return mIndexBuffer;
539 }
540
541 SPtr<VertexDataDesc> MeshHeap::getVertexDesc() const
542 {
543 return mVertexDesc;
544 }
545
546 UINT32 MeshHeap::getVertexOffset(UINT32 meshId) const
547 {
548 auto findIter = mMeshAllocData.find(meshId);
549 assert(findIter != mMeshAllocData.end());
550
551 UINT32 chunkIdx = findIter->second.vertChunkIdx;
552 return mVertChunks[chunkIdx].start;
553 }
554
555 UINT32 MeshHeap::getIndexOffset(UINT32 meshId) const
556 {
557 auto findIter = mMeshAllocData.find(meshId);
558 assert(findIter != mMeshAllocData.end());
559
560 UINT32 chunkIdx = findIter->second.idxChunkIdx;
561 return mIdxChunks[chunkIdx].start;
562 }
563
564 void MeshHeap::notifyUsedOnGPU(UINT32 meshId)
565 {
566 auto findIter = mMeshAllocData.find(meshId);
567 assert(findIter != mMeshAllocData.end());
568
569 AllocatedData& allocData = findIter->second;
570 assert(allocData.useFlags != UseFlags::Free);
571
572 if (allocData.useFlags == UseFlags::GPUFree)
573 allocData.useFlags = UseFlags::Used;
574
575 SPtr<MeshHeap> thisPtr = std::static_pointer_cast<MeshHeap>(getThisPtr());
576
577 QueryData& queryData = mEventQueries[allocData.eventQueryIdx];
578 queryData.queryId = mNextQueryId++;
579 queryData.query->onTriggered.clear();
580 queryData.query->onTriggered.connect(std::bind(&MeshHeap::queryTriggered, thisPtr, meshId, queryData.queryId));
581 queryData.query->begin();
582 }
583
584 // Note: Need to use a shared ptr here to ensure MeshHeap doesn't get deallocated sometime during this callback
585 void MeshHeap::queryTriggered(SPtr<MeshHeap> thisPtr, UINT32 meshId, UINT32 queryId)
586 {
587 auto findIter = thisPtr->mMeshAllocData.find(meshId);
588 assert(findIter != thisPtr->mMeshAllocData.end());
589
590 AllocatedData& allocData = findIter->second;
591
592 // If query ids don't match then it means there either a more recent query or
593 // the buffer was discarded and we are not interested in query result
594 QueryData& queryData = thisPtr->mEventQueries[allocData.eventQueryIdx];
595 if (queryId == queryData.queryId)
596 {
597 assert(allocData.useFlags != UseFlags::Free && allocData.useFlags != UseFlags::GPUFree);
598
599 if (allocData.useFlags == UseFlags::CPUFree)
600 {
601 allocData.useFlags = UseFlags::Free;
602 thisPtr->freeEventQuery(allocData.eventQueryIdx);
603
604 thisPtr->mFreeVertChunks.push_back(allocData.vertChunkIdx);
605 thisPtr->mFreeIdxChunks.push_back(allocData.idxChunkIdx);
606
607 thisPtr->mergeWithNearbyChunks(allocData.vertChunkIdx, allocData.idxChunkIdx);
608
609 thisPtr->mMeshAllocData.erase(findIter);
610 }
611 else
612 allocData.useFlags = UseFlags::GPUFree;
613 }
614
615 queryData.query->onTriggered.clear();
616 }
617
618 void MeshHeap::mergeWithNearbyChunks(UINT32 chunkVertIdx, UINT32 chunkIdxIdx)
619 {
620 // Merge vertex chunks
621 ChunkData& vertChunk = mVertChunks[chunkVertIdx];
622 for (auto& freeChunkIdx : mFreeVertChunks)
623 {
624 if (chunkVertIdx == freeChunkIdx)
625 continue;
626
627 ChunkData& curChunk = mVertChunks[freeChunkIdx];
628 if (curChunk.size == 0) // Already merged
629 continue;
630
631 bool merged = false;
632 if (curChunk.start == (vertChunk.start + vertChunk.size))
633 {
634 vertChunk.size += curChunk.size;
635
636 merged = true;
637 }
638 else if ((curChunk.start + curChunk.size) == vertChunk.start)
639 {
640 vertChunk.start = curChunk.start;
641 vertChunk.size += curChunk.size;
642
643 merged = true;
644 }
645
646 if (merged)
647 {
648 // We can't remove the chunk since that would break the indexing scheme, so
649 // mark it as empty and set size to 0. It will be reused when needed.
650 curChunk.start = 0;
651 curChunk.size = 0;
652 mEmptyVertChunks.push(freeChunkIdx);
653 }
654 }
655
656 // Merge index chunks
657 ChunkData& idxChunk = mIdxChunks[chunkIdxIdx];
658 for (auto& freeChunkIdx : mFreeIdxChunks)
659 {
660 if (chunkIdxIdx == freeChunkIdx)
661 continue;
662
663 ChunkData& curChunk = mIdxChunks[freeChunkIdx];
664 if (curChunk.size == 0) // Already merged
665 continue;
666
667 bool merged = false;
668 if (curChunk.start == (idxChunk.start + idxChunk.size))
669 {
670 idxChunk.size += curChunk.size;
671
672 merged = true;
673 }
674 else if ((curChunk.start + curChunk.size) == idxChunk.start)
675 {
676 idxChunk.start = curChunk.start;
677 idxChunk.size += curChunk.size;
678
679 merged = true;
680 }
681
682 if (merged)
683 {
684 // We can't remove the chunk since that would break the indexing scheme, so
685 // mark it as empty and set size to 0. It will be reused when needed.
686 curChunk.start = 0;
687 curChunk.size = 0;
688 mEmptyIdxChunks.push(freeChunkIdx);
689 }
690 }
691 }
692 }
693}