Skip to content

Build batched topology passes on NanoVDB's batch-capable TopologyBuilder - #835

Open
swahtz wants to merge 3 commits into
openvdb:mainfrom
swahtz:feature/topology-builder-engine
Open

swahtz wants to merge 3 commits into
openvdb:mainfrom
swahtz:feature/topology-builder-engine

Conversation

@swahtz

@swahtz swahtz commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Part of #785. Stacked on #834 (NanoVDB pin bump). Until #834 merges, this PR also shows that commit. Review only the commits after 1b79f2fd: 2268751e (the engine) and 737eabca (comment cleanup).

Summary

This PR replaces the sort-based batched topology engine from #757 with NanoVDB's batch-capable tools::cuda::TopologyBuilder (AcademySoftwareFoundation/openvdb#2336). After it lands, TopologyBuilder is fvdb's only batched topology engine. A reference copy of the sort engine lives on swahtz:reference/sort-engine.

The API (TopologyPassSpec, batchedTopologyStorage) and every call site stay the same. Refine, coarsen and box dilation produce the same topology as before, bit for bit.

How a pass runs

  1. Read every source grid's upper-node bounding boxes back to the host, which takes one host wait per pass.
  2. Derive each output grid's speculative root tiles the way NanoVDB's RefineGrid, CoarsenGrid and DilateGrid do.
  3. Fill the builder's dense child masks in one launch over all source leaves. Refine and coarsen use NanoVDB's functors. BoxDilate uses fvdb's own marking, with one work item per (source leaf, target leaf offset) pair.
  4. The builder counts nodes, which takes one host wait per chunk, and then writes every node.
  5. Write the leaf value masks in a second launch.

Chunking

Benchmark

The table compares this PR with main's sort engine. Times are medians over 20 runs. Peak is the extra device memory one call allocates.

  • GPU: RTX PRO 6000 Blackwell Workstation Edition, CUDA 13.4.
  • Ops: conv_transpose_grid(2, 2) for refine, conv_grid(2, 2) for coarsen, conv_grid(3, 1) for dilate.
  • corner64 shifts each shell64 grid so it touches ijk = 0. A k3s1 dilation then reaches ijk = -1, which adds 7 more root tiles per grid.
case op main ms this PR ms ratio main peak MiB this PR peak MiB
shell64 B=1 refine 0.51 0.45 0.89x 0.8 16.6
shell64 B=1 coarsen 0.51 0.45 0.87x 0.4 16.6
shell64 B=1 dilate 0.53 0.46 0.88x 1.7 16.6
shell64 B=16 refine 0.58 0.63 1.10x 12.2 266
shell64 B=16 coarsen 0.58 0.63 1.08x 5.5 265
shell64 B=16 dilate 0.61 0.64 1.05x 24.6 265
shell64 B=64 refine 0.76 1.37 1.81x 47.2 1064
shell64 B=64 coarsen 0.75 1.34 1.79x 21.8 1059
shell64 B=64 dilate 0.82 1.41 1.72x 99.8 1060
shell512 B=4 refine 2.24 0.51 0.23x 138.6 109.9
shell512 B=4 coarsen 0.60 0.44 0.73x 15.8 68.4
shell512 B=4 dilate 6.47 0.67 0.10x 325.0 79.8
scattered B=4 refine 1.23 2.92 2.37x 109.5 829
scattered B=4 coarsen 0.56 0.77 1.38x 20.5 519
scattered B=4 dilate 3.08 1.77 0.57x 188.0 837
corner64 B=1 dilate 0.54 0.50 0.93x 2.6 132
corner64 B=16 dilate 0.61 2.14 3.48x 37.2 1066
corner64 B=64 refine 0.76 1.44 1.89x 45.8 1064
corner64 B=64 coarsen 0.76 1.35 1.78x 21.6 1059
corner64 B=64 dilate 0.84 7.75 9.19x 149.2 1123

Refine and coarsen on corner64 match shell64, so the table leaves those rows out for B=1 and B=16. Every output matched main's ijk and offsets exactly.

  • Large grids get much faster. On 512³ shells, dilation is 10x faster and uses 4x less memory.
  • Batches of small grids get slower and use more memory. The dense per-tile scratch dominates, and chunking caps the memory at about 1.1 GiB.
  • Grids that dilate across ijk = 0 are the worst case. They need 8 tiles per grid, so a B=64 batch runs in 8 chunks.
  • NanoVDB: allocate TopologyBuilder's lower masks only for marked upper-node children (CUDA) AcademySoftwareFoundation/openvdb#2392 tracks the upstream fix, which allocates lower masks only for touched upper children. We accept the small-grid regression until it lands rather than keep a second engine.

Other changes

  • Two comments in BuildCoarseGridFromFine.cu and BuildFineGridFromCoarse.cu now say "two host waits per pass" instead of one stream synchronization.
  • The 4 files that include BatchedTopologyBuilder.cuh now compile in 71-73 s each, down from 113-116 s on main.

Test plan

  • Build: ./build.sh wheel (CUDA 13.4, sm_120)
  • Topology and conv tests: cd tests && pytest -q unit/test_batched_topology_builder.py unit/test_conv_default.py unit/test_conv_ground_truth.py unit/test_conv_semantics.py unit/test_conv_semantics_integration.py unit/test_conv_transpose_default.py unit/test_conv_transpose_ground_truth.py gives 349 passed
  • Full unit suite: cd tests && pytest unit -q gives 3115 passed, 85 skipped
  • New wide_bbox fixture: 3 grids, each with 12 clusters spread over [-12000, 12000)³, so each grid spans many root tiles
  • New test_chunked_build_matches_unchunked: refine x4, coarsen x4, conv k3s1, conv k5s1 and convT k3s2 with FVDB_TOPOLOGY_MAX_TILES=1, compared with the unchunked build
  • clang-format-18 --style=file --dry-run and ruff check --select I / ruff format --check report nothing on the changed files

🤖 Generated with Claude Code

swahtz and others added 2 commits October 23, 2026 00:01
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>
@swahtz
swahtz requested a review from a team as a code owner October 22, 2026 20:04
@swahtz
swahtz requested a review from harrism October 22, 2026 20:04
@swahtz swahtz added enhancement New feature or request NanoVDB Code changes to NanoVDB itself labels Oct 22, 2026
@swahtz
swahtz requested a review from matthewdcong October 22, 2026 20:04
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Jonathan Swartz <jonathan@jswartz.info>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request NanoVDB Code changes to NanoVDB itself

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant