Repository navigation
Conversation
Pin NanoVDB to 71ae97d42, the head of openvdb's develop/nanovdb branch, which adds the batch-capable TopologyBuilder (AcademySoftwareFoundation/openvdb#2336) that fvdb's batched topology construction will move onto (openvdb#785). The pin crosses 23 NanoVDB commits; three of them need changes here, because fvdb builds with -Werror: - AcademySoftwareFoundation/openvdb#2373 deprecates memory resources without operator==. TorchResource is stateless, so any two instances compare equal. Two TorchDeviceResources compare equal when they share a device type: torch's CUDA allocator frees a block on whichever device holds it, and PrivateUse1 memory frees only through the PrivateUse1 allocator. - AcademySoftwareFoundation/openvdb#2378 deprecates cuda::detail::HandleFactory, which already forwarded to GridHandle::adopt and GridHandle::metaData. Call those directly in GridStorage.cu and DeviceGridHandleUtils.cuh; behavior is unchanged. - AcademySoftwareFoundation/openvdb#2207 made the Vec3 constructor constexpr, so nvcc now reports the unused `half` variable in primalVoxelTransformForSizeAndOrigin as error 177-D. Remove it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Swartz <jonathan@jswartz.info>
Replace the sort-based batched topology engine from openvdb#757 with NanoVDB's batch-capable tools::cuda::TopologyBuilder (AcademySoftwareFoundation/openvdb#2336), as planned in openvdb#785. The API (TopologyPassSpec, batchedTopologyStorage) and every call site are unchanged; the emit/sort/dedup engine is removed. Each pass reads every source grid's upper-node bounding boxes back with one host wait and derives each output grid's speculative root tiles the way NanoVDB's RefineGrid, CoarsenGrid and DilateGrid do. One launch over all source leaves fills the builder's densified child masks, through NanoVDB's Refine/Coarsen functors or fvdb's BoxDilate marking; the builder counts nodes and writes every node; a second launch writes the leaf value masks. BoxDilate runs one work item per (source leaf, target leaf offset) pair. The builder densifies about 16.5 MiB of lower-node masks per speculative root tile, so a pass splits the batch into chunks of at most FVDB_TOPOLOGY_MAX_TILES tiles (default 64). Chained passes read their sources across chunks; the last pass's chunks are assembled with assembleGridStorage, or adopted when there is one. The builder is cached per stream so its pinned staging is allocated once. Tests add a fixture spread over many root tiles and a check that a chunked build matches an unchunked one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Swartz <jonathan@jswartz.info>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Swartz <jonathan@jswartz.info>
dispatchBuildPaddedGrid<torch::kCUDA> looped over batch members, ran one PadGrid pass per member per unit of padding, and merged the per-member storages, so wall clock grew linearly with batch size regardless of topology size. For pure positive padding (bmin == 0, no exclude_border: dual_grid() and build_padded_grid(0, k)) the whole batch is now built in k chained batched::TopologyPassSpec::boxDilate(Coord(0), Coord(1)) passes through batched::batchedTopologyStorage. The identity branch (bmin == bmax == 0) and the negative-pad / erosion branch are unchanged; the latter is issue openvdb#775 item 7. The dual-transform fix-up in buildPaddedGrid is untouched. Tests: dual_grid() and build_padded_grid(0, k) for k in (1, 2) over the tricky batches, pinned elementwise against from_ijk builds of the Minkowski sum ijk + {0..k}^3 and as coordinate sets against CPU, plus the dual transform fix-up and a sliced (non-contiguous) batch view. Part of issue openvdb#775 (item 1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Swartz <jonathan@jswartz.info>
swahtz
force-pushed
the
batched/pad-dual
branch
from
October 22, 2026 23:23
a5351d2 to
0e0bb3d
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #775 (item 1). Stacked on #835, which is stacked on #834. Until those merge, this PR also shows their commits. Review only the last commit,
0e0bb3d5.Summary
dispatchBuildPaddedGrid<torch::kCUDA>sits behindGridBatch.dual_grid()and thebuild_padded_gridbinding. It looped over batch members, ran onePadGridpass per member per unit of padding, and merged the per-member storages. So its wall clock grew linearly with batch size.This PR covers pure positive padding:
bmin == 0withexclude_border == False, which isdual_grid()andbuild_padded_grid(0, k). That case now builds the whole batch inkchainedbatched::TopologyPassSpec::boxDilate(Coord(0), Coord(1))passes throughbatched::batchedTopologyStorage. After #835 those passes run on NanoVDB'sTopologyBuilder.Changes
src/fvdb/detail/ops/BuildPaddedGrid.cuincludesBatchedTopologyBuilder.cuh. After thetotalPasses == 0identity branch, whennumNegative == 0 && !excludeBorder, it buildsbmaxboxDilate(0, +1)passes and returnsbatchedTopologyStorage(...).bmaxchained Minkowski sums with{0,1}^3equal the Minkowski sum with{0..bmax}^3, which is whatbuildPaddedGridFromGridCPUcomputes for[0, bmax]^3.exclude_bordererosion branch, which is Batch the remaining per-member CUDA topology builders #775 item 7buildPaddedGridBenchmark
GridBatch.dual_grid(), median of 20 runs after 3 warmups. GPU: RTX PRO 6000 Blackwell Workstation Edition, CUDA 13.4. "main" is main's per-member path. Peak is the extra device memory one call allocates.random128members are 100k random coordinates in [-64, 64)^3. They straddle 0, so each grid spans 8 root tiles, which isTopologyBuilder's worst case.shell64members are sphere shells inside one root tile.Every output matched main's ijk and offsets exactly.
Peak memory grows because
TopologyBuilderallocates about 16.5 MiB of lower-node masks per speculative root tile. Chunking byFVDB_TOPOLOGY_MAX_TILES(#835) caps it near 1.1 GiB plus the output. AcademySoftwareFoundation/openvdb#2392 tracks the upstream fix.Tests
tests/unit/test_batched_topology_builder.pygains these tests:test_dual_grid_matches_expected_and_cpuruns over_tricky_batches()and checks three things:ijk.jdataelementwise against afrom_ijkbuild ofunique(ijk + {0,1}^3)dual_gridtest_padded_grid_positive_matches_expected_and_cpurunsbuild_padded_grid(0, k)fork in (1, 2). It checks againstunique(ijk + {0..k}^3)and CPU, and checks that the origin is unchanged.test_dual_grid_of_sliced_batchrunsdual_gridon a[1:6]slice of a batch with empty members. It checks the result against per-member builds and the expected coordinates.Test plan
./build.sh wheel(CUDA 13.4, sm_120)cd tests && pytest unit -qgives 3130 passed, 85 skippedclang-format-18 --style=file --dry-run,ruff check --select Iandruff format --checkreport nothing on the changed files🤖 Generated with Claude Code