Skip to content

Commit c4767fd

Browse files
swahtzclaude
andcommitted
Adapt TSDF / ESDF stack to current nanovdb and fvdb APIs
Three mechanical adaptations to API drift since the feature commit was written (May 2026), no behavioral change: - fvdb::HDDAVoxelIterator was renamed; the two point-cloud integrators now use fvdb::HDDALeafVoxelIterator, the alias whose contract matches their per-leaf-voxel sidecar indexing (getValue(ijk) - 1). - nanovdb's raw-pointer buildVoxelBlockManager overload was removed in favor of a VoxelBlockManagerHandle API. ComputeESDF keeps its firstLeafID / jumpMap arrays in torch tensors (same pool as all other fvdb allocations) and launches the public BuildVoxelBlockManagerFunctor directly -- the identical launch the handle-based builder performs. The handle itself can't own TorchDeviceBuffers until AcademySoftwareFoundation/openvdb#2274 is fixed: its accessors static_cast from deviceData(), which TorchDeviceBuffer types as uint8_t*. - nanovdb::tools::cuda::VoxelBlockManager is now templated on Log2BlockWidth rather than BlockWidth; the ESDF sweep kernel passes ESDF_BLOCK_WIDTH_LOG2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Jonathan Swartz <jonathan@jswartz.info>
1 parent 1d9ddff commit c4767fd

3 files changed

Lines changed: 46 additions & 35 deletions

File tree

src/fvdb/detail/ops/ComputeESDF.cu

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include <nanovdb/NanoVDB.h>
5050
#include <nanovdb/tools/cuda/VoxelBlockManager.cuh>
5151
#include <nanovdb/util/cuda/DeviceGridTraits.cuh>
52+
#include <nanovdb/util/cuda/Util.h>
5253

