Skip to content

Commit 14613ce

Browse files
committed
Fork a small set of nanoVDB headers to share PyTorch's allocator
Add an override directory at `src/fvdb/nanovdb_overrides/` that holds modified copies of a few upstream nanoVDB headers. The build prepends this directory to the include search path so that, e.g., `#include <nanovdb/cuda/DeviceBuffer.h>` resolves to the forked copy under `src/fvdb/nanovdb_overrides/`, while the rest of nanoVDB continues to come from the upstream source tree. Forked headers: - `nanovdb/cuda/DeviceBuffer.h` and `nanovdb/cuda/DeviceResource.h` route device-side allocations through `c10::cuda::CUDACachingAllocator` (PyTorch's caching allocator) instead of `cudaMallocAsync` / `cudaFreeAsync`. This keeps transient scratch allocations made by nanoVDB internals (MergeGrids, TopologyBuilder, DilateGrid, ...) inside the same pool that fvdb / PyTorch tensors use. Without this, the two pools fragment each other and large workloads such as multi-frame TSDF integration hit a clean OOM even when the GPU still has free memory in aggregate. - `nanovdb/tools/cuda/TopologyBuilder.cuh` adds an opt-in debug trace gated on the `FVDB_NANOVDB_TRACE_ALLOCS` env var that prints tile count and per-call scratch size from `allocateInternalMaskBuffers`. Useful for diagnosing topology-op memory blowup on large scenes; off by default and otherwise byte-identical with upstream. Each forked header carries a short `FVDB FORK:` banner documenting what diverges from upstream and why, with inline `// FVDB FORK:` tags on every non-trivial change so `git blame` and text search make the delta obvious. `src/fvdb/nanovdb_overrides/README.md` documents the resync and add-an-override procedures. CMake wiring lives in `src/cmake/get_nanovdb.cmake`, with mirror include-path orderings in `src/CMakeLists.txt` (fvdb target) and the top-level `CMakeLists.txt` (_fvdb_cpp pybind module). Signed-off-by: Francis Williams <francis@fwilliams.info>
1 parent 7e8213e commit 14613ce

7 files changed

Lines changed: 1229 additions & 1 deletion

File tree

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ set_target_properties(_fvdb_cpp PROPERTIES
169169
target_include_directories(_fvdb_cpp PUBLIC
170170
${CMAKE_CURRENT_SOURCE_DIR}/src
171171
${TORCH_INCLUDE_DIRS}
172+
# Overrides directory must come BEFORE the upstream nanoVDB source so that
173+
# forked headers (e.g. nanovdb/cuda/DeviceBuffer.h) win the include search.
174+
# Mirror the ordering used in src/CMakeLists.txt for the fvdb target.
175+
# See src/fvdb/nanovdb_overrides/README.md.
176+
${FVDB_NANOVDB_OVERRIDES_DIR}
172177
${nanovdb_SOURCE_DIR}/nanovdb
173178
${NANOVDB_EDITOR_INCLUDE_DIR})
174179
target_link_libraries(_fvdb_cpp PRIVATE

src/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ message(STATUS "fvdb: TORCH_INCLUDE_DIRS: ${TORCH_INCLUDE_DIRS}")
170170

171171
target_include_directories(fvdb PUBLIC
172172
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
173+
# Overrides directory must come BEFORE the upstream nanoVDB source so that
174+
# forked headers (e.g. nanovdb/cuda/DeviceBuffer.h) win the include search.
175+
# See src/fvdb/nanovdb_overrides/README.md.
176+
$<BUILD_INTERFACE:${FVDB_NANOVDB_OVERRIDES_DIR}>
173177
$<BUILD_INTERFACE:${nanovdb_SOURCE_DIR}/nanovdb>
174178
$<BUILD_INTERFACE:${nvtx3_SOURCE_DIR}/include>
175179
$<BUILD_INTERFACE:${NANOVDB_EDITOR_INCLUDE_DIR}>

src/cmake/get_nanovdb.cmake

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,24 @@ CPMAddPackage(
1111

1212
# NanoVDB is header only, so we don't build it. Instead, we just add the headers
1313
# to the include path and create an interface target.
14+
#
15+
# We also prepend an override directory that contains modified copies of a small
16+
# number of upstream nanoVDB headers. The override directory comes first in the
17+
# include search path so that, e.g., `#include <nanovdb/cuda/DeviceBuffer.h>`
18+
# resolves to our forked copy under
19+
# `src/fvdb/nanovdb_overrides/nanovdb/cuda/DeviceBuffer.h`. The rest of nanoVDB
20+
# is still picked up from the upstream source tree, so the patch surface stays
21+
# minimal and easy to resync.
22+
#
23+
# See src/fvdb/nanovdb_overrides/README.md for the rationale and the procedure
24+
# for adding new overrides.
1425
if(nanovdb_ADDED)
26+
get_filename_component(_fvdb_src_dir "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY)
27+
set(FVDB_NANOVDB_OVERRIDES_DIR
28+
"${_fvdb_src_dir}/fvdb/nanovdb_overrides"
29+
CACHE INTERNAL "Directory with fvdb-local overrides for nanoVDB headers")
1530
add_library(nanovdb INTERFACE)
16-
target_include_directories(nanovdb INTERFACE ${nanovdb_SOURCE_DIR}/nanovdb)
31+
target_include_directories(nanovdb INTERFACE
32+
${FVDB_NANOVDB_OVERRIDES_DIR}
33+
${nanovdb_SOURCE_DIR}/nanovdb)
1734
endif()
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# fvdb-local overrides for nanoVDB headers
2+
3+
This directory holds **modified copies of a small number of upstream nanoVDB
4+
headers**. The build prepends this directory to the include search path before
5+
the upstream nanoVDB source tree, so any `#include <nanovdb/...>` that matches
6+
a file in this tree resolves here. Everything else falls through to upstream.
7+
8+
The wiring lives in `src/cmake/get_nanovdb.cmake`.
9+
10+
## Why not just patch the upstream checkout?
11+
12+
We previously tried `CPM`'s `PATCH_COMMAND` to fix a specific bug (nanoVDB
13+
scratch allocations going through `cudaMallocAsync` instead of PyTorch's
14+
caching allocator, which fragments into two pools and OOMs). Patch-based
15+
approaches are fragile: they silently stop applying if upstream moves the
16+
surrounding lines, and they make it hard to edit nanoVDB during development.
17+
18+
Forking the exact headers we care about into this tree gives us full edit
19+
access with a narrow, reviewable patch surface and a clean diff-against-upstream
20+
workflow.
21+
22+
## Layout
23+
24+
The directory structure **mirrors upstream** starting from the `nanovdb/`
25+
include root:
26+
27+
```
28+
nanovdb_overrides/
29+
nanovdb/
30+
cuda/
31+
DeviceBuffer.h # forked: uses c10::cuda::CUDACachingAllocator
32+
... # add more as needed
33+
```
34+
35+
## Adding a new override
36+
37+
1. Copy the upstream header from
38+
`build/<...>/_deps/nanovdb-src/nanovdb/nanovdb/<rel_path>` into
39+
`nanovdb_overrides/nanovdb/<rel_path>`, preserving the relative path.
40+
2. Add a short `FVDB FORK:` banner at the top documenting *what* diverges from
41+
upstream and *why*. Keep the rest of the file byte-identical so a future
42+
resync with upstream is a clean 3-way merge.
43+
3. Every non-trivial code change inside the file should be tagged with an
44+
inline `// FVDB FORK:` comment pointing at the banner, so `git blame` and
45+
text searches make the delta obvious.
46+
47+
## Resyncing with upstream
48+
49+
When bumping the nanoVDB pin in `get_nanovdb.cmake`:
50+
51+
1. `diff` each file in this directory against its upstream counterpart.
52+
2. Port the upstream changes over, keeping the `FVDB FORK` deltas.
53+
3. Rebuild + rerun the fvdb test suite.
54+
55+
## Current overrides
56+
57+
| File | Reason |
58+
|------------------------------|---------------------------------------------------------------------------------------|
59+
| `nanovdb/cuda/DeviceBuffer.h`| Route device allocations through PyTorch's caching allocator (avoids dual-pool OOM). |

0 commit comments

Comments
 (0)