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/BsMesh.h"
4#include "Private/RTTI/BsMeshRTTI.h"
5#include "Mesh/BsMeshData.h"
6#include "Debug/BsDebug.h"
7#include "Managers/BsHardwareBufferManager.h"
8#include "Managers/BsMeshManager.h"
9#include "CoreThread/BsCoreThread.h"
10#include "Threading/BsAsyncOp.h"
11#include "RenderAPI/BsVertexDataDesc.h"
12#include "Resources/BsResources.h"
13#include "RenderAPI/BsRenderAPI.h"
14
15namespace bs
16{
17 MESH_DESC MESH_DESC::DEFAULT = MESH_DESC();
18
19 Mesh::Mesh(const MESH_DESC& desc)
20 :MeshBase(desc.numVertices, desc.numIndices, desc.subMeshes), mVertexDesc(desc.vertexDesc), mUsage(desc.usage),
21 mIndexType(desc.indexType), mSkeleton(desc.skeleton), mMorphShapes(desc.morphShapes)
22 {
23
24 }
25
26 Mesh::Mesh(const SPtr<MeshData>& initialMeshData, const MESH_DESC& desc)
27 :MeshBase(initialMeshData->getNumVertices(), initialMeshData->getNumIndices(), desc.subMeshes),
28 mCPUData(initialMeshData), mVertexDesc(initialMeshData->getVertexDesc()),
29 mUsage(desc.usage), mIndexType(initialMeshData->getIndexType()), mSkeleton(desc.skeleton),
30 mMorphShapes(desc.morphShapes)
31 { }
32
33 Mesh::Mesh()
34 :MeshBase(0, 0, DOT_TRIANGLE_LIST)
35 { }
36
37 AsyncOp Mesh::writeData(const SPtr<MeshData>& data, bool discardEntireBuffer)
38 {
39 updateBounds(*data);
40 updateCPUBuffer(0, *data);
41
42 data->_lock();
43
44 std::function<void(const SPtr<ct::Mesh>&, const SPtr<MeshData>&, bool, AsyncOp&)> func =
45 [&](const SPtr<ct::Mesh>& mesh, const SPtr<MeshData>& _meshData, bool _discardEntireBuffer, AsyncOp& asyncOp)
46 {
47 mesh->writeData(*_meshData, _discardEntireBuffer, false);
48 _meshData->_unlock();
49 asyncOp._completeOperation();
50
51 };
52
53 return gCoreThread().queueReturnCommand(std::bind(func, getCore(),
54 data, discardEntireBuffer, std::placeholders::_1));
55 }
56
57 AsyncOp Mesh::readData(const SPtr<MeshData>& data)
58 {
59 data->_lock();
60
61 std::function<void(const SPtr<ct::Mesh>&, const SPtr<MeshData>&, AsyncOp&)> func =
62 [&](const SPtr<ct::Mesh>& mesh, const SPtr<MeshData>& _meshData, AsyncOp& asyncOp)
63 {
64 // Make sure any queued command start executing before reading
65 ct::RenderAPI::instance().submitCommandBuffer(nullptr);
66
67 mesh->readData(*_meshData);
68 _meshData->_unlock();
69 asyncOp._completeOperation();
70
71 };
72
73 return gCoreThread().queueReturnCommand(std::bind(func, getCore(),
74 data, std::placeholders::_1));
75 }
76
77 SPtr<MeshData> Mesh::allocBuffer() const
78 {
79 SPtr<MeshData> meshData = bs_shared_ptr_new<MeshData>(mProperties.mNumVertices, mProperties.mNumIndices,
80 mVertexDesc, mIndexType);
81
82 return meshData;
83 }
84
85 void Mesh::initialize()
86 {
87 if (mCPUData != nullptr)
88 updateBounds(*mCPUData);
89
90 MeshBase::initialize();
91
92 if ((mUsage & MU_CPUCACHED) != 0 && mCPUData == nullptr)
93 createCPUBuffer();
94 }
95
96 void Mesh::updateBounds(const MeshData& meshData)
97 {
98 mProperties.mBounds = meshData.calculateBounds();
99 markCoreDirty();
100 }
101
102 SPtr<ct::Mesh> Mesh::getCore() const
103 {
104 return std::static_pointer_cast<ct::Mesh>(mCoreSpecific);
105 }
106
107 SPtr<ct::CoreObject> Mesh::createCore() const
108 {
109 MESH_DESC desc;
110 desc.numVertices = mProperties.mNumVertices;
111 desc.numIndices = mProperties.mNumIndices;
112 desc.vertexDesc = mVertexDesc;
113 desc.subMeshes = mProperties.mSubMeshes;
114 desc.usage = mUsage;
115 desc.indexType = mIndexType;
116 desc.skeleton = mSkeleton;
117 desc.morphShapes = mMorphShapes;
118
119 ct::Mesh* obj = new (bs_alloc<ct::Mesh>()) ct::Mesh(mCPUData, desc, GDF_DEFAULT);
120
121 SPtr<ct::CoreObject> meshCore = bs_shared_ptr<ct::Mesh>(obj);
122 meshCore->_setThisPtr(meshCore);
123
124 if ((mUsage & MU_CPUCACHED) == 0)
125 mCPUData = nullptr;
126
127 return meshCore;
128 }
129
130 void Mesh::updateCPUBuffer(UINT32 subresourceIdx, const MeshData& pixelData)
131 {
132 if ((mUsage & MU_CPUCACHED) == 0)
133 return;
134
135 if (subresourceIdx > 0)
136 {
137 LOGERR("Invalid subresource index: " + toString(subresourceIdx) + ". Supported range: 0 .. 1.");
138 return;
139 }
140
141 if (pixelData.getNumIndices() != mProperties.getNumIndices() ||
142 pixelData.getNumVertices() != mProperties.getNumVertices() ||
143 pixelData.getIndexType() != mIndexType ||
144 pixelData.getVertexDesc()->getVertexStride() != mVertexDesc->getVertexStride())
145 {
146 LOGERR("Provided buffer is not of valid dimensions or format in order to update this mesh.");
147 return;
148 }
149
150 if (mCPUData->getSize() != pixelData.getSize())
151 BS_EXCEPT(InternalErrorException, "Buffer sizes don't match.");
152
153 UINT8* dest = mCPUData->getData();
154 UINT8* src = pixelData.getData();
155
156 memcpy(dest, src, pixelData.getSize());
157 }
158
159 void Mesh::createCPUBuffer()
160 {
161 mCPUData = allocBuffer();
162 }
163
164 HMesh Mesh::dummy()
165 {
166 return MeshManager::instance().getDummyMesh();
167 }
168
169 /************************************************************************/
170 /* SERIALIZATION */
171 /************************************************************************/
172
173 RTTITypeBase* Mesh::getRTTIStatic()
174 {
175 return MeshRTTI::instance();
176 }
177
178 RTTITypeBase* Mesh::getRTTI() const
179 {
180 return Mesh::getRTTIStatic();
181 }
182
183 /************************************************************************/
184 /* STATICS */
185 /************************************************************************/
186
187 HMesh Mesh::create(UINT32 numVertices, UINT32 numIndices, const SPtr<VertexDataDesc>& vertexDesc,
188 int usage, DrawOperationType drawOp, IndexType indexType)
189 {
190 MESH_DESC desc;
191 desc.numVertices = numVertices;
192 desc.numIndices = numIndices;
193 desc.vertexDesc = vertexDesc;
194 desc.usage = usage;
195 desc.subMeshes.push_back(SubMesh(0, numIndices, drawOp));
196 desc.indexType = indexType;
197
198 SPtr<Mesh> meshPtr = _createPtr(desc);
199 return static_resource_cast<Mesh>(gResources()._createResourceHandle(meshPtr));
200 }
201
202 HMesh Mesh::create(const MESH_DESC& desc)
203 {
204 SPtr<Mesh> meshPtr = _createPtr(desc);
205 return static_resource_cast<Mesh>(gResources()._createResourceHandle(meshPtr));
206 }
207
208 HMesh Mesh::create(const SPtr<MeshData>& initialMeshData, const MESH_DESC& desc)
209 {
210 SPtr<Mesh> meshPtr = _createPtr(initialMeshData, desc);
211 return static_resource_cast<Mesh>(gResources()._createResourceHandle(meshPtr));
212 }
213
214 HMesh Mesh::create(const SPtr<MeshData>& initialMeshData, int usage, DrawOperationType drawOp)
215 {
216 SPtr<Mesh> meshPtr = _createPtr(initialMeshData, usage, drawOp);
217 return static_resource_cast<Mesh>(gResources()._createResourceHandle(meshPtr));
218 }
219
220 SPtr<Mesh> Mesh::_createPtr(const MESH_DESC& desc)
221 {
222 SPtr<Mesh> mesh = bs_core_ptr<Mesh>(new (bs_alloc<Mesh>()) Mesh(desc));
223 mesh->_setThisPtr(mesh);
224 mesh->initialize();
225
226 return mesh;
227 }
228
229 SPtr<Mesh> Mesh::_createPtr(const SPtr<MeshData>& initialMeshData, const MESH_DESC& desc)
230 {
231 SPtr<Mesh> mesh = bs_core_ptr<Mesh>(new (bs_alloc<Mesh>()) Mesh(initialMeshData, desc));
232 mesh->_setThisPtr(mesh);
233 mesh->initialize();
234
235 return mesh;
236 }
237
238 SPtr<Mesh> Mesh::_createPtr(const SPtr<MeshData>& initialMeshData, int usage, DrawOperationType drawOp)
239 {
240 MESH_DESC desc;
241 desc.usage = usage;
242 desc.subMeshes.push_back(SubMesh(0, initialMeshData->getNumIndices(), drawOp));
243
244 SPtr<Mesh> mesh = bs_core_ptr<Mesh>(new (bs_alloc<Mesh>()) Mesh(initialMeshData, desc));
245 mesh->_setThisPtr(mesh);
246 mesh->initialize();
247
248 return mesh;
249 }
250
251 SPtr<Mesh> Mesh::createEmpty()
252 {
253 SPtr<Mesh> mesh = bs_core_ptr<Mesh>(new (bs_alloc<Mesh>()) Mesh());
254 mesh->_setThisPtr(mesh);
255
256 return mesh;
257 }
258
259 namespace ct
260 {
261 Mesh::Mesh(const SPtr<MeshData>& initialMeshData, const MESH_DESC& desc, GpuDeviceFlags deviceMask)
262 : MeshBase(desc.numVertices, desc.numIndices, desc.subMeshes), mVertexData(nullptr), mIndexBuffer(nullptr)
263 , mVertexDesc(desc.vertexDesc), mUsage(desc.usage), mIndexType(desc.indexType), mDeviceMask(deviceMask)
264 , mTempInitialMeshData(initialMeshData), mSkeleton(desc.skeleton), mMorphShapes(desc.morphShapes)
265
266 { }
267
268 Mesh::~Mesh()
269 {
270 THROW_IF_NOT_CORE_THREAD;
271
272 mVertexData = nullptr;
273 mIndexBuffer = nullptr;
274 mVertexDesc = nullptr;
275 mTempInitialMeshData = nullptr;
276 }
277
278 void Mesh::initialize()
279 {
280 THROW_IF_NOT_CORE_THREAD;
281
282 bool isDynamic = (mUsage & MU_DYNAMIC) != 0;
283 int usage = isDynamic ? GBU_DYNAMIC : GBU_STATIC;
284
285 INDEX_BUFFER_DESC ibDesc;
286 ibDesc.indexType = mIndexType;
287 ibDesc.numIndices = mProperties.mNumIndices;
288 ibDesc.usage = (GpuBufferUsage)usage;
289
290 mIndexBuffer = IndexBuffer::create(ibDesc, mDeviceMask);
291
292 mVertexData = bs_shared_ptr_new<VertexData>();
293 mVertexData->vertexCount = mProperties.mNumVertices;
294 mVertexData->vertexDeclaration = VertexDeclaration::create(mVertexDesc, mDeviceMask);
295
296 for (UINT32 i = 0; i <= mVertexDesc->getMaxStreamIdx(); i++)
297 {
298 if (!mVertexDesc->hasStream(i))
299 continue;
300
301 VERTEX_BUFFER_DESC vbDesc;
302 vbDesc.vertexSize = mVertexData->vertexDeclaration->getProperties().getVertexSize(i);
303 vbDesc.numVerts = mVertexData->vertexCount;
304 vbDesc.usage = (GpuBufferUsage)usage;
305
306 SPtr<VertexBuffer> vertexBuffer = VertexBuffer::create(vbDesc, mDeviceMask);
307 mVertexData->setBuffer(i, vertexBuffer);
308 }
309
310 // TODO Low priority - DX11 (and maybe OpenGL)? allow an optimization that allows you to set
311 // buffer data upon buffer construction, instead of setting it in a second step like I do here
312 if (mTempInitialMeshData != nullptr)
313 {
314 writeData(*mTempInitialMeshData, isDynamic);
315 mTempInitialMeshData = nullptr;
316 }
317
318 MeshBase::initialize();
319 }
320
321 SPtr<VertexData> Mesh::getVertexData() const
322 {
323 THROW_IF_NOT_CORE_THREAD;
324
325 return mVertexData;
326 }
327
328 SPtr<IndexBuffer> Mesh::getIndexBuffer() const
329 {
330 THROW_IF_NOT_CORE_THREAD;
331
332 return mIndexBuffer;
333 }
334
335 SPtr<VertexDataDesc> Mesh::getVertexDesc() const
336 {
337 THROW_IF_NOT_CORE_THREAD;
338
339 return mVertexDesc;
340 }
341
342 void Mesh::writeData(const MeshData& meshData, bool discardEntireBuffer, bool performUpdateBounds, UINT32 queueIdx)
343 {
344 THROW_IF_NOT_CORE_THREAD;
345
346 if (discardEntireBuffer)
347 {
348 if ((mUsage & MU_STATIC) != 0)
349 {
350 LOGWRN("Buffer discard is enabled but buffer was not created as dynamic. Disabling discard.");
351 discardEntireBuffer = false;
352 }
353 }
354 else
355 {
356 if ((mUsage & MU_DYNAMIC) != 0)
357 {
358 LOGWRN("Buffer discard is not enabled but buffer was created as dynamic. Enabling discard.");
359 discardEntireBuffer = true;
360 }
361 }
362
363 // Indices
364 const IndexBufferProperties& ibProps = mIndexBuffer->getProperties();
365
366 UINT32 indicesSize = meshData.getIndexBufferSize();
367 UINT8* srcIdxData = meshData.getIndexData();
368
369 if (meshData.getIndexElementSize() != ibProps.getIndexSize())
370 {
371 LOGERR("Provided index size doesn't match meshes index size. Needed: " +
372 toString(ibProps.getIndexSize()) + ". Got: " + toString(meshData.getIndexElementSize()));
373
374 return;
375 }
376
377 if (indicesSize > mIndexBuffer->getSize())
378 {
379 indicesSize = mIndexBuffer->getSize();
380 LOGERR("Index buffer values are being written out of valid range.");
381 }
382
383 mIndexBuffer->writeData(0, indicesSize, srcIdxData, discardEntireBuffer ? BWT_DISCARD : BWT_NORMAL, queueIdx);
384
385 // Vertices
386 for (UINT32 i = 0; i <= mVertexDesc->getMaxStreamIdx(); i++)
387 {
388 if (!mVertexDesc->hasStream(i))
389 continue;
390
391 if (!meshData.getVertexDesc()->hasStream(i))
392 continue;
393
394 // Ensure both have the same sized vertices
395 UINT32 myVertSize = mVertexDesc->getVertexStride(i);
396 UINT32 otherVertSize = meshData.getVertexDesc()->getVertexStride(i);
397 if (myVertSize != otherVertSize)
398 {
399 LOGERR("Provided vertex size for stream " + toString(i) + " doesn't match meshes vertex size. Needed: " +
400 toString(myVertSize) + ". Got: " + toString(otherVertSize));
401
402 continue;
403 }
404
405 SPtr<VertexBuffer> vertexBuffer = mVertexData->getBuffer(i);
406
407 UINT32 bufferSize = meshData.getStreamSize(i);
408 UINT8* srcVertBufferData = meshData.getStreamData(i);
409
410 if (bufferSize > vertexBuffer->getSize())
411 {
412 bufferSize = vertexBuffer->getSize();
413 LOGERR("Vertex buffer values for stream \"" + toString(i) + "\" are being written out of valid range.");
414 }
415
416 vertexBuffer->writeData(0, bufferSize, srcVertBufferData, discardEntireBuffer ? BWT_DISCARD : BWT_NORMAL,
417 queueIdx);
418 }
419
420 if (performUpdateBounds)
421 updateBounds(meshData);
422 }
423
424 void Mesh::readData(MeshData& meshData, UINT32 deviceIdx, UINT32 queueIdx)
425 {
426 THROW_IF_NOT_CORE_THREAD;
427
428 IndexType indexType = IT_32BIT;
429 if (mIndexBuffer)
430 indexType = mIndexBuffer->getProperties().getType();
431
432 if (mIndexBuffer)
433 {
434 const IndexBufferProperties& ibProps = mIndexBuffer->getProperties();
435
436 if (meshData.getIndexElementSize() != ibProps.getIndexSize())
437 {
438 LOGERR("Provided index size doesn't match meshes index size. Needed: " +
439 toString(ibProps.getIndexSize()) + ". Got: " + toString(meshData.getIndexElementSize()));
440 return;
441 }
442
443 UINT8* idxData = static_cast<UINT8*>(mIndexBuffer->lock(GBL_READ_ONLY, deviceIdx, queueIdx));
444 UINT32 idxElemSize = ibProps.getIndexSize();
445
446 UINT8* indices = nullptr;
447
448 if (indexType == IT_16BIT)
449 indices = (UINT8*)meshData.getIndices16();
450 else
451 indices = (UINT8*)meshData.getIndices32();
452
453 UINT32 numIndicesToCopy = std::min(mProperties.mNumIndices, meshData.getNumIndices());
454
455 UINT32 indicesSize = numIndicesToCopy * idxElemSize;
456 if (indicesSize > meshData.getIndexBufferSize())
457 {
458 LOGERR("Provided buffer doesn't have enough space to store mesh indices.");
459 return;
460 }
461
462 memcpy(indices, idxData, numIndicesToCopy * idxElemSize);
463
464 mIndexBuffer->unlock();
465 }
466
467 if (mVertexData)
468 {
469 auto vertexBuffers = mVertexData->getBuffers();
470
471 UINT32 streamIdx = 0;
472 for (auto iter = vertexBuffers.begin(); iter != vertexBuffers.end(); ++iter)
473 {
474 if (!meshData.getVertexDesc()->hasStream(streamIdx))
475 continue;
476
477 SPtr<VertexBuffer> vertexBuffer = iter->second;
478 const VertexBufferProperties& vbProps = vertexBuffer->getProperties();
479
480 // Ensure both have the same sized vertices
481 UINT32 myVertSize = mVertexDesc->getVertexStride(streamIdx);
482 UINT32 otherVertSize = meshData.getVertexDesc()->getVertexStride(streamIdx);
483 if (myVertSize != otherVertSize)
484 {
485 LOGERR("Provided vertex size for stream " + toString(streamIdx) + " doesn't match meshes vertex size. Needed: " +
486 toString(myVertSize) + ". Got: " + toString(otherVertSize));
487
488 continue;
489 }
490
491 UINT32 numVerticesToCopy = meshData.getNumVertices();
492 UINT32 bufferSize = vbProps.getVertexSize() * numVerticesToCopy;
493
494 if (bufferSize > vertexBuffer->getSize())
495 {
496 LOGERR("Vertex buffer values for stream \"" + toString(streamIdx) + "\" are being read out of valid range.");
497 continue;
498 }
499
500 UINT8* vertDataPtr = static_cast<UINT8*>(vertexBuffer->lock(GBL_READ_ONLY, deviceIdx, queueIdx));
501
502 UINT8* dest = meshData.getStreamData(streamIdx);
503 memcpy(dest, vertDataPtr, bufferSize);
504
505 vertexBuffer->unlock();
506
507 streamIdx++;
508 }
509 }
510 }
511
512 void Mesh::updateBounds(const MeshData& meshData)
513 {
514 mProperties.mBounds = meshData.calculateBounds();
515
516 // TODO - Sync this to sim-thread possibly?
517 }
518
519 SPtr<Mesh> Mesh::create(UINT32 numVertices, UINT32 numIndices, const SPtr<VertexDataDesc>& vertexDesc,
520 int usage, DrawOperationType drawOp, IndexType indexType, GpuDeviceFlags deviceMask)
521 {
522 MESH_DESC desc;
523 desc.numVertices = numVertices;
524 desc.numIndices = numIndices;
525 desc.vertexDesc = vertexDesc;
526 desc.subMeshes.push_back(SubMesh(0, numIndices, drawOp));
527 desc.usage = usage;
528 desc.indexType = indexType;
529
530 SPtr<Mesh> mesh = bs_shared_ptr<Mesh>(new (bs_alloc<Mesh>()) Mesh(nullptr, desc, deviceMask));
531 mesh->_setThisPtr(mesh);
532 mesh->initialize();
533
534 return mesh;
535 }
536
537 SPtr<Mesh> Mesh::create(const MESH_DESC& desc, GpuDeviceFlags deviceMask)
538 {
539 SPtr<Mesh> mesh = bs_shared_ptr<Mesh>(new (bs_alloc<Mesh>()) Mesh(nullptr, desc, deviceMask));
540
541 mesh->_setThisPtr(mesh);
542 mesh->initialize();
543
544 return mesh;
545 }
546
547 SPtr<Mesh> Mesh::create(const SPtr<MeshData>& initialMeshData, const MESH_DESC& desc, GpuDeviceFlags deviceMask)
548 {
549 MESH_DESC descCopy = desc;
550 descCopy.numVertices = initialMeshData->getNumVertices();
551 descCopy.numIndices = initialMeshData->getNumIndices();
552 descCopy.vertexDesc = initialMeshData->getVertexDesc();
553 descCopy.indexType = initialMeshData->getIndexType();
554
555 SPtr<Mesh> mesh =
556 bs_shared_ptr<Mesh>(new (bs_alloc<Mesh>()) Mesh(initialMeshData, descCopy, deviceMask));
557
558 mesh->_setThisPtr(mesh);
559 mesh->initialize();
560
561 return mesh;
562 }
563
564 SPtr<Mesh> Mesh::create(const SPtr<MeshData>& initialMeshData, int usage, DrawOperationType drawOp,
565 GpuDeviceFlags deviceMask)
566 {
567 MESH_DESC desc;
568 desc.numVertices = initialMeshData->getNumVertices();
569 desc.numIndices = initialMeshData->getNumIndices();
570 desc.vertexDesc = initialMeshData->getVertexDesc();
571 desc.indexType = initialMeshData->getIndexType();
572 desc.subMeshes.push_back(SubMesh(0, initialMeshData->getNumIndices(), drawOp));
573 desc.usage = usage;
574
575 SPtr<Mesh> mesh =
576 bs_shared_ptr<Mesh>(new (bs_alloc<Mesh>()) Mesh(initialMeshData, desc, deviceMask));
577
578 mesh->_setThisPtr(mesh);
579 mesh->initialize();
580
581 return mesh;
582 }
583 }
584}
585