5354
#include <c10/cuda/CUDAException.h>
5455
#include <c10/cuda/CUDAGuard.h>
@@ -271,17 +272,17 @@ esdfSweepVBMKernel(nanovdb::NanoGrid<nanovdb::ValueOnIndex> *__restrict__ esdfGr
271272

272273
const uint64_t blockFirstOffset = static_cast<uint64_t>(blockIdx.x) * BW + 1;
273274

274-
nanovdb::tools::cuda::VoxelBlockManager<BW>::template decodeInverseMaps<nanovdb::ValueOnIndex>(
275-
esdfGrid,
276-
firstLeafID[blockIdx.x],
277-
jumpMap + static_cast<uint64_t>(blockIdx.x) * JML,
278-
blockFirstOffset,
279-
smem_leafIndex,
280-
smem_voxelOffset);
275+
nanovdb::tools::cuda::VoxelBlockManager<ESDF_BLOCK_WIDTH_LOG2>::template decodeInverseMaps<
276+
nanovdb::ValueOnIndex>(esdfGrid,
277+
firstLeafID[blockIdx.x],
278+
jumpMap + static_cast<uint64_t>(blockIdx.x) * JML,
279+
blockFirstOffset,
280+
smem_leafIndex,
281+
smem_voxelOffset);
281282
// __syncthreads() is issued inside decodeInverseMaps.
282283

283284
const uint32_t leafID = smem_leafIndex[threadIdx.x];
284-
if (leafID == nanovdb::tools::cuda::VoxelBlockManager<BW>::UnusedLeafIndex) {
285+
if (leafID == nanovdb::tools::cuda::VoxelBlockManager<ESDF_BLOCK_WIDTH_LOG2>::UnusedLeafIndex) {
285286
return;
286287
}
287288
const uint16_t voxOff = smem_voxelOffset[threadIdx.x];
@@ -395,8 +396,6 @@ runEsdfSweepsAndFinalize(const c10::intrusive_ptr<GridBatchData> &esdfGrid,
395396
bool use_vbm,
396397
at::cuda::CUDAStream stream) {
397398
const int64_t esdfVoxels = esdfGrid->totalVoxels();
398-
auto u32Opts = torch::TensorOptions().dtype(torch::kInt32).device(esdfInit.device());
399-
auto u64Opts = torch::TensorOptions().dtype(torch::kInt64).device(esdfInit.device());
400399
auto i32Opts = torch::TensorOptions().dtype(torch::kInt32).device(esdfInit.device());
401400

402401
auto *esdfDeviceGrid = esdfGrid->mGridHdl->deviceGrid<nanovdb::ValueOnIndex>(0);
@@ -433,36 +432,48 @@ runEsdfSweepsAndFinalize(const c10::intrusive_ptr<GridBatchData> &esdfGrid,
433432
const int nBlocks =
434433
static_cast<int>((esdfVoxels + ESDF_BLOCK_WIDTH - 1) / ESDF_BLOCK_WIDTH);
435434

435+
// firstLeafID / jumpMap live in torch tensors so they come from the
436+
// same caching-allocator pool as every other fvdb allocation.
437+
// nanovdb's VoxelBlockManagerHandle can't own them (its accessors
438+
// static_cast from deviceData(), which TorchDeviceBuffer types as
439+
// uint8_t*), so launch the public build functor directly — the same
440+
// launch buildVoxelBlockManager performs on a handle it owns. The
441+
// functor expects a zeroed jumpMap, which torch::zeros provides.
442+
auto u32Opts = torch::TensorOptions().dtype(torch::kInt32).device(esdfInit.device());
443+
auto u64Opts = torch::TensorOptions().dtype(torch::kInt64).device(esdfInit.device());
436444
torch::Tensor firstLeafID = torch::zeros({nBlocks}, u32Opts);
437445
torch::Tensor jumpMap = torch::zeros({nBlocks * ESDF_JUMP_MAP_LENGTH}, u64Opts);
438-
439-
nanovdb::tools::cuda::buildVoxelBlockManager<ESDF_BLOCK_WIDTH_LOG2, 128>(
440-
/*firstOffset=*/1,
441-
/*lastOffset=*/static_cast<uint64_t>(esdfVoxels),
442-
/*nBlocks=*/nBlocks,
443-
/*lowerCount=*/lowerCount,
444-
/*grid=*/esdfDeviceGrid,
445-
/*firstLeafID=*/
446-
reinterpret_cast<uint32_t *>(firstLeafID.data_ptr<int32_t>()),
447-
/*jumpMap=*/
448-
reinterpret_cast<uint64_t *>(jumpMap.data_ptr<int64_t>()),
449-
/*stream=*/stream.stream());
446+
uint32_t *vbmFirstLeafID = reinterpret_cast<uint32_t *>(firstLeafID.data_ptr<int32_t>());
447+
uint64_t *vbmJumpMap = reinterpret_cast<uint64_t *>(jumpMap.data_ptr<int64_t>());
448+
449+
using VbmBuildOp =
450+
nanovdb::tools::cuda::BuildVoxelBlockManagerFunctor<ESDF_BLOCK_WIDTH_LOG2>;
451+
nanovdb::util::cuda::operatorKernel<VbmBuildOp>
452+
<<<dim3(lowerCount, VbmBuildOp::SlicesPerLowerNode, 1),
453+
VbmBuildOp::MaxThreadsPerBlock,
454+
0,
455+
stream.stream()>>>(
456+
/*firstOffset=*/static_cast<uint64_t>(1),
457+
/*lastOffset=*/static_cast<uint64_t>(esdfVoxels),
458+
/*nBlocks=*/nBlocks,
459+
esdfDeviceGrid,
460+
vbmFirstLeafID,
461+
vbmJumpMap);
450462
C10_CUDA_KERNEL_LAUNCH_CHECK();
451463

452464
for (int sweep = 0; sweep < numSweepsMax; ++sweep) {
453465
changedFlag.zero_();
454466
esdfSweepVBMKernel<<<static_cast<unsigned int>(nBlocks),
455467
static_cast<unsigned int>(ESDF_BLOCK_WIDTH),
456468
0,
457-
stream.stream()>>>(
458-
esdfDeviceGrid,
459-
reinterpret_cast<uint32_t *>(firstLeafID.data_ptr<int32_t>()),
460-
reinterpret_cast<uint64_t *>(jumpMap.data_ptr<int64_t>()),
461-
esdfIn->data_ptr<float>(),
462-
esdfOut->data_ptr<float>(),
463-
voxelSizeF,
464-
maxDistF,
465-
changedFlag.data_ptr<int32_t>());
469+
stream.stream()>>>(esdfDeviceGrid,
470+
vbmFirstLeafID,
471+
vbmJumpMap,
472+
esdfIn->data_ptr<float>(),
473+
esdfOut->data_ptr<float>(),
474+
voxelSizeF,
475+
maxDistF,
476+
changedFlag.data_ptr<int32_t>());
466477
C10_CUDA_KERNEL_LAUNCH_CHECK();
467478
std::swap(esdfIn, esdfOut);
468479
// .item() is a sync + host-device copy (~30 us). Each

src/fvdb/detail/ops/IntegrateOccupancyFromPoints.cu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ rayWalkLogOddsKernel(const fvdb::BatchGridAccessor unionGridAcc,
139139
auto acc = grid->getAccessor();
140140
const int64_t voxelOffsetBase = unionGridAcc.voxelOffset(batchIdx);
141141

142-
fvdb::HDDAVoxelIterator<decltype(acc), MathT> it(rayVox, acc);
142+
fvdb::HDDALeafVoxelIterator<decltype(acc), MathT> it(rayVox, acc);
143143
while (it.isValid()) {
144144
const nanovdb::Coord voxIjk = it->first;
145145
++it;

src/fvdb/detail/ops/IntegrateTSDFFromPoints.cu

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ seedAccumulatorsFromBaseGridKernel(const fvdb::BatchGridAccessor baseGridAcc,
136136
// M2: ray-walk kernel.
137137
//
138138
// One thread per input point. Walks active voxels along the ray from
139-
// sensor origin to (point + truncation along ray) via HDDAVoxelIterator
139+
// sensor origin to (point + truncation along ray) via HDDALeafVoxelIterator
140140
// over the union grid. For each active voxel, computes the signed
141141
// distance along the ray from voxel centre to endpoint and decides
142142
// whether to update it:
@@ -217,13 +217,13 @@ rayWalkIntegrateKernel(const fvdb::BatchGridAccessor unionGridAcc,
217217
auto acc = grid->getAccessor();
218218
const int64_t voxelOffsetBase = unionGridAcc.voxelOffset(batchIdx);
219219

220-
// HDDAVoxelIterator walks active voxels of the sparse grid along the
220+
// HDDALeafVoxelIterator walks active leaf voxels of the sparse grid along the
221221
// ray, automatically skipping inactive regions. This is the sparse-
222222
// native "ray-walk" primitive fvdb exposes; the per-ray thread hits
223223
// only voxels that exist in the endpoint-shell topology (see plan.md
224224
// D2 — free-space carving fills topology gaps only within the
225225
// existing union grid, does not extend it).
226-
fvdb::HDDAVoxelIterator<decltype(acc), MathT> it(rayVox, acc);
226+
fvdb::HDDALeafVoxelIterator<decltype(acc), MathT> it(rayVox, acc);
227227
while (it.isValid()) {
228228
const nanovdb::Coord voxIjk = it->first;
229229
++it;

0 commit comments

Comments
 (0)