66
77 \authors Efty Sifakis
88
9- \brief Rasterization of triangle mesh into a sparse NanoVDB indexGrid on the device
9+ \brief Rasterization of triangle mesh into a sparse NanoVDB indexGrid on the device.
10+ Optionally an Unsigned Distance Field can be returned in a newly allocated sidecar.
1011
1112 \warning The header file contains cuda device code so be sure
1213 to only include it in .cu files (or other .cuh files)
@@ -50,15 +51,15 @@ struct Triangle {
5051template <typename BuildT>
5152class MeshToGrid
5253{
53- using PointT = nanovdb::Vec3f;
54+ using PointT = nanovdb::Vec3f;
5455 using TriangleIndexT = nanovdb::Vec3i;
5556 using TriangleT = Triangle;
56- using GridT = NanoGrid<BuildT>;
57- using TreeT = NanoTree<BuildT>;
58- using RootT = NanoRoot<BuildT>;
59- using UpperT = NanoUpper<BuildT>;
60- using LowerT = NanoLower<BuildT>;
61- using LeafT = NanoLeaf<BuildT>;
57+ using GridT = NanoGrid<BuildT>;
58+ using TreeT = NanoTree<BuildT>;
59+ using RootT = NanoRoot<BuildT>;
60+ using UpperT = NanoUpper<BuildT>;
61+ using LowerT = NanoLower<BuildT>;
62+ using LeafT = NanoLeaf<BuildT>;
6263
6364public:
6465 struct alignas (16 ) BoxTrianglePair { // sizeof(BoxTrianglePair) = 16B
@@ -259,7 +260,7 @@ MeshToGrid<BuildT>::getHandle(const BufferT &pool)
259260
260261 // Allocate output grid buffer
261262 if (mVerbose ==1 ) mTimer .start (" Allocating grid buffer" );
262- auto gridBuffer = mBuilder .getBuffer (pool, mStream );
263+ auto buffer = mBuilder .getBuffer (pool, mStream );
263264 if (mVerbose ==1 ) mTimer .stop ();
264265
265266 // Initialize grid/tree/root metadata
@@ -281,8 +282,10 @@ MeshToGrid<BuildT>::getHandle(const BufferT &pool)
281282 if (mVerbose ==1 ) mTimer .start (" Rasterizing leaf nodes" );
282283 rasterizeLeafNodes ();
283284 if (mVerbose ==1 ) mTimer .stop ();
284- mXformedTriangles .clear (mStream );
285- mBoxTrianglePairsBuffer .clear (mStream );
285+ if (mBoxTrianglePairCount ) {
286+ mXformedTriangles .clear (mStream );
287+ mBoxTrianglePairsBuffer .clear (mStream );
288+ }
286289
287290 // Update leaf value offsets (prefix sums of per-leaf active voxel counts)
288291 if (mVerbose ==1 ) mTimer .start (" Processing leaf offsets" );
@@ -308,17 +311,20 @@ MeshToGrid<BuildT>::getHandle(const BufferT &pool)
308311 if (mVerbose ==1 ) mTimer .start (" Pruning empty leaves" );
309312 int device = 0 ; cudaGetDevice (&device);
310313 const uint32_t leafCount = mBuilder .data ()->nodeCount [0 ];
311- nanovdb::cuda::DeviceBuffer retainMaskBuffer = nanovdb::cuda::DeviceBuffer::create (
312- uint64_t (leafCount) * sizeof (nanovdb::Mask<3 >), nullptr , device, mStream );
313- cudaCheck (cudaMemsetAsync (retainMaskBuffer.deviceData (), 0xFF ,
314- uint64_t (leafCount) * sizeof (nanovdb::Mask<3 >), mStream ));
315- tools::cuda::PruneGrid<BuildT> pruner (
316- static_cast <const GridT*>(gridBuffer.deviceData ()),
317- static_cast <nanovdb::Mask<3 >*>(retainMaskBuffer.deviceData ()),
318- mStream );
319- auto prunedHandle = pruner.template getHandle <BufferT>(pool);
314+ auto handle = GridHandle<BufferT>(std::move (buffer));
315+ if (leafCount) {
316+ nanovdb::cuda::DeviceBuffer retainMaskBuffer = nanovdb::cuda::DeviceBuffer::create (
317+ uint64_t (leafCount) * sizeof (nanovdb::Mask<3 >), nullptr , device, mStream );
318+ cudaCheck (cudaMemsetAsync (retainMaskBuffer.deviceData (), 0xFF ,
319+ uint64_t (leafCount) * sizeof (nanovdb::Mask<3 >), mStream ));
320+ tools::cuda::PruneGrid<BuildT> pruner (
321+ static_cast <const GridT*>(handle.deviceData ()),
322+ static_cast <nanovdb::Mask<3 >*>(retainMaskBuffer.deviceData ()),
323+ mStream );
324+ handle = pruner.template getHandle <BufferT>(pool);
325+ }
320326 if (mVerbose ==1 ) mTimer .stop ();
321- return prunedHandle ;
327+ return handle ;
322328} // MeshToGrid<BuildT>::getHandle
323329
324330// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -347,9 +353,7 @@ struct TransformTrianglesFunctor
347353template <typename BuildT>
348354void MeshToGrid<BuildT>::transformTriangles()
349355{
350- // TODO: Handle null input case
351- if (mTriangleCount == 0 )
352- throw std::runtime_error (" MeshToGrid currently requires mTriangleCount > 0 (Holistic zero-handling pending)." );
356+ if (mTriangleCount == 0 ) return ;
353357
354358 int device = 0 ;
355359 cudaGetDevice (&device);
@@ -486,9 +490,7 @@ struct ScatterRootTrianglePairsFunctor
486490template <typename BuildT>
487491void MeshToGrid<BuildT>::processRootTrianglePairs()
488492{
489- // TODO: Handle null input case
490- if (mTriangleCount == 0 )
491- throw std::runtime_error (" MeshToGrid currently requires mTriangleCount > 0 (Holistic zero-handling pending)." );
493+ if (mTriangleCount == 0 ) { mBoxTrianglePairCount = 0 ; return ; }
492494
493495 int device = 0 ;
494496 cudaGetDevice (&device);
@@ -867,10 +869,6 @@ void MeshToGrid<BuildT>::buildRasterizedRoot()
867869 // Origins are already in coordToKey-sorted order from enumerateRootTiles(),
868870 // so no further sorting or deduplication is required here.
869871 uint32_t tileCount = static_cast <uint32_t >(mUniqueRootTileCount );
870- std::vector<nanovdb::Coord> hostOrigins (tileCount);
871- cudaCheck (cudaMemcpy (hostOrigins.data (),
872- deviceUniqueRootOrigins (),
873- tileCount * sizeof (nanovdb::Coord), cudaMemcpyDeviceToHost));
874872
875873 // Build the root node on CPU: one tile per unique root origin.
876874 // Only the NanoVDB tile key is set here; child pointers and values are
@@ -880,19 +878,26 @@ void MeshToGrid<BuildT>::buildRasterizedRoot()
880878 auto *rootPtr = static_cast <RootT*>(mBuilder .mProcessedRoot .data ());
881879 rootPtr->mTableSize = tileCount;
882880 rootPtr->mBackground = typename RootT::ValueType{};
883- for (uint32_t t = 0 ; t < tileCount; ++t)
884- *rootPtr->tile (t) = typename RootT::DataType::Tile{RootT::CoordToKey (hostOrigins[t])};
885-
886- mBuilder .mProcessedRoot .deviceUpload (device, mStream , false );
887881
888- mUniqueRootOriginsBuffer .clear (mStream );
882+ if (tileCount) {
883+ std::vector<nanovdb::Coord> hostOrigins (tileCount);
884+ cudaCheck (cudaMemcpy (hostOrigins.data (),
885+ deviceUniqueRootOrigins (),
886+ tileCount * sizeof (nanovdb::Coord), cudaMemcpyDeviceToHost));
887+ for (uint32_t t = 0 ; t < tileCount; ++t)
888+ *rootPtr->tile (t) = typename RootT::DataType::Tile{RootT::CoordToKey (hostOrigins[t])};
889+ mBuilder .mProcessedRoot .deviceUpload (device, mStream , false );
890+ mUniqueRootOriginsBuffer .clear (mStream );
891+ }
889892} // MeshToGrid<BuildT>::buildRasterizedRoot
890893
891894// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
892895
893896template <typename BuildT>
894897void MeshToGrid<BuildT>::rasterizeInternalNodes()
895898{
899+ if (mBoxTrianglePairCount == 0 ) return ;
900+
896901 using RasterizerT = util::rasterization::cuda::RasterizeInternalNodesFunctor<BuildT, BoxTrianglePair>;
897902
898903 auto *dUpperMasks = static_cast <Mask<5 >*>(mBuilder .deviceUpperMasks ());
@@ -949,9 +954,7 @@ void MeshToGrid<BuildT>::rasterizeLeafNodes()
949954template <typename BuildT>
950955void MeshToGrid<BuildT>::processLeafTrianglePairs()
951956{
952- // TODO: Handle null input case
953- if (mTriangleCount == 0 )
954- throw std::runtime_error (" MeshToGrid currently requires mTriangleCount > 0 (Holistic zero-handling pending)." );
957+ if (mBoxTrianglePairCount == 0 ) return ;
955958
956959 int device = 0 ;
957960 cudaGetDevice (&device);
@@ -1132,7 +1135,7 @@ MeshToGrid<BuildT>::getHandleAndUDF(const GridBufferT& gridPool, const SidecarBu
11321135 mBuilder .data ()->nodeCount [0 ]);
11331136
11341137 if (mVerbose ==1 ) mTimer .start (" Allocating grid buffer" );
1135- auto gridBuffer = mBuilder .getBuffer (gridPool, mStream );
1138+ auto buffer = mBuilder .getBuffer (gridPool, mStream );
11361139 if (mVerbose ==1 ) mTimer .stop ();
11371140
11381141 if (mVerbose ==1 ) mTimer .start (" Processing grid/tree/root" );
@@ -1167,15 +1170,18 @@ MeshToGrid<BuildT>::getHandleAndUDF(const GridBufferT& gridPool, const SidecarBu
11671170 if (mVerbose ==1 ) mTimer .start (" Pruning empty leaves" );
11681171 int device = 0 ; cudaGetDevice (&device);
11691172 const uint32_t leafCount = mBuilder .data ()->nodeCount [0 ];
1170- nanovdb::cuda::DeviceBuffer retainMaskBuffer = nanovdb::cuda::DeviceBuffer::create (
1171- uint64_t (leafCount) * sizeof (nanovdb::Mask<3 >), nullptr , device, mStream );
1172- cudaCheck (cudaMemsetAsync (retainMaskBuffer.deviceData (), 0xFF ,
1173- uint64_t (leafCount) * sizeof (nanovdb::Mask<3 >), mStream ));
1174- tools::cuda::PruneGrid<BuildT> pruner (
1175- static_cast <const GridT*>(gridBuffer.deviceData ()),
1176- static_cast <nanovdb::Mask<3 >*>(retainMaskBuffer.deviceData ()),
1177- mStream );
1178- auto handle = pruner.template getHandle <GridBufferT>(gridPool);
1173+ auto handle = GridHandle<GridBufferT>(std::move (buffer));
1174+ if (leafCount) {
1175+ nanovdb::cuda::DeviceBuffer retainMaskBuffer = nanovdb::cuda::DeviceBuffer::create (
1176+ uint64_t (leafCount) * sizeof (nanovdb::Mask<3 >), nullptr , device, mStream );
1177+ cudaCheck (cudaMemsetAsync (retainMaskBuffer.deviceData (), 0xFF ,
1178+ uint64_t (leafCount) * sizeof (nanovdb::Mask<3 >), mStream ));
1179+ tools::cuda::PruneGrid<BuildT> pruner (
1180+ static_cast <const GridT*>(handle.deviceData ()),
1181+ static_cast <nanovdb::Mask<3 >*>(retainMaskBuffer.deviceData ()),
1182+ mStream );
1183+ handle = pruner.template getHandle <GridBufferT>(gridPool);
1184+ }
11791185 if (mVerbose ==1 ) mTimer .stop ();
11801186
11811187 // ---- UDF sidecar ----
@@ -1198,16 +1204,18 @@ MeshToGrid<BuildT>::getHandleAndUDF(const GridBufferT& gridPool, const SidecarBu
11981204 if (mVerbose ==1 ) mTimer .stop ();
11991205
12001206 if (mVerbose ==1 ) mTimer .start (" Computing UDF via leaf/triangle pairs" );
1201- using UDFFunctorT = util::rasterization::cuda::ComputeUDFFunctor<BuildT, BoxTrianglePair, Triangle>;
1202- util::cuda::operatorKernelInstance<UDFFunctorT>
1203- <<<mBoxTrianglePairCount , UDFFunctorT::MaxThreadsPerBlock, 0 , mStream >>> (
1204- UDFFunctorT{ deviceBoxTrianglePairs (), deviceXformedTriangles (),
1205- handle.template deviceGrid <BuildT>(), dSidecar,
1206- mBandWidth * mBandWidth });
1207- cudaCheckError ();
1207+ if (mBoxTrianglePairCount ) {
1208+ using UDFFunctorT = util::rasterization::cuda::ComputeUDFFunctor<BuildT, BoxTrianglePair, Triangle>;
1209+ util::cuda::operatorKernelInstance<UDFFunctorT>
1210+ <<<mBoxTrianglePairCount , UDFFunctorT::MaxThreadsPerBlock, 0 , mStream >>> (
1211+ UDFFunctorT{ deviceBoxTrianglePairs (), deviceXformedTriangles (),
1212+ handle.template deviceGrid <BuildT>(), dSidecar,
1213+ mBandWidth * mBandWidth });
1214+ cudaCheckError ();
1215+ mXformedTriangles .clear (mStream );
1216+ mBoxTrianglePairsBuffer .clear (mStream );
1217+ }
12081218 if (mVerbose ==1 ) mTimer .stop ();
1209- mXformedTriangles .clear (mStream );
1210- mBoxTrianglePairsBuffer .clear (mStream );
12111219
12121220 if (mVerbose ==1 ) mTimer .start (" Finalizing UDF sidecar (sqrt + clamp)" );
12131221 util::cuda::lambdaKernel<<<numBlocks(activeVoxelCount + 1 ), mNumThreads , 0 , mStream >>> (
0 commit comments