Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
816901a
VoxelBlockManager: add resolveWenoLeafPtrs and computeWenoStencil
sifakis Apr 2, 2026
c9906f4
ex_voxelBlockManager_host_cuda: add host/CUDA VBM example
sifakis Apr 2, 2026
372b806
VBMImplementationKnowledge: distill decodeInverseMaps design philosophy
sifakis Apr 2, 2026
9a31aed
ex_voxelBlockManager_host_cuda: remove DecodeInverseMapsCPUPlan.md
sifakis Apr 2, 2026
aae9b80
ex_voxelBlockManager_host_cuda: add StencilGather planning doc
sifakis Apr 2, 2026
da91919
StencilGather: add stencil type interface and kernel/output design
sifakis Apr 2, 2026
57dec68
simd_test: add liftToSimd vectorization experiment and investigation …
sifakis Apr 2, 2026
fb395f1
simd_test: replace liftToSimd with generic-T Simd<T,W> approach
sifakis Apr 3, 2026
b45ebad
simd_test/Simd.h: add std::experimental::simd backend for C++26
sifakis Apr 3, 2026
693fd2b
simd_test: update investigation notes with dual-backend Simd.h findings
sifakis Apr 3, 2026
c4d1631
simd_test: Generic-T kernel hierarchy + Simd.h refinements
sifakis Apr 3, 2026
42e54cc
StencilGather.md: document CPU batch leaf-ptr design (probedMask, bat…
sifakis Apr 11, 2026
536467e
ex_stencil_gather_cpu: prototype for CPU SIMD stencil gather Phase 1
sifakis Apr 11, 2026
f5041df
ex_stencil_gather_cpu: gather-site sentinel + AVX2 carry trick
sifakis Apr 13, 2026
bf115e2
BatchAccessor.md: design for SIMD batch leaf-neighborhood cache
sifakis Apr 13, 2026
8db0c52
BatchAccessor: SIMD batch leaf-neighborhood cache (Phase 1)
sifakis Apr 15, 2026
dce5240
Simd.h + BatchAccessor: scalar_traits, uniform shifts, SWAR cleanup
sifakis Apr 15, 2026
515be21
Simd.h + BatchAccessor + Util.h: 2-arg where, util::reduce, countTrai…
sifakis Apr 16, 2026
b15d2a0
BatchAccessor: SIMD gather chain for cachedGetValue ingredient fetch
sifakis Apr 16, 2026
6a4711b
BatchAccessor.md: document prefetch SIMD structure + assembly findings
sifakis Apr 16, 2026
d511842
BatchAccessor: SWAR constant naming, ASCII cleanup, explicit parentheses
sifakis Apr 17, 2026
87c3e1b
BatchAccessor/Simd: scalar overloads, LeafIDVecT/LeafDataVecT class a…
sifakis Apr 17, 2026
14c9db7
BatchAccessor: complete SIMD cachedGetValue pipeline (Steps 1-8)
sifakis Apr 17, 2026
16b43ac
BatchAccessor.md: document GCC/Clang codegen findings for cachedGetValue
sifakis Apr 17, 2026
1c93d78
BatchAccessor.md: document popcount alternatives and scalar popcnt tr…
sifakis Apr 17, 2026
0b7524e
StencilAccessor: SIMD batch stencil-index gatherer for CPU WENO5 Phase 1
sifakis Apr 18, 2026
c605ec2
refactor: move Simd.h, BatchAccessor.h, StencilAccessor.h to nanovdb/…
sifakis Apr 18, 2026
432bec4
refactor: move BatchAccessor.md, StencilAccessor.md, HaloStencilAcces…
sifakis Apr 18, 2026
8db0218
cleanup: remove simd_test/ scratchpad directory
sifakis Apr 18, 2026
2610a11
cleanup: remove prefix-count sanity check from vbm_host_cuda_kernels.cu
sifakis Apr 18, 2026
6fce798
StencilAccessor: end-to-end perf analysis + Legacy scalar oracle
sifakis Apr 18, 2026
a05dce4
stencil_gather_cpu: add Legacy cost decomposition (framing / cache+le…
sifakis Apr 18, 2026
c90db49
StencilAccessor/BatchAccessor: hybrid SIMD -> scalar-tail cachedGetValue
sifakis Apr 18, 2026
c4b2e90
StencilAccessor/BatchAccessor: hybrid SIMD -> scalar-tail cachedGetVa…
sifakis Apr 19, 2026
8b37dbd
BatchAccessor/StencilAccessor: document perf investigation and revise…
sifakis Apr 19, 2026
8a24ddf
NanoVDB: add LeafData<ValueOnIndex>::getValueBranchless + use leaf-on…
sifakis Apr 19, 2026
d06b094
ex_narrowband_stencil_cpu: CPU stencil-gather benchmark on real .vdb …
sifakis Apr 19, 2026
1847b1a
BatchAccessor/StencilAccessor: document getValueBranchless, narrow-ba…
sifakis Apr 19, 2026
6459695
NanoVDB: LeafData<ValueOnIndex>::getValue branchless by default
sifakis Apr 19, 2026
24c2de7
ex_*stencil_cpu: prune diagnostic passes, keep just the two shipped p…
sifakis Apr 20, 2026
f3b53ed
ex_*stencil_cpu: add legacy-transposed pass; BatchAccessor: document §8l
sifakis Apr 20, 2026
cc073c1
Weno5Stencil: add center tap (19 total); BatchAccessor: document §11 …
sifakis Apr 20, 2026
5a92059
ex_*stencil_cpu: batch-by-batch loops in legacy/framing/transposed
sifakis Apr 20, 2026
110d852
ex_narrowband_stencil_cpu: sidecar-legacy/stencil/transposed passes
sifakis Apr 20, 2026
a6b0871
WenoStencil<W>: single-source scalar/SIMD stencil + out-of-band extra…
sifakis Apr 20, 2026
0f8fccd
WenoStencil.md + BatchAccessor.md §11.6: Stage 1-3 documentation
sifakis Apr 20, 2026
3c33d85
WenoStencil<W>: Simd-typed storage + normSqGrad; caller-owned fill bu…
sifakis Apr 21, 2026
c00fe65
ex_narrowband_stencil_cpu: add sidecar-stencil-normsqgrad full-pipeli…
sifakis Apr 21, 2026
1aa985b
Simd + WenoStencil: [[gnu::always_inline]] on tiny wrappers + Phase-3…
sifakis Apr 21, 2026
8623483
ex_weno_nanovdb_cpu: full Phase-2+3 pipeline with scalar-reference va…
sifakis Apr 21, 2026
3ffe1e7
ex_weno_nanovdb_cpu: remove stray emacs lock symlink
sifakis Apr 21, 2026
2d8d94f
WenoStencil: self-contained Taps tuple, no more Weno5Stencil policy dep
sifakis Apr 23, 2026
79004aa
nanovdb: cleanup — remove hybrid StencilAccessor/BatchAccessor stack
sifakis Apr 23, 2026
412cd4b
nanovdb: drop HaloStencilAccessor design doc and cross-references
sifakis Apr 27, 2026
4565d48
nanovdb/util/Simd.h: remove unused gather and popcount
sifakis Apr 27, 2026
bc0c853
nanovdb/util/Simd.h: cleanup pass
sifakis Apr 28, 2026
9b2ef25
WenoStencil: ValueType-templated, generic-T WENO5/Godunov, rename tap…
sifakis Apr 28, 2026
7cc54ab
WenoStencil: extrapolate convention switch; add Sqrt(Simd<T,W>)
sifakis Apr 28, 2026
158e3df
WenoStencil: absorb gather as static gatherIndices(); drop LegacySten…
sifakis Apr 28, 2026
0c8b582
nanovdb/util/ForEach.h: drop hardware_concurrency()>>1 in std::thread…
sifakis Apr 29, 2026
2b75d97
tools: introduce host-side MergeGrids + TopologyBuilder; add ex_merge…
sifakis May 20, 2026
85c1098
tools: document host-side topology-port plan
sifakis May 26, 2026
0179f8c
tools::MergeGrids: default output buffer to UnifiedBuffer
sifakis May 27, 2026
003b0a5
tools::TopologyBuilder: route device-only scratch through ScratchBuff…
sifakis May 27, 2026
858afe7
tools::TopologyBuilder::countNodes: collapse onto offsets-only buffer…
sifakis May 27, 2026
67e8d5c
util/Morphology.h: add host EnumerateNodes; route countNodes through it
sifakis May 27, 2026
729804d
tools::TopologyBuilder::processUpperNodes: inline BuildUpperNodesFunc…
sifakis May 28, 2026
4438dfe
util/Morphology.h: add host ProcessLowerNodes; route processLowerNode…
sifakis Jun 10, 2026
fe80448
util: add host-callable atomicMin/atomicMax; port TopologyBuilder::pr…
sifakis Jun 10, 2026
28e0d65
tools::TopologyBuilder::postProcessGridTree: run PostProcessGridTreeF…
sifakis Jun 10, 2026
ed109dd
tools::TopologyBuilder::processLeafOffsets: port to host forEach + ut…
sifakis Jun 10, 2026
b7f41d2
util/Morphology.h: add host MergeInternalNodes; route MergeGrids::mer…
sifakis Jun 10, 2026
a73eb2b
util/Morphology.h: add host MergeLeafNodes; route MergeGrids::mergeLe…
sifakis Jun 10, 2026
7e5f965
tools::MergeGrids::processGridTreeRoot: run on host (memcpy + BuildGr…
sifakis Jun 10, 2026
96415ce
docs: record MergeGrids host-port completion state in plan + CLAUDE.md
sifakis Jun 10, 2026
333c942
tools::TopologyBuilder::postProcessGridTree: use host updateChecksum
sifakis Jun 11, 2026
a5543f8
docs: record MergeGrids perf findings (§7.8) and mark updateChecksum …
sifakis Jun 11, 2026
6e7401e
util/MorphologyHelpers.h: add host shuffleDown/shuffleUp + host MaskS…
sifakis Jun 11, 2026
180b081
tools::DilateGrid: host DilateInternalNodes + transitional host opera…
sifakis Jun 11, 2026
0110561
util/Morphology.h: add host DilateLeafNodes; route DilateGrid::dilate…
sifakis Jun 11, 2026
c199fcb
util/cuda/Morphology.cuh: replace UB reinterpret-view stencil in Dila…
sifakis Jun 11, 2026
410dbb6
docs: TODO to share thread-centric leaf-dilation body via a __hostdev…
sifakis Jun 11, 2026
1494c80
docs: record DilateGrid progress (§7.7) and mark Phase 4.8 in progress
sifakis Jun 11, 2026
ce25500
tools::DilateGrid::processGridTreeRoot: run on host (memcpy + BuildGr…
sifakis Jun 12, 2026
38fa025
tools::DilateGrid::dilateRoot: read source root/uppers from managed g…
sifakis Jun 12, 2026
517f94b
tools::TopologyBuilder: drop __hostdev__ from the host-only detail fu…
sifakis Jun 12, 2026
a9ed2d7
docs: DilateGrid now kernel-free (§7.7); record processGridTreeRoot/d…
sifakis Jun 12, 2026
e86cdee
tools::PruneGrid: host port + dilate->prune round-trip in ex_dilate_n…
sifakis Jun 12, 2026
4af7084
ex_dilate_nanovdb_cpu: default to NN_FACE
sifakis Jun 12, 2026
d59b32b
docs: record PruneGrid host port (§7.7) and dilate->prune round-trip
sifakis Jun 12, 2026
db14618
tools::TopologyBuilder: migrate scratch buffers UnifiedBuffer -> Host…
sifakis Jun 12, 2026
56f480b
tools: migrate mProcessedRoot DeviceBuffer -> HostBuffer (cleanup ste…
sifakis Jun 12, 2026
82f71cd
tools::TopologyBuilder: drop Data::d_upperOffsets; read mUpperOffsets…
sifakis Jun 12, 2026
5fee27c
tools::TopologyBuilder: mData DeviceBuffer -> plain Data member (clea…
sifakis Jun 12, 2026
4db30f7
docs: record host-only cleanup steps 1-3 (§7.3)
sifakis Jun 12, 2026
8d8993e
tools: MergeGrids runs on HostBuffer end-to-end; getBuffer made backe…
sifakis Jun 12, 2026
7931906
util/Injection.h: add host injectGridMask; use it in ex_dilate (valid…
sifakis Jun 12, 2026
7eb3a50
tools: DilateGrid + PruneGrid run on HostBuffer end-to-end (step 4, d…
sifakis Jun 12, 2026
dae9740
tools: swap util::cuda::Timer -> host util::Timer in the operators (a…
sifakis Jun 12, 2026
d5a962b
tools: rip out the cudaStream_t plumbing (no async work remains)
sifakis Jun 12, 2026
4c93f7c
docs: record HostBuffer/stream cleanup (§7.3, §7.1); re-scope remaini…
sifakis Jun 12, 2026
98cc34a
docs(TopologyCpuPortPlan): record three-way OpenVDB/host/CUDA benchmark
sifakis Jun 12, 2026
a4beab1
util/cuda/Morphology.cuh: fix prunedRoot/prunedTile copy-paste misnomer
sifakis Jun 15, 2026
0bc2bce
tools: host port of CoarsenGrid (2x topological coarsening)
sifakis Jun 15, 2026
02411c0
tools: host port of RefineGrid (2x topological refinement)
sifakis Jun 15, 2026
c684bd3
tools: purge dead CUDA code and includes from the host topology headers
sifakis Jun 15, 2026
9728a1d
examples/tools: flip topology _cpu examples to pure C++ (.cu -> .cpp)
sifakis Jun 15, 2026
6c87781
examples: fix stale warm-loop timer labels in merge/refine CUDA examples
sifakis Jun 15, 2026
cb1d516
util/Timer.h: report fractional milliseconds (match util::cuda::Timer)
sifakis Jun 15, 2026
988e806
util/Injection.h: add host injectGridData (sidecar value injection)
sifakis Jun 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Build System

OpenVDB uses CMake (minimum 3.24) and requires out-of-source builds.

**Minimal core build:**
```bash
mkdir build && cd build
cmake .. -DOPENVDB_BUILD_UNITTESTS=ON
make -j$(nproc)
```

**Using the CI build script (recommended for full builds):**
```bash
./ci/build.sh --build-type=Release \
--components="core,test" \
--cargs="-DOPENVDB_ABI_VERSION_NUMBER=13"
```

Component flags for `--components`: `core`, `python`, `bin`, `view`, `render`, `test`, `hou`, `axcore`, `nano`, `nanotest`

**Key CMake options:**
| Option | Default | Description |
|--------|---------|-------------|
| `OPENVDB_BUILD_CORE` | ON | Core library |
| `OPENVDB_BUILD_UNITTESTS` | OFF | Unit tests |
| `OPENVDB_BUILD_NANOVDB` | OFF | NanoVDB |
| `OPENVDB_BUILD_AX` | OFF | OpenVDB AX |
| `OPENVDB_ABI_VERSION_NUMBER` | 13 | ABI version (6–13) |
| `OPENVDB_CXX_STRICT` | OFF | Strict warnings |
| `NANOVDB_USE_CUDA` | OFF | CUDA support for NanoVDB |

## Running Tests

```bash
cd build
ctest -V # all tests
ctest -V -R TestGrid # single test by name
```

To build only specific unit tests (avoids full rebuild):
```bash
cmake .. -DOPENVDB_TESTS="Grid;Tree;LeafNode"
```

Tests use Google Test (minimum 1.10). Test sources live in:
- `openvdb/openvdb/unittest/` — core library tests (`TestFoo.cc` pattern)
- `nanovdb/nanovdb/unittest/` — NanoVDB tests

## Code Architecture

### Repository Layout

```
openvdb/openvdb/ Core OpenVDB library
tree/ Tree node hierarchy (RootNode, InternalNode, LeafNode)
tools/ Algorithm implementations (level sets, CSG, smoothing, etc.)
math/ Math primitives (Vec, Mat, Quat, Transform, BBox)
io/ VDB file format I/O
points/ Point data grids
python/ Python bindings (nanobind)
unittest/ Unit tests

nanovdb/nanovdb/ NanoVDB — compact, GPU-friendly VDB subset
tools/ CPU algorithms
tools/cuda/ CUDA kernels
examples/ Standalone example programs

openvdb_ax/openvdb_ax/ OpenVDB AX — JIT expression language for VDB operations
ast/ Abstract syntax tree
codegen/ LLVM code generation
compiler/ Compilation pipeline

openvdb_cmd/ Command-line tools (vdb_print, vdb_lod, vdb_tool, vdb_view, vdb_render)
openvdb_houdini/ Houdini plugin
openvdb_maya/ Maya plugin
cmake/ CMake find-modules and configuration
ci/ CI build/install scripts
```

### Core Data Model

OpenVDB uses a **B+tree-like hierarchical sparse data structure**:
- `Grid<TreeType>` — top-level container with transform and metadata
- `Tree` — composed of `RootNode → InternalNode(s) → LeafNode`
- Leaf nodes are 8×8×8 voxel blocks; internal nodes are 16³ and 32³ by default
- `ValueAccessor` caches tree traversal paths for repeated access patterns
- `GridBase` / `TypedGrid` provide the runtime-polymorphic/compile-time-typed split

### NanoVDB vs OpenVDB

NanoVDB is a read-optimized, single-allocation, GPU-portable subset of OpenVDB. It cannot be modified after construction. The `nanovdb/tools/CreateNanoGrid.h` and adjacent files handle conversion from OpenVDB grids to NanoVDB grids.

### Topology Operators — CPU Port (this branch)

The branch `vbm-cpu-port` back-ports the five CUDA-only NanoVDB topology operators (`DilateGrid`, `MergeGrids`, `PruneGrid`, `RefineGrid`, `CoarsenGrid`) from `nanovdb/tools/cuda/*.cuh` (namespace `nanovdb::tools::cuda`) to host-only headers at `nanovdb/tools/*.h` (namespace `nanovdb::tools`). The design plan is in `nanovdb/nanovdb/tools/TopologyCpuPortPlan.md`.

**Key files:**
- `nanovdb/tools/TopologyBuilder.h` — shared base class (in-progress port; still has CUDA includes during transition)
- `nanovdb/tools/MergeGrids.h` — first operator being ported
- `nanovdb/util/Morphology.h` — host-side morphology functors (parallel to `util/cuda/Morphology.cuh`)
- `nanovdb/util/PrefixSum.h` — host `inclusiveScan` used in place of CUB scans
- `nanovdb/tools/VoxelBlockManager.h` — VBM acceleration structure for active-voxel SIMT/SIMD access

**CUDA → host transition pattern:**
- Device-only scratch buffers use `ScratchBufferT = UnifiedBuffer` (transitional) → will become `HostBuffer`
- `lambdaKernel` launches → `util::forEach` calls
- CUB inclusive scans → `util::inclusiveScan`
- Morphology functors move from `util/cuda/Morphology.cuh` (`__device__`) to `util/Morphology.h` (host `inline`)
- CUDA stays on (`NANOVDB_USE_CUDA=ON`) throughout; host-only completion is signaled by renaming `.cu` → `.cpp` in examples

**Build config for this branch** (build dir: `nanovdb/nanovdb/build/`):
```bash
cd nanovdb/nanovdb/build
cmake -DCMAKE_BUILD_TYPE=Release \
-DNANOVDB_BUILD_EXAMPLES=ON \
-DNANOVDB_USE_CUDA=ON \
-DNANOVDB_USE_OPENVDB=ON \
-DNANOVDB_USE_TBB=ON \
-DCMAKE_CUDA_ARCHITECTURES=120 \
-DCMAKE_PREFIX_PATH=~/local \
-DOpenVDB_ROOT=~/local \
..
make -j$(nproc)
```

OpenVDB is installed at `~/local` (not `/usr/local`). The project's `cmake/FindOpenVDB.cmake` requires `-DOpenVDB_ROOT=~/local` explicitly — `CMAKE_PREFIX_PATH` alone is not enough because the finder is invoked from the repo's `cmake/` directory, not the installed `OpenVDB/` directory, so it doesn't auto-detect the prefix. Always clear `CMakeCache.txt` before re-running cmake if you change this path, as NOTFOUND results are cached.

Always use a specific `-DCMAKE_CUDA_ARCHITECTURES=` value (here `120` for Blackwell). The default `=75` (Turing) causes silent kernel failures on non-sm_75 hardware, surfacing as misleading `cudaErrorInvalidDevice`.

**Validation:**
```bash
./ex_merge_nanovdb_cpu /path/to/dragon.vdb /path/to/torus.vdb 3
./ex_dilate_nanovdb_cpu /path/to/dragon.vdb 3
./ex_refine_nanovdb_cpu /path/to/dragon.vdb 3
./ex_coarsen_nanovdb_cpu /path/to/dragon.vdb 3
```
Each should print `Result of <Op> check out CORRECT against reference`.

**Only `OnIndexGrid` buildtypes are supported** (`static_assert(BuildTraits<BuildT>::is_onindex)`). Grids with tile values at any level cause the operator to throw.

### OpenVDB AX

AX compiles a domain-specific expression language to LLVM IR for execution over OpenVDB volumes and point grids. The pipeline is: source string → AST (`ast/`) → typed analysis → LLVM codegen (`codegen/`) → JIT execution via `compiler/`.

## C++ Standard and ABI

- Requires C++17 minimum
- ABI version is set at compile time via `OPENVDB_ABI_VERSION_NUMBER`; the current version is 13
- Headers are in `openvdb/openvdb/` and installed to `include/openvdb/`

## Dependencies

Core: Boost ≥ 1.82, TBB ≥ 2020.3, Blosc ≥ 1.17, OpenEXR/Imath ≥ 3.2, zlib ≥ 1.2.7
Tests: GTest ≥ 1.10
Python bindings: Python ≥ 3.11, nanobind ≥ 2.5.0
NanoVDB GPU: CUDA toolkit
AX: LLVM

On Linux, ASWF Docker containers (used by CI) bundle most dependencies. See `ci/install_macos.sh` and `ci/install_windows.ps1` for platform-specific setup.

## Coding Standards

Follow the style guide at https://www.openvdb.org/documentation/doxygen/codingStyle.html. Contributions require a Developer Certificate of Origin sign-off (`git commit -s`) and a CLA on file — see CONTRIBUTING.md.
15 changes: 13 additions & 2 deletions nanovdb/nanovdb/NanoVDB.h
Original file line number Diff line number Diff line change
Expand Up @@ -4136,15 +4136,26 @@ struct NANOVDB_ALIGN(NANOVDB_DATA_ALIGNMENT) LeafData<ValueOnIndex, CoordT, Mask
__hostdev__ uint64_t getMax() const { return this->hasStats() ? this->lastOffset() + 2u : 0u; }
__hostdev__ uint64_t getAvg() const { return this->hasStats() ? this->lastOffset() + 3u : 0u; }
__hostdev__ uint64_t getDev() const { return this->hasStats() ? this->lastOffset() + 4u : 0u; }
// Default branchless; define NANOVDB_USE_BRANCHY_GETVALUE to restore the
// pre-2026 branchy form.
__hostdev__ uint64_t getValue(uint32_t i) const
{
//return mValueMask.isOn(i) ? mOffset + mValueMask.countOn(i) : 0u;// for debugging
#ifdef NANOVDB_USE_BRANCHY_GETVALUE
uint32_t n = i >> 6;
const uint64_t w = BaseT::mValueMask.words()[n], mask = uint64_t(1) << (i & 63u);
if (!(w & mask)) return uint64_t(0); // if i'th value is inactive return offset to background value
if (!(w & mask)) return uint64_t(0);
uint64_t sum = BaseT::mOffset + util::countOn(w & (mask - 1u));
if (n--) sum += BaseT::mPrefixSum >> (9u * n) & 511u;
return sum;
#else
const uint32_t n = i >> 6;
const uint64_t w = BaseT::mValueMask.words()[n];
const uint64_t bit = uint64_t(1) << (i & 63u);
const uint64_t prefix = n == 0u ? uint64_t(0)
: (BaseT::mPrefixSum >> (9u * (n - 1u))) & 511u;
const uint64_t sum = BaseT::mOffset + prefix + util::countOn(w & (bit - 1u));
return ((w & bit) ? ~uint64_t(0) : uint64_t(0)) & sum;
#endif
}
}; // LeafData<ValueOnIndex>

Expand Down
24 changes: 24 additions & 0 deletions nanovdb/nanovdb/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,30 @@ nanovdb_example(NAME "ex_merge_nanovdb_cuda" OPENVDB)
nanovdb_example(NAME "ex_refine_nanovdb_cuda" OPENVDB)
nanovdb_example(NAME "ex_coarsen_nanovdb_cuda" OPENVDB)

# Host-side port of topology operators (in progress).
# These examples start identical to their _cuda siblings (using nanovdb::tools::
# instead of nanovdb::tools::cuda::) and progressively shed CUDA dependencies.
nanovdb_example(NAME "ex_merge_nanovdb_cpu" OPENVDB)
nanovdb_example(NAME "ex_dilate_nanovdb_cpu" OPENVDB)
nanovdb_example(NAME "ex_coarsen_nanovdb_cpu" OPENVDB)
nanovdb_example(NAME "ex_refine_nanovdb_cpu" OPENVDB)

nanovdb_example(NAME "ex_voxelBlockManager_host_cuda")
if(TARGET ex_voxelBlockManager_host_cuda)
target_compile_options(ex_voxelBlockManager_host_cuda PRIVATE
$<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=-mavx2,-fopenmp-simd>
$<$<COMPILE_LANGUAGE:CXX>:-mavx2 -fopenmp-simd>)
endif()

# End-to-end CPU WENO5 norm-square-gradient on a narrow-band level set,
# with a scalar reference for correctness validation.
nanovdb_example(NAME "ex_weno_nanovdb_cpu" OPENVDB)
if(TARGET ex_weno_nanovdb_cpu)
target_compile_options(ex_weno_nanovdb_cpu PRIVATE -march=native -fopenmp-simd)
target_include_directories(ex_weno_nanovdb_cpu PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../../..)
endif()

if(CUDAToolkit_FOUND)
nanovdb_example(NAME "ex_make_mgpu_nanovdb") # requires cuRAND
target_link_libraries(ex_make_mgpu_nanovdb PRIVATE CUDA::curand)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Copyright Contributors to the OpenVDB Project
// SPDX-License-Identifier: Apache-2.0

// the following files are from OpenVDB
#include <openvdb/tree/LeafManager.h>
#include <openvdb/util/CpuTimer.h>

// the following files are from NanoVDB
#include <nanovdb/NanoVDB.h>
#include <nanovdb/tools/CreateNanoGrid.h>

template<class CoordT>
inline CoordT
coarsenCoord(const CoordT& coord)
{
auto coarsenComponent = [](const typename CoordT::ValueType n) {return (n>=0) ? (n>>1) : -((-n+1)>>1);};
CoordT result;
result[0] = coarsenComponent(coord[0]);
result[1] = coarsenComponent(coord[1]);
result[2] = coarsenComponent(coord[2]);
return result;
}

template<typename BuildT>
void mainCoarsenGrid(
nanovdb::NanoGrid<BuildT> *srcGrid, // original (un-coarsened) grid
nanovdb::NanoGrid<BuildT> *indexGridCoarsened,
uint32_t benchmark_iters
);

/// @brief This example depends on OpenVDB, NanoVDB, and CUDA
int main(int argc, char *argv[])
{
using GridT = openvdb::FloatGrid;
using BuildT = nanovdb::ValueOnIndex;

openvdb::util::CpuTimer cpuTimer;
const bool printGridDiagnostics = true;

try {
if (argc<2) OPENVDB_THROW(openvdb::ValueError, "usage: "+std::string(argv[0])+" input.vdb [<iterations>]\n");
int benchmark_iters = 10;
if (argc > 2) sscanf(argv[2], "%d", &benchmark_iters);

// Read the initial level set from file
cpuTimer.start("Read input VDB file");
openvdb::initialize();
openvdb::io::File inFile(argv[1]);
inFile.open(false); // disable delayed loading
auto baseGrids = inFile.getGrids();
inFile.close();
auto grid = openvdb::gridPtrCast<GridT>(baseGrids->at(0));
if (!grid) OPENVDB_THROW(openvdb::ValueError, "First grid is not a FloatGrid\n");
cpuTimer.stop();

// Convert to indexGrid (original, un-coarsened)
cpuTimer.start("Converting openVDB input to indexGrid (original version)");
auto handleOriginal = nanovdb::tools::openToIndexVDB<BuildT, nanovdb::HostBuffer>(
grid,
0u, // Don't copy data channel
false, // No stats
false, // No tiles
1 // Verbose mode
);
auto *indexGridOriginal = handleOriginal.grid<BuildT>();
cpuTimer.stop();

if (printGridDiagnostics) {
std::cout << "============ Original Grid ===========" << std::endl;
std::cout << "Allocated values [valueCount()] : " << indexGridOriginal->valueCount() << std::endl;
std::cout << "Active voxels [activeVoxelCount()] : " << indexGridOriginal->activeVoxelCount() << std::endl;
auto minCorner = indexGridOriginal->indexBBox().min(), maxCorner = indexGridOriginal->indexBBox().max();
std::cout << "Index-space bounding box : [" << minCorner.x() << "," << minCorner.y() << "," << minCorner.z()
<< "] -> [" << maxCorner.x() << "," << maxCorner.y() << "," << maxCorner.z() << "]" << std::endl;
std::cout << "Leaf nodes : " << indexGridOriginal->tree().nodeCount(0) << std::endl;
std::cout << "Lower internal nodes : " << indexGridOriginal->tree().nodeCount(1) << std::endl;
std::cout << "Upper internal nodes : " << indexGridOriginal->tree().nodeCount(2) << std::endl;
std::cout << "Leaf-level occupancy : "
<< 100.f * (float)(indexGridOriginal->activeVoxelCount())/(float)(indexGridOriginal->tree().nodeCount(0) * 512)
<< "%" << std::endl;
std::cout << "Memory usage : " << indexGridOriginal->gridSize() << " bytes" << std::endl;
}

// Coarsening (CPU/OpenVDB version), used as the correctness reference
cpuTimer.start("Coarsening OpenVDB (on CPU)");
using TreeT = GridT::TreeType;
using LeafManagerT = openvdb::tree::LeafManager<const TreeT>;
LeafManagerT leafMgr(grid->tree());
auto coarsenedGrid = openvdb::FloatGrid::create(grid->background());
coarsenedGrid->setTransform(grid->transform().copy());
coarsenedGrid->setName(grid->getName());
auto dstAcc = coarsenedGrid->getAccessor();
for (std::size_t leafID = 0; leafID < leafMgr.leafCount(); ++leafID) {
const auto& srcLeaf = leafMgr.leaf(leafID);
for (auto iter = srcLeaf.cbeginValueOn(); iter; ++iter) {
const auto dstCoord = coarsenCoord(iter.getCoord());
if (!dstAcc.isValueOn(dstCoord))
dstAcc.setValue(dstCoord, iter.getValue());
}
}
cpuTimer.stop();

// Convert to indexGrid (coarsened reference)
cpuTimer.start("Converting openVDB input to indexGrid (coarsened version)");
auto handleCoarsened = nanovdb::tools::openToIndexVDB<BuildT, nanovdb::HostBuffer>(
coarsenedGrid,
0u, // Don't copy data channel
false, // No stats
false, // No tiles
1 // Verbose mode
);
auto *indexGridCoarsened = handleCoarsened.grid<BuildT>();
cpuTimer.stop();

if (printGridDiagnostics) {
std::cout << "=========== Coarsened Grid ===========" << std::endl;
std::cout << "Allocated values [valueCount()] : " << indexGridCoarsened->valueCount() << std::endl;
std::cout << "Active voxels [activeVoxelCount()] : " << indexGridCoarsened->activeVoxelCount() << std::endl;
auto minCorner = indexGridCoarsened->indexBBox().min(), maxCorner = indexGridCoarsened->indexBBox().max();
std::cout << "Index-space bounding box : [" << minCorner.x() << "," << minCorner.y() << "," << minCorner.z()
<< "] -> [" << maxCorner.x() << "," << maxCorner.y() << "," << maxCorner.z() << "]" << std::endl;
std::cout << "Leaf nodes : " << indexGridCoarsened->tree().nodeCount(0) << std::endl;
std::cout << "Lower internal nodes : " << indexGridCoarsened->tree().nodeCount(1) << std::endl;
std::cout << "Upper internal nodes : " << indexGridCoarsened->tree().nodeCount(2) << std::endl;
std::cout << "Leaf-level occupancy : "
<< 100.f * (float)(indexGridCoarsened->activeVoxelCount())/(float)(indexGridCoarsened->tree().nodeCount(0) * 512)
<< "%" << std::endl;
std::cout << "Memory usage : " << indexGridCoarsened->gridSize() << " bytes" << std::endl;
}

// All grids are host-resident (HostBuffer); the operators read them directly.
mainCoarsenGrid( indexGridOriginal, indexGridCoarsened, benchmark_iters );
}
catch (const std::exception& e) {
std::cerr << "An exception occurred: \"" << e.what() << "\"" << std::endl;
}
return 0;
}
Loading
Loading