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 | |
14 | namespace 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 | } |