Compare commits
261
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02b186de3c | ||
|
|
8b3ef6ae9e | ||
|
|
22bb394214 | ||
|
|
b777c47370 | ||
|
|
1b9ca6408f | ||
|
|
df578c6bbc | ||
|
|
c69404e432 | ||
|
|
cf5ddffbfb | ||
|
|
2086e3c932 | ||
|
|
c78b9efb87 | ||
|
|
9aa8050c3c | ||
|
|
2072e1d82c | ||
|
|
75c1985ec9 | ||
|
|
8b64f41042 | ||
|
|
b44b2b31de | ||
|
|
57434bc985 | ||
|
|
84eea021f6 | ||
|
|
fae3ee95d1 | ||
|
|
a0e513862a | ||
|
|
a273ab358f | ||
|
|
8e901cc609 | ||
|
|
8e5972026d | ||
|
|
5d9e78992f | ||
|
|
ddb4629dec | ||
|
|
e5896971a9 | ||
|
|
fc745334da | ||
|
|
c98f29afcd | ||
|
|
a2c60a3a46 | ||
|
|
3cc39c7d25 | ||
|
|
92ec8d2c46 | ||
|
|
c3f0336f1a | ||
|
|
4fd1a8cd6c | ||
|
|
40e4129229 | ||
|
|
f2df83c304 | ||
|
|
bd7a2416d4 | ||
|
|
e269c9a6e0 | ||
|
|
93d0b3cb33 | ||
|
|
185ecc48ba | ||
|
|
e3beb85592 | ||
|
|
4027e3d66e | ||
|
|
8897a22033 | ||
|
|
893a03bed8 | ||
|
|
09b8ab83a0 | ||
|
|
91af0f5293 | ||
|
|
61a30b3e98 | ||
|
|
70b1593a18 | ||
|
|
bd16782e77 | ||
|
|
7f5538fe2c | ||
|
|
352e672e5b | ||
|
|
d3aaf4fe64 | ||
|
|
ae64b3c50c | ||
|
|
2dbc79024c | ||
|
|
4bdee3bd47 | ||
|
|
55258d0da9 | ||
|
|
e8f1f84f37 | ||
|
|
4e02cba85a | ||
|
|
ddaf9f42e3 | ||
|
|
dd34dc583b | ||
|
|
dc69e26dd5 | ||
|
|
592df88c88 | ||
|
|
3780d983a1 | ||
|
|
114db385da | ||
|
|
32dfc0e792 | ||
|
|
f564d35dca | ||
|
|
30a08a27c8 | ||
|
|
6f9ca0c05a | ||
|
|
747d029abc | ||
|
|
71a5f289cb | ||
|
|
1c5d8d1f57 | ||
|
|
0952c900f9 | ||
|
|
02478bd95f | ||
|
|
dd72d1f137 | ||
|
|
04f4dfba83 | ||
|
|
eab9aaaa7a | ||
|
|
e419f1c745 | ||
|
|
3b64696777 | ||
|
|
cf3c1923a5 | ||
|
|
86aa980c9c | ||
|
|
d3c8c640ce | ||
|
|
e3a9b1c1d9 | ||
|
|
e1944e98e5 | ||
|
|
176a3f3725 | ||
|
|
cb2ebfc840 | ||
|
|
7c108a5d9c | ||
|
|
796a07f3b8 | ||
|
|
345b74ef71 | ||
|
|
1c5a3bf382 | ||
|
|
d09c9a4f67 | ||
|
|
c7f7d6e737 | ||
|
|
f1c46db025 | ||
|
|
1f713afeee | ||
|
|
aa8dfc4f2a | ||
|
|
037aa886e5 | ||
|
|
ff78199ba9 | ||
|
|
29e764bac5 | ||
|
|
5f0432b338 | ||
|
|
b96fcf77a6 | ||
|
|
0cbfa274b2 | ||
|
|
edde9a605c | ||
|
|
4675f5b4bc | ||
|
|
3d45de107e | ||
|
|
ddc3d7a754 | ||
|
|
a25d824db4 | ||
|
|
5c1097e362 | ||
|
|
a02ae47d06 | ||
|
|
af240d55d4 | ||
|
|
c3219ccfd6 | ||
|
|
a948ff318e | ||
|
|
43329bd962 | ||
|
|
f91141ba9d | ||
|
|
559d7642c8 | ||
|
|
fc3f1d180f | ||
|
|
b3aead3529 | ||
|
|
08931d62b4 | ||
|
|
4fb969759e | ||
|
|
6f3f350daf | ||
|
|
178b816479 | ||
|
|
37c159260d | ||
|
|
5dddfa1fc0 | ||
|
|
759f8e59ee | ||
|
|
5bf378f89f | ||
|
|
372526723c | ||
|
|
2e576dbca8 | ||
|
|
4f6d0803b7 | ||
|
|
4e2546fe0c | ||
|
|
3b13980be2 | ||
|
|
90e84aa320 | ||
|
|
39b16cdd6c | ||
|
|
ac2e876b71 | ||
|
|
29dd88e5d9 | ||
|
|
ef3f85da9a | ||
|
|
cbe458cd8a | ||
|
|
166946c036 | ||
|
|
f6b627cb09 | ||
|
|
46371bbde9 | ||
|
|
dd0ac130d0 | ||
|
|
e4621ae7b3 | ||
|
|
1fceec9c6b | ||
|
|
9acc3af646 | ||
|
|
313af9a72a | ||
|
|
93b397cf81 | ||
|
|
cbda14ec8e | ||
|
|
62b95b4115 | ||
|
|
c2bc66e16d | ||
|
|
92ea3888c5 | ||
|
|
691cf1bcd1 | ||
|
|
556a326c4a | ||
|
|
3966b9f991 | ||
|
|
93549a75f2 | ||
|
|
8fc5038ecb | ||
|
|
c13dfc379c | ||
|
|
c391f89af5 | ||
|
|
c4a3cb3643 | ||
|
|
3917418d99 | ||
|
|
c7d1408aa9 | ||
|
|
8cd9a62f53 | ||
|
|
77fc6d05f7 | ||
|
|
c5f5777e08 | ||
|
|
ed5ad461d8 | ||
|
|
d3f37e7dfe | ||
|
|
952a86321c | ||
|
|
36b6043f93 | ||
|
|
6a43628236 | ||
|
|
a7cc7208d2 | ||
|
|
04a2340d2d | ||
|
|
1dc3f9d206 | ||
|
|
fed0143846 | ||
|
|
7885f4704f | ||
|
|
d7aea5d1a3 | ||
|
|
52406bf30f | ||
|
|
aaf0569678 | ||
|
|
b341d3cc50 | ||
|
|
8348da235c | ||
|
|
7ebe25fe55 | ||
|
|
d008d49eb3 | ||
|
|
188b8aeb1c | ||
|
|
286a197a80 | ||
|
|
6d238d412e | ||
|
|
584b2b5a1b | ||
|
|
786a33c2c7 | ||
|
|
5e1658e69b | ||
|
|
7b8f054f5f | ||
|
|
67964d1e15 | ||
|
|
135616d177 | ||
|
|
7ac379d34b | ||
|
|
a58117a3df | ||
|
|
528c470d06 | ||
|
|
4ab3f27e48 | ||
|
|
4b7328db67 | ||
|
|
6e7ab0d49b | ||
|
|
2ba65fe8b4 | ||
|
|
16fb13286c | ||
|
|
5a0c07d06e | ||
|
|
1f35dfe61e | ||
|
|
f60217a439 | ||
|
|
aba9a149eb | ||
|
|
ff943a30eb | ||
|
|
ef94a3b184 | ||
|
|
5f1033dfd7 | ||
|
|
e48a8f8d5c | ||
|
|
b91cb6535a | ||
|
|
9b728cba18 | ||
|
|
88eb8c998b | ||
|
|
316e0b4d03 | ||
|
|
dd7f12eb23 | ||
|
|
72406ff08d | ||
|
|
4f96110d5a | ||
|
|
d0e37dff19 | ||
|
|
8598155fcb | ||
|
|
462aafda79 | ||
|
|
c945c36a09 | ||
|
|
267da7d91d | ||
|
|
74ae3a5989 | ||
|
|
1db28418eb | ||
|
|
690104f408 | ||
|
|
4adbccaee1 | ||
|
|
22e17e7956 | ||
|
|
d130267347 | ||
|
|
1d4cf67d80 | ||
|
|
4d500e10d0 | ||
|
|
1d1899bb0e | ||
|
|
242b573c75 | ||
|
|
d21bec89dc | ||
|
|
d71b7417ad | ||
|
|
9329349461 | ||
|
|
40853821a1 | ||
|
|
f96095f6cf | ||
|
|
97b229964c | ||
|
|
5f54d98080 | ||
|
|
a96cb41408 | ||
|
|
a79ec33944 | ||
|
|
0c94550eaa | ||
|
|
92d689c3bd | ||
|
|
a05dc7a903 | ||
|
|
8b7c521f4d | ||
|
|
e2e0cb6d03 | ||
|
|
f8a1b1ef71 | ||
|
|
2d7ae513ef | ||
|
|
692d63e3b8 | ||
|
|
109c3f2591 | ||
|
|
ac56c2496d | ||
|
|
68d9753437 | ||
|
|
dcd6744424 | ||
|
|
5df8871827 | ||
|
|
748ace48cd | ||
|
|
8a13cb2b02 | ||
|
|
ecb6e7b07c | ||
|
|
5e6a0d260b | ||
|
|
18c2c3a992 | ||
|
|
5beb2c5367 | ||
|
|
b44248b200 | ||
|
|
f25319edbb | ||
|
|
26ada31fa1 | ||
|
|
7aa4136886 | ||
|
|
efa5176137 | ||
|
|
a065b5e3de | ||
|
|
5c2f8e80f9 | ||
|
|
9bb34dff3f | ||
|
|
40a3ef4c05 | ||
|
|
29a17e36bc | ||
|
|
3ed5f6e93d |
+22
-14
@@ -67,17 +67,22 @@ jobs:
|
||||
matrix:
|
||||
# Only test unified builds (core+ax). weekly CI should test standalone
|
||||
config:
|
||||
- { image: '2023-clang15', cxx: 'clang++', build: 'Release', cmake: '' }
|
||||
- { image: '2023-clang15', cxx: 'g++', build: 'Release', cmake: '' }
|
||||
- { image: '2023-clang15', cxx: 'clang++', build: 'Debug', cmake: '' }
|
||||
- { image: '2026', cxx: 'clang++', build: 'Release', cmake: '' }
|
||||
- { image: '2026', cxx: 'g++', build: 'Release', cmake: '' }
|
||||
- { image: '2026', cxx: 'clang++', build: 'Debug', cmake: '' }
|
||||
- { image: '2025-clang19.1', cxx: 'clang++', build: 'Release', cmake: '' }
|
||||
- { image: '2024-clang16.2', cxx: 'clang++', build: 'Release', cmake: '' }
|
||||
# Needed to test LLVM 15.X
|
||||
- { image: '2023-clang15', cxx: 'clang++', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: nanobind
|
||||
#if: contains(matrix.config.image, '2023') == false
|
||||
run: ./ci/install_nanobind.sh 2.0.0
|
||||
- name: install_gtest
|
||||
run: ./ci/install_gtest.sh 1.15.2
|
||||
run: ./ci/install_nanobind.sh 2.5.0
|
||||
# 2026 images are broken with gtest/glfw installs
|
||||
- name: deps
|
||||
if: contains(matrix.config.image, '2026')
|
||||
run: ./ci/install_gtest.sh latest
|
||||
- name: timestamp
|
||||
id: timestamp
|
||||
run: echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
@@ -94,12 +99,11 @@ jobs:
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--build-type=${{ matrix.config.build }}
|
||||
--components='core,python,bin,axcore,axbin,axtest'
|
||||
--components='core,python,axcore,axbin,axtest'
|
||||
--cargs=\"
|
||||
${{ matrix.config.cmake }}
|
||||
-DOPENVDB_AX_TEST_CMD_DOWNLOADS=ON
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF
|
||||
-DOPENVDB_CXX_STRICT=ON
|
||||
\"
|
||||
- name: test
|
||||
run: cd build && ctest -V
|
||||
@@ -122,9 +126,13 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
# Last macos runner before M1 (macos-14)
|
||||
- { runner: 'macos-13', cxx: 'clang++', build: 'Release', llvm: '15' }
|
||||
- { runner: 'macos-13', cxx: 'clang++', build: 'Release', llvm: '13' }
|
||||
- { runner: 'macos-15', cxx: 'clang++', build: 'Release', llvm: '21' }
|
||||
- { runner: 'macos-14', cxx: 'clang++', build: 'Release', llvm: '21' }
|
||||
- { runner: 'macos-14', cxx: 'clang++', build: 'Release', llvm: '20' }
|
||||
- { runner: 'macos-14', cxx: 'clang++', build: 'Release', llvm: '19' }
|
||||
- { runner: 'macos-14', cxx: 'clang++', build: 'Release', llvm: '18' }
|
||||
- { runner: 'macos-14', cxx: 'clang++', build: 'Release', llvm: '17' }
|
||||
- { runner: 'macos-14', cxx: 'clang++', build: 'Release', llvm: '16' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -136,7 +144,7 @@ jobs:
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--build-type=${{ matrix.config.build }}
|
||||
--components="core,python,bin,axcore,axbin,axtest"
|
||||
--components="core,python,axcore,axbin,axtest"
|
||||
--cargs=\"
|
||||
-DOPENVDB_AX_TEST_CMD_DOWNLOADS=ON
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF
|
||||
@@ -154,7 +162,7 @@ jobs:
|
||||
github.event.inputs.type == 'grammar'
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-22.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
container:
|
||||
image: aswf/ci-openvdb:2023-clang15
|
||||
image: aswf/ci-openvdb:2025-clang19.1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: build
|
||||
|
||||
+25
-24
@@ -33,11 +33,8 @@ on:
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
schedule:
|
||||
# run this workflow every day at 7am UTC except Monday
|
||||
- cron: '0 7 * * 0,2-6'
|
||||
# run this workflow Monday 7am UTC
|
||||
# warning: This pattern is checked in various places below
|
||||
- cron: '0 7 * * 1'
|
||||
# run this workflow every day 7am UTC
|
||||
- cron: '0 7 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
type:
|
||||
@@ -64,10 +61,7 @@ jobs:
|
||||
github.event.inputs.type == 'linux'
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-22.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
name: >
|
||||
linux-vfx:${{ matrix.config.image }}-
|
||||
abi:${{ matrix.config.abi }}-
|
||||
cxx:${{ matrix.config.cxx }}-
|
||||
type:${{ matrix.config.build }}
|
||||
linux:${{ matrix.config.image }}-abi:${{ matrix.config.abi }}-cxx:${{ matrix.config.cxx }}-type:${{ matrix.config.build }}
|
||||
container:
|
||||
image: aswf/ci-openvdb:${{ matrix.config.image }}
|
||||
env:
|
||||
@@ -76,21 +70,22 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { cxx: clang++, image: '2024', abi: '12', build: 'Release', cmake: '' }
|
||||
- { cxx: g++, image: '2024', abi: '12', build: 'Release', cmake: '' }
|
||||
- { cxx: clang++, image: '2024', abi: '12', build: 'Debug', cmake: '' }
|
||||
- { cxx: clang++, image: '2023', abi: '11', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' }
|
||||
- { cxx: g++, image: '2023', abi: '11', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' }
|
||||
- { cxx: clang++, image: '2026', abi: '13', build: 'Release', cmake: '-DOPENVDB_USE_FUTURE_ABI_13=ON' }
|
||||
- { cxx: g++, image: '2026', abi: '13', build: 'Release', cmake: '-DOPENVDB_USE_FUTURE_ABI_13=ON' }
|
||||
- { cxx: clang++, image: '2026', abi: '13', build: 'Debug', cmake: '-DOPENVDB_USE_FUTURE_ABI_13=ON' }
|
||||
- { cxx: clang++, image: '2025-clang19.1', abi: '12', build: 'Release', cmake: '' }
|
||||
- { cxx: clang++, image: '2024-clang17.2', abi: '11', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: nanobind
|
||||
run: ./ci/install_nanobind.sh 2.0.0
|
||||
- name: glfw
|
||||
if: contains(matrix.config.image, '2023') == true
|
||||
run: ./ci/install_glfw.sh 3.3.10
|
||||
- name: install_gtest
|
||||
run: ./ci/install_gtest.sh 1.15.2
|
||||
run: ./ci/install_nanobind.sh 2.5.0
|
||||
# 2026 images are broken with gtest/glfw installs
|
||||
- name: deps
|
||||
if: contains(matrix.config.image, '2026')
|
||||
run: |
|
||||
./ci/install_gtest.sh latest
|
||||
./ci/install_glfw.sh latest
|
||||
- name: timestamp
|
||||
id: timestamp
|
||||
run: echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
@@ -114,9 +109,8 @@ jobs:
|
||||
-DOPENVDB_ABI_VERSION_NUMBER=${{ matrix.config.abi }}
|
||||
\"
|
||||
- name: test
|
||||
# Always run tests on weekly builds but skip Debug on commits as they take a while.
|
||||
# https://github.community/t/distinct-job-for-each-schedule/17811/2
|
||||
if: contains(github.event.schedule, '0 7 * * 1') || matrix.config.build == 'Release'
|
||||
# Skip Debug on commits as they take a while.
|
||||
if: github.event_name == 'schedule'|| matrix.config.build == 'Release'
|
||||
run: |
|
||||
cd build && ctest -V
|
||||
cd - && ./ci/test_install.sh
|
||||
@@ -172,9 +166,15 @@ jobs:
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'mac'
|
||||
runs-on: macos-13 # Last macos runner befor M1 (macos-14)
|
||||
runs-on: ${{ matrix.config.image }}
|
||||
name: ${{ matrix.config.image }}
|
||||
env:
|
||||
CXX: clang++
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { image: macos-14 }
|
||||
- { image: macos-15 }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install
|
||||
@@ -191,3 +191,4 @@ jobs:
|
||||
\'
|
||||
- name: test
|
||||
run: cd build && ctest -V
|
||||
|
||||
|
||||
@@ -38,21 +38,15 @@ jobs:
|
||||
env:
|
||||
CXX: g++
|
||||
container:
|
||||
# @note We can't update this as epydoc doesn't support python3. We'll
|
||||
# need to re-write the python docs to use sphinx
|
||||
image: aswf/ci-openvdb:2024
|
||||
image: aswf/ci-openvdb:2026
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install_doxygen
|
||||
run: ./ci/install_doxygen.sh 1_8_11
|
||||
- name: nanobind
|
||||
#if: contains(matrix.config.image, '2023') == false
|
||||
run: ./ci/install_nanobind.sh 2.0.0
|
||||
# - name: install_epydoc
|
||||
# run: pip install epydoc
|
||||
run: ./ci/install_nanobind.sh 2.5.0
|
||||
- name: install_latex
|
||||
run: |
|
||||
yum -y install texlive-latex-bin texlive-dvips texlive-collection-fontsrecommended texlive-collection-latexrecommended
|
||||
run: ./ci/install_texlive.sh
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
@@ -65,9 +59,6 @@ jobs:
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF
|
||||
-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON
|
||||
\"
|
||||
# - name: epydoc
|
||||
# run: |
|
||||
# epydoc --html -o /usr/local/share/doc/OpenVDB/html/python pyopenvdb
|
||||
- name: pre_deploy
|
||||
# Overwrite global SSH configuration
|
||||
# https://github.com/peaceiris/actions-gh-pages/issues/719
|
||||
@@ -106,7 +97,7 @@ jobs:
|
||||
github.event.inputs.deploy == 'coverage'
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: aswf/ci-openvdb:2023
|
||||
image: aswf/ci-openvdb:2025-clang19.1
|
||||
env:
|
||||
CXX: g++
|
||||
steps:
|
||||
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
${{ needs.checksecret.outputs.HOUDINI_SECRETS == 'true' ||
|
||||
github.repository_owner == 'AcademySoftwareFoundation' }}
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-22.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
name: hou:${{ matrix.config.hou_hash }}-vfx:${{ matrix.config.image }}-cxx:${{ matrix.config.cxx }}
|
||||
name: hou:${{ matrix.config.hou_hash }}-vfx:${{ matrix.config.image }}-cxx:${{ matrix.config.cxx }}-build:${{ matrix.config.build }}
|
||||
container:
|
||||
image: aswf/ci-base:${{ matrix.config.image }}
|
||||
env:
|
||||
@@ -75,26 +75,18 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { cxx: clang++, image: '2024', hou_hash: '20_5', build: 'Release', components: 'core,hou,bin,view,render,python,test' }
|
||||
- { cxx: clang++, image: '2023', hou_hash: '20_5', build: 'Debug', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
- { cxx: g++, image: '2023', hou_hash: '20_5', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
- { cxx: clang++, image: '2023', hou_hash: '20_0-newabi', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
- { cxx: g++, image: '2023', hou_hash: '20_0-newabi', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
- { cxx: clang++, image: '2026', hou_hash: '21_0', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
- { cxx: clang++, image: '2026', hou_hash: '21_0', build: 'Debug', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
- { cxx: g++, image: '2026', hou_hash: '21_0', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
- { cxx: clang++, image: '2024', hou_hash: '20_5', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
- { cxx: g++, image: '2024', hou_hash: '20_5', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: remove zstd
|
||||
run: yum -y remove zstd
|
||||
- uses: actions/checkout@v3
|
||||
- name: nanobind
|
||||
#if: contains(matrix.config.image, '2023') == false
|
||||
run: ./ci/install_nanobind.sh 2.0.0
|
||||
- name: glfw
|
||||
if: contains(matrix.config.image, '2023') == true
|
||||
run: ./ci/install_glfw.sh 3.3.10
|
||||
- name: cppunit
|
||||
run: ./ci/install_cppunit.sh 1.15.1
|
||||
run: ./ci/install_nanobind.sh 2.5.0
|
||||
- name: install_gtest
|
||||
run: ./ci/install_gtest.sh 1.15.2
|
||||
run: ./ci/install_gtest.sh latest
|
||||
- name: timestamp
|
||||
id: timestamp
|
||||
run: echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
@@ -125,7 +117,7 @@ jobs:
|
||||
- name: build
|
||||
run: |
|
||||
./ci/build.sh -v \
|
||||
--build-type=Release \
|
||||
--build-type="${{ matrix.config.build }}" \
|
||||
--components="${{ matrix.config.components }}" \
|
||||
--cargs=\" \
|
||||
-DHOUDINI_ROOT=$HOME/houdini_install/hou \
|
||||
@@ -135,6 +127,7 @@ jobs:
|
||||
-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON \
|
||||
\"
|
||||
- name: test
|
||||
if: matrix.config.build == 'Release'
|
||||
run: cd build && ctest -V
|
||||
# Keep ccache light by stripping out any caches not accessed in the last day
|
||||
- name: ccache_clean
|
||||
@@ -165,7 +158,7 @@ jobs:
|
||||
cp hou/hou.tar.gz $HOME/houdini_install/hou.tar.gz
|
||||
cd $HOME/houdini_install && tar -xzf hou.tar.gz && cd -
|
||||
- name: install_deps
|
||||
run: ./ci/install_macos.sh 15
|
||||
run: ./ci/install_macos.sh 20
|
||||
- name: build
|
||||
run: |
|
||||
./ci/build.sh -v \
|
||||
@@ -178,8 +171,7 @@ jobs:
|
||||
-DDISABLE_CMAKE_SEARCH_PATHS=ON \
|
||||
-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON \
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF \
|
||||
-DTbb_INCLUDE_DIR=$HOME/houdini_install/hou/Frameworks/Houdini.framework/Versions/Current/Resources/toolkit/include/tbb \
|
||||
-DLLVM_DIR=/opt/homebrew/opt/llvm@15/lib/cmake/llvm \
|
||||
-DLLVM_DIR=/opt/homebrew/opt/llvm@20/lib/cmake/llvm \
|
||||
-DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install \
|
||||
\"
|
||||
- name: test
|
||||
|
||||
@@ -61,16 +61,15 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { cxx: g++, image: '2024', build: 'Release' }
|
||||
- { cxx: g++, image: '2024', build: 'Debug' }
|
||||
- { cxx: clang++, image: '2024', build: 'Release' }
|
||||
- { cxx: clang++, image: '2024', build: 'Debug' }
|
||||
- { cxx: g++, image: '2024-clang17.2', build: 'Release' }
|
||||
- { cxx: g++, image: '2024-clang17.2', build: 'Debug' }
|
||||
- { cxx: clang++, image: '2024-clang17.2', build: 'Release' }
|
||||
- { cxx: clang++, image: '2024-clang17.2', build: 'Debug' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: setup_cuda_12
|
||||
run: |
|
||||
yum -y install yum-utils
|
||||
echo "/usr/local/cuda-12/bin" >> $GITHUB_PATH
|
||||
echo "LD_LIBRARY_PATH=/usr/local/cuda-12/lib64:$LD_LIBRARY_PATH" >> $GITHUB_ENV
|
||||
- name: build
|
||||
@@ -90,7 +89,7 @@ jobs:
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
\'
|
||||
- name: test
|
||||
run: cd build && sudo ctest -V -E ".*cuda.*"
|
||||
run: cd build && sudo ctest -V -E ".*cuda.*|.*mgpu.*"
|
||||
|
||||
windows-nanovdb:
|
||||
if: |
|
||||
@@ -118,6 +117,7 @@ jobs:
|
||||
shell: powershell
|
||||
run: .\ci\install_windows.ps1
|
||||
- name: build
|
||||
# nvcc doesn't set _WIN32 when run in bash so we need to set it manually
|
||||
shell: bash
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
@@ -129,13 +129,14 @@ jobs:
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF
|
||||
-DNANOVDB_USE_CUDA=ON
|
||||
-DCMAKE_CUDA_ARCHITECTURES="80"
|
||||
-DCMAKE_CUDA_FLAGS="-D_WIN32"
|
||||
-DNANOVDB_USE_OPENVDB=ON
|
||||
-DVCPKG_TARGET_TRIPLET=${VCPKG_DEFAULT_TRIPLET}
|
||||
-DCMAKE_TOOLCHAIN_FILE=\"${VCPKG_INSTALLATION_ROOT}\\scripts\\buildsystems\\vcpkg.cmake\"
|
||||
\'
|
||||
- name: test
|
||||
shell: bash
|
||||
run: cd build && ctest -V -E ".*cuda.*"
|
||||
run: cd build && ctest -V -E ".*cuda.*|.*mgpu.*"
|
||||
|
||||
macos-nanovdb:
|
||||
if: |
|
||||
@@ -148,8 +149,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { runner: 'macos-13', cxx: 'clang++', build: 'Release' }
|
||||
- { runner: 'macos-13', cxx: 'clang++', build: 'Debug' }
|
||||
- { runner: 'macos-14', cxx: 'clang++', build: 'Release' }
|
||||
- { runner: 'macos-14', cxx: 'clang++', build: 'Debug' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -162,9 +163,9 @@ jobs:
|
||||
./ci/build.sh -v
|
||||
--build-type=${{ matrix.config.build }}
|
||||
--components=core,nano,nanotest,nanoexam,nanobench,nanotool
|
||||
--cargs=\'-DUSE_EXPLICIT_INSTANTIATION=OFF -DNANOVDB_USE_CUDA=OFF -DNANOVDB_USE_OPENVDB=ON\'
|
||||
--cargs=\'-DUSE_EXPLICIT_INSTANTIATION=OFF -DNANOVDB_USE_CUDA=OFF -DNANOVDB_USE_OPENVDB=ON -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install \'
|
||||
- name: test
|
||||
run: cd build && ctest -V -E ".*cuda.*"
|
||||
run: cd build && ctest -V -E ".*cuda.*|.*mgpu.*"
|
||||
|
||||
nanovdb-lite:
|
||||
if: |
|
||||
@@ -173,11 +174,11 @@ jobs:
|
||||
github.event.inputs.type == 'linux'
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-22.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
container:
|
||||
image: aswf/ci-openvdb:2024
|
||||
image: aswf/ci-openvdb:2024-clang17.2
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install_gtest
|
||||
run: ./ci/install_gtest.sh 1.15.2
|
||||
run: ./ci/install_gtest.sh latest
|
||||
- name: build_and_test
|
||||
run: |
|
||||
cd nanovdb/nanovdb
|
||||
|
||||
@@ -70,23 +70,13 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { houdini_version: '20.0', platform: 'linux_x86_64_gcc11.2', hou_hash: '20_0-newabi' }
|
||||
- { houdini_version: '21.0', platform: 'linux_x86_64_gcc11.2', hou_hash: '21_0' }
|
||||
- { houdini_version: '20.5', platform: 'linux_x86_64_gcc11.2', hou_hash: '20_5' }
|
||||
fail-fast: false
|
||||
container:
|
||||
image: aswf/ci-base:2024
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
# We bumped from the 2021 CI image to 2023 here to fix some OpenSSL issues
|
||||
# with the Houdini download script. In so doing we broke some of the caching
|
||||
# between this job and the jobs in houdini.yml which _don't_ use the 2023
|
||||
# image yet. The issue is that the cache action will use zstd if it's
|
||||
# available to zip the cache and this causes it to be inserted with a unique
|
||||
# hash which images without zstd (i.e. the 2021/2022 images don't have
|
||||
# access to). For now, uninstall zstd here instead of installing it
|
||||
# everywhere and ask the LF to add zstd to the older base images.
|
||||
- name: remove zstd
|
||||
run: yum -y remove zstd
|
||||
- name: timestamp
|
||||
id: timestamp
|
||||
run: echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
@@ -113,7 +103,7 @@ jobs:
|
||||
# Note that macos-14 (current macos-latest) switches to M1. We could instead test
|
||||
# the arm build here instead of the x86 one.
|
||||
runs-on: macos-latest
|
||||
name: macos-houdini-20
|
||||
name: macos-houdini
|
||||
env:
|
||||
HOUDINI_CLIENT_ID: ${{ secrets.HOUDINI_CLIENT_ID }}
|
||||
HOUDINI_SECRET_KEY: ${{ secrets.HOUDINI_SECRET_KEY }}
|
||||
@@ -123,7 +113,7 @@ jobs:
|
||||
id: timestamp
|
||||
run: echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
- name: download_houdini
|
||||
run: ./ci/download_houdini.sh 20.0 macosx_arm64_clang14.0_13 --prod
|
||||
run: ./ci/download_houdini.sh 21.0 macosx_arm64_clang15.0_14 --prod
|
||||
- name: install_houdini
|
||||
run: |
|
||||
mkdir $HOME/houdini_install
|
||||
@@ -149,28 +139,35 @@ jobs:
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-22.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
name: linux-extra:${{ matrix.config.name }}
|
||||
container:
|
||||
image: aswf/ci-openvdb:2024
|
||||
image: aswf/ci-openvdb:${{ matrix.config.image }}
|
||||
env:
|
||||
CXX: clang++
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { name: 'all', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=ON -DUSE_ZLIB=ON -DUSE_EXR=ON -DUSE_PNG=ON' }
|
||||
- { name: 'lite', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=OFF -DUSE_ZLIB=OFF -DUSE_EXR=OFF -DUSE_PNG=OFF -DOPENVDB_USE_DELAYED_LOADING=OFF' }
|
||||
- { name: 'half', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=OFF -DUSE_IMATH_HALF=ON' }
|
||||
- { name: 'sse', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DOPENVDB_SIMD=SSE42' }
|
||||
- { name: 'avx', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DOPENVDB_SIMD=AVX' }
|
||||
- { name: 'numpy', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_NUMPY=ON -DOPENVDB_PYTHON_WRAP_ALL_GRID_TYPES=ON' }
|
||||
- { name: 'asan', build: 'asan', components: 'core,test', cmake: '-DNANOVDB_USE_OPENVDB=ON -DOPENVDB_AX_STATIC=OFF -DOPENVDB_CORE_STATIC=OFF -DUSE_BLOSC=OFF' } # We never called blosc_destroy(), so disable blosc to silence these errors
|
||||
- { name: 'ubsan', build: 'ubsan', components: 'core,test', cmake: '-DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" ' }
|
||||
- { name: 'c++20', build: 'Release', components: 'core,test', cmake: '-DCMAKE_CXX_STANDARD=20' }
|
||||
- { name: 'conf', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON' }
|
||||
- { name: 'all', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=ON -DUSE_ZLIB=ON -DUSE_EXR=ON -DUSE_PNG=ON' }
|
||||
- { name: 'lite', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=OFF -DUSE_ZLIB=OFF -DUSE_EXR=OFF -DUSE_PNG=OFF -DOPENVDB_USE_DELAYED_LOADING=OFF' }
|
||||
- { name: 'half', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=OFF -DUSE_IMATH_HALF=ON' }
|
||||
- { name: 'sse', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DOPENVDB_SIMD=SSE42' }
|
||||
- { name: 'avx', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DOPENVDB_SIMD=AVX' }
|
||||
- { name: 'pygrid', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DOPENVDB_PYTHON_WRAP_ALL_GRID_TYPES=ON' }
|
||||
- { name: 'asan', image: '2026', build: 'asan', components: 'core,test', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON -DNANOVDB_USE_OPENVDB=ON -DOPENVDB_AX_STATIC=OFF -DOPENVDB_CORE_STATIC=OFF -DUSE_BLOSC=OFF' } # We never called blosc_destroy(), so disable blosc to silence these errors
|
||||
- { name: 'ubsan', image: '2026', build: 'ubsan', components: 'core,test', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON -DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" ' }
|
||||
- { name: 'c++20', image: '2026', build: 'Release', components: 'core,test', cmake: '-DCMAKE_CXX_STANDARD=20' }
|
||||
- { name: 'conf', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: nanobind
|
||||
#if: contains(container.image, '2023') == false
|
||||
run: ./ci/install_nanobind.sh 2.0.0
|
||||
run: ./ci/install_nanobind.sh 2.5.0
|
||||
# 2026 images are broken with gtest/glfw/exr installs
|
||||
- name: deps
|
||||
if: contains(matrix.config.image, '2026')
|
||||
run: |
|
||||
./ci/install_gtest.sh latest
|
||||
./ci/install_glfw.sh latest
|
||||
./ci/install_imath.sh v3.2.2
|
||||
./ci/install_exr.sh v3.2.2
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
@@ -192,21 +189,22 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { runson: ubuntu-latest, cxx: g++, cmake: '' }
|
||||
# llvm-17 is currently the default which has symbol issues with the python module
|
||||
- { runson: ubuntu-latest, cxx: g++, cmake: '-DOPENVDB_PYTHON_USE_AX=OFF' }
|
||||
# Disable the clang job for now. See https://github.com/actions/runner-images/issues/8659
|
||||
# - { runson: ubuntu-latest, cxx: clang++, cmake: '' }
|
||||
# @todo gcc on macos
|
||||
- { runson: macos-latest, cxx: '', cmake: '-DCMAKE_CXX_COMPILER=/opt/homebrew/opt/llvm@15/bin/clang++ -DLLVM_DIR=/opt/homebrew/opt/llvm@15/lib/cmake/llvm' }
|
||||
- { runson: macos-latest, cxx: '', cmake: '-DLLVM_DIR=/opt/homebrew/opt/llvm@20/lib/cmake/llvm' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install_deps
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo apt-get -q install -y libboost-dev libboost-iostreams-dev libtbb-dev libblosc-dev llvm-15-dev libgtest-dev libgmock-dev libcppunit-dev
|
||||
./ci/install_nanobind.sh 2.0.0
|
||||
sudo apt-get -q install -y libboost-dev libboost-iostreams-dev libtbb-dev libblosc-dev libgtest-dev libgmock-dev python3-numpy
|
||||
./ci/install_nanobind.sh 2.5.0
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
./ci/install_macos.sh 15
|
||||
./ci/install_macos.sh 20
|
||||
./ci/install_tbb_macos.sh
|
||||
else
|
||||
echo "$RUNNER_OS not supported"; exit 1
|
||||
@@ -216,7 +214,12 @@ jobs:
|
||||
./ci/build.sh -v
|
||||
--build-type=Release
|
||||
--components=\"core,axcore,python,bin,render,test,axbin\"
|
||||
--cargs=\"-DCMAKE_CXX_STANDARD=20 -DOPENVDB_USE_DELAYED_LOADING=OFF -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install ${{ matrix.config.cmake }}\"
|
||||
--cargs=\'
|
||||
-DCMAKE_CXX_STANDARD=20
|
||||
-DOPENVDB_USE_DELAYED_LOADING=OFF
|
||||
-DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install
|
||||
${{ matrix.config.cmake }}
|
||||
\'
|
||||
- name: test
|
||||
run: cd build && ctest -V
|
||||
|
||||
@@ -238,9 +241,9 @@ jobs:
|
||||
# Boost as both shared and static libs are installed.
|
||||
# USE_EXPLICIT_INSTANTIATION is disabled for debug static libraries
|
||||
# due to disk space constraints
|
||||
- { vc: 'x64-windows-static', components: 'core,bin,view,render,test', build: 'Release', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON' }
|
||||
# - { vc: 'x64-windows-static', components: 'core,bin,view,render,test', build: 'Release', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON' }
|
||||
- { vc: 'x64-windows', components: 'core,bin,view,render,python,test', build: 'Release', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF' }
|
||||
- { vc: 'x64-windows', components: 'core,bin,view,render,python,test', build: 'Debug', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF' }
|
||||
# - { vc: 'x64-windows', components: 'core,bin,view,render,python,test', build: 'Debug', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -294,15 +297,14 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
# Unified
|
||||
- { image: '2023-clang15', cxx: 'clang++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' }
|
||||
- { image: '2023-clang15', cxx: 'g++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' }
|
||||
- { image: '2025-clang18.1', cxx: 'clang++', build: 'Release', components: 'core,axcore,axbin,axtest', cmake: '' }
|
||||
# @note LLVM 17 builds are incompatible with the vdb python plugin on linux
|
||||
- { image: '2024-clang17.2', cxx: 'clang++', build: 'Release', components: 'core,axcore,axbin,axtest', cmake: '-DOPENVDB_PYTHON_USE_AX=OFF' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: nanobind
|
||||
#f: contains(matrix.config.image, '2023') == false
|
||||
run: ./ci/install_nanobind.sh 2.0.0
|
||||
run: ./ci/install_nanobind.sh 2.5.0
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
@@ -341,14 +343,14 @@ jobs:
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'ax'
|
||||
runs-on: macos-13
|
||||
runs-on: ${{ matrix.config.runner }}
|
||||
name: macos-cxx:${{ matrix.config.cxx }}-llvm:${{ matrix.config.llvm }}-${{ matrix.config.build }}
|
||||
env:
|
||||
CXX: ${{ matrix.config.cxx }}
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { cxx: 'clang++', build: 'Release', llvm: '15' }
|
||||
- { runner: 'macos-latest', cxx: 'clang++', build: 'Release', llvm: '20' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -378,7 +380,7 @@ jobs:
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'ax'
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'windows-2022-8c-32g-300h') || 'windows-latest' }}
|
||||
name: windows-vc:${{ matrix.config.vc }}-type:${{ matrix.config.build }}
|
||||
name: ${{ matrix.config.vc }}-${{ matrix.config.crt }}-${{ matrix.config.build }}-llvm${{ matrix.config.llvm }}
|
||||
env:
|
||||
VCPKG_DEFAULT_TRIPLET: ${{ matrix.config.vc }}
|
||||
# Export this with '' avoid bash treating \ as escape
|
||||
@@ -392,17 +394,22 @@ jobs:
|
||||
# @todo We don't currently run the axtests with shared builds of ax
|
||||
# due to symbol issues using LLVM as a static lib (which is the only
|
||||
# option on Windows).
|
||||
- { vc: 'x64-windows', crt: 'MD', components: 'core,bin,axcore,axbin,python', build: 'Release', cmake: '-DOPENVDB_CORE_STATIC=OFF -DOPENVDB_AX_STATIC=OFF' }
|
||||
- { vc: 'x64-windows-static', crt: 'MT', components: 'core,bin,axcore,axbin,axtest', build: 'Release', cmake: '-DOPENVDB_CORE_SHARED=OFF -DOPENVDB_AX_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON' }
|
||||
- { vc: 'x64-windows-static', crt: 'MTd', components: 'core,bin,axcore,axbin,axtest', build: 'Debug', cmake: '-DOPENVDB_CORE_SHARED=OFF -DOPENVDB_AX_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON' }
|
||||
# @note currently only test static builds with MT as building LLVM as
|
||||
# a shared lib on windows is a bit dicey
|
||||
- { vc: 'x64-windows-static', llvm: '18.1.0', crt: 'MultiThreaded', components: 'core,axcore,axbin,axtest', build: 'Release', cmake: '-DOPENVDB_CORE_SHARED=OFF -DOPENVDB_AX_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON' }
|
||||
- { vc: 'x64-windows-static', llvm: '18.1.0', crt: 'MultiThreadedDebug', components: 'core,axcore,axbin,axtest', build: 'Debug', cmake: '-DOPENVDB_CORE_SHARED=OFF -DOPENVDB_AX_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON' }
|
||||
- { vc: 'x64-windows', llvm: '18.1.0', crt: 'MultiThreadedDLL', components: 'core,axcore,axbin', build: 'Release', cmake: '-DOPENVDB_CORE_STATIC=OFF -DOPENVDB_AX_STATIC=OFF' }
|
||||
- { vc: 'x64-windows-static', llvm: '15.0.0', crt: 'MultiThreaded', components: 'core,axcore,axbin,axtest', build: 'Release', cmake: '-DOPENVDB_CORE_SHARED=OFF -DOPENVDB_AX_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: llvm
|
||||
run: ./ci/install_llvm_windows.sh ${{ matrix.config.crt }}
|
||||
run: ./ci/install_llvm_windows.sh ${{ matrix.config.llvm }} ${{ matrix.config.crt }}
|
||||
- name: install
|
||||
shell: powershell
|
||||
run: .\ci\install_windows.ps1
|
||||
run: |
|
||||
vcpkg update
|
||||
vcpkg install tbb gtest
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
@@ -410,10 +417,13 @@ jobs:
|
||||
--components="${{ matrix.config.components }}"
|
||||
--cargs=\'
|
||||
-A x64 -G \"Visual Studio 17 2022\"
|
||||
-DOPENVDB_USE_DELAYED_LOADING=OFF
|
||||
-DUSE_BLOSC=OFF \
|
||||
-DUSE_ZLIB=OFF \
|
||||
-DVCPKG_TARGET_TRIPLET=${VCPKG_DEFAULT_TRIPLET}
|
||||
-DCMAKE_TOOLCHAIN_FILE=\"${VCPKG_INSTALLATION_ROOT}\\scripts\\buildsystems\\vcpkg.cmake\"
|
||||
-DMSVC_COMPRESS_PDB=ON
|
||||
-DOPENVDB_AX_TEST_CMD_DOWNLOADS=ON
|
||||
-DOPENVDB_AX_TEST_CMD_DOWNLOADS=OFF
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF
|
||||
-DLLVM_DIR=\"${HOME}\\llvm_install\\lib\\cmake\\llvm\"
|
||||
-DCMAKE_INSTALL_PREFIX=\"${VDB_INSTALL_PREFIX}\"
|
||||
@@ -436,7 +446,7 @@ jobs:
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'win'
|
||||
github.event.inputs.type == 'winnano'
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'windows-2022-8c-32g-300h') || 'windows-latest' }}
|
||||
env:
|
||||
VCPKG_DEFAULT_TRIPLET: ${{ matrix.config.vc }}
|
||||
@@ -499,10 +509,10 @@ jobs:
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-22.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
name: linux-blosc:${{ matrix.blosc }}
|
||||
container:
|
||||
image: aswf/ci-base:2023
|
||||
image: aswf/ci-openvdb:2025-clang19.1
|
||||
strategy:
|
||||
matrix:
|
||||
blosc: ['1.18.0','1.19.0','1.20.0','1.21.0']
|
||||
blosc: ['1.21.0', 'latest']
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -1,18 +1,163 @@
|
||||
OpenVDB Version History
|
||||
=======================
|
||||
|
||||
Version 12.0.2 - In development
|
||||
Version 12.1.2 - In development
|
||||
|
||||
|
||||
Version 12.1.1 - September 30, 2025
|
||||
|
||||
OpenVDB AX:
|
||||
Fixes:
|
||||
- Fixed an issue which was causing host specific optimization passes to be
|
||||
omitted when using LLVM 16 and higher.
|
||||
|
||||
Houdini:
|
||||
- Fixed a compilation issue with the OpenVDB Activate SOP with Houdini 21
|
||||
[Reported by @callen128]
|
||||
|
||||
|
||||
Version 12.1.0 - August 8, 2025
|
||||
|
||||
OpenVDB:
|
||||
New features:
|
||||
- New level set constructor tools::createLevelSetCapsule creates a grid
|
||||
containing a narrow-band level set representation of a capsule (tube
|
||||
with constant radius and sphere caps).
|
||||
- New level set constructor tools::createLevelSetTaperedCapsule creates a
|
||||
grid containing a narrow-band level set representation of a tapered
|
||||
capsule (tube with sphere caps and different radii at both ends, or
|
||||
equivalently the convex hull of two spheres with possibly different
|
||||
centers and radii).
|
||||
- New level set constructor tools::createLevelSetTubeComplex creates a grid
|
||||
containing a narrow-band level set representation of a tube complex (a
|
||||
collection of capsules or tapered capsules defined by endpoint coordinates
|
||||
and segment indices).
|
||||
- New level set constructor tools::createLevelSetDilatedMesh creates a grid
|
||||
containing a narrow-band level set representation of a dilated surface
|
||||
mesh (dilated by a radius in all directions).
|
||||
- New class tools::ConvexVoxelizer, used by the above methods, which can be
|
||||
inherited to implement a convex level set constructor.
|
||||
- Introduced anisotropic surfacing tools in points/PointRasterizeSDF.h and
|
||||
principal component analysis (PCA) methods in points/PrincipalComponentAnalysis.h.
|
||||
The latter tool analyses point neighborhoods and computes affine
|
||||
transformations representing elliptical distributions. The prior tool
|
||||
takes rotational and stretch point attributes (optionally computed from
|
||||
the prior PCA methods) and generates anisotropic surfaces.
|
||||
|
||||
Improvements:
|
||||
- Significantly improved the performance of all LeafNode ValueIterators,
|
||||
up to 5x on some platforms and up to 10x when delay loading is enabled.
|
||||
Construction of a ValueIterator from a leaf node now requests the leaf
|
||||
buffers ahead of iteration to avoid potentially expensive API calls.
|
||||
- Improved the performance of PointRasterizeSDF.h tools with larger radii
|
||||
with better bound computation approximations.
|
||||
- Significant performance improvement to delay loaded files - the warning
|
||||
logic which checks the underlying mapped file for the last modified time
|
||||
has been removed, resulting in performance gains upwards of 10x when
|
||||
loading in node data buffers. Note that the check that was removed is
|
||||
entirely redundant on Windows and would only print a warning on other
|
||||
system.
|
||||
[Contributed by jjtParadox]
|
||||
- Added OPENVDB_HAS_INVOKABLE_MEMBER_FUNCTION and OPENVDB_HAS_MEMBER_FUNCTION
|
||||
macros to help determine the presence and compatibility of interface
|
||||
member functions at compile time.
|
||||
- Added OPENVDB_ASSUME macros to mimic builtin assume and C++23 assume
|
||||
attributes.
|
||||
- Improved read and write of PLY files with vdb_tool so they support Vec3d
|
||||
and 64 bit attributes. Changed vdb_tool version number from 10.6.1 to
|
||||
10.7.0.
|
||||
|
||||
API Changes:
|
||||
- Mat3 and Mat4 functions have been marked [[nodiscard]] where it may be
|
||||
confusing if they are operating in place or returning a result, in
|
||||
particular tranpose, invert, adjoint, and timesDiagonal.
|
||||
- Re-worked the API in PointRasterizeSDF.h with the addition of anisotropic
|
||||
surfacing kernels. The new API exposes more concise free functions which
|
||||
take structs of parameters. The old API remains supported but is
|
||||
deprecated.
|
||||
- Deprecated providing a filter or interrupter as arguments to
|
||||
points::rasterize. These can instead be implemented on user transfer
|
||||
schemes.
|
||||
- Transfer schemes provided to points::rasterize can now optionally
|
||||
implement a rasterizePoints callback that takes a range of points per
|
||||
voxel.
|
||||
|
||||
Fixes:
|
||||
- Fixed a bug in points::rasterizeSpheres and points::rasterizeSmoothSpheres
|
||||
which could cause sections of the generated surface to incorrectly be
|
||||
marked as negative interior values.
|
||||
- Fixed a bug in points::rasterizeSmoothSpheres which could result in
|
||||
incomplete surface reconstruction with significantly larger search
|
||||
distances.
|
||||
- Fixed some precision issues in various Matrix methods.
|
||||
- Fixed bugs in vdb_tool that prevented build with newer namespaces in
|
||||
nanovdb.
|
||||
|
||||
OpenVDB AX:
|
||||
Improvements:
|
||||
- Added support for LLVM versions greater than 15. Upgraded CI to test with
|
||||
LLVM versions 16, 17, 18, 19 and 20.
|
||||
- As of this release, LLVM 15 is the minimum required version for OpenVDB
|
||||
AX. Note that there is an issue with LLVM 17 symbols which makes it
|
||||
incompatible with the OpenVDB Python plugin. AS such, LLVM version 18 and
|
||||
greater are the recommended LLVM versions to use, with LLVM 18 being the
|
||||
new future minimum version.
|
||||
- Significant performance improvement to the compilation of AX kernels when
|
||||
using LLVM 18 and greater.
|
||||
- Significant backend refactoring to AX IR generation to support newer
|
||||
versions of LLVM. In particular, parts of the codegen::Function and
|
||||
codegen::FunctionBuilder API require new usage from LLVM 16 due to
|
||||
the LLVM opaque pointer changes. If you're not using the OpenVDB AX
|
||||
Function codegen API then no client changes should be necessary.
|
||||
- Migrated OpenVDB AX unit tests from cppunit to gtest
|
||||
[Contributed by Tim Straubinger]
|
||||
|
||||
Fixes:
|
||||
- Fixed a case of undefined behaviour when explicitly casting variables to
|
||||
the same type as their storage type e.g:
|
||||
double a; double b = double(a);
|
||||
- Fixed an AX compiler error which would produce invalid IR when using 64bit
|
||||
integer variables for matrix array offsetting e.g:
|
||||
int64 a = 0; mat4f b = 1; float c = b[a,a];
|
||||
|
||||
NanoVDB:
|
||||
- Added nanovdb::openToIndexVDB, which can convert an OpenVDB grid into a
|
||||
NanoVDB IndexGrid.
|
||||
- Added new options "--index" and "--onIndex" to the command-line tool
|
||||
nanovdb_convert so it can produce IndexGrids.
|
||||
- Fixed memory leak in DeviceBuffer.
|
||||
- Added constructor for DeviceBuffer from an existing HostBuffer.
|
||||
- Added VoxelBlockManager, an acceleration structure for sequential access
|
||||
and stencil ops over active voxels.
|
||||
- Added helper functions for kernel launches with shared memory.
|
||||
- Added support for CUDA 13.0.
|
||||
- Fix warnings and compilation errors for GCC and Clang.
|
||||
- Fixed inconsistent usage of util::cuda::mallocAsync/freeAsync that
|
||||
affected vGPUs.
|
||||
- Added support for custom memory resources in PointsToGrid.
|
||||
- Fixed race condition in split/mergeGridHandles.
|
||||
- Fixed offset bug in nanovdb::tools::cuda::IndexToGrid related to non-POD
|
||||
build types.
|
||||
[Contributed by Andreas Henne]
|
||||
|
||||
Houdini:
|
||||
- Define OP_InputIdx and OP_OutputIdx for Houdini 21.0 compatibility
|
||||
- Add Houdini 21 style camera interface to support LOP cameras
|
||||
- Include env flag for avoiding stream writing (was backported to 19.5)
|
||||
|
||||
|
||||
Version 12.0.1 - April 3, 2025
|
||||
|
||||
OpenVDB:
|
||||
New features:
|
||||
- Added read and write support for OFF (Object File Format) files to vdb_tool
|
||||
- Added read and write support for OFF (Object File Format) files to
|
||||
vdb_tool
|
||||
|
||||
Build:
|
||||
- Fixed a build issue with vdb_render on Windows with newer versions of glew not including glu.h by default
|
||||
- Fixed issues with newer versions of clang refusing to accept older ::template syntax
|
||||
- Fixed a build issue with vdb_render on Windows with newer versions of
|
||||
glew not including glu.h by default
|
||||
- Fixed issues with newer versions of clang refusing to accept older
|
||||
::template syntax
|
||||
|
||||
NanoVDB:
|
||||
New features:
|
||||
@@ -20,10 +165,12 @@ Version 12.0.1 - April 3, 2025
|
||||
- Added a UnifiedBuffer class that wraps CUDA unified memory
|
||||
- Added example for multiGPU sparse convolution
|
||||
- Added CUDA utility functions for device queries
|
||||
- Added functions to independently stop and compute the elapsed time for timer classes
|
||||
- Added functions to independently stop and compute the elapsed time for
|
||||
timer classes
|
||||
|
||||
Improvements:
|
||||
- Replaced CUB's CachingDeviceAllocator with the default asynchronous stream ordered allocator in PointsToGrid for improved performance
|
||||
- Replaced CUB's CachingDeviceAllocator with the default asynchronous
|
||||
stream ordered allocator in PointsToGrid for improved performance
|
||||
|
||||
Bug fix:
|
||||
- Map is now properly transposed when converting NanoVDB to OpenVDB.
|
||||
|
||||
+23
-5
@@ -42,7 +42,7 @@
|
||||
|
||||
# note: cmake_minimum_required must be called before project commands to
|
||||
# ensure policy scope is set up correctly
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
# CMP0091 allows for MSVC ABI targetting via CMAKE_MSVC_RUNTIME_LIBRARY
|
||||
# from CMake 3.15 and above. Must come before project().
|
||||
@@ -53,7 +53,7 @@ endif()
|
||||
###### Version
|
||||
|
||||
set(OpenVDB_MAJOR_VERSION 12)
|
||||
set(OpenVDB_MINOR_VERSION 0)
|
||||
set(OpenVDB_MINOR_VERSION 1)
|
||||
set(OpenVDB_PATCH_VERSION 2)
|
||||
set(OpenVDB_VERSION "${OpenVDB_MAJOR_VERSION}.${OpenVDB_MINOR_VERSION}.${OpenVDB_PATCH_VERSION}")
|
||||
|
||||
@@ -268,6 +268,13 @@ mark_as_advanced(
|
||||
|
||||
include(cmake/config/OpenVDBVersions.cmake)
|
||||
|
||||
if(OPENVDB_FUTURE_DEPRECATION AND FUTURE_MINIMUM_CMAKE_VERSION)
|
||||
if(CMAKE_VERSION VERSION_LESS ${FUTURE_MINIMUM_CMAKE_VERSION})
|
||||
message(DEPRECATION "Support for CMake versions < ${FUTURE_MINIMUM_CMAKE_VERSION} "
|
||||
"is deprecated and will be removed.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
foreach(DEPRECATED_ABI ${OPENVDB_DEPRECATED_ABI_LIST})
|
||||
option(OPENVDB_USE_DEPRECATED_ABI_${DEPRECATED_ABI} "Bypass minimum ABI requirement checks" OFF)
|
||||
mark_as_advanced(OPENVDB_USE_DEPRECATED_ABI_${DEPRECATED_ABI})
|
||||
@@ -478,10 +485,7 @@ endif()
|
||||
# Locate Python and nanobind if necessary
|
||||
if(OPENVDB_BUILD_PYTHON_MODULE OR (OPENVDB_BUILD_NANOVDB AND NANOVDB_BUILD_PYTHON_MODULE))
|
||||
|
||||
# Call find_package(Python ...)
|
||||
find_package(Python ${MINIMUM_PYTHON_VERSION} REQUIRED COMPONENTS Development Interpreter)
|
||||
find_package(nanobind ${MINIMUM_NANOBIND_VERSION} REQUIRED)
|
||||
|
||||
if(OPENVDB_FUTURE_DEPRECATION AND FUTURE_MINIMUM_PYTHON_VERSION)
|
||||
if(Python_VERSION VERSION_LESS ${FUTURE_MINIMUM_PYTHON_VERSION})
|
||||
message(DEPRECATION "Support for Python versions < ${FUTURE_MINIMUM_GLFW_VERSION} "
|
||||
@@ -489,6 +493,20 @@ if(OPENVDB_BUILD_PYTHON_MODULE OR (OPENVDB_BUILD_NANOVDB AND NANOVDB_BUILD_PYTHO
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(nanobind REQUIRED CONFIG)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(nanobind
|
||||
FOUND_VAR nanobind_FOUND
|
||||
REQUIRED_VARS NB_DIR
|
||||
VERSION_VAR nanobind_VERSION)
|
||||
|
||||
if(OPENVDB_FUTURE_DEPRECATION AND FUTURE_MINIMUM_NANOBIND_VERSION)
|
||||
if(nanobind_VERSION VERSION_LESS ${FUTURE_MINIMUM_NANOBIND_VERSION})
|
||||
message(DEPRECATION "Support for nanobind versions < ${FUTURE_MINIMUM_NANOBIND_VERSION} "
|
||||
"is deprecated and will be removed.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED VDB_PYTHON_INSTALL_DIRECTORY)
|
||||
if(DEFINED Python_SITELIB)
|
||||
get_filename_component(Python_PACKAGES_DIR ${Python_SITELIB} NAME)
|
||||
|
||||
+16
-9
@@ -33,8 +33,13 @@ PARMS[--target]=install
|
||||
PARMS[--build-dir]=build
|
||||
|
||||
if [[ $RUNNER_NAME == *"8c-32g-300h"* ]]; then
|
||||
# ASWF github actions runners have 8 threads
|
||||
PARMS[-j]=8
|
||||
if [[ $CXX == "g++" ]]; then
|
||||
# GCC hits memory limits on runners, reduce threads
|
||||
PARMS[-j]=6
|
||||
else
|
||||
# ASWF github actions runners have 8 threads
|
||||
PARMS[-j]=8
|
||||
fi
|
||||
else
|
||||
# Github actions runners have 2 threads
|
||||
# https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners
|
||||
@@ -51,7 +56,8 @@ declare -A COMPONENTS
|
||||
COMPONENTS['core']='OPENVDB_BUILD_CORE'
|
||||
COMPONENTS['python']='OPENVDB_BUILD_PYTHON_MODULE'
|
||||
COMPONENTS['test']='OPENVDB_BUILD_UNITTESTS'
|
||||
COMPONENTS['bin']='OPENVDB_BUILD_BINARIES'
|
||||
# @todo split out vdb tool from this...
|
||||
COMPONENTS['bin']='OPENVDB_BUILD_VDB_PRINT,OPENVDB_BUILD_VDB_LOD,OPENVDB_BUILD_VDB_TOOL,OPENVDB_BUILD_VDB_TOOL_UNITTESTS'
|
||||
COMPONENTS['view']='OPENVDB_BUILD_VDB_VIEW'
|
||||
COMPONENTS['render']='OPENVDB_BUILD_VDB_RENDER'
|
||||
COMPONENTS['hou']='OPENVDB_BUILD_HOUDINI_PLUGIN'
|
||||
@@ -150,7 +156,11 @@ for comp in "${!COMPONENTS[@]}"; do
|
||||
setting="ON"; break
|
||||
fi
|
||||
done
|
||||
CMAKE_EXTRA+=("-D${COMPONENTS[$comp]}=$setting")
|
||||
OPENVDB_TOGGLES="${COMPONENTS[$comp]}"
|
||||
OPENVDB_TOGGLES=(${OPENVDB_TOGGLES//,/ })
|
||||
for toggle in "${!OPENVDB_TOGGLES[@]}"; do
|
||||
CMAKE_EXTRA+=("-D${OPENVDB_TOGGLES[$toggle]}=$setting")
|
||||
done
|
||||
done
|
||||
|
||||
################################################
|
||||
@@ -182,17 +192,14 @@ cd ${BUILD_DIR}
|
||||
set -x
|
||||
|
||||
# Note:
|
||||
# - print and lod binary options are always on and can be toggles with: OPENVDB_BUILD_BINARIES=ON/OFF
|
||||
# - OPENVDB_BUILD_BINARIES is always ON, but each binary will be toggled depending on the provided comps
|
||||
# - always enabled the python tests with OPENVDB_BUILD_PYTHON_UNITTESTS if the python module is in use,
|
||||
# regardless of the 'test' component being enabled or not (see the OPENVDB_BUILD_PYTHON_UNITTESTS option).
|
||||
cmake \
|
||||
-DOPENVDB_USE_DEPRECATED_ABI_10=ON \
|
||||
-DOPENVDB_USE_DEPRECATED_ABI_11=ON \
|
||||
-DOPENVDB_BUILD_VDB_PRINT=ON \
|
||||
-DOPENVDB_BUILD_VDB_LOD=ON \
|
||||
-DOPENVDB_BUILD_VDB_TOOL=ON \
|
||||
-DOPENVDB_BUILD_VDB_TOOL_UNITTESTS=ON \
|
||||
-DOPENVDB_TOOL_USE_NANO=OFF \
|
||||
-DOPENVDB_BUILD_BINARIES=ON \
|
||||
-DOPENVDB_BUILD_PYTHON_UNITTESTS=ON \
|
||||
-DMSVC_MP_THREAD_COUNT=${PARMS[-j]} \
|
||||
"${CMAKE_EXTRA[@]}" \
|
||||
|
||||
+22
-16
@@ -8,8 +8,14 @@ HOUDINI_MAJOR="$1"
|
||||
PLATFORM="$2"
|
||||
OTHER_ARGS="$3"
|
||||
|
||||
pip install --user requests
|
||||
python ci/download_houdini.py $HOUDINI_MAJOR $PLATFORM $OTHER_ARGS
|
||||
if [[ $PLATFORM =~ "linux" ]]; then
|
||||
pip3 install --user requests
|
||||
elif [[ $PLATFORM =~ "macos" ]]; then
|
||||
# Tell the homebrew install of pip3 that we don't care about installing directly through pip3
|
||||
pip3 install --user requests --break-system-packages
|
||||
fi
|
||||
|
||||
python3 ci/download_houdini.py $HOUDINI_MAJOR $PLATFORM $OTHER_ARGS
|
||||
|
||||
if [[ $PLATFORM =~ "linux" ]]; then
|
||||
# create dir hierarchy
|
||||
@@ -31,20 +37,20 @@ if [[ $PLATFORM =~ "linux" ]]; then
|
||||
ls -al dsolib/
|
||||
|
||||
# copy required libraries
|
||||
cp -r toolkit/cmake ../hou/toolkit/.
|
||||
cp -r toolkit/include ../hou/toolkit/.
|
||||
cp -r dsolib/libHoudini* ../hou/dsolib/.
|
||||
cp -r dsolib/libopenvdb_sesi* ../hou/dsolib/.
|
||||
cp -r dsolib/libblosc* ../hou/dsolib/.
|
||||
cp -r dsolib/libhboost* ../hou/dsolib/.
|
||||
cp -r dsolib/libz* ../hou/dsolib/.
|
||||
cp -r dsolib/libbz2* ../hou/dsolib/.
|
||||
cp -r dsolib/libtbb* ../hou/dsolib/.
|
||||
cp -r dsolib/libjemalloc* ../hou/dsolib/.
|
||||
cp -r dsolib/liblzma* ../hou/dsolib/.
|
||||
cp -r dsolib/libIex* ../hou/dsolib/.
|
||||
cp -r dsolib/libImath* ../hou/dsolib/.
|
||||
cp -r dsolib/libIlmThread* ../hou/dsolib/.
|
||||
mv toolkit/cmake ../hou/toolkit/.
|
||||
mv toolkit/include ../hou/toolkit/.
|
||||
mv dsolib/libHoudini* ../hou/dsolib/.
|
||||
mv dsolib/libopenvdb_sesi* ../hou/dsolib/.
|
||||
mv dsolib/libblosc* ../hou/dsolib/.
|
||||
mv dsolib/libhboost* ../hou/dsolib/.
|
||||
mv dsolib/libz* ../hou/dsolib/.
|
||||
mv dsolib/libbz2* ../hou/dsolib/.
|
||||
mv dsolib/libtbb* ../hou/dsolib/.
|
||||
mv dsolib/libjemalloc* ../hou/dsolib/.
|
||||
mv dsolib/liblzma* ../hou/dsolib/.
|
||||
mv dsolib/libIex* ../hou/dsolib/.
|
||||
mv dsolib/libImath* ../hou/dsolib/.
|
||||
mv dsolib/libIlmThread* ../hou/dsolib/.
|
||||
cd ..
|
||||
|
||||
elif [[ $PLATFORM =~ "macos" ]]; then
|
||||
|
||||
@@ -15,6 +15,6 @@ fi
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../.
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 ../.
|
||||
make -j8
|
||||
make install
|
||||
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright Contributors to the OpenVDB Project
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set -ex
|
||||
EXR_VERSION="$1"
|
||||
|
||||
git clone https://github.com/AcademySoftwareFoundation/openexr.git
|
||||
cd openexr
|
||||
|
||||
if [ "$EXR_VERSION" != "latest" ]; then
|
||||
git checkout tags/${EXR_VERSION} -b ${EXR_VERSION}
|
||||
fi
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake ..
|
||||
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
+7
-4
@@ -3,16 +3,19 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set -ex
|
||||
|
||||
GTEST_VERSION="$1"
|
||||
|
||||
|
||||
git clone https://github.com/google/googletest.git -b v${GTEST_VERSION}
|
||||
git clone https://github.com/google/googletest.git
|
||||
cd googletest
|
||||
|
||||
if [ "$GTEST_VERSION" != "latest" ]; then
|
||||
git checkout tags/${GTEST_VERSION} -b ${GTEST_VERSION}
|
||||
fi
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake ..
|
||||
|
||||
make -j$(nproc)
|
||||
|
||||
sudo make install
|
||||
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright Contributors to the OpenVDB Project
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set -ex
|
||||
IMATH_VERSION="$1"
|
||||
|
||||
git clone https://github.com/AcademySoftwareFoundation/Imath.git
|
||||
cd Imath
|
||||
|
||||
if [ "$IMATH_VERSION" != "latest" ]; then
|
||||
git checkout tags/${IMATH_VERSION} -b ${IMATH_VERSION}
|
||||
fi
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake ..
|
||||
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
@@ -4,26 +4,55 @@
|
||||
|
||||
set -ex
|
||||
|
||||
LLVM_CRT="$1"
|
||||
LLVM_VER="$1"
|
||||
LLVM_CRT="$2"
|
||||
|
||||
git clone -b llvmorg-14.0.0 --depth 1 https://github.com/llvm/llvm-project.git llvm
|
||||
# Legacy define for older versions of LLVM
|
||||
LLVM_LEGACY_CRT_DEFINE=""
|
||||
if [[ "${LLVM_CRT}" == "MultiThreaded" ]]; then
|
||||
LLVM_LEGACY_CRT_DEFINE="MT"
|
||||
elif [[ "${LLVM_CRT}" == "MultiThreadedDebug" ]]; then
|
||||
LLVM_LEGACY_CRT_DEFINE="MTd"
|
||||
elif [[ "${LLVM_CRT}" == "MultiThreadedDLL" ]]; then
|
||||
LLVM_LEGACY_CRT_DEFINE="MD"
|
||||
elif [[ "${LLVM_CRT}" == "MultiThreadedDebugDLL" ]]; then
|
||||
LLVM_LEGACY_CRT_DEFINE="MDd"
|
||||
fi
|
||||
|
||||
git clone -b llvmorg-${LLVM_VER} --depth 1 https://github.com/llvm/llvm-project.git llvm
|
||||
cd llvm
|
||||
|
||||
mkdir .build
|
||||
cd .build
|
||||
|
||||
# @note currently only test static builds with MT
|
||||
cmake -B . -S ../llvm -A x64 -G "Visual Studio 17 2022" -Thost=x64 \
|
||||
-DCMAKE_INSTALL_PREFIX="${HOME}/llvm_install" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_ENABLE_ASSERTIONS=OFF \
|
||||
-DLLVM_ENABLE_PROJECTS="" \
|
||||
-DLLVM_ENABLE_RUNTIMES="" \
|
||||
-DLLVM_ENABLE_UNWIND_TABLES=OFF \
|
||||
-DLLVM_TARGETS_TO_BUILD="host" \
|
||||
-DLLVM_BUILD_TOOLS=OFF \
|
||||
-DLLVM_INCLUDE_BENCHMARKS=OFF \
|
||||
-DLLVM_INCLUDE_EXAMPLES=OFF \
|
||||
-DLLVM_INCLUDE_TESTS=OFF \
|
||||
-DLLVM_INCLUDE_DOCS=OFF \
|
||||
-DLLVM_INCLUDE_TOOLS=OFF \
|
||||
-DLLVM_USE_CRT_RELEASE=${LLVM_CRT}
|
||||
-DLLVM_INCLUDE_UTILS=OFF \
|
||||
-DLLVM_BUILD_BENCHMARKS=OFF \
|
||||
-DLLVM_BUILD_EXAMPLES=OFF \
|
||||
-DLLVM_BUILD_TESTS=OFF \
|
||||
-DLLVM_BUILD_DOCS=OFF \
|
||||
-DLLVM_BUILD_TOOLS=OFF \
|
||||
-DLLVM_BUILD_UTILS=OFF \
|
||||
-DLLVM_ENABLE_BINDINGS=OFF \
|
||||
-DLLVM_ENABLE_OCAMLDOC=OFF \
|
||||
-DLLVM_PARALLEL_COMPILE_JOBS=4 \
|
||||
-DLLVM_PARALLEL_LINK_JOBS=4 \
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=${LLVM_CRT} \
|
||||
-DLLVM_USE_CRT_RELEASE=${LLVM_LEGACY_CRT_DEFINE}
|
||||
|
||||
cmake --build . --config Release --target install
|
||||
cmake --build . --parallel 4 --config Release --target install
|
||||
|
||||
cd ..
|
||||
|
||||
|
||||
+1
-2
@@ -22,6 +22,7 @@ brew install jq # for trivial parsing of brew json
|
||||
brew install openexr
|
||||
brew install nanobind # also installs the dependent python version
|
||||
brew install robin-map # required for nanobind
|
||||
brew install numpy # only for python tests
|
||||
brew install zlib
|
||||
brew install jemalloc
|
||||
|
||||
@@ -45,9 +46,7 @@ LLVM_VERSION=$1
|
||||
if [ ! -z "$LLVM_VERSION" ]; then
|
||||
if [ "$LLVM_VERSION" == "latest" ]; then
|
||||
brew install llvm
|
||||
brew install cppunit
|
||||
else
|
||||
brew install llvm@$LLVM_VERSION
|
||||
brew install cppunit
|
||||
fi
|
||||
fi
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright Contributors to the OpenVDB Project
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set -ex
|
||||
|
||||
# Download the TeX Live installer
|
||||
wget https://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz
|
||||
|
||||
# Extract
|
||||
tar xzf install-tl-unx.tar.gz
|
||||
rm install-tl-unx.tar.gz
|
||||
cd install-tl-*
|
||||
|
||||
# Run the installer with no docs or source installation
|
||||
./install-tl --no-interaction --no-doc-install --no-src-install --scheme=minimal
|
||||
|
||||
# Export the texlive PATH to GITHUB_PATH
|
||||
TEXLIVE_PATH=/usr/local/texlive/2025/bin/x86_64-linux
|
||||
export PATH=${TEXLIVE_PATH}:${PATH}
|
||||
echo "${TEXLIVE_PATH}" >> $GITHUB_PATH
|
||||
|
||||
# Install the required packages
|
||||
tlmgr install latex-bin dvips collection-fontsrecommended collection-latexrecommended
|
||||
|
||||
# Validate that installing dvips and latex has been successful
|
||||
dvips --version
|
||||
latex --version
|
||||
+1
-1
@@ -10,7 +10,7 @@ set -e
|
||||
# the expected VDB installation
|
||||
|
||||
cmakelists="
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(TestInstall LANGUAGES CXX)
|
||||
find_package(OpenVDB REQUIRED COMPONENTS openvdb)
|
||||
add_executable(test_vdb_print \"../openvdb_cmd/vdb_print/main.cc\")
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
|
||||
|
||||
set(CPM_DOWNLOAD_VERSION 0.40.2)
|
||||
set(CPM_HASH_SUM "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d")
|
||||
|
||||
if(CPM_SOURCE_CACHE)
|
||||
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
|
||||
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
else()
|
||||
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
endif()
|
||||
|
||||
# Expand relative path. This is important if the provided path contains a tilde (~)
|
||||
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
|
||||
|
||||
file(DOWNLOAD
|
||||
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
|
||||
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
|
||||
)
|
||||
|
||||
include(${CPM_DOWNLOAD_LOCATION})
|
||||
@@ -88,7 +88,7 @@ may be provided to tell this module where to look.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
mark_as_advanced(
|
||||
|
||||
@@ -1,320 +0,0 @@
|
||||
# Copyright Contributors to the OpenVDB Project
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
#[=======================================================================[.rst:
|
||||
|
||||
FindCppUnit
|
||||
-----------
|
||||
|
||||
Find CppUnit include dirs and libraries
|
||||
|
||||
Use this module by invoking find_package with the form::
|
||||
|
||||
find_package(CppUnit
|
||||
[version] [EXACT] # Minimum or EXACT version
|
||||
[REQUIRED] # Fail with error if CppUnit is not found
|
||||
)
|
||||
|
||||
|
||||
IMPORTED Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
``CppUnit::cppunit``
|
||||
This module defines IMPORTED target CppUnit::cppunit, if CppUnit has been
|
||||
found.
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This will define the following variables:
|
||||
|
||||
``CppUnit_FOUND``
|
||||
True if the system has the CppUnit library.
|
||||
``CppUnit_VERSION``
|
||||
The version of the CppUnit library which was found.
|
||||
``CppUnit_INCLUDE_DIRS``
|
||||
Include directories needed to use CppUnit.
|
||||
``CppUnit_RELEASE_LIBRARIES``
|
||||
Libraries needed to link to the release version of CppUnit.
|
||||
``CppUnit_RELEASE_LIBRARY_DIRS``
|
||||
CppUnit release library directories.
|
||||
``CppUnit_DEBUG_LIBRARIES``
|
||||
Libraries needed to link to the debug version of CppUnit.
|
||||
``CppUnit_DEBUG_LIBRARY_DIRS``
|
||||
CppUnit debug library directories.
|
||||
|
||||
Deprecated - use [RELEASE|DEBUG] variants:
|
||||
|
||||
``CppUnit_LIBRARIES``
|
||||
Libraries needed to link to CppUnit.
|
||||
``CppUnit_LIBRARY_DIRS``
|
||||
CppUnit library directories.
|
||||
|
||||
Cache Variables
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The following cache variables may also be set:
|
||||
|
||||
``CppUnit_INCLUDE_DIR``
|
||||
The directory containing ``cppunit/Portability.h``.
|
||||
``CppUnit_LIBRARY``
|
||||
The path to the CppUnit library.
|
||||
|
||||
Hints
|
||||
^^^^^
|
||||
|
||||
Instead of explicitly setting the cache variables, the following variables
|
||||
may be provided to tell this module where to look.
|
||||
|
||||
``CppUnit_ROOT``
|
||||
Preferred installation prefix.
|
||||
``CPPUNIT_INCLUDEDIR``
|
||||
Preferred include directory e.g. <prefix>/include
|
||||
``CPPUNIT_LIBRARYDIR``
|
||||
Preferred library directory e.g. <prefix>/lib
|
||||
``CPPUNIT_DEBUG_SUFFIX``
|
||||
Suffix of the debug version of CppUnit libs. Defaults to "_d".
|
||||
``SYSTEM_LIBRARY_PATHS``
|
||||
Global list of library paths intended to be searched by and find_xxx call
|
||||
``CPPUNIT_USE_STATIC_LIBS``
|
||||
Only search for static cppunit libraries
|
||||
``DISABLE_CMAKE_SEARCH_PATHS``
|
||||
Disable CMakes default search paths for find_xxx calls in this module
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
|
||||
mark_as_advanced(
|
||||
CppUnit_INCLUDE_DIR
|
||||
CppUnit_LIBRARY
|
||||
)
|
||||
|
||||
set(_FIND_CPPUNIT_ADDITIONAL_OPTIONS "")
|
||||
if(DISABLE_CMAKE_SEARCH_PATHS)
|
||||
set(_FIND_CPPUNIT_ADDITIONAL_OPTIONS NO_DEFAULT_PATH)
|
||||
endif()
|
||||
|
||||
# Set _CPPUNIT_ROOT based on a user provided root var. Xxx_ROOT and ENV{Xxx_ROOT}
|
||||
# are prioritised over the legacy capitalized XXX_ROOT variables for matching
|
||||
# CMake 3.12 behaviour
|
||||
# @todo deprecate -D and ENV CPPUNIT_ROOT from CMake 3.12
|
||||
if(CppUnit_ROOT)
|
||||
set(_CPPUNIT_ROOT ${CppUnit_ROOT})
|
||||
elseif(DEFINED ENV{CppUnit_ROOT})
|
||||
set(_CPPUNIT_ROOT $ENV{CppUnit_ROOT})
|
||||
elseif(CPPUNIT_ROOT)
|
||||
set(_CPPUNIT_ROOT ${CPPUNIT_ROOT})
|
||||
elseif(DEFINED ENV{CPPUNIT_ROOT})
|
||||
set(_CPPUNIT_ROOT $ENV{CPPUNIT_ROOT})
|
||||
endif()
|
||||
|
||||
# Additionally try and use pkconfig to find cppunit
|
||||
if(USE_PKGCONFIG)
|
||||
if(NOT DEFINED PKG_CONFIG_FOUND)
|
||||
find_package(PkgConfig)
|
||||
endif()
|
||||
pkg_check_modules(PC_CppUnit QUIET cppunit)
|
||||
endif()
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Search for CppUnit include DIR
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
set(_CPPUNIT_INCLUDE_SEARCH_DIRS "")
|
||||
list(APPEND _CPPUNIT_INCLUDE_SEARCH_DIRS
|
||||
${CPPUNIT_INCLUDEDIR}
|
||||
${_CPPUNIT_ROOT}
|
||||
${PC_CppUnit_INCLUDEDIR}
|
||||
${SYSTEM_LIBRARY_PATHS}
|
||||
)
|
||||
|
||||
# Look for a standard cppunit header file.
|
||||
find_path(CppUnit_INCLUDE_DIR cppunit/Portability.h
|
||||
${_FIND_CPPUNIT_ADDITIONAL_OPTIONS}
|
||||
PATHS ${_CPPUNIT_INCLUDE_SEARCH_DIRS}
|
||||
PATH_SUFFIXES ${CMAKE_INSTALL_INCLUDEDIR} include
|
||||
)
|
||||
|
||||
if(EXISTS "${CppUnit_INCLUDE_DIR}/cppunit/Portability.h")
|
||||
file(STRINGS "${CppUnit_INCLUDE_DIR}/cppunit/Portability.h"
|
||||
_cppunit_version_string REGEX "#define CPPUNIT_VERSION "
|
||||
)
|
||||
string(REGEX REPLACE "#define CPPUNIT_VERSION +\"(.+)\".*$" "\\1"
|
||||
_cppunit_version_string "${_cppunit_version_string}"
|
||||
)
|
||||
string(STRIP "${_cppunit_version_string}" CppUnit_VERSION)
|
||||
unset(_cppunit_version_string )
|
||||
endif()
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Search for CppUnit lib DIR
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
if(NOT DEFINED CPPUNIT_DEBUG_SUFFIX)
|
||||
set(CPPUNIT_DEBUG_SUFFIX "d")
|
||||
endif()
|
||||
|
||||
set(_CPPUNIT_LIBRARYDIR_SEARCH_DIRS "")
|
||||
list(APPEND _CPPUNIT_LIBRARYDIR_SEARCH_DIRS
|
||||
${CPPUNIT_LIBRARYDIR}
|
||||
${_CPPUNIT_ROOT}
|
||||
${PC_CppUnit_LIBDIR}
|
||||
${SYSTEM_LIBRARY_PATHS}
|
||||
)
|
||||
|
||||
# Library suffix handling
|
||||
|
||||
set(_CPPUNIT_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
|
||||
if(MSVC)
|
||||
if(CPPUNIT_USE_STATIC_LIBS)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
|
||||
else()
|
||||
list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES "_dll.lib")
|
||||
endif()
|
||||
else()
|
||||
if(CPPUNIT_USE_STATIC_LIBS)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(APPEND CPPUNIT_BUILD_TYPES RELEASE DEBUG)
|
||||
|
||||
foreach(BUILD_TYPE ${CPPUNIT_BUILD_TYPES})
|
||||
set(_CPPUNIT_LIB_NAME cppunit)
|
||||
if(BUILD_TYPE STREQUAL DEBUG)
|
||||
set(_CPPUNIT_LIB_NAME "${_CPPUNIT_LIB_NAME}${CPPUNIT_DEBUG_SUFFIX}")
|
||||
endif()
|
||||
|
||||
# Find the lib
|
||||
find_library(CppUnit_LIBRARY_${BUILD_TYPE} ${_CPPUNIT_LIB_NAME}
|
||||
${_FIND_CPPUNIT_ADDITIONAL_OPTIONS}
|
||||
PATHS ${_CPPUNIT_LIBRARYDIR_SEARCH_DIRS}
|
||||
PATH_SUFFIXES ${CMAKE_INSTALL_LIBDIR} lib64 lib
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# Reset library suffix
|
||||
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_CPPUNIT_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
unset(_CPPUNIT_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES)
|
||||
|
||||
if(CppUnit_LIBRARY_DEBUG AND CppUnit_LIBRARY_RELEASE)
|
||||
# if the generator is multi-config or if CMAKE_BUILD_TYPE is set for
|
||||
# single-config generators, set optimized and debug libraries
|
||||
get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(_isMultiConfig OR CMAKE_BUILD_TYPE)
|
||||
set(CppUnit_LIBRARY optimized ${CppUnit_LIBRARY_RELEASE} debug ${CppUnit_LIBRARY_DEBUG})
|
||||
else()
|
||||
# For single-config generators where CMAKE_BUILD_TYPE has no value,
|
||||
# just use the release libraries
|
||||
set(CppUnit_LIBRARY ${CppUnit_LIBRARY_RELEASE})
|
||||
endif()
|
||||
# FIXME: This probably should be set for both cases
|
||||
set(CppUnit_LIBRARIES optimized ${CppUnit_LIBRARY_RELEASE} debug ${CppUnit_LIBRARY_DEBUG})
|
||||
endif()
|
||||
|
||||
# if only the release version was found, set the debug variable also to the release version
|
||||
if(CppUnit_LIBRARY_RELEASE AND NOT CppUnit_LIBRARY_DEBUG)
|
||||
set(CppUnit_LIBRARY_DEBUG ${CppUnit_LIBRARY_RELEASE})
|
||||
set(CppUnit_LIBRARY ${CppUnit_LIBRARY_RELEASE})
|
||||
set(CppUnit_LIBRARIES ${CppUnit_LIBRARY_RELEASE})
|
||||
endif()
|
||||
|
||||
# if only the debug version was found, set the release variable also to the debug version
|
||||
if(CppUnit_LIBRARY_DEBUG AND NOT CppUnit_LIBRARY_RELEASE)
|
||||
set(CppUnit_LIBRARY_RELEASE ${CppUnit_LIBRARY_DEBUG})
|
||||
set(CppUnit_LIBRARY ${CppUnit_LIBRARY_DEBUG})
|
||||
set(CppUnit_LIBRARIES ${CppUnit_LIBRARY_DEBUG})
|
||||
endif()
|
||||
|
||||
# If the debug & release library ends up being the same, omit the keywords
|
||||
if("${CppUnit_LIBRARY_RELEASE}" STREQUAL "${CppUnit_LIBRARY_DEBUG}")
|
||||
set(CppUnit_LIBRARY ${CppUnit_LIBRARY_RELEASE} )
|
||||
set(CppUnit_LIBRARIES ${CppUnit_LIBRARY_RELEASE} )
|
||||
endif()
|
||||
|
||||
if(CppUnit_LIBRARY)
|
||||
set(CppUnit_FOUND TRUE)
|
||||
else()
|
||||
set(CppUnit_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Cache and set CppUnit_FOUND
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(CppUnit
|
||||
FOUND_VAR CppUnit_FOUND
|
||||
REQUIRED_VARS
|
||||
CppUnit_LIBRARY
|
||||
CppUnit_INCLUDE_DIR
|
||||
VERSION_VAR CppUnit_VERSION
|
||||
)
|
||||
|
||||
if(NOT CppUnit_FOUND)
|
||||
if(CppUnit_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Unable to find CppUnit")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Partition release/debug lib vars
|
||||
|
||||
set(CppUnit_RELEASE_LIBRARIES ${CppUnit_LIBRARY_RELEASE})
|
||||
get_filename_component(CppUnit_RELEASE_LIBRARY_DIRS ${CppUnit_RELEASE_LIBRARIES} DIRECTORY)
|
||||
set(CppUnit_DEBUG_LIBRARIES ${CppUnit_LIBRARY_DEBUG})
|
||||
get_filename_component(CppUnit_DEBUG_LIBRARY_DIRS ${CppUnit_DEBUG_LIBRARIES} DIRECTORY)
|
||||
set(CppUnit_LIBRARIES ${CppUnit_RELEASE_LIBRARIES})
|
||||
set(CppUnit_LIBRARY_DIRS ${CppUnit_RELEASE_LIBRARY_DIRS})
|
||||
set(CppUnit_INCLUDE_DIRS ${CppUnit_INCLUDE_DIR})
|
||||
|
||||
# Configure lib type. If XXX_USE_STATIC_LIBS, we always assume a static
|
||||
# lib is in use. If win32, we can't mark the import .libs as shared, so
|
||||
# these are always marked as UNKNOWN. Otherwise, infer from extension.
|
||||
set(CPPUNIT_LIB_TYPE UNKNOWN)
|
||||
if(CPPUNIT_USE_STATIC_LIBS)
|
||||
set(CPPUNIT_LIB_TYPE STATIC)
|
||||
elseif(UNIX)
|
||||
get_filename_component(_CPPUNIT_EXT ${CppUnit_LIBRARY_RELEASE} EXT)
|
||||
if(_CPPUNIT_EXT STREQUAL ".a")
|
||||
set(CPPUNIT_LIB_TYPE STATIC)
|
||||
elseif(_CPPUNIT_EXT STREQUAL ".so" OR
|
||||
_CPPUNIT_EXT STREQUAL ".dylib")
|
||||
set(CPPUNIT_LIB_TYPE SHARED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET CppUnit::cppunit)
|
||||
add_library(CppUnit::cppunit ${CPPUNIT_LIB_TYPE} IMPORTED)
|
||||
set_target_properties(CppUnit::cppunit PROPERTIES
|
||||
INTERFACE_COMPILE_OPTIONS "${PC_CppUnit_CFLAGS_OTHER}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${CppUnit_INCLUDE_DIRS}")
|
||||
|
||||
# Standard location
|
||||
set_target_properties(CppUnit::cppunit PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
||||
IMPORTED_LOCATION "${CppUnit_LIBRARY}")
|
||||
|
||||
# Release location
|
||||
if(EXISTS "${CppUnit_LIBRARY_RELEASE}")
|
||||
set_property(TARGET CppUnit::cppunit APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(CppUnit::cppunit PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX"
|
||||
IMPORTED_LOCATION_RELEASE "${CppUnit_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
|
||||
# Debug location
|
||||
if(EXISTS "${CppUnit_LIBRARY_DEBUG}")
|
||||
set_property(TARGET CppUnit::cppunit APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS DEBUG)
|
||||
set_target_properties(CppUnit::cppunit PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "CXX"
|
||||
IMPORTED_LOCATION_DEBUG "${CppUnit_LIBRARY_DEBUG}")
|
||||
endif()
|
||||
endif()
|
||||
@@ -64,7 +64,7 @@ may be provided to tell this module where to look.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ may be provided to tell this module where to look.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
include (FindPackageHandleStandardArgs)
|
||||
|
||||
if(NCCL_ROOT)
|
||||
set(_NCCL_ROOT ${NCCL_ROOT})
|
||||
elseif(DEFINED ENV{NCCL_ROOT})
|
||||
set(_NCCL_ROOT $ENV{NCCL_ROOT})
|
||||
endif()
|
||||
|
||||
set(_NCCL_INCLUDE_SEARCH_DIRS "")
|
||||
list(APPEND _NCCL_INCLUDE_SEARCH_DIRS
|
||||
${_NCCL_ROOT}/include
|
||||
)
|
||||
find_path(NCCL_INCLUDE_DIR
|
||||
NAMES nccl.h
|
||||
PATHS ${_NCCL_INCLUDE_SEARCH_DIRS}
|
||||
)
|
||||
|
||||
set(_NCCL_LIBRARY_SEARCH_DIRS "")
|
||||
list(APPEND _NCCL_LIBRARY_SEARCH_DIRS
|
||||
${_NCCL_ROOT}/bin
|
||||
)
|
||||
find_library(NCCL_LIBRARY
|
||||
NAMES nccl
|
||||
PATHS ${_NCCL_LIBRARY_SEARCH_DIRS}
|
||||
)
|
||||
|
||||
find_package_handle_standard_args(NCCL REQUIRED_VARS NCCL_INCLUDE_DIR NCCL_LIBRARY)
|
||||
|
||||
if (NCCL_FOUND)
|
||||
mark_as_advanced(NCCL_INCLUDE_DIR)
|
||||
mark_as_advanced(NCCL_LIBRARY)
|
||||
endif()
|
||||
|
||||
if (NCCL_FOUND AND NOT TARGET NCCL::NCCL)
|
||||
add_library(NCCL::NCCL SHARED IMPORTED)
|
||||
if(MSVC)
|
||||
string(REPLACE ".lib" ".dll" NCCL_DLL ${NCCL_LIBRARY})
|
||||
set_property(TARGET NCCL::NCCL PROPERTY IMPORTED_LOCATION ${NCCL_DLL})
|
||||
set_property(TARGET NCCL::NCCL PROPERTY IMPORTED_IMPLIB ${NCCL_LIBRARY})
|
||||
else()
|
||||
set_property(TARGET NCCL::NCCL PROPERTY IMPORTED_LOCATION ${NCCL_LIBRARY})
|
||||
endif()
|
||||
target_include_directories(NCCL::NCCL INTERFACE ${NCCL_INCLUDE_DIR})
|
||||
endif()
|
||||
@@ -93,7 +93,7 @@ may be provided to tell this module where to look.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ may be provided to tell this module where to look.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ may be provided to tell this module where to look.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ The following variables may be provided to tell this module where to look.
|
||||
# Find the glfw3 installation and use glfw's CMake to initialize
|
||||
# the glfw lib
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
set(_FIND_GLFW3_ADDITIONAL_OPTIONS "")
|
||||
if(DISABLE_CMAKE_SEARCH_PATHS)
|
||||
|
||||
@@ -73,7 +73,7 @@ may be provided to tell this module where to look.
|
||||
# Find the Houdini installation and use Houdini's CMake to initialize
|
||||
# the Houdini lib
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
# Include utility functions for version information
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/OpenVDBUtils.cmake)
|
||||
|
||||
@@ -51,7 +51,7 @@ variables may be provided to tell this module where to look.
|
||||
# Find the Maya installation and use Maya's CMake to initialize
|
||||
# the Maya lib
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
|
||||
set(_FIND_MAYA_ADDITIONAL_OPTIONS "")
|
||||
|
||||
@@ -57,7 +57,7 @@ The following functions are provided:
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
|
||||
function(OPENVDB_GET_VERSION_DEFINE HEADER KEY VALUE)
|
||||
|
||||
@@ -16,7 +16,7 @@ existing from a previous run of cmake.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt")
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
###############################################################################
|
||||
|
||||
@@ -27,7 +27,9 @@ set(OPENVDB_DEPRECATED_ABI_LIST ${MINIMUM_OPENVDB_ABI_VERSION} ${_PREV_ABI})
|
||||
unset(_PREV_ABI)
|
||||
|
||||
if(NOT DISABLE_DEPENDENCY_VERSION_CHECKS)
|
||||
# @note Currently tracking CY2022 of the VFX platform where available
|
||||
# @note Currently tracking CY2024 of the VFX platform where available
|
||||
# (that is, our minimum requirements are for the current VFX platform)
|
||||
# minus 2 years to be able to build with legacy ABI)
|
||||
|
||||
# @Note Compiler versions are not really a hard and fast rule, you just need
|
||||
# a compiler with complete support for our MINIMUM_CXX_STANDARD (currently 17).
|
||||
@@ -39,32 +41,27 @@ if(NOT DISABLE_DEPENDENCY_VERSION_CHECKS)
|
||||
set(MINIMUM_ICC_VERSION 19)
|
||||
set(MINIMUM_MSVC_VERSION 19.30) # 1928 (Visual Studio 2019 Version 16.8 + 16.9)
|
||||
|
||||
set(MINIMUM_BOOST_VERSION 1.80)
|
||||
# Nanobind does not store the version in the CMake config file in 2.0.0.
|
||||
# This issue was fixed in 2.1.0 so next time we bump the minimum version,
|
||||
# we can define this.
|
||||
#set(MINIMUM_NANOBIND_VERSION 2.0.0)
|
||||
set(MINIMUM_IMATH_VERSION 3.1)
|
||||
set(MINIMUM_OPENEXR_VERSION 3.1)
|
||||
set(MINIMUM_BOOST_VERSION 1.82)
|
||||
set(MINIMUM_OPENEXR_VERSION 3.2)
|
||||
set(MINIMUM_IMATH_VERSION 3.2)
|
||||
set(MINIMUM_ZLIB_VERSION 1.2.7)
|
||||
set(MINIMUM_TBB_VERSION 2020.3)
|
||||
set(MINIMUM_LLVM_VERSION 13.0.0)
|
||||
set(MINIMUM_LLVM_VERSION 15.0.0)
|
||||
set(MINIMUM_BLOSC_VERSION 1.17.0)
|
||||
set(MINIMUM_GLFW_VERSION 3.1)
|
||||
set(MINIMUM_GLFW_VERSION 3.3)
|
||||
|
||||
set(MINIMUM_PYTHON_VERSION 3.10)
|
||||
set(MINIMUM_NUMPY_VERSION 1.23.0)
|
||||
set(MINIMUM_PYTHON_VERSION 3.11)
|
||||
|
||||
set(MINIMUM_GOOGLETEST_VERSION 1.10)
|
||||
set(MINIMUM_LOG4CPLUS_VERSION 1.1.2)
|
||||
set(MINIMUM_HOUDINI_VERSION 19.5)
|
||||
set(MINIMUM_LOG4CPLUS_VERSION 2.0)
|
||||
set(MINIMUM_HOUDINI_VERSION 20.5)
|
||||
|
||||
# These always promote warnings rather than errors
|
||||
set(MINIMUM_MAYA_VERSION 2017)
|
||||
set(MINIMUM_DOXYGEN_VERSION 1.8.8)
|
||||
endif()
|
||||
|
||||
# VFX 23 deprecations to transition to MINIMUM_* variables in OpenVDB 12.0.0
|
||||
# VFX 24 deprecations to transition to MINIMUM_* variables in OpenVDB 14.0.0
|
||||
# @note At the time of writing, any variables that are commented out don't
|
||||
# have target transitional versions.
|
||||
|
||||
@@ -73,16 +70,16 @@ endif()
|
||||
# set(FUTURE_MINIMUM_ICC_VERSION 19)
|
||||
|
||||
# set(FUTURE_MINIMUM_CXX_STANDARD 20)
|
||||
set(FUTURE_MINIMUM_CMAKE_VERSION 3.24)
|
||||
set(FUTURE_MINIMUM_OPENEXR_VERSION 3.2)
|
||||
set(FUTURE_MINIMUM_BOOST_VERSION 1.82)
|
||||
set(FUTURE_MINIMUM_GLFW_VERSION 3.3)
|
||||
set(FUTURE_MINIMUM_LOG4CPLUS_VERSION 2.0)
|
||||
# set(FUTURE_MINIMUM_NANOBIND_VERSION 2.1.0)
|
||||
set(FUTURE_MINIMUM_CMAKE_VERSION 4.0)
|
||||
set(FUTURE_MINIMUM_OPENEXR_VERSION 3.3)
|
||||
set(FUTURE_MINIMUM_IMATH_VERSION 3.3)
|
||||
set(FUTURE_MINIMUM_TBB_VERSION 2021)
|
||||
set(FUTURE_MINIMUM_BOOST_VERSION 1.85)
|
||||
#set(FUTURE_MINIMUM_GLFW_VERSION 3.3)
|
||||
#set(FUTURE_MINIMUM_LOG4CPLUS_VERSION 2.0)
|
||||
set(FUTURE_MINIMUM_NANOBIND_VERSION 2.5.0)
|
||||
|
||||
# set(FUTURE_MINIMUM_BLOSC_VERSION 1.17.0)
|
||||
# set(FUTURE_MINIMUM_TBB_VERSION 2020.3)
|
||||
set(FUTURE_MINIMUM_PYTHON_VERSION 3.11)
|
||||
set(FUTURE_MINIMUM_NUMPY_VERSION 1.26.0)
|
||||
# set(FUTURE_MINIMUM_PYTHON_VERSION 3.11)
|
||||
# set(FUTURE_MINIMUM_HOUDINI_VERSION 20.0)
|
||||
set(FUTURE_MINIMUM_LLVM_VERSION 15.0.0)
|
||||
set(FUTURE_MINIMUM_LLVM_VERSION 18.0.0)
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(OpenVDBDocs LANGUAGES NONE)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
+6
-8
@@ -238,8 +238,8 @@ it's a good idea to read the above section on
|
||||
|
||||
DCC | Supported Version | OpenVDB ABI |
|
||||
-------- | ----------------- | ----------- |
|
||||
Houdini | 20.0 | 10 |
|
||||
Houdini | 20.5 | 11 |
|
||||
Houdini | 21.0 | 12 |
|
||||
|
||||
@subsection buildBuildHou Building Against Houdini
|
||||
|
||||
@@ -257,12 +257,10 @@ CMake | Cross-platform family of tools designed to help build software
|
||||
C++17 Compiler | Matching Houdini compiler and version | All |
|
||||
Boost | Components: iostreams | All |
|
||||
GoogleTest | A unit testing framework module for C++ | Unit Tests |
|
||||
CppUnit | A unit testing framework module for C++ | Unit Tests (AX) |
|
||||
GLFW | Simple API for OpenGL development | OpenVDB View |
|
||||
Doxygen | Documentation generation from C++ | Documentation |
|
||||
Log4cplus | An optional dependency for improved OpenVDB Logging | Optional (All) |
|
||||
nanobind | C++/python bindings | Optional (Python) |
|
||||
NumPy | Scientific computing with Python | Optional (Python) |
|
||||
LLVM | Target-independent code generation | OpenVDB AX |
|
||||
|
||||
At a minimum a matching C++17 compiler and CMake will be required. See
|
||||
@@ -349,14 +347,12 @@ Boost | Components: iostreams
|
||||
ZLIB | Compression library for disk serialization compression | All |
|
||||
Blosc | Recommended dependency for improved disk compression | All* |
|
||||
GoogleTest | A unit testing framework module for C++ | Unit Tests |
|
||||
CppUnit | A unit testing framework module for C++ | Unit Tests (AX) |
|
||||
GLFW | Simple API for OpenGL development | OpenVDB View |
|
||||
Doxygen | Documentation generation from C++ | Documentation |
|
||||
OpenEXR | EXR serialization support | OpenVDB Render |
|
||||
Imath | Used half precision floating points and EXR serialization support | Optional (All) |
|
||||
Log4cplus | An optional dependency for improved OpenVDB Logging | Optional (All) |
|
||||
nanobind | C++/python bindings | Optional (Python) |
|
||||
NumPy | Scientific computing with Python | Optional (Python) |
|
||||
LLVM | Target-independent code generation | OpenVDB AX |
|
||||
|
||||
|
||||
@@ -512,7 +508,7 @@ You can add the below CMake snippet to your main `CMakeLists.txt` to bring in
|
||||
OpenVDB as a dependency:
|
||||
|
||||
@code{.cmake}
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
list(APPEND CMAKE_MODULE_PATH "/location/of/openvdb/install/lib/cmake/OpenVDB")
|
||||
find_package(OpenVDB REQUIRED)
|
||||
target_link_libraries(myapp OpenVDB::openvdb)
|
||||
@@ -605,10 +601,12 @@ OpenVDB uses [imported targets](https://cmake.org/cmake/help/latest/command/add_
|
||||
for all its dependencies. For imported Boost compatibility, the following
|
||||
versions of CMake are required:
|
||||
|
||||
- Boost 1.80 requires CMake 3.24.2 or newer.
|
||||
- Boost 1.81 requires CMake 3.25.2 or newer.
|
||||
- Boost 1.82 requires CMake 3.27.0 or newer.
|
||||
- Boost 1.83 requires CMake 3.27.4 or newer.
|
||||
- Boost 1.84 requires CMake 3.28.2 or newer.
|
||||
- Boost 1.85 requires CMake 3.29.3 or newer.
|
||||
- Boost 1.86 requires CMake 3.30.6, 3.31.1, or newer.
|
||||
- Boost 1.87 requires CMake 3.31.4 or newer.
|
||||
|
||||
Use the above list to identify the version of CMake you require for your version
|
||||
of Boost. Note that this may be implemented into the OpenVDB CMake in the future.
|
||||
|
||||
+154
-2
@@ -2,9 +2,161 @@
|
||||
|
||||
@page changes Release Notes
|
||||
|
||||
@htmlonly <a name="v12_0_2_changes"></a>@endhtmlonly
|
||||
@htmlonly <a name="v12_1_2_changes"></a>@endhtmlonly
|
||||
@par
|
||||
<B>Version 12.0.2</B> - <I>In development</I>
|
||||
<B>Version 12.1.2</B> - <I>In development</I>
|
||||
|
||||
|
||||
@htmlonly <a name="v12_1_1_changes"></a>@endhtmlonly
|
||||
@par
|
||||
<B>Version 12.1.1</B> - <I>September 30, 2025</I>
|
||||
|
||||
@par
|
||||
OpenVDB AX:
|
||||
- Fixes:
|
||||
- Fixed an issue which was causing host specific optimization passes to be
|
||||
omitted when using LLVM 16 and higher.
|
||||
|
||||
@par
|
||||
Houdini:
|
||||
- Fixed a compilation issue with the OpenVDB Activate SOP with Houdini 21
|
||||
[Reported by @callen128]
|
||||
|
||||
|
||||
@htmlonly <a name="v12_1_0_changes"></a>@endhtmlonly
|
||||
@par
|
||||
<B>Version 12.1.0</B> - <I>August 8, 2025</I>
|
||||
|
||||
@par
|
||||
OpenVDB:
|
||||
- New features:
|
||||
- New level set constructor tools::createLevelSetCapsule creates a grid
|
||||
containing a narrow-band level set representation of a capsule (tube
|
||||
with constant radius and sphere caps).
|
||||
- New level set constructor tools::createLevelSetTaperedCapsule creates a
|
||||
grid containing a narrow-band level set representation of a tapered
|
||||
capsule (tube with sphere caps and different radii at both ends, or
|
||||
equivalently the convex hull of two spheres with possibly different
|
||||
centers and radii).
|
||||
- New level set constructor tools::createLevelSetTubeComplex creates a grid
|
||||
containing a narrow-band level set representation of a tube complex (a
|
||||
collection of capsules or tapered capsules defined by endpoint coordinates
|
||||
and segment indices).
|
||||
- New level set constructor tools::createLevelSetDilatedMesh creates a grid
|
||||
containing a narrow-band level set representation of a dilated surface
|
||||
mesh (dilated by a radius in all directions).
|
||||
- New class tools::ConvexVoxelizer, used by the above methods, which can be
|
||||
inherited to implement a convex level set constructor.
|
||||
- Introduced anisotropic surfacing tools in points/PointRasterizeSDF.h and
|
||||
principal component analysis (PCA) methods in points/PrincipalComponentAnalysis.h.
|
||||
The latter tool analyses point neighborhoods and computes affine
|
||||
transformations representing elliptical distributions. The prior tool
|
||||
takes rotational and stretch point attributes (optionally computed from
|
||||
the prior PCA methods) and generates anisotropic surfaces.
|
||||
|
||||
- Improvements:
|
||||
- Significantly improved the performance of all LeafNode ValueIterators,
|
||||
up to 5x on some platforms and up to 10x when delay loading is enabled.
|
||||
Construction of a ValueIterator from a leaf node now requests the leaf
|
||||
buffers ahead of iteration to avoid potentially expensive API calls.
|
||||
- Improved the performance of PointRasterizeSDF.h tools with larger radii
|
||||
with better bound computation approximations.
|
||||
- Significant performance improvement to delay loaded files - the warning
|
||||
logic which checks the underlying mapped file for the last modified time
|
||||
has been removed, resulting in performance gains upwards of 10x when
|
||||
loading in node data buffers. Note that the check that was removed is
|
||||
entirely redundant on Windows and would only print a warning on other
|
||||
system.
|
||||
[Contributed by jjtParadox]
|
||||
- Added OPENVDB_HAS_INVOKABLE_MEMBER_FUNCTION and OPENVDB_HAS_MEMBER_FUNCTION
|
||||
macros to help determine the presence and compatibility of interface
|
||||
member functions at compile time.
|
||||
- Added OPENVDB_ASSUME macros to mimic builtin assume and C++23 assume
|
||||
attributes.
|
||||
- Improved read and write of PLY files with vdb_tool so they support Vec3d
|
||||
and 64 bit attributes. Changed vdb_tool version number from 10.6.1 to
|
||||
10.7.0.
|
||||
|
||||
- API Changes:
|
||||
- Mat3 and Mat4 functions have been marked [[nodiscard]] where it may be
|
||||
confusing if they are operating in place or returning a result, in
|
||||
particular tranpose, invert, adjoint, and timesDiagonal.
|
||||
- Re-worked the API in PointRasterizeSDF.h with the addition of anisotropic
|
||||
surfacing kernels. The new API exposes more concise free functions which
|
||||
take structs of parameters. The old API remains supported but is
|
||||
deprecated.
|
||||
- Deprecated providing a filter or interrupter as arguments to
|
||||
points::rasterize. These can instead be implemented on user transfer
|
||||
schemes.
|
||||
- Transfer schemes provided to points::rasterize can now optionally
|
||||
implement a rasterizePoints callback that takes a range of points per
|
||||
voxel.
|
||||
|
||||
- Fixes:
|
||||
- Fixed a bug in points::rasterizeSpheres and points::rasterizeSmoothSpheres
|
||||
which could cause sections of the generated surface to incorrectly be
|
||||
marked as negative interior values.
|
||||
- Fixed a bug in points::rasterizeSmoothSpheres which could result in
|
||||
incomplete surface reconstruction with significantly larger search
|
||||
distances.
|
||||
- Fixed some precision issues in various Matrix methods.
|
||||
- Fixed bugs in vdb_tool that prevented build with newer namespaces in
|
||||
nanovdb.
|
||||
|
||||
@par
|
||||
OpenVDB AX:
|
||||
- Improvements:
|
||||
- Added support for LLVM versions greater than 15. Upgraded CI to test with
|
||||
LLVM versions 16, 17, 18, 19 and 20.
|
||||
- As of this release, LLVM 15 is the minimum required version for OpenVDB
|
||||
AX. Note that there is an issue with LLVM 17 symbols which makes it
|
||||
incompatible with the OpenVDB Python plugin. AS such, LLVM version 18 and
|
||||
greater are the recommended LLVM versions to use, with LLVM 18 being the
|
||||
new future minimum version.
|
||||
- Significant performance improvement to the compilation of AX kernels when
|
||||
using LLVM 18 and greater.
|
||||
- Significant backend refactoring to AX IR generation to support newer
|
||||
versions of LLVM. In particular, parts of the codegen::Function and
|
||||
codegen::FunctionBuilder API require new usage from LLVM 16 due to
|
||||
the LLVM opaque pointer changes. If you're not using the OpenVDB AX
|
||||
Function codegen API then no client changes should be necessary.
|
||||
- Migrated OpenVDB AX unit tests from cppunit to gtest
|
||||
[Contributed by Tim Straubinger]
|
||||
|
||||
- Fixes:
|
||||
- Fixed a case of undefined behaviour when explicitly casting variables to
|
||||
the same type as their storage type e.g:
|
||||
double a; double b = double(a);
|
||||
- Fixed an AX compiler error which would produce invalid IR when using 64bit
|
||||
integer variables for matrix array offsetting e.g:
|
||||
int64 a = 0; mat4f b = 1; float c = b[a,a];
|
||||
|
||||
@par
|
||||
NanoVDB:
|
||||
- Added nanovdb::openToIndexVDB, which can convert an OpenVDB grid into a
|
||||
NanoVDB IndexGrid.
|
||||
- Added new options "--index" and "--onIndex" to the command-line tool
|
||||
nanovdb_convert so it can produce IndexGrids.
|
||||
- Fixed memory leak in DeviceBuffer.
|
||||
- Added constructor for DeviceBuffer from an existing HostBuffer.
|
||||
- Added VoxelBlockManager, an acceleration structure for sequential access
|
||||
and stencil ops over active voxels.
|
||||
- Added helper functions for kernel launches with shared memory.
|
||||
- Added support for CUDA 13.0.
|
||||
- Fix warnings and compilation errors for GCC and Clang.
|
||||
- Fixed inconsistent usage of util::cuda::mallocAsync/freeAsync that
|
||||
affected vGPUs.
|
||||
- Added support for custom memory resources in PointsToGrid.
|
||||
- Fixed race condition in split/mergeGridHandles.
|
||||
- Fixed offset bug in nanovdb::tools::cuda::IndexToGrid related to non-POD
|
||||
build types.
|
||||
[Contributed by Andreas Henne]
|
||||
|
||||
@par
|
||||
Houdini:
|
||||
- Define OP_InputIdx and OP_OutputIdx for Houdini 21.0 compatibility
|
||||
- Add Houdini 21 style camera interface to support LOP cameras
|
||||
- Include env flag for avoiding stream writing (was backported to 19.5)
|
||||
|
||||
|
||||
@htmlonly <a name="v12_0_1_changes"></a>@endhtmlonly
|
||||
|
||||
+23
-24
@@ -41,14 +41,14 @@ OpenVDB Print | Core Library dependencies | -
|
||||
OpenVDB LOD | Core Library dependencies | -
|
||||
OpenVDB Render | Core Library dependencies | OpenEXR, Imath::Imath, libpng
|
||||
OpenVDB View | Core Library dependencies, OpenGL, GLFW3, GLEW* | -
|
||||
OpenVDB Python | Core Library dependencies, Python, nanobind | NumPy
|
||||
OpenVDB Python | Core Library dependencies, Python, nanobind | -
|
||||
OpenVDB AX | Core Library dependencies, LLVM | Bison, Flex
|
||||
NanoVDB | - | Core Library, CUDA, TBB, Blosc, ZLib
|
||||
OpenVDB UnitTests | Core Library dependencies, GoogleTest | CppUnit**
|
||||
OpenVDB UnitTests | Core Library dependencies, GoogleTest, numpy* | -
|
||||
OpenVDB Documentation | Doxygen | -
|
||||
|
||||
- @b * GLEW is only required for building the vdb_view binary on Windows.
|
||||
- @b ** CppUnit is only required for building the AX unit tests.
|
||||
- @b * numpy only required for python binding unit tests
|
||||
- @b Note - Bison and Flex are only required for re-generation of the AX
|
||||
grammar which is not typically necessary for the majority of users.
|
||||
|
||||
@@ -56,41 +56,39 @@ OpenVDB Documentation | Doxygen | -
|
||||
|
||||
Package | Minimum | Recommended | Description | apt-get | Homebrew | Source
|
||||
-------------- | ------- | ----------- | ----------------------------------------------------------------- | ------- | -------- | ------
|
||||
CMake | 3.20 | Latest | Cross-platform family of tools designed to help build software | Y | Y | https://cmake.org
|
||||
CMake | 3.24 | Latest | Cross-platform family of tools designed to help build software | Y | Y | https://cmake.org
|
||||
GCC | 11.2.1 | 11.2.1 | C++ 17 Compiler: The GNU Compiler Collection | Y | Y | https://www.gnu.org/software/gcc
|
||||
Clang | 5.0 | Latest | C++ 17 Compiler: A C language family frontend for LLVM | Y | Y | https://clang.llvm.org
|
||||
Intel ICC | 19 | Latest | C++ 17 Compiler: Intels C++ Compiler | Y | Y | https://software.intel.com/en-us/c-compilers
|
||||
MSVC | 19.30 | 19.30 | C++ 17 Compiler: Microsoft Visual C++ Compiler | Y | Y | https://visualstudio.microsoft.com/vs
|
||||
Imath | 3.1 | Latest | Half precision floating points | Y | Y | http://www.openexr.com
|
||||
OpenEXR | 3.1 | Latest | EXR serialization support | Y | Y | http://www.openexr.com
|
||||
TBB | 2020.3 | 2020.3 | Threading Building Blocks - template library for task parallelism | Y | Y | https://www.threadingbuildingblocks.org
|
||||
Imath | 3.2 | >= 3.3 | Half precision floating points | Y | Y | http://www.openexr.com
|
||||
OpenEXR | 3.2 | >= 3.3 | EXR serialization support | Y | Y | http://www.openexr.com
|
||||
TBB | 2020.3 | >= 2021 | Threading Building Blocks - template library for task parallelism | Y | Y | https://www.threadingbuildingblocks.org
|
||||
ZLIB | 1.2.7 | Latest | Compression library for disk serialization compression | Y | Y | https://www.zlib.net
|
||||
Boost | 1.80 | 1.82 | Components: iostreams | Y | Y | https://www.boost.org
|
||||
LLVM | 13.0.0 | 15.0.0* | Target-independent code generation | Y | Y | https://llvm.org/
|
||||
Boost | 1.82 | >= 1.85 | Components: iostreams | Y | Y | https://www.boost.org
|
||||
LLVM | 15.0.0* | 18.0.0 | Target-independent code generation | Y | Y | https://llvm.org/
|
||||
Bison | 3.7.0 | 3.7.0 | General-purpose parser generator | Y | Y | https://www.gnu.org/software/gcc
|
||||
Flex | 2.6.4 | 2.6.4 | Fast lexical analyzer generator | Y | Y | https://github.com/westes/flex
|
||||
Python | 3.10 | 3.11 | The python interpreter and libraries | Y | Y | https://www.python.org
|
||||
nanobind | 2.0.0 | 2.1.0 | C++/python bindings | Y | Y | https://nanobind.readthedocs.io
|
||||
NumPy | 1.23.0 | 1.26.0 | Scientific computing with Python | Y | Y | http://www.numpy.org
|
||||
nanobind** | 2.0.0 | >= 2.5.0 | C++/python bindings | Y | Y | https://nanobind.readthedocs.io
|
||||
GoogleTest | 1.10 | Latest | A unit testing framework module for C++ | Y | Y | https://github.com/google/googletest
|
||||
CppUnit | 1.10 | Latest | A unit testing framework module for C++ | N | Y | https://freedesktop.org/wiki/Software/cppunit
|
||||
Blosc | 1.17.0* | 1.17.0 | Recommended dependency for improved disk compression | Y | Y | https://github.com/Blosc/c-blosc/releases
|
||||
Blosc | 1.17.0* | Latest | Recommended dependency for improved disk compression | Y | Y | https://github.com/Blosc/c-blosc/releases
|
||||
Log4cplus | 1.1.2 | Latest | An optional dependency for improved OpenVDB Logging | Y | Y | https://github.com/log4cplus/log4cplus
|
||||
libpng | - | Latest | Library for manipulating PNG images | Y | Y | http://www.libpng.org/pub/png/libpng.html
|
||||
GLFW | 3.1 | > 3.3 | Simple API for OpenGL development | Y | Y | https://www.glfw.org
|
||||
GLFW | 3.3 | Latest | Simple API for OpenGL development | Y | Y | https://www.glfw.org
|
||||
OpenGL | 3.2 | Latest | Environment for developing portable graphics applications | Y | Y | https://www.opengl.org
|
||||
GLEW | 1.0.0 | Latest | A cross-platform OpenGL extension loading library. | Y | Y | http://glew.sourceforge.net
|
||||
CUDA | - | Latest | Parallel computing platform for graphical processing units. | Y | N | https://developer.nvidia.com/cuda-downloads
|
||||
Doxygen | 1.8.8 | 1.8.11 | Documentation generation from C++ | Y | Y | http://www.doxygen.nl
|
||||
|
||||
- @b * See the note in [known issues](@ref depKnownIssues) regarding supported blosc/llvm versions.
|
||||
- @b * See the note in [known issues](@ref depKnownIssues) regarding supported blosc/llvm versions.
|
||||
- @b ** See the note in [known issues](@ref depKnownIssues) regarding supported nanobind build issues.
|
||||
|
||||
@subsection depKnownIssues Known Issues
|
||||
|
||||
- @b LLVM >= 15. LLVM has recently changed their IR pointer data types to be
|
||||
completely opaque. Some support is provided in LLVM 15 but tests with AX show
|
||||
that this is extremely unstable. AX currently only supports LLVM versions up
|
||||
until 14.X.X. See https://llvm.org/docs/OpaquePointers.html for more details.
|
||||
- @b On Linux, LLVM == 17 has symbol issues which makes it incompatible when
|
||||
used with the OpenVDB Python plugin. LLVM versions greater than 18 should be
|
||||
preferred.
|
||||
|
||||
- @b GLFW: Crash with GLFW 3.2.1 when used with EGL on Linux. OpenVDB View may
|
||||
segfault on closure. Fixed in GLFW 3.3
|
||||
@@ -112,6 +110,11 @@ Doxygen | 1.8.8 | 1.8.11 | Documentation generation from C++
|
||||
- https://github.com/Blosc/c-blosc/issues/260
|
||||
- https://github.com/Blosc/c-blosc/issues/261
|
||||
|
||||
- @b nanobind: Nanobind requires a fast-hash-map implementation library called
|
||||
tsl-robin-map. If you're installing nanobind through homebrew, this library
|
||||
may not be installed correctly and OpenVDB's CMake will only produce a warning.
|
||||
Make sure to also: `brew install robin-map`.
|
||||
|
||||
@section depInstallingDependencies Installing Dependencies
|
||||
|
||||
This section details a number of ways to get you up and running with the
|
||||
@@ -148,14 +151,12 @@ apt-get install libblosc-dev # Blosc
|
||||
apt-get install llvm-15-dev # LLVM
|
||||
# Python
|
||||
apt-get install python-dev # Python
|
||||
apt-get install python-numpy # NumPy
|
||||
pip install nanobind # nanobind
|
||||
# Optional
|
||||
apt-get install libpng-dev # libpng
|
||||
apt-get install libopenexr-dev # OpenEXR
|
||||
apt-get install liblog4cplus-dev # Log4cplus
|
||||
apt-get install googletest # GoogleTest
|
||||
apt-get install libcppunit-dev # CppUnit
|
||||
# vdb_view
|
||||
apt-get install libglfw3-dev # GLFW
|
||||
# Documentation
|
||||
@@ -181,11 +182,9 @@ brew install c-blosc # blosc
|
||||
brew install llvm # LLVM
|
||||
# Python
|
||||
brew install python # Python
|
||||
brew install numpy # NumPy
|
||||
brew install nanobind # nanobind
|
||||
brew install nanobind robin-map # nanobind
|
||||
# Unit Tests
|
||||
brew install gtest # GoogleTest
|
||||
brew install cppunit # CppUnit
|
||||
# vdb_view
|
||||
brew install glfw # GLFW
|
||||
# vdb_render
|
||||
|
||||
+3
-5
@@ -441,11 +441,9 @@ not on their active states or other attributes.
|
||||
|
||||
Large data sets are often handled in Python using
|
||||
<a href="http://www.numpy.org/">NumPy</a>.
|
||||
The OpenVDB Python module can optionally be compiled with NumPy support.
|
||||
With NumPy enabled, the @b copyFromArray and @b copyToArray grid methods
|
||||
can be used to exchange data efficiently between scalar-valued grids
|
||||
and three-dimensional NumPy arrays and between vector-valued grids and
|
||||
four-dimensional NumPy arrays.
|
||||
The @b copyFromArray and @b copyToArray grid methods can be used to exchange
|
||||
data efficiently between scalar-valued grids and three-dimensional NumPy
|
||||
arrays and between vector-valued grids and four-dimensional NumPy arrays.
|
||||
@code{.py}
|
||||
>>> import pyopenvdb as vdb
|
||||
>>> import numpy
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(NanoVDB LANGUAGES C CXX)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
@@ -115,6 +115,18 @@ if(NANOVDB_USE_CUDA)
|
||||
endif()
|
||||
|
||||
find_package(CUDAToolkit)
|
||||
|
||||
|
||||
include(../../cmake/CPM.cmake)
|
||||
|
||||
# This will automatically clone CCCL from GitHub and make the exported cmake targets available
|
||||
CPMAddPackage(
|
||||
NAME CCCL
|
||||
GITHUB_REPOSITORY nvidia/cccl
|
||||
GIT_TAG main # Fetches the latest commit on the main branch
|
||||
)
|
||||
|
||||
find_package(NCCL)
|
||||
endif()
|
||||
|
||||
if(NANOVDB_USE_OPENVDB)
|
||||
@@ -171,9 +183,12 @@ set(NANOVDB_INCLUDE_FILES
|
||||
# NanoVDB cuda header files
|
||||
set(NANOVDB_INCLUDE_CUDA_FILES
|
||||
cuda/DeviceBuffer.h
|
||||
cuda/DeviceMesh.h
|
||||
cuda/DeviceResource.h
|
||||
cuda/DeviceStreamMap.h
|
||||
cuda/GridHandle.cuh
|
||||
cuda/NodeManager.cuh
|
||||
cuda/TempPool.h
|
||||
cuda/UnifiedBuffer.h
|
||||
)
|
||||
|
||||
@@ -202,6 +217,7 @@ set(NANOVDB_INCLUDE_TOOLS_FILES
|
||||
tools/GridStats.h
|
||||
tools/GridValidator.h
|
||||
tools/NanoToOpenVDB.h
|
||||
tools/VoxelBlockManager.h
|
||||
)
|
||||
|
||||
# NanoVDB tools/cuda header files
|
||||
@@ -213,6 +229,14 @@ set(NANOVDB_INCLUDE_TOOLS_CUDA_FILES
|
||||
tools/cuda/IndexToGrid.cuh
|
||||
tools/cuda/PointsToGrid.cuh
|
||||
tools/cuda/SignedFloodFill.cuh
|
||||
tools/cuda/MergeGrids.cuh
|
||||
tools/cuda/TopologyBuilder.cuh
|
||||
tools/cuda/RefineGrid.cuh
|
||||
tools/cuda/PruneGrid.cuh
|
||||
tools/cuda/DistributedPointsToGrid.cuh
|
||||
tools/cuda/DilateGrid.cuh
|
||||
tools/cuda/CoarsenGrid.cuh
|
||||
tools/cuda/VoxelBlockManager.cuh
|
||||
)
|
||||
|
||||
# NanoVDB util header files
|
||||
@@ -241,6 +265,7 @@ set(NANOVDB_INCLUDE_UTIL_FILES
|
||||
util/Stencils.h
|
||||
util/Timer.h
|
||||
util/Util.h
|
||||
util/MorphologyHelpers.h
|
||||
)
|
||||
|
||||
# NanoVDB util/cuda header files
|
||||
@@ -259,6 +284,10 @@ set(NANOVDB_INCLUDE_UTIL_CUDA_FILES
|
||||
util/cuda/CudaGridValidator.cuh
|
||||
util/cuda/CudaPointsToGrid.cuh
|
||||
util/cuda/GpuTimer.h
|
||||
util/cuda/Morphology.cuh
|
||||
util/cuda/MorphologyHelpers.cuh
|
||||
util/cuda/DeviceGridTraits.cuh
|
||||
util/cuda/Injection.cuh
|
||||
)
|
||||
|
||||
add_library(nanovdb INTERFACE)
|
||||
@@ -311,8 +340,12 @@ if(NANOVDB_USE_CUDA)
|
||||
endif()
|
||||
|
||||
# Add CUDA includes to any C++ units which use NanoVDB
|
||||
target_include_directories(nanovdb INTERFACE
|
||||
${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
|
||||
target_link_libraries(nanovdb INTERFACE CCCL::CCCL CUDA::cudart)
|
||||
|
||||
if(NCCL_FOUND)
|
||||
target_compile_definitions(nanovdb INTERFACE -DNANOVDB_USE_NCCL)
|
||||
target_link_libraries(nanovdb INTERFACE NCCL::NCCL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NANOVDB_USE_MAGICAVOXEL)
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
// Simple C-wrapper for the nanovdb structure
|
||||
// Meant for systems where you lack a C++ compiler.
|
||||
//
|
||||
|
||||
/// @warning This header file will soon be deprecated in favor of PNanoVDB.h
|
||||
|
||||
#ifndef __CNANOVDB__
|
||||
#define __CNANOVDB__
|
||||
|
||||
|
||||
@@ -111,6 +111,9 @@ public:
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, const void*>::type
|
||||
deviceData() const { return mBuffer.deviceData(); }
|
||||
template<typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, const void*>::type
|
||||
deviceData(int device) const { return mBuffer.deviceData(device); }
|
||||
template<typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, void*>::type
|
||||
deviceData() { return mBuffer.deviceData(); }
|
||||
template<typename U = BufferT>
|
||||
@@ -173,7 +176,7 @@ public:
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, void>::type
|
||||
deviceUpload(void* stream, bool sync = true) { mBuffer.deviceUpload(stream, sync); }
|
||||
|
||||
/// @brief Upload the host buffer to a specefic device buffer. It device buffer doesn't exist it's created first
|
||||
/// @brief Upload the host buffer to a specific device buffer. It device buffer doesn't exist it's created first
|
||||
/// @param device Device to upload host data to
|
||||
/// @param stream cuda stream
|
||||
/// @param sync if false the memory copy is asynchronous
|
||||
@@ -212,7 +215,7 @@ public:
|
||||
return sum;
|
||||
}
|
||||
|
||||
/// @brief compute the size of unusedstorage in this buffer
|
||||
/// @brief compute the size of unused storage in this buffer
|
||||
/// @return the number of unused bytes in this buffer.
|
||||
uint64_t freeSize() const {return mBuffer.size() - this->totalGridSize();}
|
||||
|
||||
@@ -286,12 +289,12 @@ public:
|
||||
/// @param is input stream containing a raw grid buffer
|
||||
/// @param gridName string name of the grid to be read
|
||||
/// @param pool optional pool from which to allocate the new grid buffer
|
||||
/// @throw Will throw a std::logic_error if the stream does not contain a valid raw grid with the speficied name
|
||||
/// @throw Will throw a std::logic_error if the stream does not contain a valid raw grid with the specified name
|
||||
void read(std::istream& is, const std::string &gridName, const BufferT& pool = BufferT());
|
||||
|
||||
/// @brief Read a raw grid buffer from a file
|
||||
/// @param filename string name of the input file containing a raw grid buffer
|
||||
/// @param pool optional pool from which to allocate the new grid buffe
|
||||
/// @param pool optional pool from which to allocate the new grid buffer
|
||||
void read(const std::string &fileName, const BufferT& pool = BufferT()) {
|
||||
std::ifstream is(fileName, std::ios::in | std::ios::binary);
|
||||
if (!is.is_open()) throw std::ios_base::failure("Unable to open file named \"" + fileName + "\" for input");
|
||||
@@ -487,7 +490,7 @@ splitGrids(const GridHandle<BufferT> &handle, const BufferT* other = nullptr)
|
||||
h = HandleT(std::move(buffer));
|
||||
ptr = util::PtrAdd(ptr, src->mGridSize);
|
||||
}
|
||||
return std::move(handles);
|
||||
return handles;
|
||||
}// splitGrids
|
||||
|
||||
/// @brief Combines (or merges) multiple GridHandles into a single GridHandle containing all grids
|
||||
|
||||
@@ -192,6 +192,13 @@ public:
|
||||
void* data() { return mData; }
|
||||
//@}
|
||||
|
||||
/// @brief Returns an offset pointer of a specific type from the allocated host memory
|
||||
/// @tparam T Type of the pointer returned
|
||||
/// @param count Numbers of elements of @c parameter type T to skip
|
||||
/// @warning might return NULL
|
||||
template <typename T>
|
||||
T* data(ptrdiff_t count = 0) const {return mData ? reinterpret_cast<T*>(mData) + count : nullptr;}
|
||||
|
||||
//@{
|
||||
/// @brief Returns the size in bytes associated with this buffer.
|
||||
uint64_t bufferSize() const { return mSize; }
|
||||
@@ -316,7 +323,6 @@ struct HostBuffer::Pool
|
||||
<< mSize << " bytes of which\n\t" << (util::PtrDiff(alignedFree, mData) - mPadding)
|
||||
<< " bytes are used by " << mRegister.size() << " other buffer(s). "
|
||||
<< "Pool is " << (mManaged ? "internally" : "externally") << " managed.\n";
|
||||
//std::cerr << ss.str();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
buffer->mSize = size;
|
||||
@@ -389,7 +395,6 @@ struct HostBuffer::Pool
|
||||
}
|
||||
|
||||
for (HostBuffer* buffer : mRegister) { // update registered buffers
|
||||
//buffer->mData = paddedData + ptrdiff_t(buffer->mData - (mData + mPadding));
|
||||
buffer->mData = util::PtrAdd(paddedData, util::PtrDiff(buffer->mData, util::PtrAdd(mData, mPadding)));
|
||||
}
|
||||
mFree = util::PtrAdd(paddedData, memUsage); // update the free pointer
|
||||
@@ -414,12 +419,8 @@ private:
|
||||
|
||||
static void* alloc(uint64_t size)
|
||||
{
|
||||
//#if (__cplusplus >= 201703L)
|
||||
// return std::aligned_alloc(NANOVDB_DATA_ALIGNMENT, size);//C++17 or newer
|
||||
//#else
|
||||
// make sure we alloc enough space to align the result
|
||||
return std::malloc(size + NANOVDB_DATA_ALIGNMENT);
|
||||
//#endif
|
||||
}
|
||||
|
||||
static void* realloc(void* const origData,
|
||||
|
||||
+633
-973
File diff suppressed because it is too large
Load Diff
@@ -42,8 +42,8 @@ NodeManagerHandle<BufferT> createNodeManager(const NanoGrid<BuildT> &grid,
|
||||
const BufferT& buffer = BufferT());
|
||||
|
||||
struct NodeManagerData
|
||||
{// 48B = 6*8B
|
||||
uint64_t mMagic;// 8B
|
||||
{// 40B = 5*8B
|
||||
__hostdev__ NodeManagerData(void *grid) : mPadding{0}, mGrid(grid), mPtr{0,0,0}{}
|
||||
union {int64_t mPadding; uint8_t mLinear;};// 8B of which 1B is used for a binary flag
|
||||
void *mGrid;// 8B pointer to either host or device grid
|
||||
union {int64_t *mPtr[3], mOff[3];};// 24B, use mOff if mLinear!=0
|
||||
@@ -288,11 +288,7 @@ NodeManagerHandle<BufferT> createNodeManager(const NanoGrid<BuildT> &grid,
|
||||
auto *data = reinterpret_cast<NodeManagerData*>(handle.data());
|
||||
NANOVDB_ASSERT(data && isAligned(data));
|
||||
NANOVDB_ASSERT(toGridType<BuildT>() == grid.gridType());
|
||||
#ifdef NANOVDB_USE_NEW_MAGIC_NUMBERS
|
||||
*data = NodeManagerData{NANOVDB_MAGIC_NODE, {0u}, (void*)&grid, {{0u,0u,0u}}};
|
||||
#else
|
||||
*data = NodeManagerData{NANOVDB_MAGIC_NUMB, {0u}, (void*)&grid, {{0u,0u,0u}}};
|
||||
#endif
|
||||
*data = NodeManagerData((void*)&grid);
|
||||
|
||||
if (NodeManager<BuildT>::isLinear(grid)) {
|
||||
data->mLinear = uint8_t(1u);
|
||||
|
||||
+345
-145
@@ -198,47 +198,110 @@ PNANOVDB_BUF_FORCE_INLINE void pnanovdb_buf_write_uint64(pnanovdb_buf_t buf, uin
|
||||
typedef uint32_t pnanovdb_grid_type_t;
|
||||
#define PNANOVDB_GRID_TYPE_GET(grid_typeIn, nameIn) pnanovdb_grid_type_constants[grid_typeIn].nameIn
|
||||
#elif defined(PNANOVDB_BUF_HLSL)
|
||||
#if defined(PNANOVDB_ADDRESS_32)
|
||||
#if defined(PNANOVDB_BUF_HLSL_RW)
|
||||
#if defined(PNANOVDB_BUF_HLSL_64)
|
||||
#define pnanovdb_buf_t RWStructuredBuffer<uint2>
|
||||
#else
|
||||
#define pnanovdb_buf_t RWStructuredBuffer<uint>
|
||||
#endif
|
||||
#else
|
||||
#if defined(PNANOVDB_BUF_HLSL_64)
|
||||
#define pnanovdb_buf_t StructuredBuffer<uint2>
|
||||
#else
|
||||
#define pnanovdb_buf_t StructuredBuffer<uint>
|
||||
#endif
|
||||
#endif
|
||||
#if defined(PNANOVDB_ADDRESS_32)
|
||||
uint pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint byte_offset)
|
||||
{
|
||||
#if defined(PNANOVDB_BUF_HLSL_64)
|
||||
uint2 val = buf[(byte_offset >> 3u)];
|
||||
return ((byte_offset & 4u) == 0u) ? val.x : val.y;
|
||||
#else
|
||||
return buf[(byte_offset >> 2u)];
|
||||
#endif
|
||||
}
|
||||
uint2 pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint byte_offset)
|
||||
{
|
||||
uint2 ret;
|
||||
#if defined(PNANOVDB_BUF_HLSL_64)
|
||||
ret = buf[(byte_offset >> 3u)];
|
||||
#else
|
||||
ret.x = pnanovdb_buf_read_uint32(buf, byte_offset + 0u);
|
||||
ret.y = pnanovdb_buf_read_uint32(buf, byte_offset + 4u);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
void pnanovdb_buf_write_uint32(pnanovdb_buf_t buf, uint byte_offset, uint value)
|
||||
{
|
||||
// NOP, by default no write in HLSL
|
||||
#if defined(PNANOVDB_BUF_HLSL_RW)
|
||||
#if defined(PNANOVDB_BUF_HLSL_64)
|
||||
if ((byte_offset & 4u) == 0u) {buf[(byte_offset >> 3u)].x = value;}
|
||||
else {buf[(byte_offset >> 3u)].y = value;}
|
||||
#else
|
||||
buf[(byte_offset >> 2u)] = value;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
void pnanovdb_buf_write_uint64(pnanovdb_buf_t buf, uint byte_offset, uint2 value)
|
||||
{
|
||||
// NOP, by default no write in HLSL
|
||||
#if defined(PNANOVDB_BUF_HLSL_RW)
|
||||
#if defined(PNANOVDB_BUF_HLSL_64)
|
||||
buf[(byte_offset >> 3u)] = value;
|
||||
#else
|
||||
pnanovdb_buf_write_uint32(buf, byte_offset + 0u, value.x);
|
||||
pnanovdb_buf_write_uint32(buf, byte_offset + 4u, value.y);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#elif defined(PNANOVDB_ADDRESS_64)
|
||||
#define pnanovdb_buf_t StructuredBuffer<uint>
|
||||
uint pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint64_t byte_offset)
|
||||
{
|
||||
#if defined(PNANOVDB_BUF_HLSL_64)
|
||||
uint2 val = buf[uint(byte_offset >> 3u)];
|
||||
return ((uint(byte_offset) & 4u) == 0u) ? val.x : val.y;
|
||||
#else
|
||||
return buf[uint(byte_offset >> 2u)];
|
||||
#endif
|
||||
}
|
||||
uint64_t pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint64_t byte_offset)
|
||||
{
|
||||
uint64_t ret;
|
||||
#if defined(PNANOVDB_BUF_HLSL_64)
|
||||
uint2 raw = buf[uint(byte_offset >> 3u)];
|
||||
ret = uint64_t(raw.x) | (uint64_t(raw.y) << 32u);
|
||||
#else
|
||||
ret = pnanovdb_buf_read_uint32(buf, byte_offset + 0u);
|
||||
ret = ret + (uint64_t(pnanovdb_buf_read_uint32(buf, byte_offset + 4u)) << 32u);
|
||||
ret = ret | (uint64_t(pnanovdb_buf_read_uint32(buf, byte_offset + 4u)) << 32u);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
void pnanovdb_buf_write_uint32(pnanovdb_buf_t buf, uint64_t byte_offset, uint value)
|
||||
{
|
||||
// NOP, by default no write in HLSL
|
||||
#if defined(PNANOVDB_BUF_HLSL_RW)
|
||||
#if defined(PNANOVDB_BUF_HLSL_64)
|
||||
if ((byte_offset & 4u) == 0u) {buf[uint(byte_offset >> 3u)].x = value;}
|
||||
else {buf[uint(byte_offset >> 3u)].y = value;}
|
||||
#else
|
||||
buf[uint(byte_offset >> 2u)] = value;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
void pnanovdb_buf_write_uint64(pnanovdb_buf_t buf, uint64_t byte_offset, uint64_t value)
|
||||
{
|
||||
// NOP, by default no write in HLSL
|
||||
#if defined(PNANOVDB_BUF_HLSL_RW)
|
||||
#if defined(PNANOVDB_BUF_HLSL_64)
|
||||
uint2 raw = uint2(uint(value), uint(value >> 32u));
|
||||
buf[uint(byte_offset >> 3u)] = raw;
|
||||
#else
|
||||
pnanovdb_buf_write_uint32(buf, byte_offset + 0u, uint(value));
|
||||
pnanovdb_buf_write_uint32(buf, byte_offset + 4u, uint(value >> 32u));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#define pnanovdb_grid_type_t uint
|
||||
@@ -343,14 +406,26 @@ typedef struct pnanovdb_vec3_t
|
||||
{
|
||||
float x, y, z;
|
||||
}pnanovdb_vec3_t;
|
||||
typedef struct pnanovdb_uvec4_t
|
||||
{
|
||||
pnanovdb_uint32_t x, y, z, w;
|
||||
}pnanovdb_uvec4_t;
|
||||
typedef struct pnanovdb_ivec4_t
|
||||
{
|
||||
pnanovdb_int32_t x, y, z, w;
|
||||
}pnanovdb_ivec4_t;
|
||||
typedef struct pnanovdb_vec4_t
|
||||
{
|
||||
float x, y, z, w;
|
||||
}pnanovdb_vec4_t;
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_int32_t pnanovdb_uint32_as_int32(pnanovdb_uint32_t v) { return (pnanovdb_int32_t)v; }
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_uint64_as_int64(pnanovdb_uint64_t v) { return (pnanovdb_int64_t)v; }
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_int64_as_uint64(pnanovdb_int64_t v) { return (pnanovdb_uint64_t)v; }
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_int32_as_uint32(pnanovdb_int32_t v) { return (pnanovdb_uint32_t)v; }
|
||||
PNANOVDB_FORCE_INLINE float pnanovdb_uint32_as_float(pnanovdb_uint32_t v) { float vf; pnanovdb_memcpy(&vf, &v, sizeof(vf)); return vf; }
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_float_as_uint32(float v) { return *((pnanovdb_uint32_t*)(&v)); }
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_float_as_uint32(float v) { pnanovdb_uint32_t vu; pnanovdb_memcpy(&vu, &v, sizeof(vu)); return vu; }
|
||||
PNANOVDB_FORCE_INLINE double pnanovdb_uint64_as_double(pnanovdb_uint64_t v) { double vf; pnanovdb_memcpy(&vf, &v, sizeof(vf)); return vf; }
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_double_as_uint64(double v) { return *((pnanovdb_uint64_t*)(&v)); }
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_double_as_uint64(double v) { pnanovdb_uint64_t vu; pnanovdb_memcpy(&vu, &v, sizeof(vu)); return vu; }
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_uint64_low(pnanovdb_uint64_t v) { return (pnanovdb_uint32_t)v; }
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_uint64_high(pnanovdb_uint64_t v) { return (pnanovdb_uint32_t)(v >> 32u); }
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_uint32_as_uint64(pnanovdb_uint32_t x, pnanovdb_uint32_t y) { return ((pnanovdb_uint64_t)x) | (((pnanovdb_uint64_t)y) << 32u); }
|
||||
@@ -373,6 +448,9 @@ typedef bool pnanovdb_bool_t;
|
||||
#define PNANOVDB_TRUE true
|
||||
typedef int3 pnanovdb_coord_t;
|
||||
typedef float3 pnanovdb_vec3_t;
|
||||
typedef uint4 pnanovdb_uvec4_t;
|
||||
typedef int4 pnanovdb_ivec4_t;
|
||||
typedef float4 pnanovdb_vec4_t;
|
||||
pnanovdb_int32_t pnanovdb_uint32_as_int32(pnanovdb_uint32_t v) { return int(v); }
|
||||
pnanovdb_uint32_t pnanovdb_int32_as_uint32(pnanovdb_int32_t v) { return uint(v); }
|
||||
float pnanovdb_uint32_as_float(pnanovdb_uint32_t v) { return asfloat(v); }
|
||||
@@ -420,6 +498,9 @@ bool pnanovdb_int64_is_zero(pnanovdb_int64_t a) { return a == 0; }
|
||||
#define pnanovdb_int64_t ivec2
|
||||
#define pnanovdb_coord_t ivec3
|
||||
#define pnanovdb_vec3_t vec3
|
||||
#define pnanovdb_uvec4_t uvec4
|
||||
#define pnanovdb_ivec4_t ivec4
|
||||
#define pnanovdb_vec4_t vec4
|
||||
pnanovdb_int32_t pnanovdb_uint32_as_int32(pnanovdb_uint32_t v) { return int(v); }
|
||||
pnanovdb_int64_t pnanovdb_uint64_as_int64(pnanovdb_uint64_t v) { return ivec2(v); }
|
||||
pnanovdb_uint64_t pnanovdb_int64_as_uint64(pnanovdb_int64_t v) { return uvec2(v); }
|
||||
@@ -525,6 +606,35 @@ PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_coord_add(pnanovdb_coord_t a, pn
|
||||
v.z = a.z + b.z;
|
||||
return v;
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uvec4_t pnanovdb_uvec4_add(pnanovdb_uvec4_t a, pnanovdb_uvec4_t b)
|
||||
{
|
||||
pnanovdb_uvec4_t ret;
|
||||
ret.x = a.x + b.x;
|
||||
ret.y = a.y + b.y;
|
||||
ret.z = a.z + b.z;
|
||||
ret.w = a.w + b.w;
|
||||
return ret;
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uvec4_t pnanovdb_uvec4_sub(pnanovdb_uvec4_t a, pnanovdb_uvec4_t b)
|
||||
{
|
||||
pnanovdb_uvec4_t ret;
|
||||
ret.x = a.x - b.x;
|
||||
ret.y = a.y - b.y;
|
||||
ret.z = a.z - b.z;
|
||||
ret.w = a.w - b.w;
|
||||
return ret;
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_vec4_t pnanovdb_vec4_add(pnanovdb_vec4_t a, pnanovdb_vec4_t b)
|
||||
{
|
||||
pnanovdb_vec4_t ret;
|
||||
ret.x = a.x + b.x;
|
||||
ret.y = a.y + b.y;
|
||||
ret.z = a.z + b.z;
|
||||
ret.w = a.w + b.w;
|
||||
return ret;
|
||||
}
|
||||
#elif defined(PNANOVDB_HLSL)
|
||||
pnanovdb_vec3_t pnanovdb_vec3_uniform(float a) { return float3(a, a, a); }
|
||||
pnanovdb_vec3_t pnanovdb_vec3_add(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a + b; }
|
||||
@@ -536,6 +646,9 @@ pnanovdb_vec3_t pnanovdb_vec3_max(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return
|
||||
pnanovdb_vec3_t pnanovdb_coord_to_vec3(pnanovdb_coord_t coord) { return float3(coord); }
|
||||
pnanovdb_coord_t pnanovdb_coord_uniform(pnanovdb_int32_t a) { return int3(a, a, a); }
|
||||
pnanovdb_coord_t pnanovdb_coord_add(pnanovdb_coord_t a, pnanovdb_coord_t b) { return a + b; }
|
||||
pnanovdb_uvec4_t pnanovdb_uvec4_add(pnanovdb_uvec4_t a, pnanovdb_uvec4_t b) { return a + b; }
|
||||
pnanovdb_uvec4_t pnanovdb_uvec4_sub(pnanovdb_uvec4_t a, pnanovdb_uvec4_t b) { return a - b; }
|
||||
pnanovdb_vec4_t pnanovdb_vec4_add(pnanovdb_vec4_t a, pnanovdb_vec4_t b) { return a + b; }
|
||||
#elif defined(PNANOVDB_GLSL)
|
||||
pnanovdb_vec3_t pnanovdb_vec3_uniform(float a) { return vec3(a, a, a); }
|
||||
pnanovdb_vec3_t pnanovdb_vec3_add(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a + b; }
|
||||
@@ -547,6 +660,9 @@ pnanovdb_vec3_t pnanovdb_vec3_max(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return
|
||||
pnanovdb_vec3_t pnanovdb_coord_to_vec3(const pnanovdb_coord_t coord) { return vec3(coord); }
|
||||
pnanovdb_coord_t pnanovdb_coord_uniform(pnanovdb_int32_t a) { return ivec3(a, a, a); }
|
||||
pnanovdb_coord_t pnanovdb_coord_add(pnanovdb_coord_t a, pnanovdb_coord_t b) { return a + b; }
|
||||
pnanovdb_uvec4_t pnanovdb_uvec4_add(pnanovdb_uvec4_t a, pnanovdb_uvec4_t b) { return a + b; }
|
||||
pnanovdb_uvec4_t pnanovdb_uvec4_sub(pnanovdb_uvec4_t a, pnanovdb_uvec4_t b) { return a - b; }
|
||||
pnanovdb_vec4_t pnanovdb_vec4_add(pnanovdb_vec4_t a, pnanovdb_vec4_t b) { return a + b; }
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------ Uint64 Utils -----------------------------------------------------------
|
||||
@@ -589,6 +705,19 @@ PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_uint64_offset(pnanovdb_uint64_t
|
||||
return pnanovdb_uint32_as_uint64(low, high);
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_uint64_diff(pnanovdb_uint64_t a, pnanovdb_uint64_t b)
|
||||
{
|
||||
pnanovdb_uint32_t low = pnanovdb_uint64_low(a);
|
||||
pnanovdb_uint32_t high = pnanovdb_uint64_high(a);
|
||||
low -= pnanovdb_uint64_low(b);
|
||||
if (low > pnanovdb_uint64_low(a))
|
||||
{
|
||||
high -= 1u;
|
||||
}
|
||||
high -= pnanovdb_uint64_high(b);
|
||||
return pnanovdb_uint64_as_int64(pnanovdb_uint32_as_uint64(low, high));
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_uint64_dec(pnanovdb_uint64_t a)
|
||||
{
|
||||
pnanovdb_uint32_t low = pnanovdb_uint64_low(a);
|
||||
@@ -636,6 +765,11 @@ PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_uint64_offset(pnanovdb_uint64_t
|
||||
return a + b;
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_uint64_diff(pnanovdb_uint64_t a, pnanovdb_uint64_t b)
|
||||
{
|
||||
return pnanovdb_uint64_as_int64(a - b);
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_uint64_dec(pnanovdb_uint64_t a)
|
||||
{
|
||||
return a - 1u;
|
||||
@@ -702,6 +836,10 @@ PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset64_product(pnano
|
||||
ret.byte_offset += pnanovdb_uint64_low(byte_offset) * multiplier;
|
||||
return ret;
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_address_diff(pnanovdb_address_t a, pnanovdb_address_t b)
|
||||
{
|
||||
return pnanovdb_uint64_diff(pnanovdb_uint32_as_uint64_low(a.byte_offset),pnanovdb_uint32_as_uint64_low(b.byte_offset));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_address_mask(pnanovdb_address_t address, pnanovdb_uint32_t mask)
|
||||
{
|
||||
return address.byte_offset & mask;
|
||||
@@ -725,6 +863,12 @@ PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_address_in_interval(pnanovdb_addr
|
||||
{
|
||||
return address.byte_offset >= min_address.byte_offset && address.byte_offset < max_address.byte_offset;
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_add(pnanovdb_address_t a, pnanovdb_address_t b)
|
||||
{
|
||||
pnanovdb_address_t sum = a;
|
||||
sum.byte_offset += b.byte_offset;
|
||||
return sum;
|
||||
}
|
||||
#elif defined(PNANOVDB_ADDRESS_64)
|
||||
struct pnanovdb_address_t
|
||||
{
|
||||
@@ -762,6 +906,10 @@ PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset64_product(pnano
|
||||
ret.byte_offset += byte_offset * pnanovdb_uint32_as_uint64_low(multiplier);
|
||||
return ret;
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_address_diff(pnanovdb_address_t a, pnanovdb_address_t b)
|
||||
{
|
||||
return pnanovdb_uint64_diff(a.byte_offset, b.byte_offset);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_address_mask(pnanovdb_address_t address, pnanovdb_uint32_t mask)
|
||||
{
|
||||
return pnanovdb_uint64_low(address.byte_offset) & mask;
|
||||
@@ -785,6 +933,12 @@ PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_address_in_interval(pnanovdb_addr
|
||||
{
|
||||
return address.byte_offset >= min_address.byte_offset && address.byte_offset < max_address.byte_offset;
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_add(pnanovdb_address_t a, pnanovdb_address_t b)
|
||||
{
|
||||
pnanovdb_address_t sum = a;
|
||||
sum.byte_offset += b.byte_offset;
|
||||
return sum;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------ High Level Buffer Read -----------------------------------------------------------
|
||||
@@ -933,7 +1087,7 @@ PNANOVDB_FORCE_INLINE void pnanovdb_write_vec3(pnanovdb_buf_t buf, pnanovdb_addr
|
||||
#define PNANOVDB_MAGIC_FILE 0x324244566f6e614eUL// "NanoVDB2" in hex - little endian (uint64_t)
|
||||
|
||||
#define PNANOVDB_MAJOR_VERSION_NUMBER 32// reflects changes to the ABI
|
||||
#define PNANOVDB_MINOR_VERSION_NUMBER 7// reflects changes to the API but not ABI
|
||||
#define PNANOVDB_MINOR_VERSION_NUMBER 9// reflects changes to the API but not ABI
|
||||
#define PNANOVDB_PATCH_VERSION_NUMBER 0// reflects bug-fixes with no ABI or API changes
|
||||
|
||||
#define PNANOVDB_GRID_TYPE_UNKNOWN 0
|
||||
@@ -957,14 +1111,16 @@ PNANOVDB_FORCE_INLINE void pnanovdb_write_vec3(pnanovdb_buf_t buf, pnanovdb_addr
|
||||
#define PNANOVDB_GRID_TYPE_VEC4D 18
|
||||
#define PNANOVDB_GRID_TYPE_INDEX 19
|
||||
#define PNANOVDB_GRID_TYPE_ONINDEX 20
|
||||
#define PNANOVDB_GRID_TYPE_INDEXMASK 21
|
||||
#define PNANOVDB_GRID_TYPE_ONINDEXMASK 22
|
||||
//#define PNANOVDB_GRID_TYPE_INDEXMASK 21 // retired - available for future use
|
||||
//#define PNANOVDB_GRID_TYPE_ONINDEXMASK 22 // retired - available for future use
|
||||
#define PNANOVDB_GRID_TYPE_POINTINDEX 23
|
||||
#define PNANOVDB_GRID_TYPE_VEC3U8 24
|
||||
#define PNANOVDB_GRID_TYPE_VEC3U16 25
|
||||
#define PNANOVDB_GRID_TYPE_UINT8 26
|
||||
#define PNANOVDB_GRID_TYPE_END 27
|
||||
|
||||
#define PNANOVDB_GRID_TYPE_CAP 32
|
||||
|
||||
#define PNANOVDB_GRID_CLASS_UNKNOWN 0
|
||||
#define PNANOVDB_GRID_CLASS_LEVEL_SET 1 // narrow band level set, e.g. SDF
|
||||
#define PNANOVDB_GRID_CLASS_FOG_VOLUME 2 // fog volume, e.g. density
|
||||
@@ -989,22 +1145,22 @@ PNANOVDB_FORCE_INLINE void pnanovdb_write_vec3(pnanovdb_buf_t buf, pnanovdb_addr
|
||||
#define PNANOVDB_LEAF_TYPE_LITE 1
|
||||
#define PNANOVDB_LEAF_TYPE_FP 2
|
||||
#define PNANOVDB_LEAF_TYPE_INDEX 3
|
||||
#define PNANOVDB_LEAF_TYPE_INDEXMASK 4
|
||||
//#define PNANOVDB_LEAF_TYPE_INDEXMASK 4 // retired - available for future use
|
||||
#define PNANOVDB_LEAF_TYPE_POINTINDEX 5
|
||||
|
||||
// BuildType = Unknown, float, double, int16_t, int32_t, int64_t, Vec3f, Vec3d, Mask, ...
|
||||
// bit count of values in leaf nodes, i.e. 8*sizeof(*nanovdb::LeafNode<BuildType>::mValues) or zero if no values are stored
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_value_strides_bits[PNANOVDB_GRID_TYPE_END] = { 0, 32, 64, 16, 32, 64, 96, 192, 0, 16, 32, 1, 32, 4, 8, 16, 0, 128, 256, 0, 0, 0, 0, 16, 24, 48, 8 };
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_value_strides_bits[PNANOVDB_GRID_TYPE_CAP] = { 0, 32, 64, 16, 32, 64, 96, 192, 0, 16, 32, 1, 32, 4, 8, 16, 0, 128, 256, 0, 0, 0, 0, 16, 24, 48, 8, 0 };
|
||||
// bit count of the Tile union in InternalNodes, i.e. 8*sizeof(nanovdb::InternalData::Tile)
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_table_strides_bits[PNANOVDB_GRID_TYPE_END] = { 64, 64, 64, 64, 64, 64, 128, 192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 128, 256, 64, 64, 64, 64, 64, 64, 64, 64 };
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_table_strides_bits[PNANOVDB_GRID_TYPE_CAP] = { 64, 64, 64, 64, 64, 64, 128, 192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 128, 256, 64, 64, 64, 64, 64, 64, 64, 64, 64 };
|
||||
// bit count of min/max values, i.e. 8*sizeof(nanovdb::LeafData::mMinimum) or zero if no min/max exists
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_minmax_strides_bits[PNANOVDB_GRID_TYPE_END] = { 0, 32, 64, 16, 32, 64, 96, 192, 8, 16, 32, 8, 32, 32, 32, 32, 32, 128, 256, 64, 64, 64, 64, 64, 24, 48, 8 };
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_minmax_strides_bits[PNANOVDB_GRID_TYPE_CAP] = { 0, 32, 64, 16, 32, 64, 96, 192, 8, 16, 32, 8, 32, 32, 32, 32, 32, 128, 256, 64, 64, 0, 0, 64, 24, 48, 8, 0 };
|
||||
// bit alignment of the value type, controlled by the smallest native type, which is why it is always 0, 8, 16, 32, or 64, e.g. for Vec3f it is 32
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_minmax_aligns_bits[PNANOVDB_GRID_TYPE_END] = { 0, 32, 64, 16, 32, 64, 32, 64, 8, 16, 32, 8, 32, 32, 32, 32, 32, 32, 64, 64, 64, 64, 64, 64, 8, 16, 8 };
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_minmax_aligns_bits[PNANOVDB_GRID_TYPE_CAP] = { 0, 32, 64, 16, 32, 64, 32, 64, 8, 16, 32, 8, 32, 32, 32, 32, 32, 32, 64, 64, 64, 0, 0, 64, 8, 16, 8, 0 };
|
||||
// bit alignment of the stats (avg/std-dev) types, e.g. 8*sizeof(nanovdb::LeafData::mAverage)
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_stat_strides_bits[PNANOVDB_GRID_TYPE_END] = { 0, 32, 64, 32, 32, 64, 32, 64, 8, 32, 32, 8, 32, 32, 32, 32, 32, 32, 64, 64, 64, 64, 64, 64, 32, 32, 32 };
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_stat_strides_bits[PNANOVDB_GRID_TYPE_CAP] = { 0, 32, 64, 32, 32, 64, 32, 64, 8, 32, 32, 8, 32, 32, 32, 32, 32, 32, 64, 64, 64, 0, 0, 64, 32, 32, 32, 0 };
|
||||
// one of the 4 leaf types defined above, e.g. PNANOVDB_LEAF_TYPE_INDEX = 3
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_leaf_type[PNANOVDB_GRID_TYPE_END] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 2, 2, 2, 2, 0, 0, 3, 3, 4, 4, 5, 0, 0, 0 };
|
||||
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_leaf_type[PNANOVDB_GRID_TYPE_CAP] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 2, 2, 2, 2, 0, 0, 3, 3, 0, 0, 5, 0, 0, 0, 0 };
|
||||
|
||||
struct pnanovdb_map_t
|
||||
{
|
||||
@@ -1099,7 +1255,9 @@ struct pnanovdb_grid_t
|
||||
pnanovdb_uint32_t grid_type; // 4 bytes, 636
|
||||
pnanovdb_int64_t blind_metadata_offset; // 8 bytes, 640
|
||||
pnanovdb_uint32_t blind_metadata_count; // 4 bytes, 648
|
||||
pnanovdb_uint32_t pad[5]; // 20 bytes, 652
|
||||
pnanovdb_uint32_t data0; // 4 bytes, 652
|
||||
pnanovdb_uint64_t data1; // 8 bytes, 656
|
||||
pnanovdb_uint64_t data2; // 8 bytes, 664
|
||||
};
|
||||
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_grid_t)
|
||||
struct pnanovdb_grid_handle_t { pnanovdb_address_t address; };
|
||||
@@ -1122,6 +1280,9 @@ PNANOVDB_STRUCT_TYPEDEF(pnanovdb_grid_handle_t)
|
||||
#define PNANOVDB_GRID_OFF_GRID_TYPE 636
|
||||
#define PNANOVDB_GRID_OFF_BLIND_METADATA_OFFSET 640
|
||||
#define PNANOVDB_GRID_OFF_BLIND_METADATA_COUNT 648
|
||||
#define PNANOVDB_GRID_OFF_DATA0 652
|
||||
#define PNANOVDB_GRID_OFF_DATA1 656
|
||||
#define PNANOVDB_GRID_OFF_DATA2 664
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_grid_get_magic(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
|
||||
return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_MAGIC));
|
||||
@@ -1170,6 +1331,15 @@ PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_grid_get_blind_metadata_offset(p
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_blind_metadata_count(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
|
||||
return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_BLIND_METADATA_COUNT));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_data0(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
|
||||
return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_DATA0));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_grid_get_data1(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
|
||||
return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_DATA1));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_grid_get_data2(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
|
||||
return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_DATA2));
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_grid_set_magic(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_uint64_t magic) {
|
||||
pnanovdb_write_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_MAGIC), magic);
|
||||
@@ -1213,6 +1383,15 @@ PNANOVDB_FORCE_INLINE void pnanovdb_grid_set_blind_metadata_offset(pnanovdb_buf_
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_grid_set_blind_metadata_count(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_uint32_t metadata_count) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_BLIND_METADATA_COUNT), metadata_count);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_grid_set_data0(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_uint32_t data0) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_DATA0), data0);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_grid_set_data1(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_uint64_t data1) {
|
||||
pnanovdb_write_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_DATA1), data1);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_grid_set_data2(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_uint64_t data2) {
|
||||
pnanovdb_write_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_DATA2), data2);
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_make_version(pnanovdb_uint32_t major, pnanovdb_uint32_t minor, pnanovdb_uint32_t patch_num)
|
||||
{
|
||||
@@ -1232,6 +1411,25 @@ PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_version_get_patch(pnanovdb_uint
|
||||
return version & ((1u << 10u) - 1u);
|
||||
}
|
||||
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_UNKNOWN 0
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_INDEX_ARRAY 1
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_ATTRIBUTE_ARRAY 2
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_GRID_NAME 3
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_CHANNEL_ARRAY 4
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_END 5
|
||||
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_UNKNOWN 0
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_POSITION 1
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_COLOR 2
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_NORMAL 3
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_RADIUS 4
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_VELOCITY 5
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_ID 6
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_WORLD_COORDS 7
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_GRID_COORDS 8
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_VOXEL_COORDS 9
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_END 10
|
||||
|
||||
struct pnanovdb_gridblindmetadata_t
|
||||
{
|
||||
pnanovdb_int64_t data_offset; // 8 bytes, 0
|
||||
@@ -1278,6 +1476,28 @@ PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_name(pnan
|
||||
return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_NAME + 4u * index));
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_gridblindmetadata_set_data_offset(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p, pnanovdb_int64_t data_offset) {
|
||||
pnanovdb_write_int64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_OFFSET), data_offset);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_gridblindmetadata_set_value_count(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p, pnanovdb_uint64_t value_count) {
|
||||
pnanovdb_write_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_VALUE_COUNT), value_count);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_gridblindmetadata_set_value_size(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p, pnanovdb_uint32_t value_size) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_VALUE_SIZE), value_size);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_gridblindmetadata_set_semantic(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p, pnanovdb_uint32_t semantic) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_SEMANTIC), semantic);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_gridblindmetadata_set_data_class(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p, pnanovdb_uint32_t data_class) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_CLASS), data_class);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_gridblindmetadata_set_data_type(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p, pnanovdb_uint32_t data_type) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_TYPE), data_type);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_gridblindmetadata_set_name(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p, pnanovdb_uint32_t index, pnanovdb_uint32_t name) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_NAME + 4u * index), name);
|
||||
}
|
||||
|
||||
struct pnanovdb_tree_t
|
||||
{
|
||||
pnanovdb_uint64_t node_offset_leaf;
|
||||
@@ -1287,9 +1507,9 @@ struct pnanovdb_tree_t
|
||||
pnanovdb_uint32_t node_count_leaf;
|
||||
pnanovdb_uint32_t node_count_lower;
|
||||
pnanovdb_uint32_t node_count_upper;
|
||||
pnanovdb_uint32_t tile_count_leaf;
|
||||
pnanovdb_uint32_t tile_count_lower;
|
||||
pnanovdb_uint32_t tile_count_upper;
|
||||
pnanovdb_uint32_t tile_count_root;
|
||||
pnanovdb_uint64_t voxel_count;
|
||||
};
|
||||
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_tree_t)
|
||||
@@ -1305,9 +1525,9 @@ PNANOVDB_STRUCT_TYPEDEF(pnanovdb_tree_handle_t)
|
||||
#define PNANOVDB_TREE_OFF_NODE_COUNT_LEAF 32
|
||||
#define PNANOVDB_TREE_OFF_NODE_COUNT_LOWER 36
|
||||
#define PNANOVDB_TREE_OFF_NODE_COUNT_UPPER 40
|
||||
#define PNANOVDB_TREE_OFF_TILE_COUNT_LEAF 44
|
||||
#define PNANOVDB_TREE_OFF_TILE_COUNT_LOWER 48
|
||||
#define PNANOVDB_TREE_OFF_TILE_COUNT_UPPER 52
|
||||
#define PNANOVDB_TREE_OFF_TILE_COUNT_LOWER 44
|
||||
#define PNANOVDB_TREE_OFF_TILE_COUNT_UPPER 48
|
||||
#define PNANOVDB_TREE_OFF_TILE_COUNT_ROOT 52
|
||||
#define PNANOVDB_TREE_OFF_VOXEL_COUNT 56
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_node_offset_leaf(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
|
||||
@@ -1331,15 +1551,15 @@ PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_node_count_lower(pnano
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_node_count_upper(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
|
||||
return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_COUNT_UPPER));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_tile_count_leaf(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
|
||||
return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_LEAF));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_tile_count_lower(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
|
||||
return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_LOWER));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_tile_count_upper(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
|
||||
return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_UPPER));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_tile_count_root(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
|
||||
return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_ROOT));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_voxel_count(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
|
||||
return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_VOXEL_COUNT));
|
||||
}
|
||||
@@ -1365,15 +1585,15 @@ PNANOVDB_FORCE_INLINE void pnanovdb_tree_set_node_count_lower(pnanovdb_buf_t buf
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_tree_set_node_count_upper(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p, pnanovdb_uint32_t node_count_upper) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_COUNT_UPPER), node_count_upper);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_tree_set_tile_count_leaf(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p, pnanovdb_uint32_t tile_count_leaf) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_LEAF), tile_count_leaf);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_tree_set_tile_count_lower(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p, pnanovdb_uint32_t tile_count_lower) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_LOWER), tile_count_lower);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_tree_set_tile_count_upper(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p, pnanovdb_uint32_t tile_count_upper) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_UPPER), tile_count_upper);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_tree_set_tile_count_root(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p, pnanovdb_uint32_t tile_count_root) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_ROOT), tile_count_root);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_tree_set_voxel_count(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p, pnanovdb_uint64_t voxel_count) {
|
||||
pnanovdb_write_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_VOXEL_COUNT), voxel_count);
|
||||
}
|
||||
@@ -1501,6 +1721,13 @@ PNANOVDB_FORCE_INLINE void pnanovdb_upper_set_bbox_min(pnanovdb_buf_t buf, pnano
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_upper_set_bbox_max(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p, PNANOVDB_IN(pnanovdb_coord_t) bbox_max) {
|
||||
pnanovdb_write_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_BBOX_MAX), bbox_max);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_upper_set_value_mask(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p, pnanovdb_uint32_t bit_index, pnanovdb_bool_t value) {
|
||||
pnanovdb_address_t addr = pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_VALUE_MASK + 4u * (bit_index >> 5u));
|
||||
pnanovdb_uint32_t valueMask = pnanovdb_read_uint32(buf, addr);
|
||||
if (!value) { valueMask &= ~(1u << (bit_index & 31u)); }
|
||||
if (value) valueMask |= (1u << (bit_index & 31u));
|
||||
pnanovdb_write_uint32(buf, addr, valueMask);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_upper_set_child_mask(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p, pnanovdb_uint32_t bit_index, pnanovdb_bool_t value) {
|
||||
pnanovdb_address_t addr = pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_CHILD_MASK + 4u * (bit_index >> 5u));
|
||||
pnanovdb_uint32_t valueMask = pnanovdb_read_uint32(buf, addr);
|
||||
@@ -1556,6 +1783,13 @@ PNANOVDB_FORCE_INLINE void pnanovdb_lower_set_bbox_min(pnanovdb_buf_t buf, pnano
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_lower_set_bbox_max(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p, PNANOVDB_IN(pnanovdb_coord_t) bbox_max) {
|
||||
pnanovdb_write_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_BBOX_MAX), bbox_max);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_lower_set_value_mask(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p, pnanovdb_uint32_t bit_index, pnanovdb_bool_t value) {
|
||||
pnanovdb_address_t addr = pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_VALUE_MASK + 4u * (bit_index >> 5u));
|
||||
pnanovdb_uint32_t valueMask = pnanovdb_read_uint32(buf, addr);
|
||||
if (!value) { valueMask &= ~(1u << (bit_index & 31u)); }
|
||||
if (value) valueMask |= (1u << (bit_index & 31u));
|
||||
pnanovdb_write_uint32(buf, addr, valueMask);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_lower_set_child_mask(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p, pnanovdb_uint32_t bit_index, pnanovdb_bool_t value) {
|
||||
pnanovdb_address_t addr = pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_CHILD_MASK + 4u * (bit_index >> 5u));
|
||||
pnanovdb_uint32_t valueMask = pnanovdb_read_uint32(buf, addr);
|
||||
@@ -1604,6 +1838,13 @@ PNANOVDB_FORCE_INLINE void pnanovdb_leaf_set_bbox_min(pnanovdb_buf_t buf, pnanov
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_leaf_set_bbox_dif_and_flags(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t p, pnanovdb_uint32_t bbox_dif_and_flags) {
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_LEAF_OFF_BBOX_DIF_AND_FLAGS), bbox_dif_and_flags);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_leaf_set_value_mask(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t p, pnanovdb_uint32_t bit_index, pnanovdb_bool_t value) {
|
||||
pnanovdb_address_t addr = pnanovdb_address_offset(p.address, PNANOVDB_LEAF_OFF_VALUE_MASK + 4u * (bit_index >> 5u));
|
||||
pnanovdb_uint32_t valueMask = pnanovdb_read_uint32(buf, addr);
|
||||
if (!value) { valueMask &= ~(1u << (bit_index & 31u)); }
|
||||
if (value) valueMask |= (1u << (bit_index & 31u));
|
||||
pnanovdb_write_uint32(buf, addr, valueMask);
|
||||
}
|
||||
|
||||
struct pnanovdb_grid_type_constants_t
|
||||
{
|
||||
@@ -1639,7 +1880,7 @@ struct pnanovdb_grid_type_constants_t
|
||||
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_grid_type_constants_t)
|
||||
|
||||
// The following table with offsets will nedd to be updates as new GridTypes are added in NanoVDB.h
|
||||
PNANOVDB_STATIC_CONST pnanovdb_grid_type_constants_t pnanovdb_grid_type_constants[PNANOVDB_GRID_TYPE_END] =
|
||||
PNANOVDB_STATIC_CONST pnanovdb_grid_type_constants_t pnanovdb_grid_type_constants[PNANOVDB_GRID_TYPE_CAP] =
|
||||
{
|
||||
{28, 28, 28, 28, 28, 32, 0, 8, 20, 32, 8224, 8224, 8224, 8224, 8224, 270368, 1056, 1056, 1056, 1056, 1056, 33824, 80, 80, 80, 80, 96, 96},
|
||||
{28, 32, 36, 40, 44, 64, 32, 8, 20, 32, 8224, 8228, 8232, 8236, 8256, 270400, 1056, 1060, 1064, 1068, 1088, 33856, 80, 84, 88, 92, 96, 2144},
|
||||
@@ -1662,12 +1903,13 @@ PNANOVDB_STATIC_CONST pnanovdb_grid_type_constants_t pnanovdb_grid_type_constant
|
||||
{32, 64, 96, 128, 136, 160, 256, 32, 24, 64, 8224, 8256, 8288, 8296, 8320, 1056896, 1056, 1088, 1120, 1128, 1152, 132224, 80, 112, 144, 152, 160, 16544},
|
||||
{32, 40, 48, 56, 64, 96, 0, 8, 24, 32, 8224, 8232, 8240, 8248, 8256, 270400, 1056, 1064, 1072, 1080, 1088, 33856, 80, 80, 80, 80, 80, 96},
|
||||
{32, 40, 48, 56, 64, 96, 0, 8, 24, 32, 8224, 8232, 8240, 8248, 8256, 270400, 1056, 1064, 1072, 1080, 1088, 33856, 80, 80, 80, 80, 80, 96},
|
||||
{32, 40, 48, 56, 64, 96, 0, 8, 24, 32, 8224, 8232, 8240, 8248, 8256, 270400, 1056, 1064, 1072, 1080, 1088, 33856, 80, 80, 80, 80, 80, 160},
|
||||
{32, 40, 48, 56, 64, 96, 0, 8, 24, 32, 8224, 8232, 8240, 8248, 8256, 270400, 1056, 1064, 1072, 1080, 1088, 33856, 80, 80, 80, 80, 80, 160},
|
||||
{28, 28, 28, 28, 28, 32, 0, 8, 20, 32, 8224, 8224, 8224, 8224, 8224, 270368, 1056, 1056, 1056, 1056, 1056, 33824, 80, 80, 80, 80, 96, 96},
|
||||
{28, 28, 28, 28, 28, 32, 0, 8, 20, 32, 8224, 8224, 8224, 8224, 8224, 270368, 1056, 1056, 1056, 1056, 1056, 33824, 80, 80, 80, 80, 96, 96},
|
||||
{32, 40, 48, 56, 64, 96, 16, 8, 24, 32, 8224, 8232, 8240, 8248, 8256, 270400, 1056, 1064, 1072, 1080, 1088, 33856, 80, 88, 96, 96, 96, 1120},
|
||||
{28, 31, 34, 40, 44, 64, 24, 8, 20, 32, 8224, 8227, 8232, 8236, 8256, 270400, 1056, 1059, 1064, 1068, 1088, 33856, 80, 83, 88, 92, 96, 1632},
|
||||
{28, 34, 40, 48, 52, 64, 48, 8, 20, 32, 8224, 8230, 8236, 8240, 8256, 270400, 1056, 1062, 1068, 1072, 1088, 33856, 80, 86, 92, 96, 128, 3200},
|
||||
{28, 29, 30, 32, 36, 64, 8, 8, 20, 32, 8224, 8225, 8228, 8232, 8256, 270400, 1056, 1057, 1060, 1064, 1088, 33856, 80, 81, 84, 88, 96, 608},
|
||||
{28, 28, 28, 28, 28, 32, 0, 8, 20, 32, 8224, 8224, 8224, 8224, 8224, 270368, 1056, 1056, 1056, 1056, 1056, 33824, 80, 80, 80, 80, 96, 96},
|
||||
};
|
||||
|
||||
// ------------------------------------------------ Basic Lookup -----------------------------------------------------------
|
||||
@@ -1719,6 +1961,11 @@ PNANOVDB_FORCE_INLINE pnanovdb_root_tile_handle_t pnanovdb_root_get_tile_zero(pn
|
||||
return tile;
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_root_tile_get_child_mask(pnanovdb_buf_t buf, pnanovdb_root_tile_handle_t tile)
|
||||
{
|
||||
return !pnanovdb_int64_is_zero(pnanovdb_root_tile_get_child(buf, tile));
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_upper_handle_t pnanovdb_root_get_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, pnanovdb_root_tile_handle_t tile)
|
||||
{
|
||||
pnanovdb_upper_handle_t upper = { root.address };
|
||||
@@ -1726,6 +1973,11 @@ PNANOVDB_FORCE_INLINE pnanovdb_upper_handle_t pnanovdb_root_get_child(pnanovdb_g
|
||||
return upper;
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_root_set_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, pnanovdb_root_tile_handle_t tile, pnanovdb_upper_handle_t upper)
|
||||
{
|
||||
pnanovdb_root_tile_set_child(buf, tile, pnanovdb_address_diff(upper.address, root.address));
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_coord_to_key(PNANOVDB_IN(pnanovdb_coord_t) ijk)
|
||||
{
|
||||
#if defined(PNANOVDB_NATIVE_64)
|
||||
@@ -1760,6 +2012,27 @@ PNANOVDB_FORCE_INLINE pnanovdb_root_tile_handle_t pnanovdb_root_find_tile(pnanov
|
||||
return null_handle;
|
||||
}
|
||||
|
||||
// ----------------------------- Grid Type Safe Set ---------------------------------------
|
||||
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_grid_set_first_gridblindmetadata(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_gridblindmetadata_handle_t gridblindmetadata) {
|
||||
pnanovdb_grid_set_blind_metadata_offset(buf, p, pnanovdb_address_diff(gridblindmetadata.address, p.address));
|
||||
}
|
||||
|
||||
// ----------------------------- Tree Type Safe Set ---------------------------------------
|
||||
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_tree_set_first_leaf(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p, pnanovdb_leaf_handle_t leaf) {
|
||||
pnanovdb_tree_set_node_offset_leaf(buf, p, pnanovdb_address_diff(leaf.address, p.address));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_tree_set_first_lower(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p, pnanovdb_lower_handle_t lower) {
|
||||
pnanovdb_tree_set_node_offset_lower(buf, p, pnanovdb_address_diff(lower.address, p.address));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_tree_set_first_upper(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p, pnanovdb_upper_handle_t upper) {
|
||||
pnanovdb_tree_set_node_offset_upper(buf, p, pnanovdb_address_diff(upper.address, p.address));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_tree_set_first_root(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p, pnanovdb_root_handle_t root) {
|
||||
pnanovdb_tree_set_node_offset_root(buf, p, pnanovdb_address_diff(root.address, p.address));
|
||||
}
|
||||
|
||||
// ----------------------------- Leaf Node ---------------------------------------
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_leaf_coord_to_offset(PNANOVDB_IN(pnanovdb_coord_t) ijk)
|
||||
@@ -1880,57 +2153,6 @@ PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_index_get_value_index(pnan
|
||||
return pnanovdb_uint64_offset(offset, n);
|
||||
}
|
||||
|
||||
// ----------------------------- Leaf IndexMask Specialization ---------------------------------------
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_leaf_indexmask_has_stats(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf)
|
||||
{
|
||||
return pnanovdb_leaf_index_has_stats(buf, leaf);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_indexmask_get_min_index(pnanovdb_buf_t buf, pnanovdb_address_t min_address)
|
||||
{
|
||||
return pnanovdb_leaf_index_get_min_index(buf, min_address);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_indexmask_get_max_index(pnanovdb_buf_t buf, pnanovdb_address_t max_address)
|
||||
{
|
||||
return pnanovdb_leaf_index_get_max_index(buf, max_address);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_indexmask_get_ave_index(pnanovdb_buf_t buf, pnanovdb_address_t ave_address)
|
||||
{
|
||||
return pnanovdb_leaf_index_get_ave_index(buf, ave_address);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_indexmask_get_dev_index(pnanovdb_buf_t buf, pnanovdb_address_t dev_address)
|
||||
{
|
||||
return pnanovdb_leaf_index_get_dev_index(buf, dev_address);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_indexmask_get_value_index(pnanovdb_buf_t buf, pnanovdb_address_t value_address, PNANOVDB_IN(pnanovdb_coord_t) ijk)
|
||||
{
|
||||
return pnanovdb_leaf_index_get_value_index(buf, value_address, ijk);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_leaf_indexmask_get_mask_bit(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, pnanovdb_uint32_t n)
|
||||
{
|
||||
pnanovdb_uint32_t word_idx = n >> 5;
|
||||
pnanovdb_uint32_t bit_idx = n & 31;
|
||||
pnanovdb_uint32_t val_mask =
|
||||
pnanovdb_read_uint32(buf, pnanovdb_address_offset(leaf.address, 96u + 4u * word_idx));
|
||||
return (val_mask & (1u << bit_idx)) != 0u;
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_leaf_indexmask_set_mask_bit(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, pnanovdb_uint32_t n, pnanovdb_bool_t v)
|
||||
{
|
||||
pnanovdb_uint32_t word_idx = n >> 5;
|
||||
pnanovdb_uint32_t bit_idx = n & 31;
|
||||
pnanovdb_uint32_t val_mask =
|
||||
pnanovdb_read_uint32(buf, pnanovdb_address_offset(leaf.address, 96u + 4u * word_idx));
|
||||
if (v)
|
||||
{
|
||||
val_mask = val_mask | (1u << bit_idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
val_mask = val_mask & ~(1u << bit_idx);
|
||||
}
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(leaf.address, 96u + 4u * word_idx), val_mask);
|
||||
}
|
||||
|
||||
// ----------------------------- Leaf OnIndex Specialization ---------------------------------------
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_leaf_onindex_get_value_count(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf)
|
||||
@@ -2022,65 +2244,6 @@ PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_onindex_get_value_index(pn
|
||||
return value_index;
|
||||
}
|
||||
|
||||
// ----------------------------- Leaf OnIndexMask Specialization ---------------------------------------
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_leaf_onindexmask_get_value_count(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf)
|
||||
{
|
||||
return pnanovdb_leaf_onindex_get_value_count(buf, leaf);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_onindexmask_get_last_offset(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf)
|
||||
{
|
||||
return pnanovdb_leaf_onindex_get_last_offset(buf, leaf);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_leaf_onindexmask_has_stats(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf)
|
||||
{
|
||||
return pnanovdb_leaf_onindex_has_stats(buf, leaf);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_onindexmask_get_min_index(pnanovdb_buf_t buf, pnanovdb_address_t min_address)
|
||||
{
|
||||
return pnanovdb_leaf_onindex_get_min_index(buf, min_address);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_onindexmask_get_max_index(pnanovdb_buf_t buf, pnanovdb_address_t max_address)
|
||||
{
|
||||
return pnanovdb_leaf_onindex_get_max_index(buf, max_address);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_onindexmask_get_ave_index(pnanovdb_buf_t buf, pnanovdb_address_t ave_address)
|
||||
{
|
||||
return pnanovdb_leaf_onindex_get_ave_index(buf, ave_address);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_onindexmask_get_dev_index(pnanovdb_buf_t buf, pnanovdb_address_t dev_address)
|
||||
{
|
||||
return pnanovdb_leaf_onindex_get_dev_index(buf, dev_address);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_onindexmask_get_value_index(pnanovdb_buf_t buf, pnanovdb_address_t value_address, PNANOVDB_IN(pnanovdb_coord_t) ijk)
|
||||
{
|
||||
return pnanovdb_leaf_onindex_get_value_index(buf, value_address, ijk);
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_leaf_onindexmask_get_mask_bit(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, pnanovdb_uint32_t n)
|
||||
{
|
||||
pnanovdb_uint32_t word_idx = n >> 5;
|
||||
pnanovdb_uint32_t bit_idx = n & 31;
|
||||
pnanovdb_uint32_t val_mask =
|
||||
pnanovdb_read_uint32(buf, pnanovdb_address_offset(leaf.address, 96u + 4u * word_idx));
|
||||
return (val_mask & (1u << bit_idx)) != 0u;
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_leaf_onindexmask_set_mask_bit(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, pnanovdb_uint32_t n, pnanovdb_bool_t v)
|
||||
{
|
||||
pnanovdb_uint32_t word_idx = n >> 5;
|
||||
pnanovdb_uint32_t bit_idx = n & 31;
|
||||
pnanovdb_uint32_t val_mask =
|
||||
pnanovdb_read_uint32(buf, pnanovdb_address_offset(leaf.address, 96u + 4u * word_idx));
|
||||
if (v)
|
||||
{
|
||||
val_mask = val_mask | (1u << bit_idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
val_mask = val_mask & ~(1u << bit_idx);
|
||||
}
|
||||
pnanovdb_write_uint32(buf, pnanovdb_address_offset(leaf.address, 96u + 4u * word_idx), val_mask);
|
||||
}
|
||||
|
||||
// ----------------------------- Leaf PointIndex Specialization ---------------------------------------
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_pointindex_get_offset(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf)
|
||||
@@ -2210,6 +2373,17 @@ PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_value_address(pnanov
|
||||
return pnanovdb_lower_get_value_address_and_level(grid_type, buf, lower, ijk, PNANOVDB_REF(level));
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_lower_set_table_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node, pnanovdb_uint32_t n, pnanovdb_int64_t child)
|
||||
{
|
||||
pnanovdb_address_t table_address = pnanovdb_lower_get_table_address(grid_type, buf, node, n);
|
||||
pnanovdb_write_int64(buf, table_address, child);
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_lower_set_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, pnanovdb_uint32_t n, pnanovdb_leaf_handle_t leaf)
|
||||
{
|
||||
pnanovdb_lower_set_table_child(grid_type, buf, lower, n, pnanovdb_address_diff(leaf.address, lower.address));
|
||||
}
|
||||
|
||||
// ------------------------------------------------ Upper Node -----------------------------------------------------------
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_upper_coord_to_offset(PNANOVDB_IN(pnanovdb_coord_t) ijk)
|
||||
@@ -2291,8 +2465,19 @@ PNANOVDB_FORCE_INLINE void pnanovdb_upper_set_table_child(pnanovdb_grid_type_t g
|
||||
pnanovdb_write_int64(buf, bufAddress, child);
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_upper_set_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, pnanovdb_uint32_t n, pnanovdb_lower_handle_t lower)
|
||||
{
|
||||
pnanovdb_upper_set_table_child(grid_type, buf, upper, n, pnanovdb_address_diff(lower.address, upper.address));
|
||||
}
|
||||
|
||||
// ------------------------------------------------ Root -----------------------------------------------------------
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_background_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root)
|
||||
{
|
||||
pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_off_background);
|
||||
return pnanovdb_address_offset(root.address, byte_offset);
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_min_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root)
|
||||
{
|
||||
pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_off_min);
|
||||
@@ -2591,12 +2776,6 @@ PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_value_address_and_ca
|
||||
return pnanovdb_lower_get_value_address_and_level_and_cache(grid_type, buf, lower, ijk, acc, PNANOVDB_REF(level));
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE void pnanovdb_lower_set_table_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node, pnanovdb_uint32_t n, pnanovdb_int64_t child)
|
||||
{
|
||||
pnanovdb_address_t table_address = pnanovdb_lower_get_table_address(grid_type, buf, node, n);
|
||||
pnanovdb_write_int64(buf, table_address, child);
|
||||
}
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_value_address_and_level_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
|
||||
{
|
||||
pnanovdb_uint32_t n = pnanovdb_upper_coord_to_offset(ijk);
|
||||
@@ -3227,6 +3406,19 @@ PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_hdda_update(PNANOVDB_INOUT(pnanov
|
||||
{
|
||||
return PNANOVDB_FALSE;
|
||||
}
|
||||
|
||||
// compute valid voxel range
|
||||
pnanovdb_coord_t voxel_max = {
|
||||
(PNANOVDB_DEREF(hdda).voxel.x + PNANOVDB_DEREF(hdda).dim - 1) & (~(dim - 1)),
|
||||
(PNANOVDB_DEREF(hdda).voxel.y + PNANOVDB_DEREF(hdda).dim - 1) & (~(dim - 1)),
|
||||
(PNANOVDB_DEREF(hdda).voxel.z + PNANOVDB_DEREF(hdda).dim - 1) & (~(dim - 1))
|
||||
};
|
||||
pnanovdb_coord_t voxel_min = {
|
||||
PNANOVDB_DEREF(hdda).voxel.x & (~(dim - 1)),
|
||||
PNANOVDB_DEREF(hdda).voxel.y & (~(dim - 1)),
|
||||
PNANOVDB_DEREF(hdda).voxel.z & (~(dim - 1))
|
||||
};
|
||||
|
||||
PNANOVDB_DEREF(hdda).dim = dim;
|
||||
|
||||
pnanovdb_vec3_t pos = pnanovdb_vec3_add(
|
||||
@@ -3237,6 +3429,14 @@ PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_hdda_update(PNANOVDB_INOUT(pnanov
|
||||
|
||||
PNANOVDB_DEREF(hdda).voxel = pnanovdb_hdda_pos_to_voxel(PNANOVDB_REF(pos), dim);
|
||||
|
||||
// clamp voxel to valid range
|
||||
PNANOVDB_DEREF(hdda).voxel.x = PNANOVDB_DEREF(hdda).voxel.x < voxel_max.x ? PNANOVDB_DEREF(hdda).voxel.x : voxel_max.x;
|
||||
PNANOVDB_DEREF(hdda).voxel.y = PNANOVDB_DEREF(hdda).voxel.y < voxel_max.y ? PNANOVDB_DEREF(hdda).voxel.y : voxel_max.y;
|
||||
PNANOVDB_DEREF(hdda).voxel.z = PNANOVDB_DEREF(hdda).voxel.z < voxel_max.z ? PNANOVDB_DEREF(hdda).voxel.z : voxel_max.z;
|
||||
PNANOVDB_DEREF(hdda).voxel.x = PNANOVDB_DEREF(hdda).voxel.x > voxel_min.x ? PNANOVDB_DEREF(hdda).voxel.x : voxel_min.x;
|
||||
PNANOVDB_DEREF(hdda).voxel.y = PNANOVDB_DEREF(hdda).voxel.y > voxel_min.y ? PNANOVDB_DEREF(hdda).voxel.y : voxel_min.y;
|
||||
PNANOVDB_DEREF(hdda).voxel.z = PNANOVDB_DEREF(hdda).voxel.z > voxel_min.z ? PNANOVDB_DEREF(hdda).voxel.z : voxel_min.z;
|
||||
|
||||
if (PNANOVDB_DEREF(hdda).step.x != 0)
|
||||
{
|
||||
PNANOVDB_DEREF(hdda).next.x = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.x - pos.x) * dir_inv.x;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(NanoVDBExamples LANGUAGES CXX)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
@@ -35,6 +35,8 @@ void usage [[noreturn]] (const std::string& progName, int exitStatus = EXIT_FAIL
|
||||
<< "--fp8\tQuantize float grids to 8 bits\n"
|
||||
<< "--fp16\tQuantize float grids to 16 bits\n"
|
||||
<< "--fpN\tQuantize float grids to variable bit depth (use -a or -r to specify a tolerance)\n"
|
||||
<< "--index\tProduce an IndexGrid where all float values are encoded as sidecar data\n"
|
||||
<< "--onIndex\tProduce an IndexGrid where active float values are encoded as sidecar data\n"
|
||||
<< "-g,--grid name\tConvert all grids matching the specified string name\n"
|
||||
<< "-h,--help\tPrints this message\n"
|
||||
<< "-r,--rel-error float\t Relative error tolerance used for variable bit depth quantization\n"
|
||||
@@ -57,14 +59,14 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
int exitStatus = EXIT_SUCCESS;
|
||||
|
||||
nanovdb::io::Codec codec = nanovdb::io::Codec::NONE;// compression codec for the file
|
||||
nanovdb::tools::StatsMode sMode = nanovdb::tools::StatsMode::Default;
|
||||
nanovdb::CheckMode cMode = nanovdb::CheckMode::Default;
|
||||
nanovdb::GridType qMode = nanovdb::GridType::Unknown;//specify the quantization mode
|
||||
bool verbose = false, overwrite = false, dither = false, absolute = true;
|
||||
float tolerance = -1.0f;
|
||||
std::string gridName;
|
||||
std::vector<std::string> fileNames;
|
||||
nanovdb::io::Codec codec = nanovdb::io::Codec::NONE;// compression codec for the file
|
||||
nanovdb::tools::StatsMode sMode = nanovdb::tools::StatsMode::Default;
|
||||
nanovdb::CheckMode cMode = nanovdb::CheckMode::Default;
|
||||
nanovdb::GridType qMode = nanovdb::GridType::Unknown;//specify the quantization mode
|
||||
bool verbose = false, overwrite = false, dither = false, absolute = true;
|
||||
float tolerance = -1.0f;
|
||||
std::string gridName;
|
||||
std::vector<std::string> fileNames;
|
||||
auto toLowerCase = [](std::string &str) {
|
||||
std::transform(str.begin(), str.end(), str.begin(),[](unsigned char c){return std::tolower(c);});
|
||||
};
|
||||
@@ -85,6 +87,10 @@ int main(int argc, char* argv[])
|
||||
qMode = nanovdb::GridType::Fp16;
|
||||
} else if (arg == "--fpN") {
|
||||
qMode = nanovdb::GridType::FpN;
|
||||
} else if (arg == "--index") {
|
||||
qMode = nanovdb::GridType::Index;
|
||||
} else if (arg == "--onIndex") {
|
||||
qMode = nanovdb::GridType::OnIndex;
|
||||
} else if (arg == "-h" || arg == "--help") {
|
||||
usage(argv[0], EXIT_SUCCESS);
|
||||
} else if (arg == "-b" || arg == "--blosc") {
|
||||
@@ -169,17 +175,22 @@ int main(int argc, char* argv[])
|
||||
std::cerr << "Expected at least one input file followed by exactly one output file\n" << std::endl;
|
||||
usage(argv[0]);
|
||||
}
|
||||
const std::string outputFile = fileNames.back();
|
||||
const std::string outputFile = fileNames.back();// last file is always the output file
|
||||
const std::string ext = outputFile.substr(outputFile.find_last_of(".") + 1);
|
||||
bool toNanoVDB = false;
|
||||
if (ext == "nvdb") {
|
||||
toNanoVDB = true;
|
||||
} else if (ext != "vdb") {
|
||||
} else if (ext == "vdb") {
|
||||
if (qMode != nanovdb::GridType::Unknown) {
|
||||
std::cerr << "The options are incompatible an output of type OpenVDB" << std::endl;
|
||||
usage(argv[0]);
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Unrecognized file extension: \"" << ext << "\"\n" << std::endl;
|
||||
usage(argv[0]);
|
||||
}
|
||||
|
||||
fileNames.pop_back();
|
||||
fileNames.pop_back();// remove the output file name
|
||||
|
||||
if (!overwrite) {
|
||||
std::ifstream is(outputFile, std::ios::in | std::ios::binary);
|
||||
@@ -203,9 +214,16 @@ int main(int argc, char* argv[])
|
||||
|
||||
auto openToNano = [&](const openvdb::GridBase::Ptr& base)
|
||||
{
|
||||
using SrcGridT = openvdb::FloatGrid;
|
||||
if (auto floatGrid = openvdb::GridBase::grid<SrcGridT>(base)) {
|
||||
nanovdb::tools::CreateNanoGrid<SrcGridT> s(*floatGrid);
|
||||
const bool includeStats = false, includeTiles = false;
|
||||
const int verb = verbose ? 1 : 0;
|
||||
if (qMode == nanovdb::GridType::OnIndex) {
|
||||
return nanovdb::tools::openToIndexVDB<nanovdb::ValueOnIndex>(base, 1u, includeStats, includeTiles, verb);
|
||||
} else if (qMode == nanovdb::GridType::Index) {
|
||||
return nanovdb::tools::openToIndexVDB<nanovdb::ValueIndex>(base, 1u, includeStats, includeTiles, verb);
|
||||
}
|
||||
|
||||
if (auto floatGrid = openvdb::GridBase::grid<openvdb::FloatGrid>(base)) {
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> s(*floatGrid);
|
||||
s.setStats(sMode);
|
||||
s.setChecksum(cMode);
|
||||
s.enableDithering(dither);
|
||||
@@ -227,7 +245,7 @@ int main(int argc, char* argv[])
|
||||
break;
|
||||
}// end of switch
|
||||
}
|
||||
return nanovdb::tools::openToNanoVDB(base, sMode, cMode, verbose ? 1 : 0);
|
||||
return nanovdb::tools::openToNanoVDB(base, sMode, cMode, verb);
|
||||
};
|
||||
try {
|
||||
if (toNanoVDB) { // OpenVDB -> NanoVDB
|
||||
@@ -237,26 +255,21 @@ int main(int argc, char* argv[])
|
||||
std::cerr << "Since the last file has extension .nvdb the remaining input files were expected to have extensions .vdb\n" << std::endl;
|
||||
usage(argv[0]);
|
||||
}
|
||||
if (verbose)
|
||||
std::cout << "Opening OpenVDB file named \"" << inputFile << "\"" << std::endl;
|
||||
if (verbose) std::cout << "Opening OpenVDB file named \"" << inputFile << "\"" << std::endl;
|
||||
openvdb::io::File file(inputFile);
|
||||
file.open(false); //disable delayed loading
|
||||
if (gridName.empty()) {// convert all grid in the file
|
||||
auto grids = file.getGrids();
|
||||
std::vector<nanovdb::GridHandle<nanovdb::HostBuffer> > handles;
|
||||
for (auto& grid : *grids) {
|
||||
if (verbose) {
|
||||
std::cout << "Converting OpenVDB grid named \"" << grid->getName() << "\" to NanoVDB" << std::endl;
|
||||
}
|
||||
if (verbose) std::cout << "Converting OpenVDB grid named \"" << grid->getName() << "\" to NanoVDB" << std::endl;
|
||||
handles.push_back(openToNano(grid));
|
||||
} // loop over OpenVDB grids in file
|
||||
auto handle = nanovdb::mergeGrids<nanovdb::HostBuffer, std::vector>(handles);
|
||||
nanovdb::io::writeGrid(os, handle, codec);
|
||||
} else {// convert only grid with matching name
|
||||
auto grid = file.readGrid(gridName);
|
||||
if (verbose) {
|
||||
std::cout << "Converting OpenVDB grid named \"" << grid->getName() << "\" to NanoVDB" << std::endl;
|
||||
}
|
||||
if (verbose) std::cout << "Converting OpenVDB grid named \"" << grid->getName() << "\" to NanoVDB" << std::endl;
|
||||
auto handle = openToNano(grid);
|
||||
nanovdb::io::writeGrid(os, handle, codec);
|
||||
}
|
||||
@@ -269,15 +282,13 @@ int main(int argc, char* argv[])
|
||||
std::cerr << "Since the last file has extension .vdb the remaining input files were expected to have extensions .nvdb\n" << std::endl;
|
||||
usage(argv[0]);
|
||||
}
|
||||
if (verbose)
|
||||
std::cout << "Opening NanoVDB file named \"" << inputFile << "\"" << std::endl;
|
||||
if (verbose) std::cout << "Opening NanoVDB file named \"" << inputFile << "\"" << std::endl;
|
||||
if (gridName.empty()) {
|
||||
auto handles = nanovdb::io::readGrids(inputFile, verbose);
|
||||
for (auto &h : handles) {
|
||||
for (uint32_t i = 0; i < h.gridCount(); ++i) {
|
||||
if (verbose)
|
||||
std::cout << "Converting NanoVDB grid named \"" << h.gridMetaData(i)->shortGridName() << "\" to OpenVDB" << std::endl;
|
||||
grids->push_back(nanovdb::tools::nanoToOpenVDB(h, 0, i));
|
||||
if (verbose) std::cout << "Converting NanoVDB grid named \"" << h.gridMetaData(i)->shortGridName() << "\" to OpenVDB" << std::endl;
|
||||
grids->push_back(nanovdb::tools::nanoToOpenVDB(h, i));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -286,8 +297,7 @@ int main(int argc, char* argv[])
|
||||
std::cerr << "File did not contain a NanoVDB grid named \"" << gridName << "\"\n" << std::endl;
|
||||
usage(argv[0]);
|
||||
}
|
||||
if (verbose)
|
||||
std::cout << "Converting NanoVDB grid named \"" << handle.gridMetaData()->shortGridName() << "\" to OpenVDB" << std::endl;
|
||||
if (verbose) std::cout << "Converting NanoVDB grid named \"" << handle.gridMetaData()->shortGridName() << "\" to OpenVDB" << std::endl;
|
||||
grids->push_back(nanovdb::tools::nanoToOpenVDB(handle));
|
||||
}
|
||||
} // loop over input files
|
||||
|
||||
@@ -166,7 +166,7 @@ int main(int argc, char* argv[])
|
||||
auto classWidth = std::string("Class").length() + padding;
|
||||
auto codecWidth = std::string("Codec").length() + padding;
|
||||
auto ibboxWidth = std::string("Index Bounding Box").length() + padding;
|
||||
auto wbboxWidth = std::string("World BBox").length() + padding;
|
||||
auto wbboxWidth = std::string("World Bounding Box").length() + padding;
|
||||
auto sizeWidth = std::string("Size").length() + padding;
|
||||
auto fileWidth = std::string("File").length() + padding;
|
||||
auto voxelsWidth = std::string("# Voxels").length() + padding;
|
||||
@@ -175,6 +175,7 @@ int main(int argc, char* argv[])
|
||||
auto configWidth = std::string("32^3->16^3->8^3").length() + padding;
|
||||
auto tileWidth = std::string("# Active tiles").length() + padding;
|
||||
auto resWidth = std::string("Resolution").length() + padding;
|
||||
auto blindWidth = std::string("Blind data count").length() + padding;
|
||||
for (auto& m : list) {
|
||||
width(nameWidth, m.gridName);
|
||||
width(typeWidth, nanovdb::toStr(str, m.gridType));
|
||||
@@ -190,6 +191,7 @@ int main(int argc, char* argv[])
|
||||
width(tileWidth, nodesToStr(m.tileCount));
|
||||
width(voxelsWidth, std::to_string(m.voxelCount));
|
||||
width(voxelSizeWidth, Vec3dToStr(m.voxelSize));
|
||||
width(blindWidth, std::to_string(m.blindDataCount));
|
||||
}
|
||||
std::cout << "\nThe file \"" << file << "\" contains the following ";
|
||||
if (list.size()>1) {
|
||||
@@ -214,7 +216,8 @@ int main(int argc, char* argv[])
|
||||
std::cout << std::left << std::setw(configWidth) << "32^3->16^3->8^3"
|
||||
<< std::left << std::setw(tileWidth) << "# Active tiles"
|
||||
<< std::left << std::setw(ibboxWidth) << "Index Bounding Box"
|
||||
<< std::left << std::setw(wbboxWidth) << "World Bounding Box";
|
||||
<< std::left << std::setw(wbboxWidth) << "World Bounding Box"
|
||||
<< std::left << std::setw(blindWidth) << "Blind data count";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
int n = 0;
|
||||
@@ -238,11 +241,12 @@ int main(int argc, char* argv[])
|
||||
std::cout << std::left << std::setw(configWidth) << nodesToStr(m.nodeCount)
|
||||
<< std::left << std::setw(tileWidth) << nodesToStr(m.tileCount)
|
||||
<< std::left << std::setw(ibboxWidth) << ibboxToStr(m.indexBBox)
|
||||
<< std::left << std::setw(wbboxWidth) << wbboxToStr(m.worldBBox);
|
||||
<< std::left << std::setw(wbboxWidth) << wbboxToStr(m.worldBBox)
|
||||
<< std::left << std::setw(blindWidth) << m.blindDataCount;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
}// loop over FileGridMetaData in list from a single file
|
||||
}// loop over files
|
||||
if (verbose) {
|
||||
size_t w = 0;
|
||||
switch (mode) {
|
||||
|
||||
@@ -41,7 +41,7 @@ class DeviceBuffer
|
||||
|
||||
/// @brief Initialize buffer
|
||||
/// @param size byte size of buffer to be initialized
|
||||
/// @param host If true buffer is initialized only on the host/CPU, else on the device/GPU
|
||||
/// @param device id of the device on which to initialize the buffer
|
||||
/// @note All existing buffers are first cleared
|
||||
/// @warning size is expected to be non-zero. Use clear() clear buffer!
|
||||
void init(uint64_t size, int device, cudaStream_t stream);
|
||||
@@ -127,6 +127,20 @@ public:
|
||||
other.mSize = other.mDeviceCount = other.mManaged = 0;
|
||||
}
|
||||
|
||||
/// @brief Copy-constructor from a HostBuffer
|
||||
/// @param buffer host buffer from which to copy data
|
||||
/// @param device id of the device on which to initialize the buffer
|
||||
/// @param stream cuda stream
|
||||
DeviceBuffer(const HostBuffer& buffer, int device = cudaCpuDeviceId, cudaStream_t stream = 0)
|
||||
: DeviceBuffer(buffer.size(), device, stream)
|
||||
{
|
||||
if (mCpuData) {
|
||||
cudaCheck(cudaMemcpy(mCpuData, buffer.data(), mSize, cudaMemcpyHostToHost));
|
||||
} else if (mGpuData[device]) {
|
||||
cudaCheck(cudaMemcpyAsync(mGpuData[device], buffer.data(), mSize, cudaMemcpyHostToDevice, stream));
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Destructor frees memory on both the host and device
|
||||
~DeviceBuffer() { this->clear(); };
|
||||
|
||||
@@ -138,6 +152,11 @@ public:
|
||||
/// @return An instance of this class using move semantics
|
||||
static DeviceBuffer create(uint64_t size, const DeviceBuffer* dummy, bool host, void* stream){return DeviceBuffer(size, host, stream);}
|
||||
|
||||
/// @brief Static factory method that returns an instance of this buffer
|
||||
/// @param size byte size of buffer to be initialized
|
||||
/// @param dummy this argument is currently ignored but required to match the API of the HostBuffer
|
||||
/// @param device id of the device on which to initialize the buffer
|
||||
/// @param stream cuda stream
|
||||
static DeviceBuffer create(uint64_t size, const DeviceBuffer* dummy = nullptr, int device = cudaCpuDeviceId, cudaStream_t stream = 0){return DeviceBuffer(size, device, stream);}
|
||||
|
||||
/// @brief Static factory method that returns an instance of this buffer that wraps externally managed memory
|
||||
@@ -153,14 +172,21 @@ public:
|
||||
/// @param list list of device IDs and device memory pointers
|
||||
static DeviceBuffer create(uint64_t size, void* cpuData, std::initializer_list<std::pair<int,void*>> list) {return DeviceBuffer(size, cpuData, list);}
|
||||
|
||||
/// @brief Static factory method that returns an instance of this buffer constructed from a HostBuffer
|
||||
/// @param buffer host buffer from which to copy data
|
||||
/// @param device id of the device on which to initialize the buffer
|
||||
/// @param stream cuda stream
|
||||
static DeviceBuffer create(const HostBuffer& buffer, int device = cudaCpuDeviceId, cudaStream_t stream = 0) {return DeviceBuffer(buffer, device, stream);}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
//@{
|
||||
/// @{
|
||||
/// @brief Factory methods that create a shared pointer to an DeviceBuffer instance
|
||||
static PtrT createPtr(uint64_t size, const DeviceBuffer* = nullptr, int device = cudaCpuDeviceId, cudaStream_t stream = 0) {return std::make_shared<DeviceBuffer>(size, device, stream);}
|
||||
static PtrT createPtr(uint64_t size, void* cpuData, void* gpuData) {return std::make_shared<DeviceBuffer>(size, cpuData, gpuData);}
|
||||
static PtrT createPtr(uint64_t size, void* cpuData, std::initializer_list<std::pair<int,void*>> list) {return std::make_shared<DeviceBuffer>(size, cpuData, list);}
|
||||
///@}
|
||||
static PtrT createPtr(const HostBuffer& buffer, int device = cudaCpuDeviceId, cudaStream_t stream = 0) {return std::make_shared<DeviceBuffer>(buffer, device, stream);}
|
||||
/// @}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -168,18 +194,7 @@ public:
|
||||
DeviceBuffer& operator=(const DeviceBuffer&) = delete;
|
||||
|
||||
/// @brief Move copy assignment operation
|
||||
DeviceBuffer& operator=(DeviceBuffer&& other) noexcept
|
||||
{
|
||||
mSize = other.mSize;
|
||||
mCpuData = other.mCpuData;
|
||||
delete [] mGpuData;
|
||||
mGpuData = other.mGpuData;
|
||||
mDeviceCount = other.mDeviceCount;
|
||||
mManaged = other.mManaged;
|
||||
other.mCpuData = other.mGpuData = nullptr;
|
||||
other.mSize = other.mDeviceCount = other.mManaged = 0;
|
||||
return *this;
|
||||
}
|
||||
DeviceBuffer& operator=(DeviceBuffer&& other) noexcept;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -190,7 +205,7 @@ public:
|
||||
/// @brief Returns an offset pointer of a specific type from the allocated host memory
|
||||
/// @tparam T Type of the pointer returned
|
||||
/// @param count Numbers of elements of @c parameter type T to skip
|
||||
/// @warning assumes that this instance is not empty!
|
||||
/// @warning might return NULL
|
||||
template <typename T>
|
||||
T* data(ptrdiff_t count = 0, int device = cudaCpuDeviceId) const
|
||||
{
|
||||
@@ -279,11 +294,11 @@ public:
|
||||
|
||||
int deviceCount() const {return mDeviceCount;}
|
||||
|
||||
//@{
|
||||
/// @{
|
||||
/// @brief Returns true if this allocator is empty, i.e. has no allocated memory
|
||||
bool empty() const { return mSize == 0; }
|
||||
bool isEmpty() const { return this->empty(); }
|
||||
//@}
|
||||
/// @}
|
||||
|
||||
/// @brief De-allocate all memory managed by this allocator and set all pointers to NULL
|
||||
void clear(cudaStream_t stream = 0);
|
||||
@@ -293,6 +308,26 @@ public:
|
||||
|
||||
// --------------------------> Implementations below <------------------------------------
|
||||
|
||||
inline DeviceBuffer& DeviceBuffer::operator=(DeviceBuffer&& other) noexcept
|
||||
{
|
||||
if (mManaged) {// first free all the managed data buffers
|
||||
cudaCheck(cudaFreeHost(mCpuData));
|
||||
for (int i=0; i<mDeviceCount; ++i) cudaCheck(util::cuda::freeAsync(mGpuData[i], 0));
|
||||
}
|
||||
delete [] mGpuData;
|
||||
mSize = other.mSize;
|
||||
mCpuData = other.mCpuData;
|
||||
mGpuData = other.mGpuData;
|
||||
mDeviceCount = other.mDeviceCount;
|
||||
mManaged = other.mManaged;
|
||||
other.mCpuData = nullptr;
|
||||
other.mGpuData = nullptr;
|
||||
other.mSize = 0;
|
||||
other.mDeviceCount = 0;
|
||||
other.mManaged = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void DeviceBuffer::init(uint64_t size, int device, cudaStream_t stream)
|
||||
{
|
||||
if (size==0) return;
|
||||
@@ -303,7 +338,7 @@ inline void DeviceBuffer::init(uint64_t size, int device, cudaStream_t stream)
|
||||
cudaCheck(cudaMallocHost((void**)&mCpuData, size)); // un-managed pinned memory on the host (can be slow to access!). Always 32B aligned
|
||||
checkPtr(mCpuData, "cuda::DeviceBuffer::init: failed to allocate host buffer");
|
||||
} else {
|
||||
cudaCheck(cudaMallocAsync(mGpuData+device, size, stream)); // un-managed memory on the device, always 32B aligned!
|
||||
cudaCheck(util::cuda::mallocAsync(mGpuData+device, size, stream)); // un-managed memory on the device, always 32B aligned!
|
||||
checkPtr(mGpuData[device], "cuda::DeviceBuffer::init: failed to allocate device buffer");
|
||||
}
|
||||
mSize = size;
|
||||
@@ -316,7 +351,7 @@ inline void DeviceBuffer::deviceUpload(int device, cudaStream_t stream, bool syn
|
||||
checkPtr(mCpuData, "uninitialized cpu source data");
|
||||
if (mGpuData[device] == nullptr) {
|
||||
if (mManaged==0) throw std::runtime_error("DeviceBuffer::deviceUpload called on externally managed memory that wasn\'t allocated.");
|
||||
cudaCheck(cudaMallocAsync(mGpuData+device, mSize, stream)); // un-managed memory on the device, always 32B aligned!
|
||||
cudaCheck(util::cuda::mallocAsync(mGpuData+device, mSize, stream)); // un-managed memory on the device, always 32B aligned!
|
||||
}
|
||||
checkPtr(mGpuData[device], "uninitialized gpu destination data");
|
||||
cudaCheck(cudaMemcpyAsync(mGpuData[device], mCpuData, mSize, cudaMemcpyHostToDevice, stream));
|
||||
@@ -352,13 +387,16 @@ inline void DeviceBuffer::deviceDownload(void* stream, bool sync)
|
||||
|
||||
inline void DeviceBuffer::clear(cudaStream_t stream)
|
||||
{
|
||||
if (mManaged!=0) {// free all the managed data buffers
|
||||
if (mManaged) {// free all the managed data buffers
|
||||
cudaCheck(cudaFreeHost(mCpuData));
|
||||
for (int i=0; i<mDeviceCount; ++i) cudaCheck(cudaFreeAsync(mGpuData[i], stream));
|
||||
for (int i=0; i<mDeviceCount; ++i) cudaCheck(util::cuda::freeAsync(mGpuData[i], stream));
|
||||
}
|
||||
delete [] mGpuData;
|
||||
mCpuData = mGpuData = nullptr;
|
||||
mSize = mDeviceCount = mManaged = 0;
|
||||
mCpuData = nullptr;
|
||||
mGpuData = nullptr;
|
||||
mSize = 0;
|
||||
mDeviceCount = 0;
|
||||
mManaged = 0;
|
||||
} // DeviceBuffer::clear
|
||||
|
||||
}// namespace cuda
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*!
|
||||
\file DeviceMesh.h
|
||||
|
||||
\brief nanovdb::cuda::DeviceMesh encapsulates device IDs, CUDA streams,
|
||||
and NCCL communicators in order to facilitate multi-GPU applications.
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_CUDA_DEVICEMESH_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_CUDA_DEVICEMESH_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
#ifdef NANOVDB_USE_NCCL
|
||||
#include <nccl.h>
|
||||
#endif
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace cuda {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// @brief RAII class that caches/restores the current device at construction/destruction
|
||||
class DeviceGuard {
|
||||
public:
|
||||
DeviceGuard() { cudaGetDevice(&deviceId); }
|
||||
~DeviceGuard() { cudaSetDevice(deviceId); }
|
||||
|
||||
/// @{
|
||||
/// @brief DeviceGuard is not copyable nor movable
|
||||
DeviceGuard(const DeviceGuard&) = delete;
|
||||
DeviceGuard& operator=(const DeviceGuard&) = delete;
|
||||
DeviceGuard(DeviceGuard&& other) = delete;
|
||||
DeviceGuard& operator=(DeviceGuard&& other) = delete;
|
||||
/// @}
|
||||
private:
|
||||
int deviceId = -1;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/// @brief POD struct representing a device id and a stream on that device
|
||||
struct DeviceNode
|
||||
{
|
||||
int id = -1;
|
||||
cudaStream_t stream = cudaStream_t(0);
|
||||
};
|
||||
|
||||
/// @brief This class wraps a vector of per-device IDs and CUDA streams and holds inter-device connectivity information and NCCL comms.
|
||||
class DeviceMesh
|
||||
{
|
||||
using DeviceVecT = std::vector<DeviceNode>;
|
||||
using const_iterator = DeviceVecT::const_iterator;
|
||||
using size_type = DeviceVecT::size_type;
|
||||
public:
|
||||
/// @brief Constructs a device mesh for all devices on the host and initializes a CUDA stream (and NCCL communicator) for each device
|
||||
DeviceMesh();
|
||||
/// @brief Destroys the per-device CUDA stream and finalizes and destroys the per-device NCCL communicators
|
||||
~DeviceMesh();
|
||||
/// @brief Disallow copy-construction
|
||||
DeviceMesh(const DeviceMesh&) = delete;
|
||||
/// @brief Move constructor. Underlying CUDA streams and NCCL communicators are not reinitialized.
|
||||
/// @param DeviceMesh instance that will be moved into this DeviceMesh.
|
||||
DeviceMesh(DeviceMesh&&) noexcept;
|
||||
/// @brief Disallow copy-assignment
|
||||
DeviceMesh& operator=(const DeviceMesh&) = delete;
|
||||
/// @brief Move assignment. Underlying CUDA streams and NCCL communicators are not reinitialized.
|
||||
/// @param DeviceMesh instance that will be moved into this DeviceMesh.
|
||||
DeviceMesh& operator=(DeviceMesh&&) noexcept;
|
||||
|
||||
/// @brief Returns the number of devices
|
||||
size_type deviceCount() const { return mDeviceNodes.size(); };
|
||||
|
||||
/// @brief Returns a const reference to the DeviceNode for a particular device
|
||||
const DeviceNode& operator [](int deviceId) const { return mDeviceNodes[deviceId]; }
|
||||
|
||||
/// @brief Returns an iterator to the first device node
|
||||
const_iterator begin() const { return mDeviceNodes.begin(); };
|
||||
/// @brief Returns an iterator past the last device node
|
||||
const_iterator end() const { return mDeviceNodes.end(); };
|
||||
|
||||
#ifdef NANOVDB_USE_NCCL
|
||||
/// @brief Returns the NCCL communicator for a particular device
|
||||
ncclComm_t comm(int deviceId) const { return mComms[deviceId]; }
|
||||
#endif
|
||||
|
||||
/// @brief Returns whether or not peer to peer access is supported between two devices.
|
||||
bool canAccessPeer(int deviceId, int peerId) const { return mConnectivity[deviceId][peerId] & (1 << cudaDevP2PAttrAccessSupported); }
|
||||
|
||||
private:
|
||||
/// @brief Returns whether or not a device supports managed memory
|
||||
bool hasManagedMemory(int deviceId) const {
|
||||
int managedMemoryValue = 0;
|
||||
cudaDeviceGetAttribute(&managedMemoryValue, cudaDevAttrManagedMemory, deviceId);
|
||||
return static_cast<bool>(managedMemoryValue);
|
||||
}
|
||||
|
||||
DeviceVecT mDeviceNodes;
|
||||
|
||||
#ifdef NANOVDB_USE_NCCL
|
||||
std::vector<ncclComm_t> mComms;
|
||||
#endif
|
||||
std::vector<std::vector<uint32_t>> mConnectivity;
|
||||
};
|
||||
|
||||
inline DeviceMesh::DeviceMesh()
|
||||
{
|
||||
detail::DeviceGuard deviceGuard;
|
||||
|
||||
int deviceCount = -1;
|
||||
cudaGetDeviceCount(&deviceCount);
|
||||
|
||||
for (int deviceId = 0; deviceId < deviceCount; ++deviceId) {
|
||||
NANOVDB_ASSERT(hasManagedMemory(deviceId));
|
||||
cudaSetDevice(deviceId);
|
||||
cudaStream_t stream;
|
||||
cudaStreamCreate(&stream);
|
||||
mDeviceNodes.push_back({deviceId, stream});
|
||||
}
|
||||
|
||||
#ifdef NANOVDB_USE_NCCL
|
||||
mComms.resize(deviceCount);
|
||||
ncclCommInitAll(mComms.data(), deviceCount, nullptr);
|
||||
#endif
|
||||
|
||||
mConnectivity.resize(deviceCount);
|
||||
for (const auto& deviceNode : mDeviceNodes) {
|
||||
mConnectivity[deviceNode.id].resize(deviceCount);
|
||||
for (const auto& peerNode : mDeviceNodes) {
|
||||
mConnectivity[deviceNode.id][peerNode.id] = 0;
|
||||
if (deviceNode.id != peerNode.id) {
|
||||
int peerAccessSupportedValue = 0;
|
||||
cudaDeviceGetP2PAttribute(&peerAccessSupportedValue, cudaDevP2PAttrAccessSupported, deviceNode.id, peerNode.id);
|
||||
if (peerAccessSupportedValue) {
|
||||
mConnectivity[deviceNode.id][peerNode.id] |= (1 << cudaDevP2PAttrAccessSupported);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline DeviceMesh::DeviceMesh(DeviceMesh&& other) noexcept
|
||||
: mDeviceNodes(std::move(other.mDeviceNodes)),
|
||||
#ifdef NANOVDB_USE_NCCL
|
||||
mComms(std::move(other.mComms)),
|
||||
#endif
|
||||
mConnectivity(std::move(other.mConnectivity))
|
||||
{
|
||||
}
|
||||
|
||||
inline DeviceMesh::~DeviceMesh()
|
||||
{
|
||||
detail::DeviceGuard deviceGuard;
|
||||
|
||||
#ifdef NANOVDB_USE_NCCL
|
||||
std::for_each(mComms.begin(), mComms.end(), [](ncclComm_t comm) {
|
||||
ncclCommFinalize(comm);
|
||||
ncclCommDestroy(comm);
|
||||
});
|
||||
#endif
|
||||
|
||||
std::for_each(mDeviceNodes.begin(), mDeviceNodes.end(), [](DeviceNode& deviceNode) {
|
||||
cudaSetDevice(deviceNode.id);
|
||||
cudaStreamDestroy(deviceNode.stream);
|
||||
});
|
||||
}
|
||||
|
||||
inline DeviceMesh& DeviceMesh::operator=(DeviceMesh&& other) noexcept
|
||||
{
|
||||
mDeviceNodes = std::move(other.mDeviceNodes);
|
||||
#ifdef NANOVDB_USE_NCCL
|
||||
mComms = std::move(other.mComms);
|
||||
#endif
|
||||
mConnectivity = std::move(other.mConnectivity);
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline void* queryAllocationGranularityEntryPoint()
|
||||
{
|
||||
void* entryPoint = nullptr;
|
||||
#if CUDART_VERSION >= 12500 // cudaGetDriverEntryPointByVersion was added in CUDA 12.5 with cudaGetDriverEntryPoint being potentially deprecated
|
||||
cudaDriverEntryPointQueryResult queryResult = cudaDriverEntryPointSymbolNotFound;
|
||||
cudaCheck(cudaGetDriverEntryPointByVersion("cuMemGetAllocationGranularity", &entryPoint, 12000, cudaEnableDefault, &queryResult));
|
||||
NANOVDB_ASSERT(queryResult == cudaDriverEntryPointSuccess);
|
||||
#elif CUDART_VERSION >= 12000 // queryResult argument was added in CUDA 12
|
||||
cudaDriverEntryPointQueryResult queryResult = cudaDriverEntryPointSymbolNotFound;
|
||||
cudaCheck(cudaGetDriverEntryPoint("cuMemGetAllocationGranularity", &entryPoint, cudaEnableDefault, &queryResult));
|
||||
NANOVDB_ASSERT(queryResult == cudaDriverEntryPointSuccess);
|
||||
#else
|
||||
cudaCheck(cudaGetDriverEntryPoint("cuMemGetAllocationGranularity", &entryPoint, cudaEnableDefault));
|
||||
#endif
|
||||
return entryPoint;
|
||||
}// queryAllocationGranularityEntryPoint
|
||||
|
||||
}
|
||||
|
||||
/// @brief Returns the minimum page size (in bytes) across all devices on the system
|
||||
inline size_t minDevicePageSize(const DeviceMesh& mesh)
|
||||
{
|
||||
using FuncT = CUresult(size_t*, const CUmemAllocationProp*, CUmemAllocationGranularity_flags);
|
||||
static FuncT* functPtr = reinterpret_cast<FuncT*>(detail::queryAllocationGranularityEntryPoint());
|
||||
|
||||
NANOVDB_ASSERT(functPtr);
|
||||
CUmemAllocationProp prop = {};
|
||||
prop.type = CU_MEM_ALLOCATION_TYPE_PINNED;
|
||||
prop.location.type = CU_MEM_LOCATION_TYPE_DEVICE;
|
||||
size_t minGranularity = 0;
|
||||
for (const auto& node : mesh) {
|
||||
prop.location.id = node.id;
|
||||
size_t granularity = 0;
|
||||
(*functPtr)(&granularity, &prop, CU_MEM_ALLOC_GRANULARITY_MINIMUM);
|
||||
if (minGranularity < granularity) minGranularity = granularity;
|
||||
}
|
||||
return minGranularity;
|
||||
}// minDevicePageSize
|
||||
|
||||
} // namespace cuda
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // end of NANOVDB_CUDA_DEVICEMESH_H_HAS_BEEN_INCLUDED
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
#ifndef NANOVDB_CUDA_DEVICERESOURCE_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_CUDA_DEVICERESOURCE_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <cuda_runtime_api.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace cuda {
|
||||
|
||||
class DeviceResource
|
||||
{
|
||||
public:
|
||||
// cudaMalloc aligns memory to 256 bytes by default
|
||||
static constexpr size_t DEFAULT_ALIGNMENT = 256;
|
||||
|
||||
static void* allocateAsync(size_t bytes, size_t, cudaStream_t stream) {
|
||||
void* p = nullptr;
|
||||
cudaCheck(util::cuda::mallocAsync(&p, bytes, stream));
|
||||
return p;
|
||||
}
|
||||
|
||||
static void deallocateAsync(void *p, size_t, size_t, cudaStream_t stream) {
|
||||
cudaCheck(util::cuda::freeAsync(p, stream));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
} // namespace nanovdb::cuda
|
||||
|
||||
#endif // end of NANOVDB_CUDA_DEVICERESOURCE_H_HAS_BEEN_INCLUDED
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
/// @brief Initiates a map between CUDA device IDs and corresponding streams that satisfy certain constraints.
|
||||
/// All devices should be able to access memory on all the other devices!
|
||||
/// @param t Type of device to include in map. Any means all available devices, PeerToPeer means only devices that
|
||||
/// access all aother devices are included, and Unified means all devices support unified memory, concurrent access,
|
||||
/// access all another devices are included, and Unified means all devices support unified memory, concurrent access,
|
||||
/// and can be access by all other devices.
|
||||
/// @param exclude optional list of device IDs to exclude from the map
|
||||
/// @param verbose 0 means quiet, 1 means print if a device is ignores and 2 means print is a device is included
|
||||
@@ -77,7 +77,11 @@ DeviceStreamMap::DeviceStreamMap(DeviceType t, std::vector<int> exclude, int ver
|
||||
cudaCheck(cudaSetDevice(current));// reset to the previous device
|
||||
|
||||
void* entryPoint = nullptr;
|
||||
#if CUDART_VERSION >= 12000// queryResult argument was added in CUDA 12
|
||||
#if CUDART_VERSION >= 13000
|
||||
cudaDriverEntryPointQueryResult queryResult;
|
||||
cudaCheck(cudaGetDriverEntryPointByVersion("cuMemGetAllocationGranularity", &entryPoint, 13000, cudaEnableDefault, &queryResult));
|
||||
NANOVDB_ASSERT(queryResult == cudaDriverEntryPointSuccess);
|
||||
#elif CUDART_VERSION >= 12000// queryResult argument was added in CUDA 12
|
||||
cudaDriverEntryPointQueryResult queryResult;
|
||||
cudaCheck(cudaGetDriverEntryPoint("cuMemGetAllocationGranularity", &entryPoint, cudaEnableDefault, &queryResult));
|
||||
NANOVDB_ASSERT(queryResult == cudaDriverEntryPointSuccess);
|
||||
|
||||
@@ -25,22 +25,25 @@ namespace nanovdb {
|
||||
|
||||
namespace cuda {
|
||||
|
||||
namespace {// anonymous namespace
|
||||
__global__ void cpyGridHandleMeta(const GridData *d_data, GridHandleMetaData *d_meta)
|
||||
namespace detail {
|
||||
|
||||
static __global__ void cpyGridHandleMeta(const GridData *d_data, GridHandleMetaData *d_meta)
|
||||
{
|
||||
nanovdb::cpyGridHandleMeta(d_data, d_meta);
|
||||
}
|
||||
|
||||
__global__ void updateGridCount(GridData *d_data, uint32_t gridIndex, uint32_t gridCount, bool *d_dirty)
|
||||
static __global__ void updateGridCount(GridData *d_data, uint32_t gridIndex, uint32_t gridCount, bool *d_dirty)
|
||||
{
|
||||
NANOVDB_ASSERT(gridIndex < gridCount);
|
||||
if (*d_dirty = d_data->mGridIndex != gridIndex || d_data->mGridCount != gridCount) {
|
||||
*d_dirty = (d_data->mGridIndex != gridIndex) || (d_data->mGridCount != gridCount);
|
||||
if (*d_dirty) {
|
||||
d_data->mGridIndex = gridIndex;
|
||||
d_data->mGridCount = gridCount;
|
||||
if (d_data->mChecksum.isEmpty()) *d_dirty = false;// no need to update checksum if it didn't already exist
|
||||
}
|
||||
}
|
||||
}// anonymous namespace
|
||||
|
||||
}// namespace detail
|
||||
|
||||
template<typename BufferT, template <class, class...> class VectorT = std::vector>
|
||||
inline typename util::enable_if<BufferTraits<BufferT>::hasDeviceDual, VectorT<GridHandle<BufferT>>>::type
|
||||
@@ -51,22 +54,22 @@ splitGridHandles(const GridHandle<BufferT> &handle, const BufferT* other = nullp
|
||||
VectorT<GridHandle<BufferT>> handles(handle.gridCount());
|
||||
bool dirty, *d_dirty;// use this to check if the checksum needs to be recomputed
|
||||
cudaCheck(util::cuda::mallocAsync((void**)&d_dirty, sizeof(bool), stream));
|
||||
int device = 0;
|
||||
cudaCheck(cudaGetDevice(&device));
|
||||
int device = util::cuda::currentDevice();
|
||||
for (uint32_t n=0; n<handle.gridCount(); ++n) {
|
||||
auto buffer = BufferT::create(handle.gridSize(n), other, device, stream);
|
||||
GridData *dst = reinterpret_cast<GridData*>(buffer.deviceData());
|
||||
const GridData *src = reinterpret_cast<const GridData*>(ptr);
|
||||
cudaCheck(cudaMemcpyAsync(dst, src, handle.gridSize(n), cudaMemcpyDeviceToDevice, stream));
|
||||
updateGridCount<<<1, 1, 0, stream>>>(dst, 0u, 1u, d_dirty);
|
||||
detail::updateGridCount<<<1, 1, 0, stream>>>(dst, 0u, 1u, d_dirty);
|
||||
cudaCheckError();
|
||||
cudaCheck(cudaMemcpyAsync(&dirty, d_dirty, sizeof(bool), cudaMemcpyDeviceToHost, stream));
|
||||
cudaCheck(cudaStreamSynchronize(stream));
|
||||
if (dirty) tools::cuda::updateChecksum(dst, CheckMode::Partial, stream);
|
||||
handles[n] = nanovdb::GridHandle<BufferT>(std::move(buffer));
|
||||
ptr = util::PtrAdd(ptr, handle.gridSize(n));
|
||||
}
|
||||
cudaCheck(util::cuda::freeAsync(d_dirty, stream));
|
||||
return std::move(handles);
|
||||
return handles;
|
||||
}// cuda::splitGridHandles
|
||||
|
||||
template<typename BufferT, template <class, class...> class VectorT>
|
||||
@@ -79,8 +82,7 @@ mergeGridHandles(const VectorT<GridHandle<BufferT>> &handles, const BufferT* oth
|
||||
gridCount += h.gridCount();
|
||||
for (uint32_t n=0; n<h.gridCount(); ++n) size += h.gridSize(n);
|
||||
}
|
||||
int device = 0;
|
||||
cudaCheck(cudaGetDevice(&device));
|
||||
int device = util::cuda::currentDevice();
|
||||
auto buffer = BufferT::create(size, other, device, stream);
|
||||
void *dst = buffer.deviceData();
|
||||
bool dirty, *d_dirty;// use this to check if the checksum needs to be recomputed
|
||||
@@ -90,9 +92,10 @@ mergeGridHandles(const VectorT<GridHandle<BufferT>> &handles, const BufferT* oth
|
||||
for (uint32_t n=0; n<h.gridCount(); ++n) {
|
||||
cudaCheck(cudaMemcpyAsync(dst, src, h.gridSize(n), cudaMemcpyDeviceToDevice, stream));
|
||||
GridData *data = reinterpret_cast<GridData*>(dst);
|
||||
updateGridCount<<<1, 1, 0, stream>>>(data, counter++, gridCount, d_dirty);
|
||||
detail::updateGridCount<<<1, 1, 0, stream>>>(data, counter++, gridCount, d_dirty);
|
||||
cudaCheckError();
|
||||
cudaCheck(cudaMemcpyAsync(&dirty, d_dirty, sizeof(bool), cudaMemcpyDeviceToHost, stream));
|
||||
cudaCheck(cudaStreamSynchronize(stream));
|
||||
if (dirty) tools::cuda::updateChecksum(data, CheckMode::Partial, stream);
|
||||
dst = util::PtrAdd(dst, h.gridSize(n));
|
||||
src = util::PtrAdd(src, h.gridSize(n));
|
||||
@@ -133,7 +136,7 @@ GridHandle<BufferT>::GridHandle(T&& buffer)
|
||||
if (!tmp.isValid()) throw std::runtime_error("GridHandle was constructed with an invalid device buffer");
|
||||
GridHandleMetaData *d_metaData;
|
||||
cudaMalloc((void**)&d_metaData, tmp.mGridCount*sizeof(GridHandleMetaData));
|
||||
cuda::cpyGridHandleMeta<<<1,1>>>(d_data, d_metaData);
|
||||
cuda::detail::cpyGridHandleMeta<<<1,1>>>(d_data, d_metaData);
|
||||
mMetaData.resize(tmp.mGridCount);
|
||||
cudaCheck(cudaMemcpy(mMetaData.data(), d_metaData,tmp.mGridCount*sizeof(GridHandleMetaData), cudaMemcpyDeviceToHost));
|
||||
cudaCheck(cudaFree(d_metaData));
|
||||
|
||||
@@ -44,11 +44,7 @@ createNodeManager(const NanoGrid<BuildT> *d_grid,
|
||||
size_t size = 0u, *d_size;
|
||||
cudaCheck(util::cuda::mallocAsync((void**)&d_size, sizeof(size_t), stream));
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, stream>>>(1, [=] __device__(size_t) {
|
||||
#ifdef NANOVDB_USE_NEW_MAGIC_NUMBERS
|
||||
*d_data = NodeManagerData{NANOVDB_MAGIC_NODE, 0u, (void*)d_grid, {0u,0u,0u}};
|
||||
#else
|
||||
*d_data = NodeManagerData{NANOVDB_MAGIC_NUMB, 0u, (void*)d_grid, {0u,0u,0u}};
|
||||
#endif
|
||||
*d_data = NodeManagerData((void*)d_grid);
|
||||
*d_size = sizeof(NodeManagerData);
|
||||
auto &tree = d_grid->tree();
|
||||
if (NodeManager<BuildT>::FIXED_SIZE && d_grid->isBreadthFirst()) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
/// @brief Defines a simple memory pool used to call cub functions that use dynamic temporary storage
|
||||
///
|
||||
/// @details See nanovdb/tools/cuda/PointToGrid.cuh and nanovdb/tools/cuda/DistributedPointToGrid.cuh
|
||||
/// for examples. Also note that this explains the somewhat unusual API with direct access to
|
||||
/// protected member data.
|
||||
//
|
||||
#ifndef NANOVDB_CUDA_TEMPPOOL_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_CUDA_TEMPPOOL_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/cuda/DeviceResource.h>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace cuda {
|
||||
|
||||
template <class Resource>
|
||||
class TempPool {
|
||||
public:
|
||||
|
||||
/// @brief Default c-tor of an empty memory pool.
|
||||
TempPool() : mData(nullptr), mSize(0), mRequestedSize(0) {}
|
||||
|
||||
/// @brief Destructor
|
||||
~TempPool() {
|
||||
mRequestedSize = 0;
|
||||
Resource::deallocateAsync(mData, mSize, Resource::DEFAULT_ALIGNMENT, nullptr);
|
||||
mData = nullptr;
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
/// @brief Returns a non-const void pointer to the data managed by this instance.
|
||||
void* data() {return mData;}
|
||||
|
||||
/// @brief Returns a non-const reference to the actual size of the data managed by this instance.
|
||||
size_t& size() {return mSize;}
|
||||
|
||||
/// @brief Returns a non-const reference to the requested size of the data managed by this instance.
|
||||
/// @note This requested size should always be less than or smaller than the actual size().
|
||||
size_t& requestedSize() {return mRequestedSize;}
|
||||
|
||||
/// @brief Re-allocation of the data managed by this instance. Only has affect if the pool in empty or
|
||||
/// the requested memory is larger than the existing size.
|
||||
/// @param stream cuda stream used for asynchronous de-allocation and allocation.
|
||||
void reallocate(cudaStream_t stream) {
|
||||
if (!mData || mRequestedSize > mSize) {
|
||||
Resource::deallocateAsync(mData, mSize, Resource::DEFAULT_ALIGNMENT, stream);
|
||||
mData = Resource::allocateAsync(mRequestedSize, Resource::DEFAULT_ALIGNMENT, stream);
|
||||
mSize = mRequestedSize;
|
||||
}
|
||||
}
|
||||
private:
|
||||
void *mData;
|
||||
size_t mSize;
|
||||
size_t mRequestedSize;
|
||||
};// TempPool<Resource> class
|
||||
|
||||
using TempDevicePool = TempPool<DeviceResource>;
|
||||
|
||||
} // namespace cuda
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // end of NANOVDB_CUDA_TEMPPOOL_H_HAS_BEEN_INCLUDED
|
||||
@@ -40,17 +40,17 @@ public:
|
||||
UnifiedBuffer() : mPtr(nullptr), mSize(0), mCapacity(0){}
|
||||
|
||||
/// @brief Constructor that specifies both the size and capacity
|
||||
/// @param size size of the bufffer in bytes, indication what is actually used
|
||||
/// @param capacity number of bytes in the vitual page table, i.e max size for growing
|
||||
/// @note Capacity can be over-estimated to allow for future groth. Memory is not allocated
|
||||
/// with this constructor, only a page table. Allocation happend on use or when calling prefetch
|
||||
/// @param size size of the buffer in bytes, indication what is actually used
|
||||
/// @param capacity number of bytes in the virtual page table, i.e max size for growing
|
||||
/// @note Capacity can be over-estimated to allow for future growth. Memory is not allocated
|
||||
/// with this constructor, only a page table. Allocation happens on usage or when calling prefetch
|
||||
UnifiedBuffer(size_t size, size_t capacity) : mPtr(nullptr), mSize(size), mCapacity(capacity)
|
||||
{
|
||||
assert(mSize <= mCapacity);
|
||||
cudaCheck(cudaMallocManaged(&mPtr, mCapacity, cudaMemAttachGlobal));
|
||||
}
|
||||
|
||||
/// @brief Simular to the constructor above except the size and capacity are equal, so no future growth is supported
|
||||
/// @brief Similar to the constructor above except the size and capacity are equal, so no future growth is supported
|
||||
UnifiedBuffer(size_t size) : UnifiedBuffer(size, size){}
|
||||
|
||||
/// @brief Constructor that specifies the size, capacity, and device (for prefetching)
|
||||
@@ -60,9 +60,10 @@ public:
|
||||
/// @param stream
|
||||
UnifiedBuffer(uint64_t size, uint64_t capacity, int device, cudaStream_t stream = 0) : mPtr(nullptr), mSize(size), mCapacity(size)
|
||||
{
|
||||
assert(mSize <= mCapacity);
|
||||
cudaCheck(cudaMallocManaged(&mPtr, mCapacity, cudaMemAttachGlobal));
|
||||
cudaCheck(cudaMemAdvise(mPtr, size, cudaMemAdviseSetPreferredLocation, device));
|
||||
cudaCheck(cudaMemPrefetchAsync(mPtr, size, device, stream));
|
||||
cudaCheck(util::cuda::memAdvise(mPtr, size, cudaMemAdviseSetPreferredLocation, device));
|
||||
cudaCheck(util::cuda::memPrefetchAsync(mPtr, size, device, stream));
|
||||
}
|
||||
|
||||
/// @brief Constructor with a specified device
|
||||
@@ -76,11 +77,10 @@ public:
|
||||
|
||||
/// @brief Move copy-constructor
|
||||
UnifiedBuffer(UnifiedBuffer&& other) noexcept
|
||||
: mSize(other.mSize)
|
||||
: mPtr(other.mPtr)
|
||||
, mSize(other.mSize)
|
||||
, mCapacity(other.mCapacity)
|
||||
{
|
||||
cudaCheck(cudaFree(mPtr));
|
||||
mPtr = other.mPtr;
|
||||
other.mPtr = nullptr;
|
||||
other.mSize = other.mCapacity = 0;
|
||||
}
|
||||
@@ -105,7 +105,7 @@ public:
|
||||
/// @brief Legacy factory method that mirrors DeviceBuffer. It creates a UnifiedBuffer from a size and a reference buffer.
|
||||
/// If a reference buffer is provided and its non-empty, it is used to defined the capacity of the new buffer
|
||||
/// @param size Size on bytes of the new buffer
|
||||
/// @param reference reference buffer optionally used to define the capcity
|
||||
/// @param reference reference buffer optionally used to define the capacity
|
||||
/// @param host Ignored for now
|
||||
/// @param stream cuda stream
|
||||
/// @return An instance of a new UnifiedBuffer using move semantics
|
||||
@@ -113,22 +113,22 @@ public:
|
||||
{
|
||||
const size_t capacity = (reference && reference->capacity()) ? reference->capacity() : size;
|
||||
UnifiedBuffer buffer(size, capacity);
|
||||
cudaCheck(cudaMemAdvise(buffer.mPtr, size, cudaMemAdviseSetPreferredLocation, device));
|
||||
cudaCheck(cudaMemPrefetchAsync(buffer.mPtr, size, device, stream));
|
||||
cudaCheck(util::cuda::memAdvise(buffer.mPtr, size, cudaMemAdviseSetPreferredLocation, device));
|
||||
cudaCheck(util::cuda::memPrefetchAsync(buffer.mPtr, size, device, stream));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// @brief Factory method that created a buffer on the host of the specified size. If the
|
||||
/// reference buffer has a capacity it is used. Also the buffer is prefethed to the host
|
||||
/// reference buffer has a capacity it is used. Also the buffer is prefetched to the host
|
||||
/// @param size byte size of buffer initiated on the host
|
||||
/// @param reference optional reference buffer from which the capacity is derived
|
||||
static UnifiedBuffer create(size_t size, const UnifiedBuffer* reference){return create(size, reference, cudaCpuDeviceId, (cudaStream_t)0);}
|
||||
|
||||
/// @brief Factory method that created a buffer on the host or device of the specified size. If the
|
||||
/// reference buffer has a capacity it is used. Also the buffer is prefethed to the host or (current) device
|
||||
/// reference buffer has a capacity it is used. Also the buffer is prefetched to the host or (current) device
|
||||
/// @param size byte size of buffer initiated on the device or host
|
||||
/// @param reference optional reference buffer from which the capacity is derived
|
||||
/// @param host If true the buffer will be prefectched to the host, else to the current device
|
||||
/// @param host If true the buffer will be prefetched to the host, else to the current device
|
||||
/// @param stream optional cuda stream
|
||||
static UnifiedBuffer create(size_t size, const UnifiedBuffer* reference, bool host, void* stream = nullptr)
|
||||
{
|
||||
@@ -184,7 +184,7 @@ public:
|
||||
} else {
|
||||
void *ptr = 0;
|
||||
cudaCheck(cudaMallocManaged(&ptr, size, cudaMemAttachGlobal));
|
||||
if (dev > -2) for (auto a : list) cudaCheck(cudaMemAdvise(ptr, size, a, dev));
|
||||
if (dev > -2) for (auto a : list) cudaCheck(util::cuda::memAdvise(ptr, size, a, dev));
|
||||
if (mSize > 0) {// copy over data from the old memory block
|
||||
cudaCheck(cudaMemcpy(ptr, mPtr, std::min(mSize, size), cudaMemcpyDefault));
|
||||
cudaCheck(cudaFree(mPtr));
|
||||
@@ -195,51 +195,51 @@ public:
|
||||
}
|
||||
|
||||
/// @brief Apply a single advise to a memory block
|
||||
/// @param byteOffset offset in bytes marking the begenning of the memory block to be advised
|
||||
/// @param byteOffset offset in bytes marking the beginning of the memory block to be advised
|
||||
/// @param size size in bytes of the memory block to be advised.
|
||||
/// @param dev the device ID on which to apply the advice provided in adv, cudaCpuDeviceId = -1, 0, 1, ...
|
||||
/// @param adv advice to be applied to the resized range
|
||||
void advise(ptrdiff_t byteOffset, size_t size, int dev, cudaMemoryAdvise adv)
|
||||
void advise(ptrdiff_t byteOffset, size_t size, int dev, cudaMemoryAdvise adv) const
|
||||
{
|
||||
cudaCheck(cudaMemAdvise(util::PtrAdd(mPtr, byteOffset), size, adv, dev));
|
||||
cudaCheck(util::cuda::memAdvise(util::PtrAdd(mPtr, byteOffset), size, adv, dev));
|
||||
}
|
||||
|
||||
/// @brief Apply a list of advices to a memory block
|
||||
/// @param byteOffset offset in bytes marking the begenning of the memory block to be advised
|
||||
/// @param byteOffset offset in bytes marking the beginning of the memory block to be advised
|
||||
/// @param size size in bytes of the memory block to be advised.
|
||||
/// @param dev the device ID to prefetch to, cudaCpuDeviceId = -1, 0, 1, ...
|
||||
/// @param list list of cuda advises
|
||||
void advise(ptrdiff_t byteOffset, size_t size, int dev, std::initializer_list<cudaMemoryAdvise> list)
|
||||
void advise(ptrdiff_t byteOffset, size_t size, int dev, std::initializer_list<cudaMemoryAdvise> list) const
|
||||
{
|
||||
void *ptr = util::PtrAdd(mPtr, byteOffset);
|
||||
for (auto a : list) cudaCheck(cudaMemAdvise(ptr, size, a, dev));
|
||||
for (auto a : list) cudaCheck(util::cuda::memAdvise(ptr, size, a, dev));
|
||||
}
|
||||
|
||||
/// @brief Prefetches data to the specified device, i.e. ensure the device has an up-to-date copy of the memory specified
|
||||
/// @param byteOffset offset in bytes marking the begenning of the memory block to be prefetched
|
||||
/// @param byteOffset offset in bytes marking the beginning of the memory block to be prefetched
|
||||
/// @param size size in bytes of the memory block to be prefetched. The default value of zero means copy all @c this->size() bytes.
|
||||
/// @param dev the device ID to prefetch to, cudaCpuDeviceId = -1, 0, 1, ...
|
||||
/// @param stream cuda stream
|
||||
void prefetch(ptrdiff_t byteOffset = 0, size_t size = 0, int dev = cudaCpuDeviceId, cudaStream_t stream = cudaStreamPerThread)
|
||||
void prefetch(ptrdiff_t byteOffset = 0, size_t size = 0, int dev = cudaCpuDeviceId, cudaStream_t stream = cudaStreamPerThread) const
|
||||
{
|
||||
cudaCheck(cudaMemPrefetchAsync(util::PtrAdd(mPtr, byteOffset), size ? size : mSize, dev, stream));
|
||||
cudaCheck(util::cuda::memPrefetchAsync(util::PtrAdd(mPtr, byteOffset), size ? size : mSize, dev, stream));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// @brief Prefectches all data to the specified device
|
||||
/// @brief Prefetches all data to the specified device
|
||||
/// @param device device ID, cudaCpuDeviceId = -1, 0, 1, ...
|
||||
/// @param stream cuda stream
|
||||
/// @param sync if false the memory copy is asynchronous
|
||||
/// @note Legacy method included for compatibility with DeviceBuffer
|
||||
void deviceUpload(int device = 0, cudaStream_t stream = cudaStreamPerThread, bool sync = false) const
|
||||
{
|
||||
cudaCheck(cudaMemPrefetchAsync(mPtr, mSize, device, stream));
|
||||
cudaCheck(util::cuda::memPrefetchAsync(mPtr, mSize, device, stream));
|
||||
if (sync) cudaCheck(cudaStreamSynchronize(stream));
|
||||
}
|
||||
void deviceUpload(int device, void* stream, bool sync) const{this->deviceUpload(device, cudaStream_t(stream));}
|
||||
|
||||
/// @brief Prefectches all data to the current device, as given by cudaGetDevice
|
||||
/// @brief Prefetches all data to the current device, as given by cudaGetDevice
|
||||
/// @param stream cuda stream
|
||||
/// @param sync if false the memory copy is asynchronous
|
||||
/// @note Legacy method included for compatibility with DeviceBuffer
|
||||
@@ -254,10 +254,9 @@ public:
|
||||
/// @brief Prefetches all data to the host
|
||||
/// @param stream cuda stream
|
||||
/// @param sync if false the memory copy is asynchronous
|
||||
|
||||
void deviceDownload(cudaStream_t stream = 0, bool sync = false) const
|
||||
{
|
||||
cudaCheck(cudaMemPrefetchAsync(mPtr, mSize, cudaCpuDeviceId, stream));
|
||||
cudaCheck(util::cuda::memPrefetchAsync(mPtr, mSize, cudaCpuDeviceId, stream));
|
||||
if (sync) cudaCheck(cudaStreamSynchronize(stream));
|
||||
}
|
||||
|
||||
@@ -271,11 +270,11 @@ public:
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// @brief Retuns a raw pointer to the unified memory managed by this instance.
|
||||
/// @brief Returns a raw pointer to the unified memory managed by this instance.
|
||||
/// @warning Note that the pointer can be NULL!
|
||||
void* data() const {return mPtr;}
|
||||
|
||||
/// @brief Returns an offset pointer of a specefic type from the allocated unified memory
|
||||
/// @brief Returns an offset pointer of a specific type from the allocated unified memory
|
||||
/// @tparam T Type of the pointer returned
|
||||
/// @param count Numbers of elements of @c parameter type T to skip (or offset) the return pointer
|
||||
/// @warning assumes that this instance is not empty!
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(NanoVDBExamples LANGUAGES CXX)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
@@ -100,14 +100,19 @@ nanovdb_example(NAME "ex_read_nanovdb_sphere")
|
||||
nanovdb_example(NAME "ex_read_nanovdb_sphere_accessor")
|
||||
nanovdb_example(NAME "ex_read_nanovdb_sphere_accessor_cuda")
|
||||
nanovdb_example(NAME "ex_index_grid_cuda")
|
||||
nanovdb_example(NAME "ex_nodemanager_cuda")
|
||||
nanovdb_example(NAME "ex_nodemanager_cuda" OPENVDB)
|
||||
nanovdb_example(NAME "ex_voxels_to_grid_cuda")
|
||||
nanovdb_example(NAME "ex_voxels_to_grid_mgpu")
|
||||
nanovdb_example(NAME "ex_modify_nanovdb_thrust")
|
||||
nanovdb_example(NAME "ex_map_pool_buffer")
|
||||
nanovdb_example(NAME "ex_bump_pool_buffer")
|
||||
nanovdb_example(NAME "ex_collide_level_set")
|
||||
nanovdb_example(NAME "ex_raytrace_fog_volume")
|
||||
nanovdb_example(NAME "ex_raytrace_level_set")
|
||||
nanovdb_example(NAME "ex_dilate_nanovdb_cuda" OPENVDB)
|
||||
nanovdb_example(NAME "ex_merge_nanovdb_cuda" OPENVDB)
|
||||
nanovdb_example(NAME "ex_refine_nanovdb_cuda" OPENVDB)
|
||||
nanovdb_example(NAME "ex_coarsen_nanovdb_cuda" OPENVDB)
|
||||
|
||||
if(CUDAToolkit_FOUND)
|
||||
nanovdb_example(NAME "ex_make_mgpu_nanovdb") # requires cuRAND
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// the following files are from OpenVDB
|
||||
#include <openvdb/tools/Composite.h>
|
||||
#include <openvdb/util/CpuTimer.h>
|
||||
|
||||
// the following files are from NanoVDB
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.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> *deviceGridOriginal,
|
||||
nanovdb::NanoGrid<BuildT> *deviceGridCoarsened,
|
||||
nanovdb::NanoGrid<BuildT> *indexGridOriginal,
|
||||
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::cuda::DeviceBuffer>(
|
||||
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)
|
||||
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)
|
||||
cpuTimer.start("Converting openVDB input to indexGrid (coarsened version)");
|
||||
auto handleCoarsened = nanovdb::tools::openToIndexVDB<BuildT, nanovdb::cuda::DeviceBuffer>(
|
||||
coarsenedGrid,
|
||||
0u, // Don't copy data channel
|
||||
false, // No stats
|
||||
false, // No tiles
|
||||
1 // Verbose mode
|
||||
);
|
||||
cpuTimer.stop();
|
||||
|
||||
auto *indexGridCoarsened = handleCoarsened.grid<BuildT>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Copy both NanoVDB grids to GPU
|
||||
handleOriginal.deviceUpload();
|
||||
handleCoarsened.deviceUpload();
|
||||
auto* deviceGridOriginal = handleOriginal.deviceGrid<BuildT>();
|
||||
auto* deviceGridCoarsened = handleCoarsened.deviceGrid<BuildT>();
|
||||
if (!deviceGridOriginal || !deviceGridCoarsened)
|
||||
OPENVDB_THROW(openvdb::RuntimeError, "Failure while uploading indexGrids to GPU");
|
||||
|
||||
// Launch benchmark
|
||||
mainCoarsenGrid( deviceGridOriginal, deviceGridCoarsened, indexGridOriginal, indexGridCoarsened, benchmark_iters );
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "An exception occurred: \"" << e.what() << "\"" << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <nanovdb/tools/cuda/CoarsenGrid.cuh>
|
||||
|
||||
template<typename T>
|
||||
bool bufferCheck(const T* deviceBuffer, const T* hostBuffer, size_t elem_count) {
|
||||
T* tmpBuffer = new T[elem_count];
|
||||
cudaCheck(cudaMemcpy(tmpBuffer, deviceBuffer, elem_count * sizeof(T), cudaMemcpyDeviceToHost));
|
||||
bool same = true;
|
||||
for (int i=0; same && i< elem_count; ++i) { same = (tmpBuffer[i] == hostBuffer[i]); }
|
||||
delete [] tmpBuffer;
|
||||
return same;
|
||||
}
|
||||
|
||||
template<typename BuildT>
|
||||
void mainCoarsenGrid(
|
||||
nanovdb::NanoGrid<BuildT> *deviceGridOriginal,
|
||||
nanovdb::NanoGrid<BuildT> *deviceGridCoarsened,
|
||||
nanovdb::NanoGrid<BuildT> *indexGridOriginal,
|
||||
nanovdb::NanoGrid<BuildT> *indexGridCoarsened,
|
||||
uint32_t benchmark_iters)
|
||||
{
|
||||
nanovdb::util::cuda::Timer gpuTimer;
|
||||
|
||||
// Initialize coarsener
|
||||
nanovdb::tools::cuda::CoarsenGrid<BuildT> coarsener( deviceGridOriginal );
|
||||
coarsener.setChecksum(nanovdb::CheckMode::Default);
|
||||
coarsener.setVerbose(1);
|
||||
|
||||
auto handle = coarsener.getHandle();
|
||||
auto dstGrid = handle.template deviceGrid<BuildT>();
|
||||
|
||||
// Check for correctness
|
||||
if (bufferCheck((char*)dstGrid, (char*)indexGridCoarsened->data(), indexGridCoarsened->gridSize()))
|
||||
std::cout << "Result of CoarsenGrid check out CORRECT against reference" << std::endl;
|
||||
else
|
||||
std::cout << "Result of CoarsenGrid compares INCORRECT against reference" << std::endl;
|
||||
|
||||
// Re-run warm-started iterations
|
||||
coarsener.setVerbose(0);
|
||||
for (int i = 0; i < benchmark_iters; i++) {
|
||||
gpuTimer.start("Re-running entire coarsening after warmstart");
|
||||
auto dummyHandle = coarsener.getHandle();
|
||||
gpuTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
template
|
||||
void mainCoarsenGrid(
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *deviceGridOriginal,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *deviceGridCoarsened,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *indexGridOriginal,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *indexGridCoarsened,
|
||||
uint32_t benchmark_iters
|
||||
);
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// the following files are from OpenVDB
|
||||
#include <openvdb/tools/Morphology.h>
|
||||
#include <openvdb/util/CpuTimer.h>
|
||||
|
||||
// the following files are from NanoVDB
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
|
||||
template<typename BuildT>
|
||||
void mainDilateGrid(
|
||||
nanovdb::NanoGrid<BuildT> *deviceGridOriginal,
|
||||
nanovdb::NanoGrid<BuildT> *deviceGridDilated,
|
||||
nanovdb::NanoGrid<BuildT> *indexGridOriginal,
|
||||
nanovdb::NanoGrid<BuildT> *indexGridDilated,
|
||||
uint32_t nnType,
|
||||
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;
|
||||
|
||||
// Select the type of dilation here. The NN_EDGE case supports leaf dilation too (currently)
|
||||
// openvdb::tools::NearestNeighbors nnType = openvdb::tools::NN_FACE_EDGE_VERTEX;
|
||||
openvdb::tools::NearestNeighbors nnType = openvdb::tools::NN_FACE;
|
||||
|
||||
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));
|
||||
openvdb::FloatGrid* ptr = grid.get(); // raw pointer
|
||||
if (!grid) OPENVDB_THROW(openvdb::ValueError, "First grid is not a FloatGrid\n");
|
||||
cpuTimer.stop();
|
||||
|
||||
// Convert to indexGrid (original, un-dilated)
|
||||
cpuTimer.start("Converting openVDB input to indexGrid (original version)");
|
||||
auto handleOriginal = nanovdb::tools::openToIndexVDB<BuildT, nanovdb::cuda::DeviceBuffer>(
|
||||
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;
|
||||
}
|
||||
|
||||
// Dilation (CPU/OpenVDB version)
|
||||
cpuTimer.start("Dilating openVDB (on CPU)");
|
||||
openvdb::tools::dilateActiveValues(grid->tree(), 1, nnType);
|
||||
cpuTimer.stop();
|
||||
|
||||
// Convert to indexGrid (dilated)
|
||||
cpuTimer.start("Converting openVDB input to indexGrid (dilated version)");
|
||||
auto handleDilated = nanovdb::tools::openToIndexVDB<BuildT, nanovdb::cuda::DeviceBuffer>(
|
||||
grid,
|
||||
0u, // Don't copy data channel
|
||||
false, // No stats
|
||||
false, // No tiles
|
||||
1 // Verbose mode
|
||||
);
|
||||
cpuTimer.stop();
|
||||
|
||||
auto *indexGridDilated = handleDilated.grid<BuildT>();
|
||||
|
||||
if (printGridDiagnostics) {
|
||||
std::cout << "============ Dilated Grid ============" << std::endl;
|
||||
std::cout << "Allocated values [valueCount()] : " << indexGridDilated->valueCount() << std::endl;
|
||||
std::cout << "Active voxels [activeVoxelCount()] : " << indexGridDilated->activeVoxelCount() << std::endl;
|
||||
auto minCorner = indexGridDilated->indexBBox().min(), maxCorner = indexGridDilated->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 : " << indexGridDilated->tree().nodeCount(0) << std::endl;
|
||||
std::cout << "Lower internal nodes : " << indexGridDilated->tree().nodeCount(1) << std::endl;
|
||||
std::cout << "Upper internal nodes : " << indexGridDilated->tree().nodeCount(2) << std::endl;
|
||||
std::cout << "Leaf-level occupancy : "
|
||||
<< 100.f * (float)(indexGridDilated->activeVoxelCount())/(float)(indexGridDilated->tree().nodeCount(0) * 512)
|
||||
<< "%" << std::endl;
|
||||
std::cout << "Memory usage : " << indexGridDilated->gridSize() << " bytes" << std::endl;
|
||||
}
|
||||
|
||||
// Copy both NanoVDB grids to GPU
|
||||
handleOriginal.deviceUpload();
|
||||
handleDilated.deviceUpload();
|
||||
auto* deviceGridOriginal = handleOriginal.deviceGrid<BuildT>();
|
||||
auto* deviceGridDilated = handleDilated.deviceGrid<BuildT>();
|
||||
if (!deviceGridOriginal || !deviceGridDilated)
|
||||
OPENVDB_THROW(openvdb::RuntimeError, "Failure while uploading indexGrids to GPU");
|
||||
|
||||
// Launch benchmark
|
||||
mainDilateGrid( deviceGridOriginal, deviceGridDilated, indexGridOriginal, indexGridDilated, nnType, benchmark_iters );
|
||||
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "An exception occurred: \"" << e.what() << "\"" << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <nanovdb/tools/cuda/DilateGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/PruneGrid.cuh>
|
||||
#include <nanovdb/util/cuda/Injection.cuh>
|
||||
|
||||
template<typename T>
|
||||
bool bufferCheck(const T* deviceBuffer, const T* hostBuffer, size_t elem_count) {
|
||||
T* tmpBuffer = new T[elem_count];
|
||||
cudaCheck(cudaMemcpy(tmpBuffer, deviceBuffer, elem_count * sizeof(T), cudaMemcpyDeviceToHost));
|
||||
bool same = true;
|
||||
for (int i=0; same && i< elem_count; ++i) { same = (tmpBuffer[i] == hostBuffer[i]); }
|
||||
delete [] tmpBuffer;
|
||||
return same;
|
||||
}
|
||||
|
||||
template<typename BuildT>
|
||||
void mainDilateGrid(
|
||||
nanovdb::NanoGrid<BuildT> *deviceGridOriginal,
|
||||
nanovdb::NanoGrid<BuildT> *deviceGridDilated,
|
||||
nanovdb::NanoGrid<BuildT> *indexGridOriginal,
|
||||
nanovdb::NanoGrid<BuildT> *indexGridDilated,
|
||||
uint32_t nnType,
|
||||
uint32_t benchmark_iters)
|
||||
{
|
||||
nanovdb::util::cuda::Timer gpuTimer;
|
||||
|
||||
// Initialize dilator
|
||||
nanovdb::tools::cuda::DilateGrid<BuildT> dilator( deviceGridOriginal );
|
||||
dilator.setOperation(nanovdb::tools::morphology::NearestNeighbors(nnType));
|
||||
dilator.setChecksum(nanovdb::CheckMode::Default);
|
||||
dilator.setVerbose(1);
|
||||
|
||||
auto handle = dilator.getHandle();
|
||||
auto dstGrid = handle.template deviceGrid<BuildT>();
|
||||
|
||||
// Check for correctness
|
||||
if (bufferCheck((char*)dstGrid, (char*)indexGridDilated->data(), indexGridDilated->gridSize()))
|
||||
std::cout << "Result of DilateGrid check out CORRECT against reference" << std::endl;
|
||||
else
|
||||
std::cout << "Result of DilateGrid compares INCORRECT against reference" << std::endl;
|
||||
|
||||
// Re-run warm-started iterations
|
||||
dilator.setVerbose(0);
|
||||
for (int i = 0; i < benchmark_iters; i++) {
|
||||
gpuTimer.start("Re-running entire dilation after warmstart");
|
||||
auto dummyHandle = dilator.getHandle();
|
||||
gpuTimer.stop();
|
||||
}
|
||||
|
||||
uint32_t dstLeafCount = nanovdb::util::cuda::DeviceGridTraits<BuildT>::getTreeData(dstGrid).mNodeCount[0];
|
||||
nanovdb::cuda::DeviceBuffer dstLeafMaskBuffer;
|
||||
nanovdb::Mask<3>* dstLeafMasks = nullptr;
|
||||
if (dstLeafCount) {
|
||||
dstLeafMaskBuffer = nanovdb::cuda::DeviceBuffer::create( std::size_t(dstLeafCount) * sizeof(nanovdb::Mask<3>), nullptr, false );
|
||||
dstLeafMasks = static_cast<nanovdb::Mask<3>*>(dstLeafMaskBuffer.deviceData());
|
||||
if (!dstLeafMasks) throw std::runtime_error("No GPU buffer for dstLeafMask");
|
||||
}
|
||||
|
||||
const unsigned int numThreads = 128;
|
||||
auto numBlocks = [numThreads] (unsigned int n) {return (n + numThreads - 1) / numThreads;};
|
||||
gpuTimer.start("Injecting un-dilated topology as a pruning mask");
|
||||
if (dstLeafCount)
|
||||
nanovdb::util::cuda::lambdaKernel<<<numBlocks(dstLeafCount), numThreads>>>(dstLeafCount,
|
||||
nanovdb::util::cuda::InjectGridMaskFunctor<BuildT>(),
|
||||
deviceGridOriginal, dstGrid, dstLeafMasks );
|
||||
gpuTimer.stop();
|
||||
|
||||
// Initialize pruner
|
||||
nanovdb::tools::cuda::PruneGrid<BuildT> pruner( dstGrid, dstLeafMasks );
|
||||
pruner.setChecksum(nanovdb::CheckMode::Default);
|
||||
pruner.setVerbose(1);
|
||||
|
||||
auto prunedHandle = pruner.getHandle();
|
||||
auto prunedGrid = prunedHandle.template deviceGrid<BuildT>();
|
||||
|
||||
// Check for correctness
|
||||
if (bufferCheck((char*)prunedGrid, (char*)indexGridOriginal->data(), indexGridOriginal->gridSize()))
|
||||
std::cout << "Result of PruneGrid check out CORRECT against reference" << std::endl;
|
||||
else
|
||||
std::cout << "Result of PruneGrid compares INCORRECT against reference" << std::endl;
|
||||
|
||||
// Re-run warm-started iterations
|
||||
pruner.setVerbose(0);
|
||||
for (int i = 0; i < benchmark_iters; i++) {
|
||||
gpuTimer.start("Re-running entire pruning after warmstart");
|
||||
auto dummyHandle = pruner.getHandle();
|
||||
gpuTimer.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template
|
||||
void mainDilateGrid(
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *deviceGridOriginal,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *deviceGridDilated,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *indexGridOriginal,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *indexGridDilated,
|
||||
uint32_t nnType,
|
||||
uint32_t benchmark_iters
|
||||
);
|
||||
@@ -3,16 +3,17 @@
|
||||
|
||||
#include <nanovdb/NanoVDB.h> // this defined the core tree data structure of NanoVDB accessable on both the host and device
|
||||
#include <nanovdb/cuda/GridHandle.cuh>// required since GridHandle<DeviceBuffer> has device code
|
||||
#include <stdio.h> // for printf
|
||||
#include <cstdio> // for printf
|
||||
#include <cinttypes>
|
||||
|
||||
// This is called by the host only
|
||||
void cpu_kernel(const nanovdb::NanoGrid<nanovdb::ValueOnIndex>* cpuGrid)
|
||||
{
|
||||
nanovdb::ChannelAccessor<float, nanovdb::ValueOnIndex> acc(*cpuGrid);
|
||||
//printf("\nNanoVDB CPU: channels=%u values=%lu\n", acc.grid().blindDataCount(), acc.root().maximum());
|
||||
printf("NanoVDB CPU; %lu\n", acc.idx( 0, 0, 0));
|
||||
printf("NanoVDB CPU; %lu\n", acc.idx( 99, 0, 0));
|
||||
printf("NanoVDB CPU; %lu\n", acc.idx(100, 0, 0));
|
||||
printf("NanoVDB CPU; %" PRIu64 "\n", acc.idx( 0, 0, 0));
|
||||
printf("NanoVDB CPU; %" PRIu64 "\n", acc.idx( 99, 0, 0));
|
||||
printf("NanoVDB CPU; %" PRIu64 "\n", acc.idx(100, 0, 0));
|
||||
printf("NanoVDB CPU; %4.2f\n", acc( 0, 0, 0));
|
||||
printf("NanoVDB CPU; %4.2f\n", acc( 99, 0, 0));
|
||||
printf("NanoVDB CPU; %4.2f\n", acc(100, 0, 0));
|
||||
@@ -23,9 +24,9 @@ __global__ void gpu_kernel(const nanovdb::NanoGrid<nanovdb::ValueOnIndex>* gpuGr
|
||||
{
|
||||
nanovdb::ChannelAccessor<float, nanovdb::ValueOnIndex> acc(*gpuGrid);
|
||||
//printf("\nNanoVDB GPU: channels=%u values=%lu\n", gpuGrid->blindDataCount(), acc.root().maximum());
|
||||
printf("NanoVDB GPU; %lu\n", acc.idx( 0, 0, 0));
|
||||
printf("NanoVDB GPU; %lu\n", acc.idx( 99, 0, 0));
|
||||
printf("NanoVDB GPU; %lu\n", acc.idx(100, 0, 0));
|
||||
printf("NanoVDB GPU; %" PRIu64 "\n", acc.idx( 0, 0, 0));
|
||||
printf("NanoVDB GPU; %" PRIu64 "\n", acc.idx( 99, 0, 0));
|
||||
printf("NanoVDB GPU; %" PRIu64 "\n", acc.idx(100, 0, 0));
|
||||
printf("NanoVDB GPU; %4.2f\n", acc( 0, 0, 0));
|
||||
printf("NanoVDB GPU; %4.2f\n", acc( 99, 0, 0));
|
||||
printf("NanoVDB GPU; %4.2f\n", acc(100, 0, 0));
|
||||
@@ -39,4 +40,4 @@ extern "C" void launch_kernels(const nanovdb::NanoGrid<nanovdb::ValueOnIndex>* g
|
||||
gpu_kernel<<<1, 1, 0, stream>>>(gpuGrid); // Launch the device kernel asynchronously
|
||||
|
||||
cpu_kernel(cpuGrid); // Launch the host "kernel" (synchronously)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/cuda/UnifiedBuffer.h>
|
||||
#include <nanovdb/cuda/DeviceStreamMap.h>
|
||||
#include <nanovdb/cuda/DeviceMesh.h>
|
||||
#include <nanovdb/util/cuda/Timer.h>
|
||||
|
||||
#include <cassert>
|
||||
@@ -177,12 +177,12 @@ void testConvolution()
|
||||
|
||||
for (int i = 0; i < Do * 27 * Di; i++) stencilHostPtr[i] = uniform_dist(gen);
|
||||
|
||||
nanovdb::cuda::DeviceStreamMap devStreamMap( nanovdb::cuda::DeviceStreamMap::DeviceType::Unified, {}, 2);
|
||||
const size_t deviceCount = devStreamMap.size();
|
||||
nanovdb::cuda::DeviceMesh deviceMesh;
|
||||
const size_t deviceCount = deviceMesh.deviceCount();
|
||||
std::cout << "Number of devices that supports unified memory: " << deviceCount << std::endl;
|
||||
|
||||
// Calculate minimum page size which corresponds to minimum physical allocation granularity
|
||||
size_t minGranularity = devStreamMap.getMinPageSize();
|
||||
size_t minGranularity = nanovdb::cuda::minDevicePageSize(deviceMesh);
|
||||
|
||||
// Ensure that we don't split an input and/or output feature across page boundaries
|
||||
minGranularity = std::lcm(std::lcm(minGranularity, sizeof(InputBufferType)), sizeof(OutputBufferType));
|
||||
@@ -194,10 +194,10 @@ void testConvolution()
|
||||
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
auto indexHandle = nanovdb::tools::createNanoGrid<nanovdb::FloatGrid, nanovdb::ValueOnIndex, BufferT>(*floatGrid, 0u, false, false, 1);
|
||||
for (auto& [device, stream] : devStreamMap) {// copy host buffer to all the device buffers
|
||||
cudaSetDevice(device);
|
||||
indexHandle.deviceUpload(device, stream, true);
|
||||
}
|
||||
std::for_each(deviceMesh.begin(), deviceMesh.end(), [&](const nanovdb::cuda::DeviceNode& node) {// copy host buffer to all the device buffers
|
||||
cudaCheck(cudaSetDevice(node.id));
|
||||
indexHandle.deviceUpload(node.id, node.stream, true);
|
||||
});
|
||||
auto* indexGrid = indexHandle.grid<nanovdb::ValueOnIndex>();
|
||||
|
||||
auto ceil = [](size_t x, size_t y)->size_t{return ((x + y - 1) / y) * y;};
|
||||
@@ -213,25 +213,39 @@ void testConvolution()
|
||||
|
||||
// Randomly initialize input features
|
||||
std::vector<std::thread> threads;
|
||||
auto streamIter = devStreamMap.begin();
|
||||
for (int i = 0; i < deviceCount; ++i, ++streamIter) {
|
||||
threads.push_back(std::thread([&, i, streamIter]() {
|
||||
cudaSetDevice(streamIter->first);
|
||||
float* inputStripePtr = inputBuffer.data<float>(deviceValueCount * Di * i);
|
||||
float* outputStripePtr = outputBuffer.data<float>(deviceValueCount * Do * i);
|
||||
std::for_each(deviceMesh.begin(), deviceMesh.end(), [&](const nanovdb::cuda::DeviceNode& node) {
|
||||
threads.emplace_back([&](int device, cudaStream_t stream) {
|
||||
cudaCheck(cudaSetDevice(device));
|
||||
float* inputStripePtr = inputBuffer.data<float>(deviceValueCount * Di * device);
|
||||
float* outputStripePtr = outputBuffer.data<float>(deviceValueCount * Do * device);
|
||||
|
||||
unsigned long long seed = 42u;
|
||||
curandGenerator_t rng;
|
||||
curandStatus_t curandStatus = curandCreateGenerator(&rng, CURAND_RNG_PSEUDO_DEFAULT);
|
||||
curandStatus = curandSetStream(rng, stream);
|
||||
curandStatus = curandSetPseudoRandomGeneratorSeed(rng, seed);
|
||||
curandStatus = curandGenerateUniform(rng, inputStripePtr, deviceValueCount * Di);
|
||||
curandStatus = curandGenerateUniform(rng, outputStripePtr, deviceValueCount * Do);
|
||||
curandStatus = curandDestroyGenerator(rng);
|
||||
NANOVDB_ASSERT(curandStatus == CURAND_STATUS_SUCCESS);
|
||||
|
||||
cudaDeviceSynchronize();
|
||||
}));
|
||||
}
|
||||
for (int i = 0; i < deviceCount; ++i) threads[i].join();
|
||||
cudaCheck(cudaStreamSynchronize(stream));
|
||||
|
||||
// If we use managed memory, we need to advise about the usage of the memory range in order to obtain an
|
||||
// equivalently optimal paging strategy. cudaMemAdviseSetReadMostly instructs the "paging policy" that data
|
||||
// is far more likely to be read than written, cudaMemAdviseSetPreferredLocation suggests the preferred device to place the data on, and cudaMemAdviseSetAccessedBy is a hint about the which devices are accessing the data.
|
||||
const size_t inPageSize = deviceValueCount * Di * sizeof(float), inPageOffset = inPageSize * device;// in bytes
|
||||
const size_t outPageSize = deviceValueCount * Do * sizeof(float), outPageOffset = outPageSize * device;// in bytes
|
||||
inputBuffer.advise(inPageOffset, inPageSize, device, {cudaMemAdviseSetReadMostly, cudaMemAdviseSetPreferredLocation});
|
||||
inputBuffer.prefetch(inPageOffset, inPageSize, device, stream);
|
||||
outputBuffer.advise(outPageOffset, outPageSize, device, cudaMemAdviseSetPreferredLocation);
|
||||
std::for_each(deviceMesh.begin(), deviceMesh.end(), [&](const nanovdb::cuda::DeviceNode& otherNode) {
|
||||
inputBuffer.advise( inPageOffset, inPageSize, otherNode.id, cudaMemAdviseSetAccessedBy);
|
||||
outputBuffer.advise(outPageOffset, outPageSize, otherNode.id, cudaMemAdviseSetAccessedBy);
|
||||
});
|
||||
}, node.id, node.stream);
|
||||
});
|
||||
std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
|
||||
threads.clear();
|
||||
|
||||
// Spawn a convolution kernel on each device that operates on a disjoint subset of the leaves. The leaves operated on by
|
||||
@@ -239,63 +253,45 @@ void testConvolution()
|
||||
// the same device.
|
||||
auto **timers = new nanovdb::util::cuda::Timer*[deviceCount];
|
||||
const size_t leafNodeCount = indexGrid->tree().nodeCount(0);
|
||||
streamIter = devStreamMap.begin();
|
||||
for (int i = 0; i < deviceCount; ++i, ++streamIter) {
|
||||
threads.emplace_back([&, i, streamIter]() {// capture iterator by value and not reference
|
||||
const int device = streamIter->first;
|
||||
cudaStream_t stream = streamIter->second;
|
||||
cudaSetDevice(device);
|
||||
timers[i] = new nanovdb::util::cuda::Timer(stream);
|
||||
|
||||
size_t deviceLeafNodeCount = (leafNodeCount + deviceCount - 1) / deviceCount;
|
||||
const size_t deviceLeafNodeOffset = deviceLeafNodeCount * i;
|
||||
deviceLeafNodeCount = std::min(deviceLeafNodeCount, leafNodeCount - deviceLeafNodeOffset);
|
||||
std::for_each(deviceMesh.begin(), deviceMesh.end(), [&](const nanovdb::cuda::DeviceNode& node) {
|
||||
threads.emplace_back([&](int device, cudaStream_t stream) {
|
||||
cudaCheck(cudaSetDevice(device));
|
||||
timers[device] = new nanovdb::util::cuda::Timer(stream);
|
||||
|
||||
void* stencilDevicePtr;
|
||||
cudaMallocAsync(&stencilDevicePtr, sizeof(float) * Do * 27 * Di, stream);
|
||||
cudaMemcpyAsync(stencilDevicePtr, stencilHostPtr, sizeof(float) * Do * 27 * Di, cudaMemcpyHostToDevice, stream);
|
||||
cudaCheck(nanovdb::util::cuda::mallocAsync(&stencilDevicePtr, sizeof(float) * Do * 27 * Di, stream));
|
||||
cudaCheck(cudaMemcpyAsync(stencilDevicePtr, stencilHostPtr, sizeof(float) * Do * 27 * Di, cudaMemcpyHostToDevice, stream));
|
||||
|
||||
// If we use managed memory, we need to advise about the usage of the memory range in order to obtain an
|
||||
// equivalently optimal paging strategy. cudaMemAdviseSetReadMostly instructs the "paging policy" that data
|
||||
// is far more likely to be read than set, cudaMemAdviseSetPreferredLocation defines the device that is most like
|
||||
// to the data, and cudaMemAdviseSetAccessedBy is a hint about the which devices are communicating data.
|
||||
const size_t inPageSize = deviceValueCount * Di * sizeof(float), inPageOffset = inPageSize * i;// in bytes
|
||||
const size_t outPageSize = deviceValueCount * Do * sizeof(float), outPageOffset = outPageSize * i;// in bytes
|
||||
inputBuffer.advise(inPageOffset, inPageSize, device, {cudaMemAdviseSetReadMostly, cudaMemAdviseSetPreferredLocation});
|
||||
inputBuffer.prefetch(inPageOffset, inPageSize, device, stream);
|
||||
outputBuffer.advise(outPageOffset, outPageSize, device, cudaMemAdviseSetPreferredLocation);
|
||||
for (auto other = devStreamMap.begin(); other != devStreamMap.end(); ++other) {
|
||||
inputBuffer.advise( inPageOffset, inPageSize, other->first, cudaMemAdviseSetAccessedBy);
|
||||
outputBuffer.advise(outPageOffset, outPageSize, other->first, cudaMemAdviseSetAccessedBy);
|
||||
}
|
||||
cudaStreamSynchronize(stream);
|
||||
auto d_indexGrid = reinterpret_cast<nanovdb::OnIndexGrid*>(indexHandle.deviceData(device));
|
||||
size_t deviceLeafNodeCount = (leafNodeCount + deviceCount - 1) / deviceCount;
|
||||
const size_t deviceLeafNodeOffset = deviceLeafNodeCount * device;
|
||||
deviceLeafNodeCount = std::min(deviceLeafNodeCount, leafNodeCount - deviceLeafNodeOffset);
|
||||
auto deviceIndexGrid = reinterpret_cast<nanovdb::OnIndexGrid*>(indexHandle.deviceData(device));
|
||||
|
||||
// Run 10 warmup iterations
|
||||
float* stencilPtr = (float*)stencilDevicePtr;
|
||||
dim3 blockDim(256);
|
||||
for (int k = 0; k < 10; ++k) {
|
||||
stencilConvolve_v7<<<deviceLeafNodeCount * 2 * 4 * 4, blockDim, 0, stream>>>(
|
||||
d_indexGrid, deviceLeafNodeOffset, inputBuffer.data<float>(), nullptr, nullptr, stencilPtr, outputBuffer.data<float>(), nullptr);
|
||||
deviceIndexGrid, deviceLeafNodeOffset, inputBuffer.data<float>(), nullptr, nullptr, stencilPtr, outputBuffer.data<float>(), nullptr);
|
||||
}
|
||||
cudaStreamSynchronize(stream);
|
||||
|
||||
timers[i]->start();
|
||||
timers[device]->start();
|
||||
stencilConvolve_v7<<<deviceLeafNodeCount * 2 * 4 * 4, blockDim, 0, stream>>>(
|
||||
d_indexGrid, deviceLeafNodeOffset, inputBuffer.data<float>(), nullptr, nullptr, stencilPtr, outputBuffer.data<float>(), nullptr);
|
||||
timers[i]->record();
|
||||
cudaFree(stencilDevicePtr);
|
||||
});
|
||||
}
|
||||
for (int i = 0; i < deviceCount; ++i) threads[i].join();
|
||||
delete [] stencilHostPtr;
|
||||
deviceIndexGrid, deviceLeafNodeOffset, inputBuffer.data<float>(), nullptr, nullptr, stencilPtr, outputBuffer.data<float>(), nullptr);
|
||||
timers[device]->record();
|
||||
cudaCheck(nanovdb::util::cuda::freeAsync(stencilDevicePtr, stream));
|
||||
cudaCheck(cudaStreamSynchronize(stream));
|
||||
}, node.id, node.stream);
|
||||
});
|
||||
std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
|
||||
threads.clear();
|
||||
delete[] stencilHostPtr;
|
||||
|
||||
streamIter = devStreamMap.begin();
|
||||
for (int i = 0; i < deviceCount; ++i, ++streamIter) {
|
||||
timers[i]->print("Device " + std::to_string(streamIter->first) + " GPU convolution ", std::cout);
|
||||
delete timers[i];
|
||||
}
|
||||
delete [] timers;
|
||||
std::for_each(deviceMesh.begin(), deviceMesh.end(), [&](const nanovdb::cuda::DeviceNode& node) {
|
||||
timers[node.id]->print("Device " + std::to_string(node.id) + " GPU convolution ", std::cout);
|
||||
delete timers[node.id];
|
||||
});
|
||||
delete[] timers;
|
||||
std::cout << "Multi-GPU sparse convolution test complete!" << std::endl;
|
||||
}// testConvolution
|
||||
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// the following files are from OpenVDB
|
||||
#include <openvdb/tools/Composite.h>
|
||||
#include <openvdb/util/CpuTimer.h>
|
||||
|
||||
// the following files are from NanoVDB
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
|
||||
template<typename BuildT>
|
||||
void mainMergeGrids(
|
||||
nanovdb::NanoGrid<BuildT> *deviceSrcGrid1,
|
||||
nanovdb::NanoGrid<BuildT> *deviceSrcGrid2,
|
||||
nanovdb::NanoGrid<BuildT> *deviceDstReferenceGrid,
|
||||
nanovdb::NanoGrid<BuildT> *hostSrcGrid1,
|
||||
nanovdb::NanoGrid<BuildT> *hostSrcGrid2,
|
||||
nanovdb::NanoGrid<BuildT> *hostDstReferenceGrid,
|
||||
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 < 3) OPENVDB_THROW(openvdb::ValueError, "usage: "+std::string(argv[0])+" input1.vdb input2.vdb [<iterations>]\n");
|
||||
int benchmark_iters = 10;
|
||||
if (argc > 3) sscanf(argv[3], "%d", &benchmark_iters);
|
||||
|
||||
// Read the openvdb inputs from files
|
||||
|
||||
cpuTimer.start("Read first input VDB file");
|
||||
openvdb::initialize();
|
||||
openvdb::io::File inFile1(argv[1]);
|
||||
inFile1.open(false); // disable delayed loading
|
||||
auto baseGrids1 = inFile1.getGrids();
|
||||
inFile1.close();
|
||||
auto grid1 = openvdb::gridPtrCast<GridT>(baseGrids1->at(0));
|
||||
if (!grid1) OPENVDB_THROW(openvdb::ValueError, "First grid is not a FloatGrid\n");
|
||||
cpuTimer.stop();
|
||||
|
||||
cpuTimer.start("Read second input VDB file");
|
||||
openvdb::initialize();
|
||||
openvdb::io::File inFile2(argv[2]);
|
||||
inFile2.open(false); // disable delayed loading
|
||||
auto baseGrids2 = inFile2.getGrids();
|
||||
inFile2.close();
|
||||
auto grid2 = openvdb::gridPtrCast<GridT>(baseGrids2->at(0));
|
||||
if (!grid2) OPENVDB_THROW(openvdb::ValueError, "Second grid is not a FloatGrid\n");
|
||||
cpuTimer.stop();
|
||||
|
||||
// Convert to indexGrid
|
||||
cpuTimer.start("Converting openVDB input to indexGrid (first component)");
|
||||
auto srcHandle1 = nanovdb::tools::openToIndexVDB<BuildT, nanovdb::cuda::DeviceBuffer>(
|
||||
grid1,
|
||||
0u, // Don't copy data channel
|
||||
false, // No stats
|
||||
false, // No tiles
|
||||
1 // Verbose mode
|
||||
);
|
||||
auto *srcIndexGrid1 = srcHandle1.grid<BuildT>();
|
||||
cpuTimer.stop();
|
||||
|
||||
cpuTimer.start("Converting openVDB input to indexGrid (second component)");
|
||||
auto srcHandle2 = nanovdb::tools::openToIndexVDB<BuildT, nanovdb::cuda::DeviceBuffer>(
|
||||
grid2,
|
||||
0u, // Don't copy data channel
|
||||
false, // No stats
|
||||
false, // No tiles
|
||||
1 // Verbose mode
|
||||
);
|
||||
auto *srcIndexGrid2 = srcHandle2.grid<BuildT>();
|
||||
cpuTimer.stop();
|
||||
|
||||
if (printGridDiagnostics) {
|
||||
std::cout << "============ Component Grid #1 ======= " << std::endl;
|
||||
std::cout << "Allocated values [valueCount()] : " << srcIndexGrid1->valueCount() << std::endl;
|
||||
std::cout << "Active voxels [activeVoxelCount()] : " << srcIndexGrid1->activeVoxelCount() << std::endl;
|
||||
auto minCorner = srcIndexGrid1->indexBBox().min(), maxCorner = srcIndexGrid1->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 : " << srcIndexGrid1->tree().nodeCount(0) << std::endl;
|
||||
std::cout << "Lower internal nodes : " << srcIndexGrid1->tree().nodeCount(1) << std::endl;
|
||||
std::cout << "Upper internal nodes : " << srcIndexGrid1->tree().nodeCount(2) << std::endl;
|
||||
std::cout << "Leaf-level occupancy : "
|
||||
<< 100.f * (float)(srcIndexGrid1->activeVoxelCount())/(float)(srcIndexGrid1->tree().nodeCount(0) * 512)
|
||||
<< "%" << std::endl;
|
||||
std::cout << "Memory usage : " << srcIndexGrid1->gridSize() << " bytes" << std::endl;
|
||||
std::cout << "============ Component Grid #2 ======= " << std::endl;
|
||||
std::cout << "Allocated values [valueCount()] : " << srcIndexGrid2->valueCount() << std::endl;
|
||||
std::cout << "Active voxels [activeVoxelCount()] : " << srcIndexGrid2->activeVoxelCount() << std::endl;
|
||||
minCorner = srcIndexGrid2->indexBBox().min(), maxCorner = srcIndexGrid2->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 : " << srcIndexGrid2->tree().nodeCount(0) << std::endl;
|
||||
std::cout << "Lower internal nodes : " << srcIndexGrid2->tree().nodeCount(1) << std::endl;
|
||||
std::cout << "Upper internal nodes : " << srcIndexGrid2->tree().nodeCount(2) << std::endl;
|
||||
std::cout << "Leaf-level occupancy : "
|
||||
<< 100.f * (float)(srcIndexGrid2->activeVoxelCount())/(float)(srcIndexGrid2->tree().nodeCount(0) * 512)
|
||||
<< "%" << std::endl;
|
||||
std::cout << "Memory usage : " << srcIndexGrid2->gridSize() << " bytes" << std::endl;
|
||||
}
|
||||
|
||||
// Merge (CPU/OpenVDB version)
|
||||
cpuTimer.start("Merging openVDBs (on CPU)");
|
||||
auto mergedGrid = grid1->deepCopy();
|
||||
openvdb::tools::compSum(*mergedGrid, *grid2);
|
||||
cpuTimer.stop();
|
||||
|
||||
// Convert to indexGrid
|
||||
cpuTimer.start("Converting merged openVDB output to indexGrid");
|
||||
auto dstReferenceHandle = nanovdb::tools::openToIndexVDB<BuildT, nanovdb::cuda::DeviceBuffer>(
|
||||
mergedGrid,
|
||||
0u, // Don't copy data channel
|
||||
false, // No stats
|
||||
false, // No tiles
|
||||
1 // Verbose mode
|
||||
);
|
||||
auto *dstReferenceGrid = dstReferenceHandle.grid<BuildT>();
|
||||
cpuTimer.stop();
|
||||
|
||||
if (printGridDiagnostics) {
|
||||
std::cout << "============ Merged Grid (CPU) ======= " << std::endl;
|
||||
std::cout << "Allocated values [valueCount()] : " << dstReferenceGrid->valueCount() << std::endl;
|
||||
std::cout << "Active voxels [activeVoxelCount()] : " << dstReferenceGrid->activeVoxelCount() << std::endl;
|
||||
auto minCorner = dstReferenceGrid->indexBBox().min(), maxCorner = dstReferenceGrid->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 : " << dstReferenceGrid->tree().nodeCount(0) << std::endl;
|
||||
std::cout << "Lower internal nodes : " << dstReferenceGrid->tree().nodeCount(1) << std::endl;
|
||||
std::cout << "Upper internal nodes : " << dstReferenceGrid->tree().nodeCount(2) << std::endl;
|
||||
std::cout << "Leaf-level occupancy : "
|
||||
<< 100.f * (float)(dstReferenceGrid->activeVoxelCount())/(float)(dstReferenceGrid->tree().nodeCount(0) * 512)
|
||||
<< "%" << std::endl;
|
||||
std::cout << "Memory usage : " << dstReferenceGrid->gridSize() << " bytes" << std::endl;
|
||||
}
|
||||
|
||||
// Copy both NanoVDB grids to GPU
|
||||
srcHandle1.deviceUpload();
|
||||
srcHandle2.deviceUpload();
|
||||
dstReferenceHandle.deviceUpload();
|
||||
auto* deviceSrcGrid1 = srcHandle1.deviceGrid<BuildT>();
|
||||
auto* deviceSrcGrid2 = srcHandle2.deviceGrid<BuildT>();
|
||||
auto* deviceDstReferenceGrid = dstReferenceHandle.deviceGrid<BuildT>();
|
||||
if (!deviceSrcGrid1 || !deviceSrcGrid2 || !deviceDstReferenceGrid)
|
||||
OPENVDB_THROW(openvdb::RuntimeError, "Failure while uploading indexGrids to GPU");
|
||||
|
||||
// Launch benchmark
|
||||
mainMergeGrids(
|
||||
deviceSrcGrid1, deviceSrcGrid2, deviceDstReferenceGrid,
|
||||
srcIndexGrid1, srcIndexGrid2, dstReferenceGrid, benchmark_iters);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "An exception occurred: \"" << e.what() << "\"" << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <nanovdb/tools/cuda/MergeGrids.cuh>
|
||||
|
||||
template<typename T>
|
||||
bool bufferCheck(const T* deviceBuffer, const T* hostBuffer, size_t elem_count) {
|
||||
T* tmpBuffer = new T[elem_count];
|
||||
cudaCheck(cudaMemcpy(tmpBuffer, deviceBuffer, elem_count * sizeof(T), cudaMemcpyDeviceToHost));
|
||||
bool same = true;
|
||||
for (int i=0; same && i< elem_count; ++i) { same = (tmpBuffer[i] == hostBuffer[i]); }
|
||||
delete [] tmpBuffer;
|
||||
return same;
|
||||
}
|
||||
|
||||
template<typename BuildT>
|
||||
void mainMergeGrids(
|
||||
nanovdb::NanoGrid<BuildT> *deviceSrcGrid1,
|
||||
nanovdb::NanoGrid<BuildT> *deviceSrcGrid2,
|
||||
nanovdb::NanoGrid<BuildT> *deviceDstReferenceGrid,
|
||||
nanovdb::NanoGrid<BuildT> *hostSrcGrid1,
|
||||
nanovdb::NanoGrid<BuildT> *hostSrcGrid2,
|
||||
nanovdb::NanoGrid<BuildT> *hostDstReferenceGrid,
|
||||
uint32_t benchmark_iters)
|
||||
{
|
||||
nanovdb::util::cuda::Timer gpuTimer;
|
||||
|
||||
// Initialize converter
|
||||
nanovdb::tools::cuda::MergeGrids<BuildT> converter( deviceSrcGrid1, deviceSrcGrid2 );
|
||||
converter.setChecksum(nanovdb::CheckMode::Default);
|
||||
converter.setVerbose(1);
|
||||
|
||||
auto handle = converter.getHandle();
|
||||
auto dstGrid = handle.template deviceGrid<BuildT>();
|
||||
|
||||
// Check for correctness
|
||||
if (bufferCheck((char*)dstGrid, (char*)hostDstReferenceGrid->data(), hostDstReferenceGrid->gridSize()))
|
||||
std::cout << "Result of MergeGrids check out CORRECT against reference" << std::endl;
|
||||
else
|
||||
std::cout << "Result of MergeGrids compares INCORRECT against reference" << std::endl;
|
||||
|
||||
// Re-run warm-started iterations
|
||||
converter.setVerbose(0);
|
||||
for (int i = 0; i < benchmark_iters; i++) {
|
||||
gpuTimer.start("Re-running entire dilation after warmstart");
|
||||
auto handle2 = converter.getHandle();
|
||||
gpuTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
template
|
||||
void mainMergeGrids(
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *deviceSrcGrid1,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *deviceSrcGrid2,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *deviceDstReferenceGrid,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *hostSrcGrid1,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *hostSrcGrid2,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *hostDstReferenceGrid,
|
||||
uint32_t benchmark_iters);
|
||||
@@ -11,12 +11,12 @@
|
||||
#include <openvdb/math/Ray.h>
|
||||
#include <openvdb/tools/LevelSetSphere.h>
|
||||
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/tools/NanoToOpenVDB.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(NANOVDB_USE_CUDA)
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
#else
|
||||
using BufferT = nanovdb::HostBuffer;
|
||||
@@ -30,10 +30,9 @@ void runOpenVDB(nanovdb::GridHandle<BufferT>& handle, int numIterations, int wid
|
||||
using Vec3T = openvdb::math::Vec3<RealT>;
|
||||
using RayT = openvdb::math::Ray<RealT>;
|
||||
|
||||
auto srcGrid = nanovdb::tools::nanoToOpenVDB(handle);
|
||||
openvdb::GridBase::Ptr srcGrid = nanovdb::tools::nanoToOpenVDB(handle);
|
||||
std::cout << "Exporting to OpenVDB grid[" << srcGrid->getName() << "]...\n";
|
||||
|
||||
auto h_grid = (GridT*)srcGrid.get();
|
||||
GridT::Ptr h_grid = openvdb::GridBase::grid<GridT>(srcGrid);
|
||||
|
||||
float* h_outImage = reinterpret_cast<float*>(imageBuffer.data());
|
||||
|
||||
@@ -82,7 +81,7 @@ void runOpenVDB(nanovdb::GridHandle<BufferT>& handle, int numIterations, int wid
|
||||
{
|
||||
float durationAvg = 0;
|
||||
for (int i = 0; i < numIterations; ++i) {
|
||||
float duration = renderImage(false, renderOp, width, height, h_outImage, h_grid);
|
||||
float duration = renderImage(false, renderOp, width, height, h_outImage, h_grid.get());
|
||||
//std::cout << "Duration(OpenVDB-Host) = " << duration << " ms" << std::endl;
|
||||
durationAvg += duration;
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
#include <openvdb/math/Ray.h>
|
||||
#include <openvdb/tools/RayIntersector.h>
|
||||
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/tools/NanoToOpenVDB.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(NANOVDB_USE_CUDA)
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
#else
|
||||
using BufferT = nanovdb::HostBuffer;
|
||||
@@ -31,7 +31,7 @@ void runOpenVDB(nanovdb::GridHandle<nanovdb::cuda::DeviceBuffer>& handle, int nu
|
||||
using RayT = openvdb::math::Ray<RealT>;
|
||||
|
||||
#if 1
|
||||
auto srcGrid = nanovdb::tools::nanoToOpenVDB(handle);
|
||||
openvdb::GridBase::Ptr srcGrid = nanovdb::tools::nanoToOpenVDB(handle);
|
||||
std::cout << "Exporting to OpenVDB grid[" << srcGrid->getName() << "]...\n";
|
||||
#else
|
||||
openvdb::initialize();
|
||||
@@ -42,7 +42,7 @@ void runOpenVDB(nanovdb::GridHandle<nanovdb::cuda::DeviceBuffer>& handle, int nu
|
||||
std::cout << "Loading OpenVDB grid[" << srcGrid->getName() << "]...\n";
|
||||
#endif
|
||||
|
||||
auto h_grid = (GridT*)srcGrid.get();
|
||||
GridT::Ptr h_grid = openvdb::GridBase::grid<GridT>(srcGrid);
|
||||
|
||||
float* h_outImage = reinterpret_cast<float*>(imageBuffer.data());
|
||||
|
||||
@@ -85,7 +85,7 @@ void runOpenVDB(nanovdb::GridHandle<nanovdb::cuda::DeviceBuffer>& handle, int nu
|
||||
{
|
||||
float durationAvg = 0;
|
||||
for (int i = 0; i < numIterations; ++i) {
|
||||
float duration = renderImage(false, renderOp, width, height, h_outImage, h_grid);
|
||||
float duration = renderImage(false, renderOp, width, height, h_outImage, h_grid.get());
|
||||
//std::cout << "Duration(OpenVDB-Host) = " << duration << " ms" << std::endl;
|
||||
durationAvg += duration;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifdef NANOVDB_USE_TBB
|
||||
#include <tbb/tbb.h>
|
||||
#endif
|
||||
|
||||
// the following files are from OpenVDB
|
||||
#include <openvdb/tools/Composite.h>
|
||||
#include <openvdb/util/CpuTimer.h>
|
||||
|
||||
// the following files are from NanoVDB
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
|
||||
template<typename BuildT>
|
||||
void mainRefineGrid(
|
||||
nanovdb::NanoGrid<BuildT> *deviceGridOriginal,
|
||||
nanovdb::NanoGrid<BuildT> *deviceGridRefined,
|
||||
nanovdb::NanoGrid<BuildT> *indexGridOriginal,
|
||||
nanovdb::NanoGrid<BuildT> *indexGridRefined,
|
||||
uint32_t benchmark_iters
|
||||
);
|
||||
|
||||
#ifdef NANOVDB_USE_TBB
|
||||
template<typename GridT>
|
||||
class VoxelRefiner
|
||||
{
|
||||
using TreeT = typename GridT::TreeType;
|
||||
using LeafMgrT = openvdb::tree::LeafManager<const TreeT>;
|
||||
|
||||
public:
|
||||
VoxelRefiner(typename GridT::Ptr srcGrid, tbb::combinable<typename GridT::Ptr>& destGrids) : mSrcGrid(srcGrid), mDestGrids(destGrids) {}
|
||||
VoxelRefiner(const VoxelRefiner& other, tbb::split) : mSrcGrid(other.mSrcGrid), mDestGrids(other.mDestGrids) {}
|
||||
|
||||
void operator()(const typename LeafMgrT::LeafRange& range) const
|
||||
{
|
||||
// Refine into a thread-local copy, for safety
|
||||
typename GridT::Ptr& dstGrid = mDestGrids.local();
|
||||
if (!dstGrid) { // Create if it doesn't exist
|
||||
dstGrid = GridT::create(mSrcGrid->background());
|
||||
dstGrid->setTransform(mSrcGrid->transform().copy());
|
||||
}
|
||||
|
||||
auto unitBox = openvdb::CoordBBox::createCube(openvdb::Coord(), 2);
|
||||
auto dstAcc = dstGrid->getAccessor();
|
||||
for (auto srcLeafIter = range.begin(); srcLeafIter; ++srcLeafIter) {
|
||||
for (auto srcIter = srcLeafIter->cbeginValueOn(); srcIter; ++srcIter) {
|
||||
const auto& ijk = srcIter.getCoord();
|
||||
const auto& value = srcIter.getValue();
|
||||
for (auto boxIter = unitBox.begin(); boxIter != unitBox.end(); ++boxIter)
|
||||
dstAcc.setValue(ijk+ijk+*boxIter, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typename GridT::Ptr mSrcGrid;
|
||||
tbb::combinable<typename GridT::Ptr>& mDestGrids;
|
||||
};
|
||||
#endif
|
||||
|
||||
/// @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-refined)
|
||||
cpuTimer.start("Converting openVDB input to indexGrid (original version)");
|
||||
auto handleOriginal = nanovdb::tools::openToIndexVDB<BuildT, nanovdb::cuda::DeviceBuffer>(
|
||||
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;
|
||||
}
|
||||
|
||||
// Refinement (CPU/OpenVDB version)
|
||||
cpuTimer.start("Refining OpenVDB (on CPU)");
|
||||
using TreeT = GridT::TreeType;
|
||||
using LeafManagerT = openvdb::tree::LeafManager<const TreeT>;
|
||||
#ifdef NANOVDB_USE_TBB
|
||||
// Phase 1: Multithreaded refinement
|
||||
tbb::combinable<GridT::Ptr> localGrids;
|
||||
LeafManagerT leafMgr(grid->tree());
|
||||
VoxelRefiner<GridT> op(grid, localGrids);
|
||||
tbb::parallel_for( leafMgr.leafRange(), op);
|
||||
// Phase 2: Recombine partial grids into final refined result
|
||||
auto refinedGrid = openvdb::FloatGrid::create(grid->background());
|
||||
refinedGrid->setTransform(grid->transform().copy());
|
||||
refinedGrid->setName(grid->getName());
|
||||
localGrids.combine_each(
|
||||
[&refinedGrid](const openvdb::FloatGrid::Ptr& localGrid) { if (localGrid) openvdb::tools::compSum(*refinedGrid, *localGrid); } );
|
||||
#else
|
||||
// Single-threaded refinement
|
||||
LeafManagerT leafMgr(grid->tree());
|
||||
auto refinedGrid = openvdb::FloatGrid::create(grid->background());
|
||||
refinedGrid->setTransform(grid->transform().copy());
|
||||
refinedGrid->setName(grid->getName());
|
||||
auto dstAcc = refinedGrid->getAccessor();
|
||||
auto unitBox = openvdb::CoordBBox::createCube(openvdb::Coord(), 2);
|
||||
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& ijk = iter.getCoord();
|
||||
const auto& value = iter.getValue();
|
||||
for (auto boxIter = unitBox.begin(); boxIter != unitBox.end(); ++boxIter)
|
||||
dstAcc.setValue(ijk+ijk+*boxIter, value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
cpuTimer.stop();
|
||||
|
||||
// Convert to indexGrid (refined)
|
||||
cpuTimer.start("Converting openVDB input to indexGrid (refineed version)");
|
||||
auto handleRefined = nanovdb::tools::openToIndexVDB<BuildT, nanovdb::cuda::DeviceBuffer>(
|
||||
refinedGrid,
|
||||
0u, // Don't copy data channel
|
||||
false, // No stats
|
||||
false, // No tiles
|
||||
1 // Verbose mode
|
||||
);
|
||||
cpuTimer.stop();
|
||||
|
||||
auto *indexGridRefined = handleRefined.grid<BuildT>();
|
||||
|
||||
if (printGridDiagnostics) {
|
||||
std::cout << "============ Refined Grid ============" << std::endl;
|
||||
std::cout << "Allocated values [valueCount()] : " << indexGridRefined->valueCount() << std::endl;
|
||||
std::cout << "Active voxels [activeVoxelCount()] : " << indexGridRefined->activeVoxelCount() << std::endl;
|
||||
auto minCorner = indexGridRefined->indexBBox().min(), maxCorner = indexGridRefined->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 : " << indexGridRefined->tree().nodeCount(0) << std::endl;
|
||||
std::cout << "Lower internal nodes : " << indexGridRefined->tree().nodeCount(1) << std::endl;
|
||||
std::cout << "Upper internal nodes : " << indexGridRefined->tree().nodeCount(2) << std::endl;
|
||||
std::cout << "Leaf-level occupancy : "
|
||||
<< 100.f * (float)(indexGridRefined->activeVoxelCount())/(float)(indexGridRefined->tree().nodeCount(0) * 512)
|
||||
<< "%" << std::endl;
|
||||
std::cout << "Memory usage : " << indexGridRefined->gridSize() << " bytes" << std::endl;
|
||||
}
|
||||
|
||||
// Copy both NanoVDB grids to GPU
|
||||
handleOriginal.deviceUpload();
|
||||
handleRefined.deviceUpload();
|
||||
auto* deviceGridOriginal = handleOriginal.deviceGrid<BuildT>();
|
||||
auto* deviceGridRefined = handleRefined.deviceGrid<BuildT>();
|
||||
if (!deviceGridOriginal || !deviceGridRefined)
|
||||
OPENVDB_THROW(openvdb::RuntimeError, "Failure while uploading indexGrids to GPU");
|
||||
|
||||
// Launch benchmark
|
||||
mainRefineGrid( deviceGridOriginal, deviceGridRefined, indexGridOriginal, indexGridRefined, benchmark_iters );
|
||||
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "An exception occurred: \"" << e.what() << "\"" << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <nanovdb/tools/cuda/RefineGrid.cuh>
|
||||
|
||||
template<typename T>
|
||||
bool bufferCheck(const T* deviceBuffer, const T* hostBuffer, size_t elem_count) {
|
||||
T* tmpBuffer = new T[elem_count];
|
||||
cudaCheck(cudaMemcpy(tmpBuffer, deviceBuffer, elem_count * sizeof(T), cudaMemcpyDeviceToHost));
|
||||
bool same = true;
|
||||
for (int i=0; same && i< elem_count; ++i) { same = (tmpBuffer[i] == hostBuffer[i]); }
|
||||
delete [] tmpBuffer;
|
||||
return same;
|
||||
}
|
||||
|
||||
template<typename BuildT>
|
||||
void mainRefineGrid(
|
||||
nanovdb::NanoGrid<BuildT> *deviceGridOriginal,
|
||||
nanovdb::NanoGrid<BuildT> *deviceGridRefined,
|
||||
nanovdb::NanoGrid<BuildT> *indexGridOriginal,
|
||||
nanovdb::NanoGrid<BuildT> *indexGridRefined,
|
||||
uint32_t benchmark_iters)
|
||||
{
|
||||
nanovdb::util::cuda::Timer gpuTimer;
|
||||
|
||||
// Initialize refiner
|
||||
nanovdb::tools::cuda::RefineGrid<BuildT> refiner( deviceGridOriginal );
|
||||
refiner.setChecksum(nanovdb::CheckMode::Default);
|
||||
refiner.setVerbose(1);
|
||||
|
||||
auto handle = refiner.getHandle();
|
||||
auto dstGrid = handle.template deviceGrid<BuildT>();
|
||||
|
||||
// Check for correctness
|
||||
if (bufferCheck((char*)dstGrid, (char*)indexGridRefined->data(), indexGridRefined->gridSize()))
|
||||
std::cout << "Result of RefineGrid check out CORRECT against reference" << std::endl;
|
||||
else
|
||||
std::cout << "Result of RefineGrid compares INCORRECT against reference" << std::endl;
|
||||
|
||||
// Re-run warm-started iterations
|
||||
refiner.setVerbose(0);
|
||||
for (int i = 0; i < benchmark_iters; i++) {
|
||||
gpuTimer.start("Re-running entire refinment after warmstart");
|
||||
auto dummyHandle = refiner.getHandle();
|
||||
gpuTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
template
|
||||
void mainRefineGrid(
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *deviceGridOriginal,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *deviceGridRefined,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *indexGridOriginal,
|
||||
nanovdb::NanoGrid<nanovdb::ValueOnIndex> *indexGridRefined,
|
||||
uint32_t benchmark_iters
|
||||
);
|
||||
@@ -29,7 +29,7 @@ int main()
|
||||
using OpT = nanovdb::SetVoxel<float>;// defines type of random-access operation (set value)
|
||||
const nanovdb::Coord &ijk = d_coords[tid];
|
||||
d_grid->tree().set<OpT>(ijk, d_values[tid]);// normally one should use a ValueAccessor
|
||||
printf("GPU: voxel # %lu, grid(%4i,%4i,%4i) = %5.1f\n", tid, ijk[0], ijk[1], ijk[2], d_grid->tree().getValue(ijk));
|
||||
printf("GPU: voxel # %zu, grid(%4i,%4i,%4i) = %5.1f\n", tid, ijk[0], ijk[1], ijk[2], d_grid->tree().getValue(ijk));
|
||||
}); cudaCheckError();
|
||||
|
||||
// Copy grid from GPU to CPU and print the voxel values for validation
|
||||
@@ -37,7 +37,7 @@ int main()
|
||||
auto *grid = handle.grid<float>();
|
||||
for (size_t i=0; i<numVoxels; ++i) {
|
||||
const nanovdb::Coord &ijk = coords[i];
|
||||
printf("CPU: voxel # %lu, grid(%4i,%4i,%4i) = %5.1f\n", i, ijk[0], ijk[1], ijk[2], grid->tree().getValue(ijk));
|
||||
printf("CPU: voxel # %zu, grid(%4i,%4i,%4i) = %5.1f\n", i, ijk[0], ijk[1], ijk[2], grid->tree().getValue(ijk));
|
||||
}
|
||||
|
||||
// free arrays allocated on the device
|
||||
@@ -49,4 +49,4 @@ int main()
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <nanovdb/tools/cuda/DistributedPointsToGrid.cuh>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template<typename BufferT = nanovdb::HostBuffer>
|
||||
std::vector<nanovdb::Coord>
|
||||
initializeCoordListFromCSV(const std::string& filename)
|
||||
{
|
||||
std::ifstream file;
|
||||
file.open(filename);
|
||||
std::string line;
|
||||
std::vector<nanovdb::Coord> coords;
|
||||
while (getline(file, line)) {
|
||||
std::istringstream stream(line);
|
||||
nanovdb::Coord coord;
|
||||
stream >> coord.x(); stream.ignore(1, ',' );
|
||||
stream >> coord.y(); stream.ignore(1, ',' );
|
||||
stream >> coord.z();
|
||||
coords.emplace_back(coord);
|
||||
}
|
||||
file.close();
|
||||
return coords;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @brief Demonstrates how to create a NanoVDB grid from voxel coordinates on the GPU
|
||||
void testVoxelsToGrid(const std::string& filename)
|
||||
{
|
||||
nanovdb::cuda::DeviceMesh deviceMesh;
|
||||
|
||||
std::cout << "CSV index source : " << filename << std::endl;
|
||||
std::cout << "==================================================" << std::endl;
|
||||
std::vector<nanovdb::Coord> hostCoords = initializeCoordListFromCSV(filename);
|
||||
std::cout << "Number of coords : " << hostCoords.size() << std::endl;
|
||||
|
||||
size_t coordCount = hostCoords.size();
|
||||
nanovdb::Coord* coords = nullptr;
|
||||
cudaMallocManaged(&coords, coordCount * sizeof(nanovdb::Coord));
|
||||
cudaMemcpy(coords, hostCoords.data(), coordCount * sizeof(nanovdb::Coord), cudaMemcpyHostToDevice);
|
||||
|
||||
nanovdb::tools::cuda::DistributedPointsToGrid<nanovdb::ValueOnIndex> distributedConverter(deviceMesh);
|
||||
auto distributedHandle = distributedConverter.getHandle(coords, coordCount);
|
||||
|
||||
cudaFree(coords);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc != 2)
|
||||
throw std::runtime_error("Expected CSV filename as command-line argument");
|
||||
std::string filename(argv[1]);
|
||||
testVoxelsToGrid(filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
\note This file does NOT depend on OpenVDB, but optionally on ZIP and BLOSC
|
||||
|
||||
\details NanoVDB files take on one of two following formats:
|
||||
\details NanoVDB files take on one of the two following formats:
|
||||
1) multiple segments each with multiple grids (segments have easy to access metadata about its grids)
|
||||
2) starting with verion 32.6.0 nanovdb files also support a raw buffer with one or more grids (just a
|
||||
dump of a raw grid buffer, so no new metadata in headers as when using segments mentioned above).
|
||||
@@ -223,7 +223,7 @@ fileSize_t Internal::write(std::ostream& os, const GridHandle<BufferT>& handle,
|
||||
uLongf size = compressBound(static_cast<uLongf>(residual)); // Get an upper bound on the size of the compressed data.
|
||||
std::unique_ptr<Bytef[]> tmp(new Bytef[size]);
|
||||
const int status = compress(tmp.get(), &size, reinterpret_cast<const Bytef*>(data), static_cast<uLongf>(residual));
|
||||
if (status != Z_OK) std::runtime_error("Internal write error in ZIP");
|
||||
if (status != Z_OK) throw std::runtime_error("Internal write error in ZIP");
|
||||
if (size > residual) std::cerr << "\nWarning: Unexpected ZIP compression from " << residual << " to " << size << " bytes\n";
|
||||
const fileSize_t outBytes = size;
|
||||
os.write(reinterpret_cast<const char*>(&outBytes), sizeof(fileSize_t));
|
||||
@@ -240,7 +240,7 @@ fileSize_t Internal::write(std::ostream& os, const GridHandle<BufferT>& handle,
|
||||
fileSize_t chunk = residual < MAX_SIZE ? residual : MAX_SIZE, size = chunk + BLOSC_MAX_OVERHEAD;
|
||||
std::unique_ptr<char[]> tmp(new char[size]);
|
||||
const int count = blosc_compress_ctx(9, 1, sizeof(float), chunk, data, tmp.get(), size, BLOSC_LZ4_COMPNAME, 1 << 18, 1);
|
||||
if (count <= 0) std::runtime_error("Internal write error in BLOSC");
|
||||
if (count <= 0) throw std::runtime_error("Internal write error in BLOSC");
|
||||
const fileSize_t outBytes = count;
|
||||
os.write(reinterpret_cast<const char*>(&outBytes), sizeof(fileSize_t));
|
||||
os.write(reinterpret_cast<const char*>(tmp.get()), outBytes);
|
||||
@@ -284,7 +284,7 @@ void Internal::read(std::istream& is, char* data, fileSize_t residual, Codec cod
|
||||
is.read(reinterpret_cast<char*>(tmp.get()), size);
|
||||
uLongf numBytes = static_cast<uLongf>(residual);
|
||||
int status = uncompress(reinterpret_cast<Bytef*>(data), &numBytes, tmp.get(), static_cast<uLongf>(size));
|
||||
if (status != Z_OK) std::runtime_error("Internal read error in ZIP");
|
||||
if (status != Z_OK) throw std::runtime_error("Internal read error in ZIP");
|
||||
if (fileSize_t(numBytes) != residual) throw std::runtime_error("UNZIP failed on byte size");
|
||||
#else
|
||||
throw std::runtime_error("ZIP compression codec was disabled during build");
|
||||
@@ -300,7 +300,7 @@ void Internal::read(std::istream& is, char* data, fileSize_t residual, Codec cod
|
||||
is.read(reinterpret_cast<char*>(tmp.get()), size);
|
||||
const fileSize_t chunk = residual < MAX_SIZE ? residual : MAX_SIZE;
|
||||
const int count = blosc_decompress_ctx(tmp.get(), data, size_t(chunk), 1); //fails with more threads :(
|
||||
if (count < 1) std::runtime_error("Internal read error in BLOSC");
|
||||
if (count < 1) throw std::runtime_error("Internal read error in BLOSC");
|
||||
if (count != int(chunk)) throw std::runtime_error("BLOSC failed on byte size");
|
||||
data += size_t(chunk);
|
||||
residual -= chunk;
|
||||
@@ -332,10 +332,11 @@ inline FileGridMetaData::FileGridMetaData(uint64_t size, Codec c, const GridData
|
||||
{0, 0, 0, 1}, // nodeCount[4]
|
||||
{0, 0, 0}, // tileCount[3]
|
||||
c, // codec
|
||||
0, // padding
|
||||
uint16_t(gridData.mBlindMetadataCount), // number of blind meta data
|
||||
Version()}// version
|
||||
, gridName(gridData.gridName())
|
||||
{
|
||||
NANOVDB_ASSERT(gridData.mBlindMetadataCount <= uint32_t(1 << 16));// due to uint32_t -> uin16_t conversion
|
||||
auto &treeData = *reinterpret_cast<const TreeData*>(gridData.treePtr());
|
||||
nameKey = stringHash(gridName);
|
||||
voxelCount = treeData.mVoxelCount;
|
||||
|
||||
@@ -74,9 +74,23 @@ public:
|
||||
{
|
||||
if (mDim == dim)
|
||||
return false;
|
||||
|
||||
// compute valid voxel range
|
||||
Coord voxelMax = (mVoxel + Coord(mDim - 1)) & (~(dim - 1));
|
||||
Coord voxelMin = mVoxel & (~(dim - 1));
|
||||
|
||||
mDim = dim;
|
||||
const Vec3T &pos = ray(mT0), &inv = ray.invDir();
|
||||
mVoxel = RoundDown<CoordT>(pos) & (~(dim - 1));
|
||||
|
||||
// clamp mVoxel to valid range
|
||||
mVoxel[0] = nanovdb::math::Min(mVoxel[0], voxelMax[0]);
|
||||
mVoxel[1] = nanovdb::math::Min(mVoxel[1], voxelMax[1]);
|
||||
mVoxel[2] = nanovdb::math::Min(mVoxel[2], voxelMax[2]);
|
||||
mVoxel[0] = nanovdb::math::Max(mVoxel[0], voxelMin[0]);
|
||||
mVoxel[1] = nanovdb::math::Max(mVoxel[1], voxelMin[1]);
|
||||
mVoxel[2] = nanovdb::math::Max(mVoxel[2], voxelMin[2]);
|
||||
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
if (mStep[axis] == 0)
|
||||
continue;
|
||||
|
||||
+800
-1
@@ -17,6 +17,10 @@
|
||||
|
||||
#include <nanovdb/util/Util.h>// for __hostdev__ and lots of other utility functions
|
||||
|
||||
#if defined(__CUDA_ARCH__)
|
||||
#include <cuda/std/limits>// for ::cuda::std::numeric_limits
|
||||
#endif
|
||||
|
||||
namespace nanovdb {// =================================================================
|
||||
|
||||
namespace math {// =============================================================
|
||||
@@ -83,7 +87,13 @@ struct Delta<double>
|
||||
/// Maximum floating-point values
|
||||
template<typename T>
|
||||
struct Maximum;
|
||||
#if defined(__CUDA_ARCH__) || defined(__HIP__)
|
||||
#if defined(__CUDA_ARCH__)
|
||||
template<typename T>
|
||||
struct Maximum
|
||||
{
|
||||
__hostdev__ static T value() { return ::cuda::std::numeric_limits<T>::max(); }
|
||||
};
|
||||
#elif defined(__HIP__)
|
||||
template<>
|
||||
struct Maximum<int>
|
||||
{
|
||||
@@ -576,6 +586,774 @@ public:
|
||||
__hostdev__ inline Coord round() const { return *this; }
|
||||
}; // Coord class
|
||||
|
||||
|
||||
/// @brief Type alias for Coord so we have a consistent naming convention
|
||||
using Coord3 = Coord;
|
||||
|
||||
|
||||
template <typename T>
|
||||
class Vec2;
|
||||
|
||||
/// @brief Signed (i, j) 32-bit integer coordinate class, similar to openvdb::math::Coord
|
||||
class Coord2
|
||||
{
|
||||
int32_t mVec[2]; // private member data - three signed index coordinates
|
||||
public:
|
||||
using ValueType = int32_t;
|
||||
using IndexType = uint32_t;
|
||||
|
||||
/// @brief Initialize all coordinates to zero.
|
||||
__hostdev__ Coord2()
|
||||
: mVec{0, 0}
|
||||
{
|
||||
}
|
||||
|
||||
/// @brief Initializes all coordinates to the given signed integer.
|
||||
__hostdev__ explicit Coord2(ValueType n)
|
||||
: mVec{n, n}
|
||||
{
|
||||
}
|
||||
|
||||
/// @brief Initializes coordinate to the given signed integers.
|
||||
__hostdev__ Coord2(ValueType i, ValueType j)
|
||||
: mVec{i, j}
|
||||
{
|
||||
}
|
||||
|
||||
__hostdev__ Coord2(ValueType* ptr)
|
||||
: mVec{ptr[0], ptr[1]}
|
||||
{
|
||||
}
|
||||
|
||||
__hostdev__ int32_t x() const { return mVec[0]; }
|
||||
__hostdev__ int32_t y() const { return mVec[1]; }
|
||||
|
||||
__hostdev__ int32_t& x() { return mVec[0]; }
|
||||
__hostdev__ int32_t& y() { return mVec[1]; }
|
||||
|
||||
__hostdev__ static Coord2 max() { return Coord2(int32_t((1u << 31) - 1)); }
|
||||
|
||||
__hostdev__ static Coord2 min() { return Coord2(-int32_t((1u << 31) - 1) - 1); }
|
||||
|
||||
__hostdev__ static size_t memUsage() { return sizeof(Coord2); }
|
||||
|
||||
/// @brief Return a const reference to the given Coord component.
|
||||
/// @warning The argument is assumed to be 0 or 1,
|
||||
__hostdev__ const ValueType& operator[](IndexType i) const { return mVec[i]; }
|
||||
|
||||
/// @brief Return a non-const reference to the given Coord component.
|
||||
/// @warning The argument is assumed to be 0 or 1.
|
||||
__hostdev__ ValueType& operator[](IndexType i) { return mVec[i]; }
|
||||
|
||||
/// @brief Assignment operator that works with openvdb::Coord
|
||||
template<typename CoordT>
|
||||
__hostdev__ Coord2& operator=(const CoordT& other)
|
||||
{
|
||||
static_assert(sizeof(Coord2) == sizeof(CoordT), "Mis-matched sizeof");
|
||||
mVec[0] = other[0];
|
||||
mVec[1] = other[1];
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief Return a new instance with coordinates masked by the given unsigned integer.
|
||||
__hostdev__ Coord2 operator&(IndexType n) const { return Coord2(mVec[0] & n, mVec[1] & n); }
|
||||
|
||||
// @brief Return a new instance with coordinates left-shifted by the given unsigned integer.
|
||||
__hostdev__ Coord2 operator<<(IndexType n) const { return Coord2(mVec[0] << n, mVec[1] << n); }
|
||||
|
||||
// @brief Return a new instance with coordinates right-shifted by the given unsigned integer.
|
||||
__hostdev__ Coord2 operator>>(IndexType n) const { return Coord2(mVec[0] >> n, mVec[1] >> n); }
|
||||
|
||||
/// @brief Return true if this Coord is lexicographically less than the given Coord.
|
||||
__hostdev__ bool operator<(const Coord2& rhs) const
|
||||
{
|
||||
return mVec[0] < rhs[0] ? true
|
||||
: mVec[0] > rhs[0] ? false
|
||||
: mVec[1] < rhs[1] ? true : false;
|
||||
}
|
||||
|
||||
/// @brief Return true if this Coord is lexicographically less or equal to the given Coord.
|
||||
__hostdev__ bool operator<=(const Coord2& rhs) const
|
||||
{
|
||||
return mVec[0] < rhs[0] ? true
|
||||
: mVec[0] > rhs[0] ? false
|
||||
: mVec[1] <= rhs[1] ? true : false;
|
||||
}
|
||||
|
||||
// @brief Return true if this Coord is lexicographically greater than the given Coord.
|
||||
__hostdev__ bool operator>(const Coord2& rhs) const
|
||||
{
|
||||
return mVec[0] > rhs[0] ? true
|
||||
: mVec[0] < rhs[0] ? false
|
||||
: mVec[1] > rhs[1] ? true : false;
|
||||
}
|
||||
|
||||
// @brief Return true if this Coord is lexicographically greater or equal to the given Coord.
|
||||
__hostdev__ bool operator>=(const Coord2& rhs) const
|
||||
{
|
||||
return mVec[0] > rhs[0] ? true
|
||||
: mVec[0] < rhs[0] ? false
|
||||
: mVec[1] >= rhs[1] ? true : false;
|
||||
}
|
||||
|
||||
// @brief Return true if the Coord components are identical.
|
||||
__hostdev__ bool operator==(const Coord2& rhs) const { return mVec[0] == rhs[0] && mVec[1] == rhs[1]; }
|
||||
__hostdev__ bool operator!=(const Coord2& rhs) const { return mVec[0] != rhs[0] || mVec[1] != rhs[1]; }
|
||||
__hostdev__ Coord2& operator&=(int n)
|
||||
{
|
||||
mVec[0] &= n;
|
||||
mVec[1] &= n;
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Coord2& operator<<=(uint32_t n)
|
||||
{
|
||||
mVec[0] <<= n;
|
||||
mVec[1] <<= n;
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Coord2& operator>>=(uint32_t n)
|
||||
{
|
||||
mVec[0] >>= n;
|
||||
mVec[1] >>= n;
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Coord2& operator+=(int n)
|
||||
{
|
||||
mVec[0] += n;
|
||||
mVec[1] += n;
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Coord2 operator+(const Coord2& rhs) const { return Coord2(mVec[0] + rhs[0], mVec[1] + rhs[1]); }
|
||||
__hostdev__ Coord2 operator-(const Coord2& rhs) const { return Coord2(mVec[0] - rhs[0], mVec[1] - rhs[1]); }
|
||||
__hostdev__ Coord2 operator-() const { return Coord2(-mVec[0], -mVec[1]); }
|
||||
__hostdev__ Coord2& operator+=(const Coord2& rhs)
|
||||
{
|
||||
mVec[0] += rhs[0];
|
||||
mVec[1] += rhs[1];
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Coord2& operator-=(const Coord2& rhs)
|
||||
{
|
||||
mVec[0] -= rhs[0];
|
||||
mVec[1] -= rhs[1];
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief Perform a component-wise minimum with the other Coord.
|
||||
__hostdev__ Coord2& minComponent(const Coord2& other)
|
||||
{
|
||||
if (other[0] < mVec[0])
|
||||
mVec[0] = other[0];
|
||||
if (other[1] < mVec[1])
|
||||
mVec[1] = other[1];
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief Perform a component-wise maximum with the other Coord.
|
||||
__hostdev__ Coord2& maxComponent(const Coord2& other)
|
||||
{
|
||||
if (other[0] > mVec[0])
|
||||
mVec[0] = other[0];
|
||||
if (other[1] > mVec[1])
|
||||
mVec[1] = other[1];
|
||||
return *this;
|
||||
}
|
||||
#if defined(__CUDACC__) // the following functions only run on the GPU!
|
||||
__device__ inline Coord2& minComponentAtomic(const Coord2& other)
|
||||
{
|
||||
atomicMin(&mVec[0], other[0]);
|
||||
atomicMin(&mVec[1], other[1]);
|
||||
return *this;
|
||||
}
|
||||
__device__ inline Coord2& maxComponentAtomic(const Coord2& other)
|
||||
{
|
||||
atomicMax(&mVec[0], other[0]);
|
||||
atomicMax(&mVec[1], other[1]);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
__hostdev__ Coord2 offsetBy(ValueType dx, ValueType dy) const
|
||||
{
|
||||
return Coord2(mVec[0] + dx, mVec[1] + dy);
|
||||
}
|
||||
|
||||
__hostdev__ Coord2 offsetBy(ValueType n) const { return this->offsetBy(n, n); }
|
||||
|
||||
/// Return true if any of the components of @a a are smaller than the
|
||||
/// corresponding components of @a b.
|
||||
__hostdev__ static inline bool lessThan(const Coord2& a, const Coord2& b)
|
||||
{
|
||||
return (a[0] < b[0] || a[1] < b[1]);
|
||||
}
|
||||
|
||||
/// @brief Return the largest integer coordinates that are not greater
|
||||
/// than @a xyz (node centered conversion).
|
||||
template<typename Vec2T>
|
||||
__hostdev__ static Coord2 Floor(const Vec2T& xy) { return Coord2(math::Floor(xy[0]), math::Floor(xy[1])); }
|
||||
|
||||
/// @brief Return a single precision floating-point vector of this coordinate
|
||||
__hostdev__ inline Vec2<float> asVec2s() const;
|
||||
|
||||
/// @brief Return a double precision floating-point vector of this coordinate
|
||||
__hostdev__ inline Vec2<double> asVec2d() const;
|
||||
|
||||
// returns a copy of itself, so it mimics the behaviour of Vec3<T>::round()
|
||||
__hostdev__ inline Coord2 round() const { return *this; }
|
||||
}; // Coord2 class
|
||||
|
||||
// ----------------------------> Vec2 <--------------------------------------
|
||||
|
||||
/// @brief A simple vector class with three components, similar to openvdb::math::Vec3
|
||||
template<typename T>
|
||||
class Vec2
|
||||
{
|
||||
T mVec[2];
|
||||
|
||||
public:
|
||||
static const int SIZE = 2;
|
||||
static const int size = 2; // in openvdb::math::Tuple
|
||||
using ValueType = T;
|
||||
Vec2() = default;
|
||||
__hostdev__ explicit Vec2(T x)
|
||||
: mVec{x, x}
|
||||
{
|
||||
}
|
||||
__hostdev__ Vec2(T x, T y)
|
||||
: mVec{x, y}
|
||||
{
|
||||
}
|
||||
template<template<class> class Vec2T, class T2>
|
||||
__hostdev__ Vec2(const Vec2T<T2>& v)
|
||||
: mVec{T(v[0]), T(v[1])}
|
||||
{
|
||||
static_assert(Vec2T<T2>::size == size, "expected Vec2T::size==2!");
|
||||
}
|
||||
template<typename T2>
|
||||
__hostdev__ explicit Vec2(const Vec2<T2>& v)
|
||||
: mVec{T(v[0]), T(v[1])}
|
||||
{
|
||||
}
|
||||
__hostdev__ explicit Vec2(const Coord2& ijk)
|
||||
: mVec{T(ijk[0]), T(ijk[1])}
|
||||
{
|
||||
}
|
||||
__hostdev__ bool operator==(const Vec2& rhs) const { return mVec[0] == rhs[0] && mVec[1] == rhs[1]; }
|
||||
__hostdev__ bool operator!=(const Vec2& rhs) const { return mVec[0] != rhs[0] || mVec[1] != rhs[1]; }
|
||||
template<template<class> class Vec2T, class T2>
|
||||
__hostdev__ Vec2& operator=(const Vec2T<T2>& rhs)
|
||||
{
|
||||
static_assert(Vec2T<T2>::size == size, "expected Vec2T::size==2!");
|
||||
mVec[0] = rhs[0];
|
||||
mVec[1] = rhs[1];
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ const T& operator[](int i) const { return mVec[i]; }
|
||||
__hostdev__ T& operator[](int i) { return mVec[i]; }
|
||||
template<typename Vec2T>
|
||||
__hostdev__ T dot(const Vec2T& v) const { return mVec[0] * v[0] + mVec[1] * v[1]; }
|
||||
__hostdev__ T lengthSqr() const
|
||||
{
|
||||
return mVec[0] * mVec[0] + mVec[1] * mVec[1]; // 3 flops
|
||||
}
|
||||
__hostdev__ T length() const { return Sqrt(this->lengthSqr()); }
|
||||
__hostdev__ Vec2 operator-() const { return Vec2(-mVec[0], -mVec[1]); }
|
||||
__hostdev__ Vec2 operator*(const Vec2& v) const { return Vec2(mVec[0] * v[0], mVec[1] * v[1]); }
|
||||
__hostdev__ Vec2 operator/(const Vec2& v) const { return Vec2(mVec[0] / v[0], mVec[1] / v[1]); }
|
||||
__hostdev__ Vec2 operator+(const Vec2& v) const { return Vec2(mVec[0] + v[0], mVec[1] + v[1]); }
|
||||
__hostdev__ Vec2 operator-(const Vec2& v) const { return Vec2(mVec[0] - v[0], mVec[1] - v[1]); }
|
||||
__hostdev__ Vec2 operator+(const Coord& ijk) const { return Vec2(mVec[0] + ijk[0], mVec[1] + ijk[1]); }
|
||||
__hostdev__ Vec2 operator-(const Coord& ijk) const { return Vec2(mVec[0] - ijk[0], mVec[1] - ijk[1]); }
|
||||
__hostdev__ Vec2 operator*(const T& s) const { return Vec2(s * mVec[0], s * mVec[1]); }
|
||||
__hostdev__ Vec2 operator/(const T& s) const { return (T(1) / s) * (*this); }
|
||||
__hostdev__ Vec2& operator+=(const Vec2& v)
|
||||
{
|
||||
mVec[0] += v[0];
|
||||
mVec[1] += v[1];
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Vec2& operator+=(const Coord& ijk)
|
||||
{
|
||||
mVec[0] += T(ijk[0]);
|
||||
mVec[1] += T(ijk[1]);
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Vec2& operator-=(const Vec2& v)
|
||||
{
|
||||
mVec[0] -= v[0];
|
||||
mVec[1] -= v[1];
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Vec2& operator-=(const Coord& ijk)
|
||||
{
|
||||
mVec[0] -= T(ijk[0]);
|
||||
mVec[1] -= T(ijk[1]);
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Vec2& operator*=(const T& s)
|
||||
{
|
||||
mVec[0] *= s;
|
||||
mVec[1] *= s;
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Vec2& operator/=(const T& s) { return (*this) *= T(1) / s; }
|
||||
__hostdev__ Vec2& normalize() { return (*this) /= this->length(); }
|
||||
/// @brief Perform a component-wise minimum with the other Coord.
|
||||
__hostdev__ Vec2& minComponent(const Vec2& other)
|
||||
{
|
||||
if (other[0] < mVec[0])
|
||||
mVec[0] = other[0];
|
||||
if (other[1] < mVec[1])
|
||||
mVec[1] = other[1];
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief Perform a component-wise maximum with the other Coord.
|
||||
__hostdev__ Vec2& maxComponent(const Vec2& other)
|
||||
{
|
||||
if (other[0] > mVec[0])
|
||||
mVec[0] = other[0];
|
||||
if (other[1] > mVec[1])
|
||||
mVec[1] = other[1];
|
||||
return *this;
|
||||
}
|
||||
/// @brief Return the smallest vector component
|
||||
__hostdev__ ValueType min() const
|
||||
{
|
||||
return mVec[0] < mVec[1] ? mVec[0] : mVec[1];
|
||||
}
|
||||
/// @brief Return the largest vector component
|
||||
__hostdev__ ValueType max() const
|
||||
{
|
||||
return mVec[0] > mVec[1] ? mVec[0] : mVec[1];
|
||||
}
|
||||
/// @brief Round each component if this Vec<T> up to its integer value
|
||||
/// @return Return an integer Coord
|
||||
__hostdev__ Coord2 floor() const { return Coord2(Floor(mVec[0]), Floor(mVec[1])); }
|
||||
/// @brief Round each component if this Vec<T> down to its integer value
|
||||
/// @return Return an integer Coord
|
||||
__hostdev__ Coord2 ceil() const { return Coord2(Ceil(mVec[0]), Ceil(mVec[1])); }
|
||||
/// @brief Round each component if this Vec<T> to its closest integer value
|
||||
/// @return Return an integer Coord
|
||||
__hostdev__ Coord2 round() const
|
||||
{
|
||||
if constexpr(util::is_same<T, float>::value) {
|
||||
return Coord2(Floor(mVec[0] + 0.5f), Floor(mVec[1] + 0.5f));
|
||||
} else if constexpr(util::is_same<T, int>::value) {
|
||||
return Coord2(mVec[0], mVec[1]);
|
||||
} else {
|
||||
return Coord2(Floor(mVec[0] + 0.5), Floor(mVec[1] + 0.5));
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief return a non-const raw constant pointer to array of three vector components
|
||||
__hostdev__ T* asPointer() { return mVec; }
|
||||
/// @brief return a const raw constant pointer to array of three vector components
|
||||
__hostdev__ const T* asPointer() const { return mVec; }
|
||||
}; // Vec2<T>
|
||||
|
||||
template<typename T1, typename T2>
|
||||
__hostdev__ inline Vec2<T2> operator*(T1 scalar, const Vec2<T2>& vec)
|
||||
{
|
||||
return Vec2<T2>(scalar * vec[0], scalar * vec[1]);
|
||||
}
|
||||
template<typename T1, typename T2>
|
||||
__hostdev__ inline Vec2<T2> operator/(T1 scalar, const Vec2<T2>& vec)
|
||||
{
|
||||
return Vec2<T2>(scalar / vec[0], scalar / vec[1]);
|
||||
}
|
||||
|
||||
/// @brief Return a single precision floating-point vector of this coordinate
|
||||
__hostdev__ inline Vec2<float> Coord2::asVec2s() const
|
||||
{
|
||||
return Vec2<float>(float(mVec[0]), float(mVec[1]));
|
||||
}
|
||||
|
||||
/// @brief Return a double precision floating-point vector of this coordinate
|
||||
__hostdev__ inline Vec2<double> Coord2::asVec2d() const
|
||||
{
|
||||
return Vec2<double>(double(mVec[0]), double(mVec[1]));
|
||||
}
|
||||
|
||||
|
||||
// Matrix base class
|
||||
template<typename T, int ROWS, int COLS>
|
||||
class MatBase {
|
||||
protected:
|
||||
T mData[ROWS * COLS]; // 1D array storage
|
||||
|
||||
static constexpr int rows() { return ROWS; }
|
||||
static constexpr int cols() { return COLS; }
|
||||
static constexpr int size() { return ROWS * COLS; }
|
||||
|
||||
public:
|
||||
MatBase() = default;
|
||||
|
||||
template<typename S>
|
||||
__hostdev__ MatBase(S* array) {
|
||||
for (int i = 0; i < ROWS * COLS; ++i) {
|
||||
mData[i] = static_cast<T>(array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 2D array access
|
||||
__hostdev__ T* operator[](int row) {
|
||||
return &mData[row * COLS];
|
||||
}
|
||||
__hostdev__ const T* operator[](int row) const {
|
||||
return &mData[row * COLS];
|
||||
}
|
||||
};
|
||||
|
||||
// Forward declarations
|
||||
template<typename T> class Mat2;
|
||||
template<typename T> class Mat2x3;
|
||||
template<typename T> class Mat3x2;
|
||||
template<typename T> class Mat3;
|
||||
template<typename T> class Mat4;
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
class Mat2 : public MatBase<T, 2, 2> {
|
||||
using Base = MatBase<T, 2, 2>;
|
||||
public:
|
||||
Mat2() = default;
|
||||
/// @brief Constructor given individual array elements, the ordering is in row major form:
|
||||
/** @verbatim
|
||||
a b
|
||||
c d
|
||||
@endverbatim */
|
||||
__hostdev__ Mat2(T a, T b, T c, T d) {
|
||||
this->mData[0] = a; this->mData[1] = b;
|
||||
this->mData[2] = c; this->mData[3] = d;
|
||||
}
|
||||
|
||||
/// @brief Constructor given array of elements, the ordering is in row major form
|
||||
template<typename Source>
|
||||
__hostdev__ Mat2(Source* array) : Base(array) {}
|
||||
|
||||
__hostdev__ Mat2 operator-() const { return Mat2(-(*this)[0][0], -(*this)[0][1], -(*this)[1][0], -(*this)[1][1]); }
|
||||
|
||||
/// @brief Multiply by 2x2 matrix @a m and return the resulting matrix.
|
||||
__hostdev__ Mat2<T> operator*(const Mat2<T>& m) const {
|
||||
return Mat2<T>(
|
||||
(*this)[0][0] * m[0][0] + (*this)[0][1] * m[1][0],
|
||||
(*this)[0][0] * m[0][1] + (*this)[0][1] * m[1][1],
|
||||
(*this)[1][0] * m[0][0] + (*this)[1][1] * m[1][0],
|
||||
(*this)[1][0] * m[0][1] + (*this)[1][1] * m[1][1]
|
||||
);
|
||||
}
|
||||
|
||||
/// @brief Add each element of the given matrix to the corresponding element of this matrix.
|
||||
__hostdev__ Mat2<T>& operator+=(const Mat2<T>& m) {
|
||||
(*this)[0][0] += m[0][0];
|
||||
(*this)[0][1] += m[0][1];
|
||||
(*this)[1][0] += m[1][0];
|
||||
(*this)[1][1] += m[1][1];
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief returns transpose of this
|
||||
__hostdev__ Mat2<T> transpose() const {
|
||||
return Mat2<T>((*this)[0][0], (*this)[1][0], (*this)[0][1], (*this)[1][1]);
|
||||
}
|
||||
|
||||
/// @brief returns inverse of this
|
||||
__hostdev__ Mat2<T> inverse() const {
|
||||
T det = (*this)[0][0] * (*this)[1][1] - (*this)[0][1] * (*this)[1][0];
|
||||
if (isApproxZero(det)) {
|
||||
return Mat2<T>();
|
||||
}
|
||||
T invDet = 1.f / det;
|
||||
return Mat2<T>((*this)[1][1] * invDet, -(*this)[0][1] * invDet, -(*this)[1][0] * invDet, (*this)[0][0] * invDet);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Mat2x3 : public MatBase<T, 2, 3> {
|
||||
using Base = MatBase<T, 2, 3>;
|
||||
public:
|
||||
Mat2x3() = default;
|
||||
/// @brief Constructor given individual array elements, the ordering is in row major form:
|
||||
/** @verbatim
|
||||
a b c
|
||||
d e f
|
||||
@endverbatim */
|
||||
__hostdev__ Mat2x3(T a, T b, T c, T d, T e, T f) {
|
||||
this->mData[0] = a; this->mData[1] = b; this->mData[2] = c;
|
||||
this->mData[3] = d; this->mData[4] = e; this->mData[5] = f;
|
||||
}
|
||||
|
||||
/// @brief Constructor given array of elements, the ordering is in row major form
|
||||
template<typename Source>
|
||||
__hostdev__ Mat2x3(Source* array) : Base(array) {}
|
||||
|
||||
|
||||
/// @brief Add two matrices and return the resulting matrix.
|
||||
__hostdev__ Mat2x3<T> operator+(const Mat2x3<T>& m) const {
|
||||
return Mat2x3<T>(
|
||||
(*this)[0][0] + m[0][0], (*this)[0][1] + m[0][1], (*this)[0][2] + m[0][2],
|
||||
(*this)[1][0] + m[1][0], (*this)[1][1] + m[1][1], (*this)[1][2] + m[1][2]
|
||||
);
|
||||
}
|
||||
|
||||
/// @brief Add a 2x3 matrix to this matrix.
|
||||
__hostdev__ Mat2x3<T>& operator+=(const Mat2x3<T>& m) {
|
||||
(*this)[0][0] += m[0][0]; (*this)[0][1] += m[0][1]; (*this)[0][2] += m[0][2];
|
||||
(*this)[1][0] += m[1][0]; (*this)[1][1] += m[1][1]; (*this)[1][2] += m[1][2];
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief returns transpose of this
|
||||
__hostdev__ Mat3x2<T> transpose() const {
|
||||
return Mat3x2<T>(
|
||||
(*this)[0][0], (*this)[1][0], // First row
|
||||
(*this)[0][1], (*this)[1][1], // Second row
|
||||
(*this)[0][2], (*this)[1][2] // Third row
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Mat3x2: public MatBase<T, 3, 2> {
|
||||
using Base = MatBase<T, 3, 2>;
|
||||
public:
|
||||
Mat3x2() = default;
|
||||
|
||||
/// @brief Constructor given individual array elements, the ordering is in row major form:
|
||||
/** @verbatim
|
||||
a b
|
||||
c d
|
||||
e f
|
||||
@endverbatim */
|
||||
template<typename Source>
|
||||
__hostdev__ Mat3x2(Source a, Source b, Source c, Source d, Source e, Source f)
|
||||
{
|
||||
this->mData[0] = a; this->mData[1] = b;
|
||||
this->mData[2] = c; this->mData[3] = d;
|
||||
this->mData[4] = e; this->mData[5] = f;
|
||||
}
|
||||
|
||||
/// @brief Constructor given array of elements, the ordering is in row major form
|
||||
template<typename Source>
|
||||
__hostdev__ Mat3x2(Source *a): Base(a) {}
|
||||
|
||||
/// @brief returns transpose of this
|
||||
__hostdev__ Mat2x3<T> transpose() const {
|
||||
return Mat2x3<T>((*this)[0][0], (*this)[1][0], (*this)[2][0], // First row
|
||||
(*this)[0][1], (*this)[1][1], (*this)[2][1]); // Second row
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Mat3 : public MatBase<T, 3, 3> {
|
||||
using Base = MatBase<T, 3, 3>;
|
||||
public:
|
||||
Mat3() = default;
|
||||
|
||||
/// @brief Constructor given individual array elements, the ordering is in row major form:
|
||||
/** @verbatim
|
||||
a b c
|
||||
d e f
|
||||
g h i
|
||||
@endverbatim */
|
||||
template<typename Source>
|
||||
__hostdev__ Mat3(Source a, Source b, Source c,
|
||||
Source d, Source e, Source f,
|
||||
Source g, Source h, Source i)
|
||||
{
|
||||
this->mData[0] = a; this->mData[1] = b; this->mData[2] = c;
|
||||
this->mData[3] = d; this->mData[4] = e; this->mData[5] = f;
|
||||
this->mData[6] = g; this->mData[7] = h; this->mData[8] = i;
|
||||
}
|
||||
|
||||
/// @brief Constructor given array of elements, the ordering is in row major form
|
||||
template<typename Source>
|
||||
__hostdev__ Mat3(Source *a): Base(a) {}
|
||||
|
||||
|
||||
/// @brief Add two matrices and return the resulting matrix.
|
||||
__hostdev__ Mat3<T> operator+(const Mat3<T>& m) const {
|
||||
return Mat3<T>(
|
||||
(*this)[0][0] + m[0][0], (*this)[0][1] + m[0][1], (*this)[0][2] + m[0][2],
|
||||
(*this)[1][0] + m[1][0], (*this)[1][1] + m[1][1], (*this)[1][2] + m[1][2],
|
||||
(*this)[2][0] + m[2][0], (*this)[2][1] + m[2][1], (*this)[2][2] + m[2][2]
|
||||
);
|
||||
}
|
||||
|
||||
/// @brief Multiply by @a v and return the resulting vector.
|
||||
__hostdev__ Vec3<T> operator*(const Vec3<T>& v) const {
|
||||
return Vec3<T>(
|
||||
(*this)[0][0] * v[0] + (*this)[0][1] * v[1] + (*this)[0][2] * v[2],
|
||||
(*this)[1][0] * v[0] + (*this)[1][1] * v[1] + (*this)[1][2] * v[2],
|
||||
(*this)[2][0] * v[0] + (*this)[2][1] * v[1] + (*this)[2][2] * v[2]
|
||||
);
|
||||
}
|
||||
|
||||
/// @brief Multiply by 3x3 matrix @a m and return the resulting matrix.
|
||||
__hostdev__ Mat3<T> operator*(const Mat3<T>& m) const {
|
||||
return Mat3<T>(
|
||||
(*this)[0][0] * m[0][0] + (*this)[0][1] * m[1][0] + (*this)[0][2] * m[2][0],
|
||||
(*this)[0][0] * m[0][1] + (*this)[0][1] * m[1][1] + (*this)[0][2] * m[2][1],
|
||||
(*this)[0][0] * m[0][2] + (*this)[0][1] * m[1][2] + (*this)[0][2] * m[2][2],
|
||||
(*this)[1][0] * m[0][0] + (*this)[1][1] * m[1][0] + (*this)[1][2] * m[2][0],
|
||||
(*this)[1][0] * m[0][1] + (*this)[1][1] * m[1][1] + (*this)[1][2] * m[2][1],
|
||||
(*this)[1][0] * m[0][2] + (*this)[1][1] * m[1][2] + (*this)[1][2] * m[2][2],
|
||||
(*this)[2][0] * m[0][0] + (*this)[2][1] * m[1][0] + (*this)[2][2] * m[2][0],
|
||||
(*this)[2][0] * m[0][1] + (*this)[2][1] * m[1][1] + (*this)[2][2] * m[2][1],
|
||||
(*this)[2][0] * m[0][2] + (*this)[2][1] * m[1][2] + (*this)[2][2] * m[2][2]
|
||||
);
|
||||
}
|
||||
|
||||
/// @brief Add each element of the given matrix to the corresponding element of this matrix.
|
||||
__hostdev__ Mat3<T>& operator+=(const Mat3<T>& m) {
|
||||
(*this)[0][0] += m[0][0]; (*this)[0][1] += m[0][1]; (*this)[0][2] += m[0][2];
|
||||
(*this)[1][0] += m[1][0]; (*this)[1][1] += m[1][1]; (*this)[1][2] += m[1][2];
|
||||
(*this)[2][0] += m[2][0]; (*this)[2][1] += m[2][1]; (*this)[2][2] += m[2][2];
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief returns transpose of this
|
||||
__hostdev__ Mat3 transpose() const {
|
||||
return Mat3((*this)[0][0], (*this)[1][0], (*this)[2][0],
|
||||
(*this)[0][1], (*this)[1][1], (*this)[2][1],
|
||||
(*this)[0][2], (*this)[1][2], (*this)[2][2]);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Mat4 : public MatBase<T, 4, 4> {
|
||||
using Base = MatBase<T, 4, 4>;
|
||||
public:
|
||||
Mat4() = default;
|
||||
|
||||
/// @brief Constructor given individual array elements, the ordering is in row major form:
|
||||
/** @verbatim
|
||||
a b c d
|
||||
e f g h
|
||||
i j k l
|
||||
m n o p
|
||||
@endverbatim */
|
||||
template<typename Source>
|
||||
__hostdev__ Mat4(Source a, Source b, Source c, Source d,
|
||||
Source e, Source f, Source g, Source h,
|
||||
Source i, Source j, Source k, Source l,
|
||||
Source m, Source n, Source o, Source p)
|
||||
{
|
||||
this->mData[0] = a; this->mData[1] = b; this->mData[2] = c; this->mData[3] = d;
|
||||
this->mData[4] = e; this->mData[5] = f; this->mData[6] = g; this->mData[7] = h;
|
||||
this->mData[8] = i; this->mData[9] = j; this->mData[10] = k; this->mData[11] = l;
|
||||
this->mData[12] = m; this->mData[13] = n; this->mData[14] = o; this->mData[15] = p;
|
||||
}
|
||||
|
||||
/// @brief Constructor given array of elements, the ordering is in row major form
|
||||
template<typename Source>
|
||||
__hostdev__ Mat4(Source *a): Base(a) {}
|
||||
|
||||
/// @brief returns transpose of this
|
||||
__hostdev__ Mat4 transpose() const {
|
||||
return Mat4((*this)[0][0], (*this)[1][0], (*this)[2][0], (*this)[3][0],
|
||||
(*this)[0][1], (*this)[1][1], (*this)[2][1], (*this)[3][1],
|
||||
(*this)[0][2], (*this)[1][2], (*this)[2][2], (*this)[3][2],
|
||||
(*this)[0][3], (*this)[1][3], (*this)[2][3], (*this)[3][3]);
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Multiply a scalar by a 2x2 matrix, result is a 2x2 matrix
|
||||
template<typename T>
|
||||
__hostdev__ Mat2<T> operator*(const T& s, const Mat2<T>& m) {
|
||||
return Mat2<T>(m[0][0] * s, m[0][1] * s, m[1][0] * s, m[1][1] * s);
|
||||
}
|
||||
|
||||
/// @brief Multiply a 2x3 matrix by a 3x2 matrix, result is a 2x2 matrix
|
||||
template<typename T>
|
||||
__hostdev__ Mat2<T> operator*(const Mat2x3<T>& lhs, const Mat3x2<T>& rhs) {
|
||||
return Mat2<T>(
|
||||
// First row
|
||||
lhs[0][0] * rhs[0][0] + lhs[0][1] * rhs[1][0] + lhs[0][2] * rhs[2][0], // [0][0]
|
||||
lhs[0][0] * rhs[0][1] + lhs[0][1] * rhs[1][1] + lhs[0][2] * rhs[2][1], // [0][1]
|
||||
|
||||
// Second row
|
||||
lhs[1][0] * rhs[0][0] + lhs[1][1] * rhs[1][0] + lhs[1][2] * rhs[2][0], // [1][0]
|
||||
lhs[1][0] * rhs[0][1] + lhs[1][1] * rhs[1][1] + lhs[1][2] * rhs[2][1] // [1][1]
|
||||
);
|
||||
}
|
||||
/// @brief Multiply a 3x3 matrix by a 2x3 matrix, result is a 2x3 matrix
|
||||
template<typename T>
|
||||
__hostdev__ Mat2x3<T> operator*(const Mat3<T>& lhs, const Mat2x3<T>& rhs) {
|
||||
return Mat2x3<T>(
|
||||
// First row
|
||||
lhs[0][0] * rhs[0][0] + lhs[0][1] * rhs[1][0],
|
||||
lhs[0][0] * rhs[0][1] + lhs[0][1] * rhs[1][1],
|
||||
lhs[0][0] * rhs[0][2] + lhs[0][1] * rhs[1][2],
|
||||
|
||||
// Second row
|
||||
lhs[1][0] * rhs[0][0] + lhs[1][1] * rhs[1][0],
|
||||
lhs[1][0] * rhs[0][1] + lhs[1][1] * rhs[1][1],
|
||||
lhs[1][0] * rhs[0][2] + lhs[1][1] * rhs[1][2]
|
||||
);
|
||||
}
|
||||
/// @brief Multiply a 2x3 matrix by a 3x3 matrix, result is a 2x3 matrix
|
||||
template<typename T>
|
||||
__hostdev__ Mat2x3<T> operator*(const Mat2x3<T>& lhs, const Mat3<T>& rhs) {
|
||||
return Mat2x3<T>(
|
||||
// First row (3 elements)
|
||||
lhs[0][0] * rhs[0][0] + lhs[0][1] * rhs[1][0] + lhs[0][2] * rhs[2][0],
|
||||
lhs[0][0] * rhs[0][1] + lhs[0][1] * rhs[1][1] + lhs[0][2] * rhs[2][1],
|
||||
lhs[0][0] * rhs[0][2] + lhs[0][1] * rhs[1][2] + lhs[0][2] * rhs[2][2],
|
||||
|
||||
// Second row (3 elements)
|
||||
lhs[1][0] * rhs[0][0] + lhs[1][1] * rhs[1][0] + lhs[1][2] * rhs[2][0],
|
||||
lhs[1][0] * rhs[0][1] + lhs[1][1] * rhs[1][1] + lhs[1][2] * rhs[2][1],
|
||||
lhs[1][0] * rhs[0][2] + lhs[1][1] * rhs[1][2] + lhs[1][2] * rhs[2][2]
|
||||
);
|
||||
}
|
||||
/// @brief Multiply a 3x2 matrix by a 2x2 matrix, result is a 3x2 matrix
|
||||
template<typename T>
|
||||
__hostdev__ Mat3x2<T> operator*(const Mat3x2<T>& lhs, const Mat2<T>& rhs) {
|
||||
return Mat3x2<T>(
|
||||
lhs[0][0] * rhs[0][0] + lhs[0][1] * rhs[1][0],
|
||||
lhs[0][0] * rhs[0][1] + lhs[0][1] * rhs[1][1],
|
||||
lhs[1][0] * rhs[0][0] + lhs[1][1] * rhs[1][0],
|
||||
lhs[1][0] * rhs[0][1] + lhs[1][1] * rhs[1][1],
|
||||
lhs[2][0] * rhs[0][0] + lhs[2][1] * rhs[1][0],
|
||||
lhs[2][0] * rhs[0][1] + lhs[2][1] * rhs[1][1]
|
||||
);
|
||||
}
|
||||
/// @brief Multiply a 2x2 matrix by a 2x3 matrix, result is a 2x3 matrix
|
||||
template<typename T>
|
||||
__hostdev__ Mat2x3<T> operator*(const Mat2<T>& lhs, const Mat2x3<T>& rhs) {
|
||||
return Mat2x3<T>(
|
||||
// First row (3 elements)
|
||||
lhs[0][0] * rhs[0][0] + lhs[0][1] * rhs[1][0],
|
||||
lhs[0][0] * rhs[0][1] + lhs[0][1] * rhs[1][1],
|
||||
lhs[0][0] * rhs[0][2] + lhs[0][1] * rhs[1][2],
|
||||
|
||||
// Second row (3 elements)
|
||||
lhs[1][0] * rhs[0][0] + lhs[1][1] * rhs[1][0],
|
||||
lhs[1][0] * rhs[0][1] + lhs[1][1] * rhs[1][1],
|
||||
lhs[1][0] * rhs[0][2] + lhs[1][1] * rhs[1][2]
|
||||
);
|
||||
}
|
||||
/// @brief Multiply a 3x2 matrix by a 2x3 matrix, result is a 3x3 matrix
|
||||
template<typename T>
|
||||
__hostdev__ Mat3<T> operator*(const Mat3x2<T>& lhs, const Mat2x3<T>& rhs) {
|
||||
return Mat3<T>(
|
||||
lhs[0][0] * rhs[0][0] + lhs[0][1] * rhs[1][0],
|
||||
lhs[0][0] * rhs[0][1] + lhs[0][1] * rhs[1][1],
|
||||
lhs[0][0] * rhs[0][2] + lhs[0][1] * rhs[1][2],
|
||||
|
||||
lhs[1][0] * rhs[0][0] + lhs[1][1] * rhs[1][0],
|
||||
lhs[1][0] * rhs[0][1] + lhs[1][1] * rhs[1][1],
|
||||
lhs[1][0] * rhs[0][2] + lhs[1][1] * rhs[1][2],
|
||||
|
||||
lhs[2][0] * rhs[0][0] + lhs[2][1] * rhs[1][0],
|
||||
lhs[2][0] * rhs[0][1] + lhs[2][1] * rhs[1][1],
|
||||
lhs[2][0] * rhs[0][2] + lhs[2][1] * rhs[1][2]
|
||||
);
|
||||
}
|
||||
// ----------------------------> Vec3 <--------------------------------------
|
||||
|
||||
/// @brief A simple vector class with three components, similar to openvdb::math::Vec3
|
||||
@@ -634,6 +1412,14 @@ public:
|
||||
mVec[2] * v[0] - mVec[0] * v[2],
|
||||
mVec[0] * v[1] - mVec[1] * v[0]);
|
||||
}
|
||||
/// @brief Outer product of a 3x1 vector and a 1x3 vector, result is a 3x3 matrix
|
||||
template<typename Vec3T>
|
||||
__hostdev__ Mat3<ValueType> outer(const Vec3T& v) const
|
||||
{
|
||||
return Mat3<ValueType>(mVec[0] * v[0], mVec[0] * v[1], mVec[0] * v[2],
|
||||
mVec[1] * v[0], mVec[1] * v[1], mVec[1] * v[2],
|
||||
mVec[2] * v[0], mVec[2] * v[1], mVec[2] * v[2]);
|
||||
}
|
||||
__hostdev__ T lengthSqr() const
|
||||
{
|
||||
return mVec[0] * mVec[0] + mVec[1] * mVec[1] + mVec[2] * mVec[2]; // 5 flops
|
||||
@@ -892,6 +1678,19 @@ __hostdev__ inline Vec4<T2> operator/(T1 scalar, const Vec4<T2>& vec)
|
||||
{
|
||||
return Vec4<T2>(scalar / vec[0], scalar / vec[1], scalar / vec[2], scalar / vec[3]);
|
||||
}
|
||||
/// @brief Return the matrix vector product of a 4x4 matrix and a 4d vector
|
||||
/// @param m 4x4 matrix
|
||||
/// @param v 4d vector
|
||||
/// @return result of matrix-vector multiplication, i.e. m x v
|
||||
template <typename T>
|
||||
__hostdev__ inline Vec4<T> operator*(const Mat4<T>& m, const Vec4<T>& v) {
|
||||
return Vec4<T>(
|
||||
m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2] + m[0][3] * v[3],
|
||||
m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2] + m[1][3] * v[3],
|
||||
m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2] + m[2][3] * v[3],
|
||||
m[3][0] * v[0] + m[3][1] * v[1] + m[3][2] * v[2] + m[3][3] * v[3]
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------> matMult <--------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,781 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/putil/IO.h
|
||||
|
||||
\author Andrew Reidmeyer
|
||||
|
||||
\brief This file contains IO utilities for PNanoVDB.
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_PUTILS_IO_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_PUTILS_IO_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__CUDACC__)
|
||||
#define PNANOVDB_IO_INLINE static __host__ __device__
|
||||
#elif defined(_WIN32)
|
||||
#define PNANOVDB_IO_INLINE static inline
|
||||
#else
|
||||
#define PNANOVDB_IO_INLINE static inline
|
||||
#endif
|
||||
|
||||
// needs to match NANOVDB_DATA_ALIGNMENT from nanovdb/NanoVDB.h
|
||||
#define PNANOVDB_DATA_ALIGNMENT 32
|
||||
PNANOVDB_IO_INLINE void* pnanovdb_aligned_malloc(size_t alignment, size_t size)
|
||||
{
|
||||
size_t aligned_size = size + alignment + sizeof(void*);
|
||||
void* alloc_ptr = malloc(aligned_size);
|
||||
void* ptr = 0;
|
||||
if (alloc_ptr)
|
||||
{
|
||||
ptr = (void*)(alignment * (((size_t)alloc_ptr + sizeof(void*) + alignment - 1u) / alignment));
|
||||
void** header_ptr = (void**)((size_t)ptr - sizeof(void*));
|
||||
*header_ptr = alloc_ptr;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
PNANOVDB_IO_INLINE void pnanovdb_aligned_free(void* ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
void** header_ptr = (void**)((size_t)ptr - sizeof(void*));
|
||||
free(*header_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// abstraction for file-like read/write
|
||||
typedef struct pnanovdb_io_buf_t
|
||||
{
|
||||
void* file;
|
||||
void(*read)(void* file, uint64_t offset, void* dst_data, uint64_t size);
|
||||
void(*write)(void* file, uint64_t offset, void* src_data, uint64_t size);
|
||||
}pnanovdb_io_buf_t;
|
||||
|
||||
// implementation for fread/fwrite
|
||||
PNANOVDB_IO_INLINE void pnanovdb_io_buf_fread(void* file, uint64_t offset, void* dst_data, uint64_t size)
|
||||
{
|
||||
fseek((FILE*)file, offset, SEEK_SET);
|
||||
fread(dst_data, size, 1u, (FILE*)file);
|
||||
}
|
||||
PNANOVDB_IO_INLINE void pnanovdb_io_buf_fwrite(void* file, uint64_t offset, void* src_data, uint64_t size)
|
||||
{
|
||||
fseek((FILE*)file, offset, SEEK_SET);
|
||||
fwrite(src_data, size, 1u, (FILE*)file);
|
||||
}
|
||||
|
||||
// implementation for dynamic buffer
|
||||
typedef struct pnanovdb_io_dynamic_buf_t
|
||||
{
|
||||
void* data;
|
||||
uint64_t size;
|
||||
uint64_t capacity;
|
||||
}pnanovdb_io_dynamic_buf_t;
|
||||
PNANOVDB_IO_INLINE void pnanovdb_io_dynamic_buf_read(void* file, uint64_t offset, void* dst_data, uint64_t size)
|
||||
{
|
||||
pnanovdb_io_dynamic_buf_t* ptr = (pnanovdb_io_dynamic_buf_t*)file;
|
||||
uint8_t* dst8 = (uint8_t*)dst_data;
|
||||
uint8_t* src8 = (uint8_t*)ptr->data;
|
||||
uint64_t size_in_bounds = size;
|
||||
if (offset >= ptr->size)
|
||||
{
|
||||
size_in_bounds = 0;
|
||||
}
|
||||
else if (offset + size > ptr->size)
|
||||
{
|
||||
size_in_bounds = ptr->size - offset;
|
||||
}
|
||||
memcpy(dst8, src8 + offset, size_in_bounds);
|
||||
memset(dst8 + size_in_bounds, 0, size - size_in_bounds);
|
||||
}
|
||||
PNANOVDB_IO_INLINE void pnanovdb_io_dynamic_buf_write(void* file, uint64_t offset, void* src_data, uint64_t size)
|
||||
{
|
||||
pnanovdb_io_dynamic_buf_t* ptr = (pnanovdb_io_dynamic_buf_t*)file;
|
||||
// grow capacity as needed
|
||||
if (offset + size > ptr->capacity)
|
||||
{
|
||||
uint64_t new_capacity = 1024u;
|
||||
while (new_capacity < offset + size)
|
||||
{
|
||||
new_capacity = (new_capacity << 1u) - (new_capacity >> 1u);
|
||||
}
|
||||
void* new_data = pnanovdb_aligned_malloc(PNANOVDB_DATA_ALIGNMENT, new_capacity);
|
||||
memcpy(new_data, ptr->data, ptr->size);
|
||||
if (ptr->capacity != 0u)
|
||||
{
|
||||
pnanovdb_aligned_free(ptr->data);
|
||||
}
|
||||
ptr->data = new_data;
|
||||
ptr->capacity = new_capacity;
|
||||
}
|
||||
// expand size and zero initialize as needed
|
||||
if (offset + size > ptr->size)
|
||||
{
|
||||
uint8_t* data8 = (uint8_t*)ptr->data;
|
||||
memset(data8 + ptr->size, 0, offset + size - ptr->size);
|
||||
ptr->size = offset + size;
|
||||
}
|
||||
// write data
|
||||
uint8_t* src8 = (uint8_t*)src_data;
|
||||
uint8_t* dst8 = (uint8_t*)ptr->data;
|
||||
memcpy(dst8 + offset, src8, size);
|
||||
}
|
||||
|
||||
#if defined(PNANOVDB_IO_BUF_CUSTOM)
|
||||
#define PNANOVDB_BUF_CUSTOM
|
||||
|
||||
#define pnanovdb_buf_t pnanovdb_io_buf_t*
|
||||
PNANOVDB_IO_INLINE uint32_t pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint64_t byte_offset)
|
||||
{
|
||||
uint32_t ret = 0u;
|
||||
buf->read(buf->file, byte_offset, &ret, 4u);
|
||||
return ret;
|
||||
}
|
||||
PNANOVDB_IO_INLINE uint64_t pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint64_t byte_offset)
|
||||
{
|
||||
uint64_t ret = 0u;
|
||||
buf->read(buf->file, byte_offset, &ret, 8u);
|
||||
return ret;
|
||||
}
|
||||
PNANOVDB_IO_INLINE void pnanovdb_buf_write_uint32(pnanovdb_buf_t buf, uint64_t byte_offset, uint32_t value)
|
||||
{
|
||||
buf->write(buf->file, byte_offset, &value, 4u);
|
||||
}
|
||||
PNANOVDB_IO_INLINE void pnanovdb_buf_write_uint64(pnanovdb_buf_t buf, uint64_t byte_offset, uint64_t value)
|
||||
{
|
||||
buf->write(buf->file, byte_offset, &value, 8u);
|
||||
}
|
||||
#define pnanovdb_grid_type_t uint32_t
|
||||
#define PNANOVDB_GRID_TYPE_GET(grid_typeIn, nameIn) pnanovdb_grid_type_constants[grid_typeIn].nameIn
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef PNANOVDB_C
|
||||
#define PNANOVDB_C
|
||||
#endif
|
||||
#include <nanovdb/PNanoVDB.h>
|
||||
|
||||
typedef struct pnanovdb_io_fileheader_t
|
||||
{
|
||||
pnanovdb_uint64_t magic; // 8 bytes, 0
|
||||
pnanovdb_uint32_t version; // 4 bytes, 8
|
||||
pnanovdb_uint32_t grid_count_and_codec; // 4 bytes, 12
|
||||
}pnanovdb_io_fileheader_t;
|
||||
|
||||
#define PNANOVDB_IO_FILEHEADER_SIZE 16
|
||||
|
||||
typedef struct pnanovdb_io_filemetadata_t
|
||||
{
|
||||
pnanovdb_uint64_t grid_size; // 8 bytes, 0
|
||||
pnanovdb_uint64_t file_size; // 8 bytes, 8
|
||||
pnanovdb_uint64_t name_key; // 8 bytes, 16
|
||||
pnanovdb_uint64_t voxel_count; // 8 bytes, 24
|
||||
pnanovdb_uint32_t grid_type; // 4 bytes, 32
|
||||
pnanovdb_uint32_t grid_class; // 4 bytes, 36
|
||||
double world_bbox[6u]; // 48 bytes, 40
|
||||
pnanovdb_coord_t index_bbox_min; // 12 bytes, 88
|
||||
pnanovdb_coord_t index_bbox_max; // 12 bytes, 100
|
||||
double voxel_size[3u]; // 24 bytes, 112
|
||||
pnanovdb_uint32_t name_size; // 4 bytes, 136
|
||||
pnanovdb_uint32_t node_count_leaf; // 4 bytes, 140
|
||||
pnanovdb_uint32_t node_count_lower; // 4 bytes, 144
|
||||
pnanovdb_uint32_t node_count_upper; // 4 bytes, 148
|
||||
pnanovdb_uint32_t node_count_root; // 4 bytes, 152
|
||||
pnanovdb_uint32_t tile_count_lower; // 4 bytes, 156
|
||||
pnanovdb_uint32_t tile_count_upper; // 4 bytes, 160
|
||||
pnanovdb_uint32_t tile_count_root; // 4 bytes, 164
|
||||
pnanovdb_uint32_t codec_and_padding; // 4 bytes, 168
|
||||
pnanovdb_uint32_t version; // 4 bytes, 172
|
||||
}pnanovdb_io_filemetadata_t;
|
||||
|
||||
#define PNANOVDB_IO_FILEMETADATA_SIZE 176
|
||||
|
||||
#if defined(PNANOVDB_IO_BUF_CUSTOM)
|
||||
|
||||
PNANOVDB_IO_INLINE pnanovdb_grid_handle_t pnanovdb_io_fread_fileheader(
|
||||
pnanovdb_buf_t file,
|
||||
pnanovdb_io_fileheader_t* dst_header,
|
||||
pnanovdb_io_filemetadata_t* dst_metadatas,
|
||||
pnanovdb_uint64_t dst_metadata_count)
|
||||
{
|
||||
pnanovdb_grid_t local_grid = {};
|
||||
file->read(file->file, 0u, &local_grid, PNANOVDB_GRID_SIZE);
|
||||
pnanovdb_bool_t is_raw_grid = PNANOVDB_FALSE;
|
||||
if (local_grid.magic == PNANOVDB_MAGIC_GRID ||
|
||||
local_grid.data2 == PNANOVDB_MAGIC_GRID)
|
||||
{
|
||||
is_raw_grid = PNANOVDB_TRUE;
|
||||
}
|
||||
if (is_raw_grid)
|
||||
{
|
||||
pnanovdb_grid_handle_t raw_grid = {pnanovdb_address_null()};
|
||||
return raw_grid;
|
||||
}
|
||||
|
||||
pnanovdb_uint64_t offset = 0u;
|
||||
pnanovdb_io_fileheader_t local_header = {};
|
||||
file->read(file->file, offset, &local_header, PNANOVDB_IO_FILEHEADER_SIZE);
|
||||
if (dst_header)
|
||||
{
|
||||
*dst_header = local_header;
|
||||
}
|
||||
offset += PNANOVDB_IO_FILEHEADER_SIZE;
|
||||
pnanovdb_uint32_t grid_count = local_header.grid_count_and_codec & 0xFFFF;
|
||||
for (uint32_t idx = 0u; idx < grid_count; idx++)
|
||||
{
|
||||
pnanovdb_io_filemetadata_t local_metadata = {};
|
||||
file->read(file->file, offset, &local_metadata, PNANOVDB_IO_FILEMETADATA_SIZE);
|
||||
if (idx < dst_metadata_count)
|
||||
{
|
||||
dst_metadatas[idx] = local_metadata;
|
||||
}
|
||||
offset += PNANOVDB_IO_FILEMETADATA_SIZE;
|
||||
// skip past name
|
||||
offset += local_metadata.name_size;
|
||||
}
|
||||
pnanovdb_grid_handle_t first_grid = {pnanovdb_address_offset64(pnanovdb_address_null(), offset)};
|
||||
return first_grid;
|
||||
}
|
||||
|
||||
PNANOVDB_IO_INLINE void pnanovdb_io_memcpy(
|
||||
pnanovdb_buf_t dst_buf, pnanovdb_address_t dst_addr,
|
||||
pnanovdb_buf_t src_buf, pnanovdb_address_t src_addr,
|
||||
pnanovdb_uint64_t num_bytes)
|
||||
{
|
||||
pnanovdb_uint64_t num_words = (num_bytes >> 2u);
|
||||
for (pnanovdb_uint64_t word_idx = 0u; word_idx < num_words; word_idx++)
|
||||
{
|
||||
pnanovdb_uint32_t val = pnanovdb_read_uint32(src_buf, pnanovdb_address_offset_product(src_addr, word_idx, 4u));
|
||||
pnanovdb_write_uint32(dst_buf, pnanovdb_address_offset_product(dst_addr, word_idx, 4u), val);
|
||||
}
|
||||
}
|
||||
|
||||
PNANOVDB_IO_INLINE pnanovdb_uint32_t pnanovdb_io_get_leaf_size(pnanovdb_uint32_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf)
|
||||
{
|
||||
pnanovdb_uint32_t size = PNANOVDB_GRID_TYPE_GET(grid_type, leaf_size);
|
||||
if (grid_type == PNANOVDB_GRID_TYPE_FPN)
|
||||
{
|
||||
pnanovdb_uint32_t flags = pnanovdb_leaf_get_bbox_dif_and_flags(buf, leaf) >> 24u;
|
||||
pnanovdb_uint32_t value_log_bits = flags >> 5;
|
||||
pnanovdb_uint32_t value_bits = 1u << value_log_bits;
|
||||
pnanovdb_uint32_t table_bytes = value_bits << (9u - 3u);
|
||||
size += table_bytes;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
PNANOVDB_IO_INLINE void pnanovdb_io_copy_subregion(
|
||||
pnanovdb_buf_t src_buf,
|
||||
pnanovdb_grid_handle_t src_grid,
|
||||
pnanovdb_buf_t dst_buf,
|
||||
pnanovdb_grid_handle_t dst_grid,
|
||||
pnanovdb_coord_t ijk_min,
|
||||
pnanovdb_coord_t ijk_max)
|
||||
{
|
||||
// leaf precise, not voxel precise for now
|
||||
ijk_min.x &= ~7;
|
||||
ijk_min.y &= ~7;
|
||||
ijk_min.z &= ~7;
|
||||
ijk_max.x |= 7;
|
||||
ijk_max.y |= 7;
|
||||
ijk_max.z |= 7;
|
||||
|
||||
pnanovdb_buf_t buf = src_buf;
|
||||
pnanovdb_grid_handle_t grid = src_grid;
|
||||
|
||||
// copy grid
|
||||
pnanovdb_io_memcpy(dst_buf, dst_grid.address, buf, grid.address, PNANOVDB_GRID_SIZE);
|
||||
|
||||
pnanovdb_grid_type_t grid_type = pnanovdb_grid_get_grid_type(buf, grid);
|
||||
|
||||
// copy tree
|
||||
pnanovdb_tree_handle_t tree = pnanovdb_grid_get_tree(buf, grid);
|
||||
pnanovdb_tree_handle_t dst_tree = pnanovdb_grid_get_tree(dst_buf, dst_grid);
|
||||
pnanovdb_io_memcpy(dst_buf, dst_tree.address, buf, tree.address, PNANOVDB_TREE_SIZE);
|
||||
|
||||
// copy root
|
||||
pnanovdb_root_handle_t root = pnanovdb_tree_get_root(buf, tree);
|
||||
pnanovdb_root_handle_t dst_root = pnanovdb_tree_get_root(dst_buf, dst_tree);
|
||||
pnanovdb_io_memcpy(dst_buf, dst_root.address, buf, root.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_size));
|
||||
|
||||
// set new root to clipped bounds
|
||||
pnanovdb_root_set_bbox_min(dst_buf, dst_root, &ijk_min);
|
||||
pnanovdb_root_set_bbox_max(dst_buf, dst_root, &ijk_max);
|
||||
|
||||
// copy root tiles
|
||||
pnanovdb_uint32_t tile_count = pnanovdb_root_get_tile_count(buf, root);
|
||||
for (pnanovdb_uint32_t tile_idx = 0u; tile_idx < tile_count; tile_idx++)
|
||||
{
|
||||
pnanovdb_root_tile_handle_t root_tile = pnanovdb_root_get_tile(grid_type, root, tile_idx);
|
||||
pnanovdb_root_tile_handle_t dst_root_tile = pnanovdb_root_get_tile(grid_type, dst_root, tile_idx);
|
||||
pnanovdb_io_memcpy(dst_buf, dst_root_tile.address, buf, root_tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_tile_size));
|
||||
}
|
||||
|
||||
// copy upper
|
||||
for (pnanovdb_uint32_t tile_idx = 0u; tile_idx < tile_count; tile_idx++)
|
||||
{
|
||||
pnanovdb_root_tile_handle_t root_tile = pnanovdb_root_get_tile(grid_type, root, tile_idx);
|
||||
pnanovdb_root_tile_handle_t dst_root_tile = pnanovdb_root_get_tile(grid_type, dst_root, tile_idx);
|
||||
if (pnanovdb_root_tile_get_child_mask(buf, root_tile))
|
||||
{
|
||||
pnanovdb_upper_handle_t upper = pnanovdb_root_get_child(grid_type, buf, root, root_tile);
|
||||
pnanovdb_upper_handle_t dst_upper = pnanovdb_root_get_child(grid_type, dst_buf, dst_root, dst_root_tile);
|
||||
pnanovdb_io_memcpy(dst_buf, dst_upper.address, buf, upper.address, PNANOVDB_GRID_TYPE_GET(grid_type, upper_size));
|
||||
}
|
||||
}
|
||||
|
||||
// copy lower
|
||||
for (pnanovdb_uint32_t tile_idx = 0u; tile_idx < tile_count; tile_idx++)
|
||||
{
|
||||
pnanovdb_root_tile_handle_t root_tile = pnanovdb_root_get_tile(grid_type, root, tile_idx);
|
||||
pnanovdb_root_tile_handle_t dst_root_tile = pnanovdb_root_get_tile(grid_type, dst_root, tile_idx);
|
||||
if (pnanovdb_root_tile_get_child_mask(buf, root_tile))
|
||||
{
|
||||
pnanovdb_upper_handle_t upper = pnanovdb_root_get_child(grid_type, buf, root, root_tile);
|
||||
pnanovdb_upper_handle_t dst_upper = pnanovdb_root_get_child(grid_type, dst_buf, dst_root, dst_root_tile);
|
||||
for (pnanovdb_uint32_t upper_n = 0u; upper_n < PNANOVDB_UPPER_TABLE_COUNT; upper_n++)
|
||||
{
|
||||
if (pnanovdb_upper_get_child_mask(buf, upper, upper_n))
|
||||
{
|
||||
pnanovdb_lower_handle_t lower = pnanovdb_upper_get_child(grid_type, buf, upper, upper_n);
|
||||
pnanovdb_lower_handle_t dst_lower = pnanovdb_upper_get_child(grid_type, dst_buf, dst_upper, upper_n);
|
||||
pnanovdb_io_memcpy(dst_buf, dst_lower.address, buf, lower.address, PNANOVDB_GRID_TYPE_GET(grid_type, lower_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy leaf
|
||||
pnanovdb_uint32_t dst_node_count_leaf = 0u;
|
||||
pnanovdb_leaf_handle_t dst_current_leaf = {
|
||||
pnanovdb_address_offset(dst_tree.address, pnanovdb_tree_get_node_offset_leaf(dst_buf, dst_tree))};
|
||||
for (pnanovdb_uint32_t tile_idx = 0u; tile_idx < tile_count; tile_idx++)
|
||||
{
|
||||
pnanovdb_root_tile_handle_t root_tile = pnanovdb_root_get_tile(grid_type, root, tile_idx);
|
||||
pnanovdb_root_tile_handle_t dst_root_tile = pnanovdb_root_get_tile(grid_type, dst_root, tile_idx);
|
||||
if (pnanovdb_root_tile_get_child_mask(buf, root_tile))
|
||||
{
|
||||
pnanovdb_upper_handle_t upper = pnanovdb_root_get_child(grid_type, buf, root, root_tile);
|
||||
pnanovdb_upper_handle_t dst_upper = pnanovdb_root_get_child(grid_type, dst_buf, dst_root, dst_root_tile);
|
||||
for (pnanovdb_uint32_t upper_n = 0u; upper_n < PNANOVDB_UPPER_TABLE_COUNT; upper_n++)
|
||||
{
|
||||
if (pnanovdb_upper_get_child_mask(buf, upper, upper_n))
|
||||
{
|
||||
pnanovdb_lower_handle_t lower = pnanovdb_upper_get_child(grid_type, buf, upper, upper_n);
|
||||
pnanovdb_lower_handle_t dst_lower = pnanovdb_upper_get_child(grid_type, dst_buf, dst_upper, upper_n);
|
||||
for (pnanovdb_uint32_t lower_n = 0u; lower_n < PNANOVDB_LOWER_TABLE_COUNT; lower_n++)
|
||||
{
|
||||
if (pnanovdb_lower_get_child_mask(buf, lower, lower_n))
|
||||
{
|
||||
pnanovdb_leaf_handle_t leaf = pnanovdb_lower_get_child(grid_type, buf, lower, lower_n);
|
||||
|
||||
pnanovdb_coord_t leaf_ijk_min = pnanovdb_leaf_get_bbox_min(buf, leaf);
|
||||
pnanovdb_uint32_t dif_and_flags = pnanovdb_leaf_get_bbox_dif_and_flags(buf, leaf);
|
||||
pnanovdb_coord_t leaf_ijk_max = {
|
||||
pnanovdb_uint32_as_int32(dif_and_flags & 255u) + leaf_ijk_min.x,
|
||||
pnanovdb_uint32_as_int32((dif_and_flags >> 8u) & 255u) + leaf_ijk_min.y,
|
||||
pnanovdb_uint32_as_int32((dif_and_flags >> 16u) & 255u) + leaf_ijk_min.z
|
||||
};
|
||||
|
||||
// leaf precise, not voxel precise for now
|
||||
leaf_ijk_min.x &= ~7;
|
||||
leaf_ijk_min.y &= ~7;
|
||||
leaf_ijk_min.z &= ~7;
|
||||
leaf_ijk_max.x |= 7;
|
||||
leaf_ijk_max.y |= 7;
|
||||
leaf_ijk_max.z |= 7;
|
||||
|
||||
pnanovdb_bool_t pred = !(
|
||||
leaf_ijk_max.x < ijk_min.x || leaf_ijk_min.x > ijk_max.x ||
|
||||
leaf_ijk_max.y < ijk_min.y || leaf_ijk_min.y > ijk_max.y ||
|
||||
leaf_ijk_max.z < ijk_min.z || leaf_ijk_min.z > ijk_max.z );
|
||||
if (pred)
|
||||
{
|
||||
pnanovdb_uint32_t leaf_size = pnanovdb_io_get_leaf_size(grid_type, buf, leaf);
|
||||
pnanovdb_io_memcpy(dst_buf, dst_current_leaf.address, buf, leaf.address, leaf_size);
|
||||
|
||||
pnanovdb_lower_set_child(grid_type, dst_buf, dst_lower, lower_n, dst_current_leaf);
|
||||
|
||||
// voxel precise
|
||||
#if 0
|
||||
for (pnanovdb_uint32_t leaf_n = 0u; leaf_n < PNANOVDB_LEAF_TABLE_COUNT; leaf_n++)
|
||||
{
|
||||
pnanovdb_coord_t voxel_ijk = {
|
||||
(leaf_ijk_min.x & ~7) + (pnanovdb_uint32_as_int32(leaf_n >> 6u) & 7),
|
||||
(leaf_ijk_min.y & ~7) + (pnanovdb_uint32_as_int32(leaf_n >> 3u) & 7),
|
||||
(leaf_ijk_min.z & ~7) + (pnanovdb_uint32_as_int32(leaf_n >> 0u) & 7)
|
||||
};
|
||||
|
||||
pnanovdb_bool_t voxel_pred = !(
|
||||
voxel_ijk.x < ijk_min.x || voxel_ijk.x > ijk_max.x ||
|
||||
voxel_ijk.y < ijk_min.y || voxel_ijk.y > ijk_max.y ||
|
||||
voxel_ijk.z < ijk_min.z || voxel_ijk.z > ijk_max.z );
|
||||
if (!voxel_pred)
|
||||
{
|
||||
pnanovdb_leaf_set_value_mask(dst_buf, dst_current_leaf, leaf_n, PNANOVDB_FALSE);
|
||||
pnanovdb_address_t val_addr =
|
||||
pnanovdb_leaf_get_table_address(grid_type, dst_buf, dst_current_leaf, leaf_n);
|
||||
pnanovdb_address_t background_addr = pnanovdb_root_get_background_address(grid_type, buf, root);
|
||||
|
||||
pnanovdb_io_memcpy(dst_buf, val_addr, buf, background_addr, (PNANOVDB_GRID_TYPE_GET(grid_type, value_stride_bits) >> 3u));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
dst_current_leaf.address = pnanovdb_address_offset(dst_current_leaf.address, leaf_size);
|
||||
dst_node_count_leaf++;
|
||||
}
|
||||
else
|
||||
{
|
||||
pnanovdb_address_t background_addr = pnanovdb_root_get_background_address(grid_type, buf, root);
|
||||
pnanovdb_uint64_t background_index = pnanovdb_read_uint64(buf, background_addr);
|
||||
pnanovdb_lower_set_table_child(grid_type, dst_buf, dst_lower, lower_n, pnanovdb_uint64_as_int64(background_index));
|
||||
pnanovdb_lower_set_child_mask(dst_buf, dst_lower, lower_n, PNANOVDB_FALSE);
|
||||
pnanovdb_lower_set_value_mask(dst_buf, dst_lower, lower_n, PNANOVDB_FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//pnanovdb_grid_set_blind_metadata_count(dst_buf, dst_grid, 0u);
|
||||
|
||||
// need to update tree for new reduced leaf range
|
||||
pnanovdb_tree_set_node_count_leaf(dst_buf, dst_tree, dst_node_count_leaf);
|
||||
|
||||
pnanovdb_uint32_t blindmetadata_count = pnanovdb_grid_get_blind_metadata_count(buf, grid);
|
||||
|
||||
pnanovdb_uint64_t dst_blind_metadata_offset =
|
||||
dst_current_leaf.address.byte_offset -
|
||||
dst_grid.address.byte_offset;
|
||||
pnanovdb_grid_set_blind_metadata_offset(dst_buf, dst_grid, dst_blind_metadata_offset);
|
||||
|
||||
// copy blindmetadata headers
|
||||
for (pnanovdb_uint32_t metadata_idx = 0u; metadata_idx < blindmetadata_count; metadata_idx++)
|
||||
{
|
||||
pnanovdb_gridblindmetadata_handle_t metadata = pnanovdb_grid_get_gridblindmetadata(buf, grid, metadata_idx);
|
||||
pnanovdb_gridblindmetadata_handle_t dst_metadata = pnanovdb_grid_get_gridblindmetadata(dst_buf, dst_grid, metadata_idx);
|
||||
|
||||
pnanovdb_io_memcpy(dst_buf, dst_metadata.address, buf, metadata.address, PNANOVDB_GRIDBLINDMETADATA_SIZE);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// copy blindmetadata
|
||||
for (pnanovdb_uint32_t metadata_idx = 0u; metadata_idx < blindmetadata_count; metadata_idx++)
|
||||
{
|
||||
pnanovdb_gridblindmetadata_handle_t metadata = pnanovdb_grid_get_gridblindmetadata(buf, grid, metadata_idx);
|
||||
pnanovdb_gridblindmetadata_handle_t dst_metadata = pnanovdb_grid_get_gridblindmetadata(dst_buf, dst_grid, metadata_idx);
|
||||
|
||||
pnanovdb_int64_t byte_offset = pnanovdb_gridblindmetadata_get_data_offset(buf, metadata);
|
||||
pnanovdb_address_t address = pnanovdb_address_offset64(metadata.address, pnanovdb_int64_as_uint64(byte_offset));
|
||||
|
||||
pnanovdb_int64_t dst_byte_offset = pnanovdb_gridblindmetadata_get_data_offset(dst_buf, dst_metadata);
|
||||
pnanovdb_address_t dst_address = pnanovdb_address_offset64(dst_metadata.address, pnanovdb_int64_as_uint64(dst_byte_offset));
|
||||
|
||||
pnanovdb_uint32_t value_size = pnanovdb_gridblindmetadata_get_value_size(buf, metadata);
|
||||
pnanovdb_uint64_t value_count = pnanovdb_gridblindmetadata_get_value_count(buf, metadata);
|
||||
|
||||
pnanovdb_io_memcpy(dst_buf, dst_address, buf, address, value_count * value_size);
|
||||
}
|
||||
#else
|
||||
for (pnanovdb_uint32_t metadata_idx = 0u; metadata_idx < blindmetadata_count; metadata_idx++)
|
||||
{
|
||||
pnanovdb_gridblindmetadata_handle_t metadata = pnanovdb_grid_get_gridblindmetadata(buf, grid, metadata_idx);
|
||||
pnanovdb_gridblindmetadata_handle_t dst_metadata = pnanovdb_grid_get_gridblindmetadata(dst_buf, dst_grid, metadata_idx);
|
||||
|
||||
pnanovdb_int64_t byte_offset = pnanovdb_gridblindmetadata_get_data_offset(buf, metadata);
|
||||
pnanovdb_address_t address = pnanovdb_address_offset64(metadata.address, pnanovdb_int64_as_uint64(byte_offset));
|
||||
|
||||
pnanovdb_int64_t dst_byte_offset = pnanovdb_gridblindmetadata_get_data_offset(dst_buf, dst_metadata);
|
||||
pnanovdb_address_t dst_address = pnanovdb_address_offset64(dst_metadata.address, pnanovdb_int64_as_uint64(dst_byte_offset));
|
||||
|
||||
pnanovdb_uint32_t value_size = pnanovdb_gridblindmetadata_get_value_size(buf, metadata);
|
||||
pnanovdb_uint64_t value_count = pnanovdb_gridblindmetadata_get_value_count(buf, metadata);
|
||||
|
||||
pnanovdb_uint64_t dst_value_index = 0llu;
|
||||
pnanovdb_uint64_t value_index = 0llu;
|
||||
|
||||
pnanovdb_address_t index_addr = pnanovdb_root_get_background_address(grid_type, buf, root);
|
||||
value_index = pnanovdb_read_uint64(buf, index_addr);
|
||||
|
||||
pnanovdb_address_t dst_index_addr = pnanovdb_root_get_background_address(grid_type, dst_buf, dst_root);
|
||||
pnanovdb_write_uint64(dst_buf, dst_index_addr, dst_value_index);
|
||||
|
||||
pnanovdb_uint64_t last_root_tile_index = value_index;
|
||||
pnanovdb_uint64_t last_upper_tile_index = value_index;
|
||||
pnanovdb_uint64_t last_lower_tile_index = value_index;
|
||||
pnanovdb_uint64_t last_root_tile_dst_index = dst_value_index;
|
||||
pnanovdb_uint64_t last_upper_tile_dst_index = dst_value_index;
|
||||
pnanovdb_uint64_t last_lower_tile_dst_index = dst_value_index;
|
||||
|
||||
pnanovdb_io_memcpy(
|
||||
dst_buf, pnanovdb_address_offset64_product(dst_address, dst_value_index, value_size),
|
||||
buf, pnanovdb_address_offset64_product(address, value_index, value_size), value_size);
|
||||
dst_value_index++;
|
||||
|
||||
// walk subregion, copy referenced blindmetadata
|
||||
pnanovdb_uint32_t dst_tile_count = pnanovdb_root_get_tile_count(dst_buf, dst_root);
|
||||
for (pnanovdb_uint32_t tile_idx = 0u; tile_idx < dst_tile_count; tile_idx++)
|
||||
{
|
||||
pnanovdb_root_tile_handle_t dst_root_tile = pnanovdb_root_get_tile(grid_type, dst_root, tile_idx);
|
||||
pnanovdb_root_tile_handle_t root_tile = pnanovdb_root_get_tile(grid_type, root, tile_idx);
|
||||
|
||||
if (pnanovdb_root_tile_get_child_mask(dst_buf, dst_root_tile))
|
||||
{
|
||||
pnanovdb_upper_handle_t dst_upper = pnanovdb_root_get_child(grid_type, dst_buf, dst_root, dst_root_tile);
|
||||
pnanovdb_upper_handle_t upper = pnanovdb_root_get_child(grid_type, buf, root, root_tile);
|
||||
for (pnanovdb_uint32_t upper_n = 0u; upper_n < PNANOVDB_UPPER_TABLE_COUNT; upper_n++)
|
||||
{
|
||||
if (pnanovdb_upper_get_child_mask(dst_buf, dst_upper, upper_n))
|
||||
{
|
||||
pnanovdb_lower_handle_t dst_lower = pnanovdb_upper_get_child(grid_type, dst_buf, dst_upper, upper_n);
|
||||
pnanovdb_lower_handle_t lower = pnanovdb_upper_get_child(grid_type, buf, upper, upper_n);
|
||||
for (pnanovdb_uint32_t lower_n = 0u; lower_n < PNANOVDB_LOWER_TABLE_COUNT; lower_n++)
|
||||
{
|
||||
if (pnanovdb_lower_get_child_mask(dst_buf, dst_lower, lower_n))
|
||||
{
|
||||
pnanovdb_leaf_handle_t dst_leaf = pnanovdb_lower_get_child(grid_type, dst_buf, dst_lower, lower_n);
|
||||
pnanovdb_leaf_handle_t leaf = pnanovdb_lower_get_child(grid_type, buf, lower, lower_n);
|
||||
|
||||
pnanovdb_uint32_t leaf_value_count = pnanovdb_leaf_onindex_get_value_count(dst_buf, dst_leaf);
|
||||
|
||||
dst_index_addr = pnanovdb_leaf_get_table_address(grid_type, dst_buf, dst_leaf, 0u);
|
||||
pnanovdb_write_uint64(dst_buf, dst_index_addr, dst_value_index);
|
||||
|
||||
pnanovdb_address_t index_addr = pnanovdb_leaf_get_table_address(grid_type, buf, leaf, 0u);
|
||||
value_index = pnanovdb_read_uint64(buf, index_addr);
|
||||
|
||||
pnanovdb_io_memcpy(
|
||||
dst_buf, pnanovdb_address_offset64_product(dst_address, dst_value_index, value_size),
|
||||
buf, pnanovdb_address_offset64_product(address, value_index, value_size), value_size * leaf_value_count);
|
||||
dst_value_index+=leaf_value_count;
|
||||
}
|
||||
else if(pnanovdb_lower_get_child_mask(buf, lower, lower_n))
|
||||
{
|
||||
// this covers leaves disabled by pred above
|
||||
pnanovdb_address_t background_addr = pnanovdb_root_get_background_address(grid_type, dst_buf, dst_root);
|
||||
pnanovdb_uint64_t background_index = pnanovdb_read_uint64(buf, background_addr);
|
||||
|
||||
dst_index_addr = pnanovdb_lower_get_table_address(grid_type, dst_buf, dst_lower, lower_n);
|
||||
pnanovdb_write_uint64(dst_buf, dst_index_addr, background_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
pnanovdb_address_t index_addr = pnanovdb_lower_get_table_address(grid_type, buf, lower, lower_n);
|
||||
value_index = pnanovdb_read_uint64(buf, index_addr);
|
||||
if (last_lower_tile_index != value_index)
|
||||
{
|
||||
last_lower_tile_index = value_index;
|
||||
|
||||
dst_index_addr = pnanovdb_lower_get_table_address(grid_type, dst_buf, dst_lower, lower_n);
|
||||
pnanovdb_write_uint64(dst_buf, dst_index_addr, dst_value_index);
|
||||
last_lower_tile_dst_index = dst_value_index;
|
||||
|
||||
pnanovdb_io_memcpy(
|
||||
dst_buf, pnanovdb_address_offset64_product(dst_address, dst_value_index, value_size),
|
||||
buf, pnanovdb_address_offset64_product(address, value_index, value_size), value_size);
|
||||
dst_value_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_index_addr = pnanovdb_lower_get_table_address(grid_type, dst_buf, dst_lower, lower_n);
|
||||
pnanovdb_write_uint64(dst_buf, dst_index_addr, last_lower_tile_dst_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pnanovdb_address_t index_addr = pnanovdb_upper_get_table_address(grid_type, buf, upper, upper_n);
|
||||
value_index = pnanovdb_read_uint64(buf, index_addr);
|
||||
if (last_upper_tile_index != value_index)
|
||||
{
|
||||
last_upper_tile_index = value_index;
|
||||
|
||||
dst_index_addr = pnanovdb_upper_get_table_address(grid_type, dst_buf, dst_upper, upper_n);
|
||||
pnanovdb_write_uint64(dst_buf, dst_index_addr, dst_value_index);
|
||||
last_upper_tile_dst_index = dst_value_index;
|
||||
|
||||
pnanovdb_io_memcpy(
|
||||
dst_buf, pnanovdb_address_offset64_product(dst_address, dst_value_index, value_size),
|
||||
buf, pnanovdb_address_offset64_product(address, value_index, value_size), value_size);
|
||||
dst_value_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_index_addr = pnanovdb_upper_get_table_address(grid_type, dst_buf, dst_upper, upper_n);
|
||||
pnanovdb_write_uint64(dst_buf, dst_index_addr, last_upper_tile_dst_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pnanovdb_address_t index_addr = pnanovdb_root_tile_get_value_address(grid_type, buf, root_tile);
|
||||
value_index = pnanovdb_read_uint64(buf, index_addr);
|
||||
if (last_root_tile_index != value_index)
|
||||
{
|
||||
last_root_tile_index = value_index;
|
||||
|
||||
dst_index_addr = pnanovdb_root_tile_get_value_address(grid_type, dst_buf, dst_root_tile);
|
||||
pnanovdb_write_uint64(dst_buf, dst_index_addr, dst_value_index);
|
||||
last_root_tile_dst_index = dst_value_index;
|
||||
|
||||
pnanovdb_io_memcpy(
|
||||
dst_buf, pnanovdb_address_offset64_product(dst_address, dst_value_index, value_size),
|
||||
buf, pnanovdb_address_offset64_product(address, value_index, value_size), value_size);
|
||||
dst_value_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_index_addr = pnanovdb_root_tile_get_value_address(grid_type, dst_buf, dst_root_tile);
|
||||
pnanovdb_write_uint64(dst_buf, dst_index_addr, last_root_tile_dst_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pnanovdb_gridblindmetadata_set_value_count(dst_buf, dst_metadata, dst_value_index);
|
||||
|
||||
pnanovdb_int64_t metadata_size_diff = pnanovdb_uint64_as_int64(dst_value_index - value_count) * value_size;
|
||||
// compact later metadata ranges against new count
|
||||
for (pnanovdb_uint32_t local_metadata_idx = metadata_idx + 1u; local_metadata_idx < blindmetadata_count; local_metadata_idx++)
|
||||
{
|
||||
pnanovdb_gridblindmetadata_handle_t local_dst_metadata = pnanovdb_grid_get_gridblindmetadata(dst_buf, dst_grid, metadata_idx);
|
||||
|
||||
pnanovdb_int64_t local_dst_byte_offset = pnanovdb_gridblindmetadata_get_data_offset(dst_buf, local_dst_metadata);
|
||||
local_dst_byte_offset += metadata_size_diff;
|
||||
pnanovdb_gridblindmetadata_set_data_offset(dst_buf, local_dst_metadata, local_dst_byte_offset);
|
||||
}
|
||||
}
|
||||
|
||||
// compute final grid size based on maximum of final leaf and final metadata
|
||||
pnanovdb_uint64_t grid_size =
|
||||
dst_current_leaf.address.byte_offset -
|
||||
dst_grid.address.byte_offset;
|
||||
for (pnanovdb_uint32_t metadata_idx = 0u; metadata_idx < blindmetadata_count; metadata_idx++)
|
||||
{
|
||||
pnanovdb_gridblindmetadata_handle_t dst_metadata = pnanovdb_grid_get_gridblindmetadata(dst_buf, dst_grid, metadata_idx);
|
||||
|
||||
pnanovdb_int64_t dst_byte_offset = pnanovdb_gridblindmetadata_get_data_offset(dst_buf, dst_metadata);
|
||||
pnanovdb_address_t dst_address = pnanovdb_address_offset64(dst_metadata.address, pnanovdb_int64_as_uint64(dst_byte_offset));
|
||||
|
||||
pnanovdb_uint32_t value_size = pnanovdb_gridblindmetadata_get_value_size(dst_buf, dst_metadata);
|
||||
pnanovdb_uint64_t value_count = pnanovdb_gridblindmetadata_get_value_count(dst_buf, dst_metadata);
|
||||
|
||||
pnanovdb_uint64_t cmp_grid_size =
|
||||
dst_address.byte_offset + value_count * value_size -
|
||||
dst_grid.address.byte_offset;
|
||||
if (grid_size < cmp_grid_size)
|
||||
{
|
||||
grid_size = cmp_grid_size;
|
||||
}
|
||||
}
|
||||
pnanovdb_grid_set_grid_size(dst_buf, dst_grid, grid_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
PNANOVDB_IO_INLINE void pnanovdb_io_print_slice(pnanovdb_io_buf_t* src_nanovdb, FILE* dst_bmp)
|
||||
{
|
||||
pnanovdb_grid_handle_t grid = pnanovdb_io_fread_fileheader(src_nanovdb, 0, 0, 0u);
|
||||
|
||||
pnanovdb_buf_t buf = src_nanovdb;
|
||||
|
||||
// set grid at post header print offset
|
||||
printf("pnanovdb_io_print_slice grid(%lld)\n", (long long int)grid.address.byte_offset);
|
||||
|
||||
printf("pnanovdb_io_print_slice magic(%llx)\n",
|
||||
(unsigned long long int)pnanovdb_grid_get_magic(buf, grid));
|
||||
printf("pnanovdb_io_print_slice version(%x)\n", pnanovdb_grid_get_version(buf, grid));
|
||||
|
||||
pnanovdb_tree_handle_t tree = pnanovdb_grid_get_tree(buf, grid);
|
||||
printf("pnanovdb_io_print_slice tree(%lld)\n", (long long int)tree.address.byte_offset);
|
||||
|
||||
printf("pnanovdb_io_print_slice node_count_leaf(%d)\n", pnanovdb_tree_get_node_count_leaf(buf, tree));
|
||||
printf("pnanovdb_io_print_slice node_count_lower(%d)\n", pnanovdb_tree_get_node_count_lower(buf, tree));
|
||||
printf("pnanovdb_io_print_slice node_count_upper(%d)\n", pnanovdb_tree_get_node_count_upper(buf, tree));
|
||||
|
||||
pnanovdb_root_handle_t root = pnanovdb_tree_get_root(buf, tree);
|
||||
pnanovdb_grid_type_t grid_type = pnanovdb_grid_get_grid_type(buf, grid);
|
||||
|
||||
pnanovdb_coord_t bbox_min = pnanovdb_root_get_bbox_min(buf, root);
|
||||
pnanovdb_coord_t bbox_max = pnanovdb_root_get_bbox_max(buf, root);
|
||||
|
||||
printf("pnanovdb_io_print_slice range([(%d, %d, %d),(%d, %d, %d)])\n",
|
||||
bbox_min.x, bbox_min.y, bbox_min.z,
|
||||
bbox_max.x, bbox_max.y, bbox_max.z);
|
||||
|
||||
pnanovdb_int32_t image_width = bbox_max.x - bbox_min.x;
|
||||
pnanovdb_int32_t image_height = bbox_max.y - bbox_min.y;
|
||||
pnanovdb_int32_t k = (bbox_max.z + bbox_min.z) / 2;
|
||||
|
||||
char headerField0 = 'B';
|
||||
char headerField1 = 'M';
|
||||
uint32_t size = 54 + image_width * image_height * 4u;
|
||||
uint16_t reserved1 = 0;
|
||||
uint16_t reserved2 = 0;
|
||||
uint32_t offset = 54;
|
||||
uint32_t headerSize = 40;
|
||||
uint32_t width = image_width;
|
||||
uint32_t height = image_height;
|
||||
uint16_t colorPlanes = 1;
|
||||
uint16_t bitsPerPixel = 32;
|
||||
uint32_t compressionMethod = 0;
|
||||
uint32_t imageSize = image_width * image_height * 4u;
|
||||
uint32_t hRes = 2000;
|
||||
uint32_t vRes = 2000;
|
||||
uint32_t numColors = 0;
|
||||
uint32_t numImportantColors = 0;
|
||||
|
||||
fwrite(&headerField0, 1, 1, dst_bmp);
|
||||
fwrite(&headerField1, 1, 1, dst_bmp);
|
||||
fwrite(&size, 4, 1, dst_bmp);
|
||||
fwrite(&reserved1, 2, 1, dst_bmp);
|
||||
fwrite(&reserved2, 2, 1, dst_bmp);
|
||||
fwrite(&offset, 4, 1, dst_bmp);
|
||||
fwrite(&headerSize, 4, 1, dst_bmp);
|
||||
fwrite(&width, 4, 1, dst_bmp);
|
||||
fwrite(&height, 4, 1, dst_bmp);
|
||||
fwrite(&colorPlanes, 2, 1, dst_bmp);
|
||||
fwrite(&bitsPerPixel, 2, 1, dst_bmp);
|
||||
fwrite(&compressionMethod, 4, 1, dst_bmp);
|
||||
fwrite(&imageSize, 4, 1, dst_bmp);
|
||||
fwrite(&hRes, 4, 1, dst_bmp);
|
||||
fwrite(&vRes, 4, 1, dst_bmp);
|
||||
fwrite(&numColors, 4, 1, dst_bmp);
|
||||
fwrite(&numImportantColors, 4, 1, dst_bmp);
|
||||
|
||||
for (pnanovdb_int32_t j = image_height - 1; j >= 0; j--)
|
||||
{
|
||||
for (pnanovdb_int32_t i = 0; i < image_width; i++)
|
||||
{
|
||||
pnanovdb_coord_t ijk = {
|
||||
i + bbox_min.x,
|
||||
j + bbox_min.y,
|
||||
k
|
||||
};
|
||||
|
||||
pnanovdb_address_t addr = pnanovdb_root_get_value_address(grid_type, buf, root, &ijk);
|
||||
|
||||
float density_value = 1.f;
|
||||
float color_value = pnanovdb_read_float(buf, addr);
|
||||
|
||||
pnanovdb_uint32_t raw_value = pnanovdb_uint32_t(255.f * pnanovdb_max(0.f, pnanovdb_min(1.f, color_value))) |
|
||||
(pnanovdb_uint32_t(255.f * pnanovdb_max(0.f, pnanovdb_min(1.f, color_value))) << 8u) |
|
||||
(pnanovdb_uint32_t(255.f * pnanovdb_max(0.f, pnanovdb_min(1.f, color_value))) << 16u) |
|
||||
(pnanovdb_uint32_t(255.f * pnanovdb_max(0.f, pnanovdb_min(1.f, density_value))) << 24u);
|
||||
|
||||
fwrite(&raw_value, 4u, 1u, dst_bmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // end of NANOVDB_PUTILS_IO_H_HAS_BEEN_INCLUDED
|
||||
@@ -323,8 +323,6 @@ NB_MODULE(nanovdb, m)
|
||||
.value("Vec4d", GridType::Vec4d)
|
||||
.value("Index", GridType::Index)
|
||||
.value("OnIndex", GridType::OnIndex)
|
||||
.value("IndexMask", GridType::IndexMask)
|
||||
.value("OnIndexMask", GridType::OnIndexMask)
|
||||
.value("PointIndex", GridType::PointIndex)
|
||||
.value("Vec3u8", GridType::Vec3u8)
|
||||
.value("Vec3u16", GridType::Vec3u16)
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
auto dstGrid = handle.grid<float>();// get a pointer to the destination grid
|
||||
\endcode
|
||||
|
||||
\brief Convert a base-pointer to an openvdb grid, denoted srcGrid, to a nanovdb
|
||||
\brief Convert a base-pointer to an openvdb grid, denoted srcGrid, to a nanovdb
|
||||
grid of the same type, e.g. float -> float or openvdb::Vec3f -> nanovdb::Vec3f
|
||||
\code
|
||||
auto handle = nanovdb::openToNanoVDB(*srcGrid);// convert source grid to a grid handle
|
||||
@@ -96,7 +96,7 @@
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <cstring> // for memcpy
|
||||
#include <cstring> // for strncpy
|
||||
#include <type_traits>
|
||||
|
||||
namespace nanovdb {// ============================================================================
|
||||
@@ -124,6 +124,23 @@ openToNanoVDB(const openvdb::GridBase::Ptr& base,
|
||||
StatsMode sMode = StatsMode::Default,
|
||||
CheckMode cMode = CheckMode::Default,
|
||||
int verbose = 0);
|
||||
|
||||
/// @brief Forward declaration of free-standing function that converts an OpenVDB GridBase into a NanoVDB GridHandle with an IndexGrid
|
||||
/// @tparam DstBuildT Should be either nanovdb::ValueIndex or nanovdb::ValueOnIndex
|
||||
/// @tparam BufferT Type of the buffer used to allocate the destination grid
|
||||
/// @param base Shared pointer to a base openvdb grid to be converted
|
||||
/// @param channels Number of sidecar channels with the values (active or all) in the source grid
|
||||
/// @param includeStats If true stats are also indexed
|
||||
/// @param includeTiles If true tile values (active or all) are also indexed
|
||||
/// @param verbose Mode of verbosity
|
||||
/// @return Handle to the destination NanoGrid of type IndexGrid or OnIndexGrid
|
||||
template<typename DstBuildT = nanovdb::ValueOnIndex, typename BufferT = HostBuffer>
|
||||
typename util::enable_if<BuildTraits<DstBuildT>::is_index, GridHandle<BufferT>>::type
|
||||
openToIndexVDB(const openvdb::GridBase::Ptr& base,
|
||||
uint32_t channels = 1u,
|
||||
bool includeStats = true,
|
||||
bool includeTiles = true,
|
||||
int verbose = 0);
|
||||
#endif
|
||||
|
||||
//================================================================================================
|
||||
@@ -260,12 +277,6 @@ public:
|
||||
}
|
||||
};// AbsDiff
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const AbsDiff& diff)
|
||||
{
|
||||
os << "Absolute tolerance: " << diff.getTolerance();
|
||||
return os;
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
|
||||
/// @brief Compression oracle based on relative difference
|
||||
@@ -290,15 +301,9 @@ public:
|
||||
}
|
||||
};// RelDiff
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const RelDiff& diff)
|
||||
{
|
||||
os << "Relative tolerance: " << diff.getTolerance();
|
||||
return os;
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
|
||||
/// @brief The NodeAccessor provides a uniform API for accessing nodes got NanoVDB, OpenVDB and build Grids
|
||||
/// @brief The NodeAccessor provides a uniform API for accessing nodes in NanoVDB, OpenVDB and build Grids
|
||||
///
|
||||
/// @note General implementation that works with nanovdb::tools::build::Grid
|
||||
template <typename GridT>
|
||||
@@ -769,8 +774,8 @@ private:
|
||||
|
||||
std::unique_ptr<SrcNodeAccT> mSrcNodeAccPtr;// placeholder for potential local instance
|
||||
const SrcNodeAccT &mSrcNodeAcc;
|
||||
struct BlindMetaData; // forward declaration
|
||||
std::set<BlindMetaData> mBlindMetaData; // sorted according to BlindMetaData.order
|
||||
struct OrderedBlindMetaData; // forward declaration
|
||||
std::set<OrderedBlindMetaData> mBlindMetaData; // sorted set of GridBlindMetaData
|
||||
struct Codec { float min, max; uint64_t offset; uint8_t log2; };// used for adaptive bit-rate quantization
|
||||
std::unique_ptr<Codec[]> mCodec;// defines a codec per leaf node when DstBuildT = FpN
|
||||
StatsMode mStats;
|
||||
@@ -812,47 +817,31 @@ CreateNanoGrid<SrcGridT>::CreateNanoGrid(const SrcNodeAccT &srcNodeAcc)
|
||||
//================================================================================================
|
||||
|
||||
template <typename SrcGridT>
|
||||
struct CreateNanoGrid<SrcGridT>::BlindMetaData
|
||||
struct CreateNanoGrid<SrcGridT>::OrderedBlindMetaData
|
||||
{
|
||||
BlindMetaData(const std::string& name,// name + used to derive GridBlindDataSemantic
|
||||
const std::string& type,// used to derive GridType of blind data
|
||||
GridBlindDataClass dataClass,
|
||||
size_t i, size_t valueCount, size_t valueSize)
|
||||
: metaData(reinterpret_cast<GridBlindMetaData*>(new char[sizeof(GridBlindMetaData)]))
|
||||
OrderedBlindMetaData(const std::string& name,// name, also used to derive GridBlindDataSemantic
|
||||
const std::string& type,// used to derive GridType of blind data
|
||||
GridBlindDataClass dataClass,
|
||||
size_t i, size_t valueCount, size_t valueSize)
|
||||
: metaData(new GridBlindMetaData(0, valueCount, valueSize, this->mapToSemantics(name), dataClass, this->mapToType(type)))
|
||||
, order(i)// sorted id of meta data
|
||||
, size(math::AlignUp<NANOVDB_DATA_ALIGNMENT>(valueCount * valueSize))
|
||||
{
|
||||
util::memzero(metaData, sizeof(GridBlindMetaData));// zero out all meta data
|
||||
if (name.length()>=GridData::MaxNameSize) throw std::runtime_error("blind data name exceeds limit");
|
||||
std::memcpy(metaData->mName, name.c_str(), name.length() + 1);
|
||||
metaData->mValueCount = valueCount;
|
||||
metaData->mSemantic = BlindMetaData::mapToSemantics(name);
|
||||
metaData->mDataClass = dataClass;
|
||||
metaData->mDataType = BlindMetaData::mapToType(type);
|
||||
metaData->mValueSize = valueSize;
|
||||
if (!metaData->setName(name.c_str())) throw std::runtime_error("blind data name exceeds character limit");
|
||||
NANOVDB_ASSERT(metaData->isValid());
|
||||
}
|
||||
BlindMetaData(const std::string& name,// only name
|
||||
GridBlindDataSemantic dataSemantic,
|
||||
GridBlindDataClass dataClass,
|
||||
GridType dataType,
|
||||
size_t i, size_t valueCount, size_t valueSize)
|
||||
: metaData(reinterpret_cast<GridBlindMetaData*>(new char[sizeof(GridBlindMetaData)]))
|
||||
OrderedBlindMetaData(const std::string& name,// only used to name blind data
|
||||
GridBlindDataSemantic dataSemantic,
|
||||
GridBlindDataClass dataClass,
|
||||
GridType dataType,
|
||||
size_t i, size_t valueCount, size_t valueSize)
|
||||
: metaData(new GridBlindMetaData(0, valueCount, valueSize, dataSemantic, dataClass, dataType))
|
||||
, order(i)// sorted id of meta data
|
||||
, size(math::AlignUp<NANOVDB_DATA_ALIGNMENT>(valueCount * valueSize))
|
||||
{
|
||||
std::memset(metaData, 0, sizeof(GridBlindMetaData));// zero out all meta data
|
||||
if (name.length()>=GridData::MaxNameSize) throw std::runtime_error("blind data name exceeds character limit");
|
||||
std::memcpy(metaData->mName, name.c_str(), name.length() + 1);
|
||||
metaData->mValueCount = valueCount;
|
||||
metaData->mSemantic = dataSemantic;
|
||||
metaData->mDataClass = dataClass;
|
||||
metaData->mDataType = dataType;
|
||||
metaData->mValueSize = valueSize;
|
||||
if (!metaData->setName(name.c_str())) throw std::runtime_error("blind data name exceeds character limit");
|
||||
NANOVDB_ASSERT(metaData->isValid());
|
||||
}
|
||||
~BlindMetaData(){ delete [] reinterpret_cast<char*>(metaData); }
|
||||
bool operator<(const BlindMetaData& other) const { return order < other.order; } // required by std::set
|
||||
~OrderedBlindMetaData(){ delete metaData; }
|
||||
bool operator<(const OrderedBlindMetaData& other) const { return order < other.order; } // required by std::set
|
||||
static GridType mapToType(const std::string& name)
|
||||
{
|
||||
GridType type = GridType::Unknown;
|
||||
@@ -869,6 +858,9 @@ struct CreateNanoGrid<SrcGridT>::BlindMetaData
|
||||
}
|
||||
return type;
|
||||
}
|
||||
/// @brief Maps from string names of point attributes in openvdb to GridBlindDataSemantic
|
||||
/// @param String name, typically used for point attributes in OpenVDB
|
||||
/// @return GridBlindDataSemantic
|
||||
static GridBlindDataSemantic mapToSemantics(const std::string& name)
|
||||
{
|
||||
GridBlindDataSemantic semantic = GridBlindDataSemantic::Unknown;
|
||||
@@ -885,9 +877,10 @@ struct CreateNanoGrid<SrcGridT>::BlindMetaData
|
||||
}
|
||||
return semantic;
|
||||
}
|
||||
GridBlindMetaData *metaData;
|
||||
const size_t order, size;
|
||||
}; // CreateNanoGrid::BlindMetaData
|
||||
size_t memUsage() const {return metaData->blindDataSize();}
|
||||
GridBlindMetaData *metaData;// a pointer is preferred since it avoids deep copy during sorting
|
||||
const size_t order;// index used for sorting of the blind meta data
|
||||
}; // CreateNanoGrid::OrderedBlindMetaData
|
||||
|
||||
//================================================================================================
|
||||
|
||||
@@ -949,7 +942,7 @@ GridHandle<BufferT> CreateNanoGrid<SrcGridT>::initHandle(const BufferT& pool)
|
||||
mOffset.meta = mOffset.leaf + mLeafNodeSize;// leaf nodes end and blind meta data begins
|
||||
mOffset.blind = mOffset.meta + sizeof(GridBlindMetaData)*mBlindMetaData.size(); // meta data ends and blind data begins
|
||||
mOffset.size = mOffset.blind;// end of buffer
|
||||
for (const auto& b : mBlindMetaData) mOffset.size += b.size; // accumulate all the blind data
|
||||
for (const auto& b : mBlindMetaData) mOffset.size += b.memUsage(); // accumulate all the blind data
|
||||
|
||||
auto buffer = BufferT::create(mOffset.size, &pool);
|
||||
mBufferPtr = buffer.data();
|
||||
@@ -1171,7 +1164,7 @@ CreateNanoGrid<SrcGridT>::preProcess(uint32_t channels)
|
||||
uint32_t order = mBlindMetaData.size();
|
||||
char str[16];
|
||||
for (uint32_t i=0; i<channels; ++i) {
|
||||
mBlindMetaData.emplace("channel_"+std::to_string(i),
|
||||
mBlindMetaData.emplace("channel_" + std::to_string(i),
|
||||
toStr(str, toGridType<SrcValueT>()),
|
||||
GridBlindDataClass::AttributeArray,
|
||||
order++,
|
||||
@@ -1269,7 +1262,6 @@ CreateNanoGrid<SrcGridT>::processLeafs()
|
||||
} else {
|
||||
dstLeaf->mPrefixSum = 0u;
|
||||
}
|
||||
if constexpr(BuildTraits<DstBuildT>::is_indexmask) dstLeaf->mMask = dstLeaf->mValueMask;
|
||||
}
|
||||
});
|
||||
} // CreateNanoGrid::processLeafs<ValueIndex or ValueOnIndex>
|
||||
@@ -1679,12 +1671,12 @@ void CreateNanoGrid<SrcGridT>::processTree()
|
||||
dstTree->mNodeCount[2] = static_cast<uint32_t>(nodeCount[2]);
|
||||
|
||||
// Count number of active leaf level tiles
|
||||
dstTree->mTileCount[0] = util::reduce(util::Range1D(0,nodeCount[1]), uint32_t(0), [&](util::Range1D &r, uint32_t sum){
|
||||
dstTree->mTileCount[0] = util::reduce(util::Range1D(0,nodeCount[1]), uint32_t(0), [&](const util::Range1D &r, uint32_t sum){
|
||||
for (auto i=r.begin(); i!=r.end(); ++i) sum += mSrcNodeAcc.template node<1>(i).getValueMask().countOn();
|
||||
return sum;}, std::plus<uint32_t>());
|
||||
|
||||
// Count number of active lower internal node tiles
|
||||
dstTree->mTileCount[1] = util::reduce(util::Range1D(0,nodeCount[2]), uint32_t(0), [&](util::Range1D &r, uint32_t sum){
|
||||
dstTree->mTileCount[1] = util::reduce(util::Range1D(0,nodeCount[2]), uint32_t(0), [&](const util::Range1D &r, uint32_t sum){
|
||||
for (auto i=r.begin(); i!=r.end(); ++i) sum += mSrcNodeAcc.template node<2>(i).getValueMask().countOn();
|
||||
return sum;}, std::plus<uint32_t>());
|
||||
|
||||
@@ -1693,7 +1685,7 @@ void CreateNanoGrid<SrcGridT>::processTree()
|
||||
for (auto it = mSrcNodeAcc.root().cbeginValueOn(); it; ++it) dstTree->mTileCount[2] += 1;
|
||||
|
||||
// Count number of active voxels
|
||||
dstTree->mVoxelCount = util::reduce(util::Range1D(0, nodeCount[0]), uint64_t(0), [&](util::Range1D &r, uint64_t sum){
|
||||
dstTree->mVoxelCount = util::reduce(util::Range1D(0, nodeCount[0]), uint64_t(0), [&](const util::Range1D &r, uint64_t sum){
|
||||
for (auto i=r.begin(); i!=r.end(); ++i) sum += mSrcNodeAcc.template node<0>(i).getValueMask().countOn();
|
||||
return sum;}, std::plus<uint64_t>());
|
||||
|
||||
@@ -1715,16 +1707,6 @@ void CreateNanoGrid<SrcGridT>::processGrid()
|
||||
dstGrid->mBlindMetadataCount = static_cast<uint32_t>(mBlindMetaData.size());
|
||||
dstGrid->mData1 = this->valueCount();
|
||||
|
||||
// if (!isValid(dstGrid->mGridType, dstGrid->mGridClass)) {
|
||||
//#if 1
|
||||
// char str[30];
|
||||
// fprintf(stderr,"Warning: Strange combination of GridType(\"%s\") and GridClass(\"%s\"). Consider changing GridClass to \"Unknown\"\n",
|
||||
// toStr(str, dstGrid->mGridType), toStr(str + 15, dstGrid->mGridClass));
|
||||
//#else
|
||||
// throw std::runtime_error("Invalid combination of GridType("+std::to_string(int(dstGrid->mGridType))+
|
||||
// ") and GridClass("+std::to_string(int(dstGrid->mGridClass))+"). See NanoVDB.h for details!");
|
||||
//#endif
|
||||
// }
|
||||
util::memzero(dstGrid->mGridName, GridData::MaxNameSize);// initialize mGridName to zero
|
||||
strncpy(dstGrid->mGridName, mSrcNodeAcc.getName().c_str(), GridData::MaxNameSize-1);
|
||||
if (mSrcNodeAcc.hasLongGridName()) dstGrid->setLongGridNameOn();// grid name is long so store it as blind data
|
||||
@@ -1736,11 +1718,11 @@ void CreateNanoGrid<SrcGridT>::processGrid()
|
||||
dstGrid->mBlindMetadataCount = static_cast<uint32_t>(mBlindMetaData.size());
|
||||
char *blindData = util::PtrAdd<char>(mBufferPtr, mOffset.blind);
|
||||
for (const auto &b : mBlindMetaData) {
|
||||
std::memcpy(metaData, b.metaData, sizeof(GridBlindMetaData));
|
||||
*metaData = *b.metaData;
|
||||
metaData->setBlindData(blindData);// sets metaData.mOffset
|
||||
if (metaData->mDataClass == GridBlindDataClass::GridName) strcpy(blindData, mSrcNodeAcc.getName().c_str());
|
||||
++metaData;
|
||||
blindData += b.size;
|
||||
blindData += b.memUsage();
|
||||
}
|
||||
mBlindMetaData.clear();
|
||||
}
|
||||
@@ -1820,10 +1802,13 @@ CreateNanoGrid<SrcGridT>::postProcess(uint32_t channels)
|
||||
for (uint32_t i=0; i<channels; ++i) {
|
||||
const std::string name = "channel_"+std::to_string(i);
|
||||
int j = dstGrid->findBlindData(name.c_str());
|
||||
if (j<0) throw std::runtime_error("missing " + name);
|
||||
if (j<0) throw std::runtime_error("CreateNanoGrid::postProcess: missing " + name);
|
||||
auto *metaData = this->dstMeta(j);// partially set in processGrid
|
||||
metaData->mDataClass = GridBlindDataClass::ChannelArray;
|
||||
metaData->mDataType = toGridType<SrcValueT>();
|
||||
if (metaData->mSemantic == GridBlindDataSemantic::Unknown) {// try to derive it from the source grid
|
||||
metaData->mSemantic = toSemantic( mSrcNodeAcc.gridClass());
|
||||
}
|
||||
SrcValueT *blindData = const_cast<SrcValueT*>(metaData->template getBlindData<SrcValueT>());
|
||||
if (i>0) {// concurrent copy from previous channel
|
||||
util::forEach(0,valueCount,1024,[&](const util::Range1D &r){
|
||||
@@ -2024,7 +2009,7 @@ template<typename BufferT>
|
||||
GridHandle<BufferT>
|
||||
openToNanoVDB(const openvdb::GridBase::Ptr& base,
|
||||
StatsMode sMode,
|
||||
CheckMode cMode,
|
||||
CheckMode cMode,
|
||||
int verbose)
|
||||
{
|
||||
// We need to define these types because they are not defined in OpenVDB
|
||||
@@ -2064,10 +2049,68 @@ openToNanoVDB(const openvdb::GridBase::Ptr& base,
|
||||
OPENVDB_THROW(openvdb::RuntimeError, "Unrecognized OpenVDB grid type");
|
||||
}
|
||||
}// openToNanoVDB
|
||||
|
||||
template<typename DstBuildT, typename BufferT>
|
||||
typename util::enable_if<BuildTraits<DstBuildT>::is_index, GridHandle<BufferT>>::type
|
||||
openToIndexVDB(const openvdb::GridBase::Ptr& base,
|
||||
uint32_t channels,
|
||||
bool includeStats,
|
||||
bool includeTiles,
|
||||
int verbose)
|
||||
{
|
||||
// We need to define these types because they are not defined in OpenVDB
|
||||
using openvdb_Vec4fTree = typename openvdb::tree::Tree4<openvdb::Vec4f, 5, 4, 3>::Type;
|
||||
using openvdb_Vec4dTree = typename openvdb::tree::Tree4<openvdb::Vec4d, 5, 4, 3>::Type;
|
||||
using openvdb_Vec4fGrid = openvdb::Grid<openvdb_Vec4fTree>;
|
||||
using openvdb_Vec4dGrid = openvdb::Grid<openvdb_Vec4dTree>;
|
||||
using openvdb_UInt32Grid = openvdb::Grid<openvdb::UInt32Tree>;
|
||||
|
||||
if (auto grid = openvdb::GridBase::grid<openvdb::FloatGrid>(base)) {
|
||||
return createNanoGrid<openvdb::FloatGrid, DstBuildT, BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb::DoubleGrid>(base)) {
|
||||
return createNanoGrid<openvdb::DoubleGrid, DstBuildT, BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb::Int32Grid>(base)) {
|
||||
return createNanoGrid<openvdb::Int32Grid, DstBuildT,BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb::Int64Grid>(base)) {
|
||||
return createNanoGrid<openvdb::Int64Grid, DstBuildT, BufferT>(*grid, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb_UInt32Grid>(base)) {
|
||||
return createNanoGrid<openvdb_UInt32Grid, DstBuildT, BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb::Vec3fGrid>(base)) {
|
||||
return createNanoGrid<openvdb::Vec3fGrid, DstBuildT, BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb::Vec3dGrid>(base)) {
|
||||
return createNanoGrid<openvdb::Vec3dGrid, DstBuildT, BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb::tools::PointIndexGrid>(base)) {
|
||||
return createNanoGrid<openvdb::tools::PointIndexGrid, DstBuildT, BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb::points::PointDataGrid>(base)) {
|
||||
return createNanoGrid<openvdb::points::PointDataGrid, DstBuildT, BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb::MaskGrid>(base)) {
|
||||
return createNanoGrid<openvdb::MaskGrid, DstBuildT, BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb::BoolGrid>(base)) {
|
||||
return createNanoGrid<openvdb::BoolGrid, DstBuildT, BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb_Vec4fGrid>(base)) {
|
||||
return createNanoGrid<openvdb_Vec4fGrid, DstBuildT, BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else if (auto grid = openvdb::GridBase::grid<openvdb_Vec4dGrid>(base)) {
|
||||
return createNanoGrid<openvdb_Vec4dGrid, DstBuildT, BufferT>(*grid, channels, includeStats, includeTiles, verbose);
|
||||
} else {
|
||||
OPENVDB_THROW(openvdb::RuntimeError, "Unrecognized OpenVDB grid type");
|
||||
}
|
||||
}// openToIndexVDB
|
||||
#endif
|
||||
|
||||
}// namespace tools ===============================================================================
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const nanovdb::tools::AbsDiff& diff)
|
||||
{
|
||||
os << "Absolute tolerance: " << diff.getTolerance();
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const nanovdb::tools::RelDiff& diff)
|
||||
{
|
||||
os << "Relative tolerance: " << diff.getTolerance();
|
||||
return os;
|
||||
}
|
||||
|
||||
#endif // NANOVDB_TOOLS_CREATENANOGRID_H_HAS_BEEN_INCLUDED
|
||||
|
||||
@@ -617,7 +617,7 @@ initSphere(double radius, // radius of sphere in world units
|
||||
grid->setTransform(voxelSize, origin);
|
||||
|
||||
// Define radius of sphere with narrow-band in voxel units
|
||||
const ValueT r0 = radius / ValueT(voxelSize), rmax = r0 + ValueT(halfWidth);
|
||||
const ValueT r0 = ValueT(radius / voxelSize), rmax = r0 + ValueT(halfWidth);
|
||||
|
||||
// Radius below the Nyquist frequency
|
||||
if (r0 < ValueT(1.5f)) return grid;
|
||||
@@ -689,7 +689,7 @@ initTorus(double radius1, // major radius of torus in world units
|
||||
grid->setTransform(voxelSize, origin);
|
||||
|
||||
// Define size of torus with narrow-band in voxel units
|
||||
const ValueT r1 = radius1 / ValueT(voxelSize), r2 = radius2 / ValueT(voxelSize), rmax1 = r1 + r2 + ValueT(halfWidth), rmax2 = r2 + ValueT(halfWidth);
|
||||
const ValueT r1 = ValueT(radius1 / voxelSize), r2 = ValueT(radius2 / voxelSize), rmax1 = r1 + r2 + ValueT(halfWidth), rmax2 = r2 + ValueT(halfWidth);
|
||||
|
||||
// Radius below the Nyquist frequency
|
||||
if (r2 < ValueT(1.5)) return grid;
|
||||
@@ -1706,7 +1706,7 @@ createPointScatter(const NanoGrid<SrcBuildT>& srcGrid, // origin of grid in worl
|
||||
dstLeaf->mValueMask = srcLeaf.valueMask();
|
||||
for (uint32_t j = 0, m = 0; j < 512; ++j) {
|
||||
if (dstLeaf->mValueMask.isOn(j)) {
|
||||
const Vec3f ijk = dstLeaf->offsetToGlobalCoord(j).asVec3s();// floating-point representatrion of index coorindates
|
||||
const Vec3f ijk = dstLeaf->offsetToGlobalCoord(j).asVec3s();// floating-point representation of index coordinates
|
||||
for (int n = 0; n < pointsPerVoxel; ++n) xyz.push_back(srcGrid.indexToWorld(randomPoint() + ijk));
|
||||
m += pointsPerVoxel;
|
||||
}// active voxels
|
||||
|
||||
@@ -1968,7 +1968,7 @@ void Grid<BuildT>::operator()(const Func& func, const CoordBBox& bbox, ValueType
|
||||
NANOVDB_ASSERT(leaf == nullptr);
|
||||
}
|
||||
}// loop over sub-part of leafBBox
|
||||
if (leaf) delete leaf;
|
||||
delete leaf;
|
||||
});
|
||||
|
||||
// Prune leaf and tile nodes
|
||||
|
||||
@@ -15,179 +15,317 @@
|
||||
|
||||
#include <nanovdb/NanoVDB.h> // manages and streams the raw memory buffer of a NanoVDB grid.
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/util/Util.h>
|
||||
#include <nanovdb/util/ForEach.h>
|
||||
|
||||
#include <openvdb/openvdb.h>
|
||||
#include <openvdb/tools/SignedFloodFill.h>
|
||||
|
||||
#ifndef NANOVDB_TOOLS_NANOTOOPENVDB_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_TOOLS_NANOTOOPENVDB_H_HAS_BEEN_INCLUDED
|
||||
|
||||
template<typename T>
|
||||
struct ConvertTrait {using Type = T;};
|
||||
|
||||
template<typename T>
|
||||
struct ConvertTrait<nanovdb::math::Vec3<T>> {using Type = openvdb::math::Vec3<T>;};
|
||||
|
||||
template<typename T>
|
||||
struct ConvertTrait<nanovdb::math::Vec4<T>> {using Type = openvdb::math::Vec4<T>;};
|
||||
|
||||
template<>
|
||||
struct ConvertTrait<nanovdb::Fp4> {using Type = float;};
|
||||
|
||||
template<>
|
||||
struct ConvertTrait<nanovdb::Fp8> {using Type = float;};
|
||||
|
||||
template<>
|
||||
struct ConvertTrait<nanovdb::Fp16> {using Type = float;};
|
||||
|
||||
template<>
|
||||
struct ConvertTrait<nanovdb::FpN> {using Type = float;};
|
||||
|
||||
template<>
|
||||
struct ConvertTrait<nanovdb::ValueMask> {using Type = openvdb::ValueMask;};
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools {
|
||||
|
||||
/// @brief Forward declaration of free-standing function that de-serializes a typed NanoVDB grid into an OpenVDB Grid
|
||||
template<typename NanoBuildT>
|
||||
typename openvdb::Grid<typename openvdb::tree::Tree4<typename ConvertTrait<NanoBuildT>::Type>::Type>::Ptr
|
||||
nanoToOpenVDB(const NanoGrid<NanoBuildT>& grid, int verbose = 0);
|
||||
namespace trait {
|
||||
|
||||
/// @brief Forward declaration of free-standing function that de-serializes a NanoVDB GridHandle into an OpenVDB GridBase
|
||||
/// @brief Struct that maps all nanovdb types to corresponding openvdb types.
|
||||
/// @tparam T NanoVDB build type, e.g. nanovdb::ValueMask -> openvdb::ValueMask
|
||||
/// nanovdb::Fp8 -> float, nanovdb::ValueIndex -> uin64_t, float -> float
|
||||
/// @note Since Windows appears to always template instantiate the return_type in
|
||||
/// enable_if<bool, return_type>, MapToOpen is designed to work for all types T,
|
||||
/// even types like ValueOnIndex that doesn't have an openvdb analogue!
|
||||
template<typename T>// see below for more template specializations
|
||||
struct MapToOpen { using type = typename BuildToValueMap<T>::type; };
|
||||
|
||||
template<typename T>
|
||||
using MapToOpenT = typename MapToOpen<T>::type;
|
||||
|
||||
template <typename T>
|
||||
using OpenTree = typename openvdb::tree::Tree4<trait::MapToOpenT<T>, 5, 4, 3>::Type;
|
||||
|
||||
template <typename T>
|
||||
using OpenGrid = openvdb::Grid< OpenTree<T> >;
|
||||
|
||||
template <typename T>
|
||||
using OpenGridPtr = typename OpenGrid< T >::Ptr;
|
||||
|
||||
}// trait namespace
|
||||
|
||||
/// @brief Free-standing function that de-serializes a typed NanoVDB grid into an OpenVDB Grid
|
||||
/// @tparam NanoBuildT Template build type for the @c grid
|
||||
/// @param grid NanoVDB grid to be converted to an OpenVDB grid
|
||||
/// @return If NanoBuildT is an index type, e.g. ValueOnIndex, a shared pointer to an OpenVDB GridBase
|
||||
/// is returned. Otherwise a shared pointer to an OpenVDB Grid of a matching type
|
||||
///. (trait::OpenGridPtr<NanoBuildT>) is returned.
|
||||
template<typename NanoBuildT>
|
||||
auto nanoToOpenVDB(const NanoGrid<NanoBuildT>& grid);
|
||||
|
||||
/// @brief Free-standing function that de-serializes an IndexGrid with a sidecar into an OpenVDB Grid
|
||||
/// @tparam NanoValueT Template type of the sidecar data accompanying the index @c grid
|
||||
/// @param grid IndexGrid into the sidecar data
|
||||
/// @param sideCar Linear array of sidecar data
|
||||
/// @param gridClass GridClass corresponding to the sidecar data
|
||||
/// @param gridName Name of the output grid
|
||||
/// @return Shared pointer to an OpenVDB Grid of a matching type (trait::OpenGridPtr<NanoValueT>)
|
||||
template<typename NanoBuildT, typename NanoValueT>
|
||||
util::enable_if_t<BuildTraits<NanoBuildT>::is_index, trait::OpenGridPtr<NanoValueT>>
|
||||
nanoToOpenVDB(const NanoGrid<NanoBuildT>& grid,
|
||||
const NanoValueT *sideCar,
|
||||
GridClass gridClass = GridClass::Unknown,
|
||||
const char *gridName = nullptr);
|
||||
|
||||
/// @brief Forward declaration of free-standing function that de-serializes a grid in a NanoVDB GridHandle into an OpenVDB GridBase
|
||||
/// @tparam BufferT Template type of the buffer used to allocate the grid handle
|
||||
/// @param handle Handle for the input grid to be converted
|
||||
/// @param gridID ID of the grid in the GridHandle to be converted (defaults to first grid)
|
||||
/// @return Shared pointer to an OpenVDB BaseGrid
|
||||
/// @note If grid number @c gridID in @c handle has GridType IndexGrid, then the first suitable blind data is used as the sidecar data
|
||||
template<typename BufferT>
|
||||
openvdb::GridBase::Ptr
|
||||
nanoToOpenVDB(const GridHandle<BufferT>& handle, int verbose = 0, uint32_t n = 0);
|
||||
nanoToOpenVDB(const GridHandle<BufferT>& handle, uint32_t gridID = 0);
|
||||
|
||||
// ================================================================================================
|
||||
|
||||
namespace trait {
|
||||
|
||||
template<>
|
||||
struct MapToOpen<ValueMask> {using type = openvdb::ValueMask;};
|
||||
|
||||
template<typename T>
|
||||
struct MapToOpen<math::Vec3<T>>{using type = openvdb::math::Vec3<T>;};
|
||||
|
||||
template<typename T>
|
||||
struct MapToOpen<math::Vec4<T>>{using type = openvdb::math::Vec4<T>;};
|
||||
|
||||
template<typename T, uint32_t LEVEL>
|
||||
struct OpenNode;
|
||||
|
||||
// Partial template specialization of the OpenNode struct
|
||||
template<typename T>
|
||||
struct OpenNode<T, 0> {using type = openvdb::tree::LeafNode<MapToOpenT<T>, 3>;};
|
||||
|
||||
template<typename T>
|
||||
struct OpenNode<T, 1> {using type = openvdb::tree::InternalNode<typename OpenNode<T, 0>::type, 4>;};
|
||||
|
||||
template<typename T>
|
||||
struct OpenNode<T, 2> {using type = openvdb::tree::InternalNode<typename OpenNode<T, 1>::type, 5>;};
|
||||
|
||||
template<typename T, uint32_t LEVEL>
|
||||
using OpenNodeT = typename OpenNode<T, LEVEL>::type;
|
||||
|
||||
/// @brief Maps from nanovdb::GridClass to openvdb::GridClass
|
||||
/// @param gridClass nanovdb::GridClass
|
||||
/// @return openvdb::GridClass
|
||||
static openvdb::GridClass toOpenGridClass(GridClass gridClass)
|
||||
{
|
||||
switch (gridClass) {
|
||||
case GridClass::LevelSet:
|
||||
return openvdb::GRID_LEVEL_SET;
|
||||
case GridClass::FogVolume:
|
||||
return openvdb::GRID_FOG_VOLUME;
|
||||
case GridClass::Staggered:
|
||||
return openvdb::GRID_STAGGERED;
|
||||
case GridClass::PointIndex:
|
||||
throw std::runtime_error("NanoToOpenVDB does not yet support PointIndexGrids");
|
||||
case GridClass::PointData:
|
||||
throw std::runtime_error("NanoToOpenVDB does not yet support PointDataGrids");
|
||||
default:
|
||||
return openvdb::GRID_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static const auto* mapPtr(const T *v) {return reinterpret_cast<const typename MapToOpen<T>::type*>(v);}
|
||||
|
||||
static openvdb::Coord mapCoord(const Coord &ijk){return openvdb::Coord(ijk[0], ijk[1], ijk[2]);}
|
||||
template<uint32_t LOG2DIM>
|
||||
|
||||
static auto mapMask(const Mask<LOG2DIM> &m){return reinterpret_cast<const openvdb::util::NodeMask<LOG2DIM>&>(m);}
|
||||
|
||||
}// trait namespace
|
||||
|
||||
/// @brief This class will serialize an OpenVDB grid into a NanoVDB grid managed by a GridHandle.
|
||||
template<typename NanoBuildT>
|
||||
class NanoToOpenVDB
|
||||
{
|
||||
using NanoNode0 = nanovdb::LeafNode<NanoBuildT, openvdb::Coord, openvdb::util::NodeMask>; // note that it's using openvdb coord nd mask types!
|
||||
using NanoNode1 = nanovdb::InternalNode<NanoNode0>;
|
||||
using NanoNode2 = nanovdb::InternalNode<NanoNode1>;
|
||||
using NanoRootT = nanovdb::RootNode<NanoNode2>;
|
||||
using NanoTreeT = nanovdb::Tree<NanoRootT>;
|
||||
using NanoGridT = nanovdb::Grid<NanoTreeT>;
|
||||
using NanoValueT = typename NanoGridT::ValueType;
|
||||
|
||||
using OpenBuildT = typename ConvertTrait<NanoBuildT>::Type; // e.g. float -> float but nanovdb::math::Vec3<float> -> openvdb::Vec3<float>
|
||||
using OpenNode0 = openvdb::tree::LeafNode<OpenBuildT, NanoNode0::LOG2DIM>; // leaf
|
||||
using OpenNode1 = openvdb::tree::InternalNode<OpenNode0, NanoNode1::LOG2DIM>; // lower
|
||||
using OpenNode2 = openvdb::tree::InternalNode<OpenNode1, NanoNode2::LOG2DIM>; // upper
|
||||
using OpenRootT = openvdb::tree::RootNode<OpenNode2>;
|
||||
using OpenTreeT = openvdb::tree::Tree<OpenRootT>;
|
||||
using OpenGridT = openvdb::Grid<OpenTreeT>;
|
||||
using OpenValueT = typename OpenGridT::ValueType;
|
||||
|
||||
public:
|
||||
/// @brief Construction from an existing const OpenVDB Grid.
|
||||
NanoToOpenVDB(){};
|
||||
|
||||
/// @brief Return a shared pointer to a NanoVDB grid constructed from the specified OpenVDB grid
|
||||
typename OpenGridT::Ptr operator()(const NanoGrid<NanoBuildT>& grid, int verbose = 0);
|
||||
/// @brief Default c-tor
|
||||
NanoToOpenVDB(){}
|
||||
|
||||
/// @brief Converts nanovdb::Grid<T> -> openvdb::Grid<T>::Ptr
|
||||
/// @tparam NanoBuildT Template type for the input NanoVDB grid
|
||||
/// @param grid NanoVDB grid to be converted
|
||||
/// @return Shared pointer to an OpenVDB grid of matching type
|
||||
template<typename NanoBuildT>
|
||||
util::disable_if_t<BuildTraits<NanoBuildT>::is_index, trait::OpenGridPtr<NanoBuildT>>
|
||||
operator()(const NanoGrid<NanoBuildT>& grid);
|
||||
|
||||
/// @brief Converts nanovdb::Grid<Index> + blind data -> openvdb::GridBase::Ptr
|
||||
/// @param idxGrid NanoVDB IndexGrid with blind data to be converted
|
||||
/// @param blindDataID Id of the bind data to be used as the sidecar. A negative values means
|
||||
/// pick the first available (relevant) blind data.
|
||||
/// @return shared pointer to an OpenVDB GridBase
|
||||
template<typename NanoIndexT>
|
||||
util::enable_if_t<BuildTraits<NanoIndexT>::is_index, openvdb::GridBase::Ptr>
|
||||
operator()(const NanoGrid<NanoIndexT>& idxGrid, int blindDataID = -1);
|
||||
|
||||
/// @brief Converts nanovdb::Grid<NanoIndexT> + NanoValueT[] -> openvdb::Grid<NanoValueT>::Ptr
|
||||
/// @tparam NanoValueT Template to of the sidecar data
|
||||
/// @param grid NanoVDB IndexGrid
|
||||
/// @param sideCar Linear array of sidecar data
|
||||
/// @param gridClass GridClass of the @c sideCar data
|
||||
/// @param name Name of the output grid
|
||||
/// @return Shared pointer to an OpenVDB grid of matching type
|
||||
template<typename NanoIndexT, typename NanoValueT>
|
||||
util::enable_if_t<BuildTraits<NanoIndexT>::is_index, trait::OpenGridPtr<NanoValueT>>
|
||||
operator()(const NanoGrid<NanoIndexT>& grid,
|
||||
const NanoValueT *sideCar,
|
||||
GridClass gridClass = GridClass::Unknown,
|
||||
const char *name = nullptr);
|
||||
|
||||
private:
|
||||
|
||||
template<typename NanoNodeT, typename OpenNodeT>
|
||||
OpenNodeT* processNode(const NanoNodeT*);
|
||||
template<int LEVEL, typename NanoBuildT>
|
||||
util::disable_if_t<BuildTraits<NanoBuildT>::is_index || LEVEL == 0>
|
||||
process(trait::OpenNodeT<NanoBuildT, LEVEL>*, const NanoNodeT<NanoBuildT, LEVEL>*);
|
||||
|
||||
OpenNode2* process(const NanoNode2* node) {return this->template processNode<NanoNode2, OpenNode2>(node);}
|
||||
OpenNode1* process(const NanoNode1* node) {return this->template processNode<NanoNode1, OpenNode1>(node);}
|
||||
template<int LEVEL, typename NanoIndexT, typename NanoValueT>
|
||||
util::enable_if_t<BuildTraits<NanoIndexT>::is_index && LEVEL != 0>
|
||||
process(trait::OpenNodeT<NanoValueT, LEVEL>*, const NanoNodeT<NanoIndexT, LEVEL>*, const NanoValueT*);
|
||||
|
||||
template <typename NanoLeafT>
|
||||
typename std::enable_if<!std::is_same<bool, typename NanoLeafT::BuildType>::value &&
|
||||
!std::is_same<ValueMask, typename NanoLeafT::BuildType>::value &&
|
||||
!std::is_same<Fp4, typename NanoLeafT::BuildType>::value &&
|
||||
!std::is_same<Fp8, typename NanoLeafT::BuildType>::value &&
|
||||
!std::is_same<Fp16,typename NanoLeafT::BuildType>::value &&
|
||||
!std::is_same<FpN, typename NanoLeafT::BuildType>::value,
|
||||
OpenNode0*>::type
|
||||
process(const NanoLeafT* node);
|
||||
template<int LEVEL, typename NanoBuildT>
|
||||
util::disable_if_t<BuildTraits<NanoBuildT>::is_index || LEVEL != 0>
|
||||
process(trait::OpenNodeT<NanoBuildT, LEVEL>*, const NanoNodeT<NanoBuildT, LEVEL>*);
|
||||
|
||||
template <typename NanoLeafT>
|
||||
typename std::enable_if<std::is_same<Fp4, typename NanoLeafT::BuildType>::value ||
|
||||
std::is_same<Fp8, typename NanoLeafT::BuildType>::value ||
|
||||
std::is_same<Fp16,typename NanoLeafT::BuildType>::value ||
|
||||
std::is_same<FpN, typename NanoLeafT::BuildType>::value,
|
||||
OpenNode0*>::type
|
||||
process(const NanoLeafT* node);
|
||||
|
||||
template <typename NanoLeafT>
|
||||
typename std::enable_if<std::is_same<ValueMask, typename NanoLeafT::BuildType>::value,
|
||||
OpenNode0*>::type
|
||||
process(const NanoLeafT* node);
|
||||
|
||||
template <typename NanoLeafT>
|
||||
typename std::enable_if<std::is_same<bool, typename NanoLeafT::BuildType>::value,
|
||||
OpenNode0*>::type
|
||||
process(const NanoLeafT* node);
|
||||
|
||||
/// converts nanovdb value types to openvdb value types, e.g. nanovdb::Vec3f& -> openvdb::Vec3f&
|
||||
static const OpenValueT& Convert(const NanoValueT &v) {return reinterpret_cast<const OpenValueT&>(v);}
|
||||
static const OpenValueT* Convert(const NanoValueT *v) {return reinterpret_cast<const OpenValueT*>(v);}
|
||||
template<int LEVEL, typename NanoIndexT, typename NanoValueT>
|
||||
util::enable_if_t<BuildTraits<NanoIndexT>::is_index && LEVEL == 0>
|
||||
process(trait::OpenNodeT<NanoValueT, LEVEL>*, const NanoNodeT<NanoIndexT, LEVEL>*, const NanoValueT*);
|
||||
|
||||
}; // NanoToOpenVDB class
|
||||
|
||||
template<typename NanoBuildT>
|
||||
typename NanoToOpenVDB<NanoBuildT>::OpenGridT::Ptr
|
||||
NanoToOpenVDB<NanoBuildT>::operator()(const NanoGrid<NanoBuildT>& grid, int /*verbose*/)
|
||||
{
|
||||
// since the input nanovdb grid might use nanovdb types (Coord, Mask, Vec3) we cast to use openvdb types
|
||||
const NanoGridT *srcGrid = reinterpret_cast<const NanoGridT*>(&grid);
|
||||
// ================================================================================================
|
||||
|
||||
auto dstGrid = openvdb::createGrid<OpenGridT>(Convert(srcGrid->tree().background()));
|
||||
dstGrid->setName(srcGrid->gridName()); // set grid name
|
||||
switch (srcGrid->gridClass()) { // set grid class
|
||||
case nanovdb::GridClass::LevelSet:
|
||||
dstGrid->setGridClass(openvdb::GRID_LEVEL_SET);
|
||||
break;
|
||||
case nanovdb::GridClass::FogVolume:
|
||||
dstGrid->setGridClass(openvdb::GRID_FOG_VOLUME);
|
||||
break;
|
||||
case nanovdb::GridClass::Staggered:
|
||||
dstGrid->setGridClass(openvdb::GRID_STAGGERED);
|
||||
break;
|
||||
case nanovdb::GridClass::PointIndex:
|
||||
throw std::runtime_error("NanoToOpenVDB does not yet support PointIndexGrids");
|
||||
case nanovdb::GridClass::PointData:
|
||||
throw std::runtime_error("NanoToOpenVDB does not yet support PointDataGrids");
|
||||
default:
|
||||
dstGrid->setGridClass(openvdb::GRID_UNKNOWN);
|
||||
}
|
||||
// set transform
|
||||
const nanovdb::Map& nanoMap = reinterpret_cast<const GridData*>(srcGrid)->mMap;
|
||||
auto mat = openvdb::math::Mat4<double>::identity();
|
||||
template<typename NanoBuildT>
|
||||
util::disable_if_t<BuildTraits<NanoBuildT>::is_index, trait::OpenGridPtr<NanoBuildT>>
|
||||
NanoToOpenVDB::operator()(const NanoGrid<NanoBuildT>& srcGrid)
|
||||
{
|
||||
// Create an empty OpenVDB destination grid
|
||||
auto dstGrid = openvdb::createGrid<trait::OpenGrid<NanoBuildT>>(*trait::mapPtr(&srcGrid.tree().background()));
|
||||
dstGrid->setName(srcGrid.gridName()); // set grid name
|
||||
dstGrid->setGridClass(trait::toOpenGridClass(srcGrid.gridClass()));
|
||||
|
||||
// set world to index transform
|
||||
const Map& nanoMap = reinterpret_cast<const GridData&>(srcGrid).mMap;
|
||||
auto mat = openvdb::math::Mat4<double>::identity();
|
||||
mat.setMat3(openvdb::math::Mat3<double>(nanoMap.mMatD));
|
||||
mat = mat.transpose(); // the 3x3 in nanovdb is transposed relative to openvdb's 3x3
|
||||
mat.setTranslation(openvdb::math::Vec3<double>(nanoMap.mVecD));
|
||||
dstGrid->setTransform(openvdb::math::Transform::createLinearTransform(mat)); // calls simplify!
|
||||
|
||||
// process root node
|
||||
// process root node and recursively call its child inner nodes
|
||||
auto &root = dstGrid->tree().root();
|
||||
auto *data = srcGrid->tree().root().data();
|
||||
auto *data = srcGrid.tree().root().data();
|
||||
for (uint32_t i=0; i<data->mTableSize; ++i) {
|
||||
auto *tile = data->tile(i);
|
||||
if (tile->isChild()) {
|
||||
root.addChild( this->process( data->getChild(tile)) );
|
||||
auto *dstChild = new trait::OpenNodeT<NanoBuildT, 2>();// un-initialized
|
||||
this->template process<2, NanoBuildT>( dstChild, data->getChild(tile) );
|
||||
root.addChild( dstChild );
|
||||
} else {
|
||||
root.addTile(tile->origin(), Convert(tile->value), tile->state);
|
||||
root.addTile(trait::mapCoord(tile->origin()), *trait::mapPtr(&tile->value), tile->state);
|
||||
}
|
||||
}
|
||||
|
||||
return dstGrid;
|
||||
}
|
||||
}// NanoToOpenVDB::operator()(const NanoGrid<NanoBuildT>& grid)
|
||||
|
||||
template<typename T>
|
||||
template<typename SrcNodeT, typename DstNodeT>
|
||||
DstNodeT*
|
||||
NanoToOpenVDB<T>::processNode(const SrcNodeT *srcNode)
|
||||
template<typename NanoIndexT, typename NanoValueT>
|
||||
util::enable_if_t<BuildTraits<NanoIndexT>::is_index, trait::OpenGridPtr<NanoValueT>>
|
||||
NanoToOpenVDB::operator()(const NanoGrid<NanoIndexT>& indexGrid,
|
||||
const NanoValueT *sideCar,
|
||||
GridClass gridClass,
|
||||
const char *name)
|
||||
{
|
||||
DstNodeT *dstNode = new DstNodeT(); // un-initialized for fast construction
|
||||
dstNode->setOrigin(srcNode->origin());
|
||||
const auto& childMask = srcNode->childMask();
|
||||
const_cast<typename DstNodeT::NodeMaskType&>(dstNode->getValueMask()) = srcNode->valueMask();
|
||||
// Create an empty OpenVDB destination grid
|
||||
auto dstGrid = openvdb::createGrid<trait::OpenGrid<NanoValueT>>(*trait::mapPtr(sideCar));//background is always the first value
|
||||
if (name) dstGrid->setName(name); // set grid name
|
||||
dstGrid->setGridClass(trait::toOpenGridClass(gridClass));
|
||||
|
||||
// set world to index transform
|
||||
const Map& nanoMap = reinterpret_cast<const GridData&>(indexGrid).mMap;
|
||||
auto mat = openvdb::math::Mat4<double>::identity();
|
||||
mat.setMat3(openvdb::math::Mat3<double>(nanoMap.mMatD));
|
||||
mat = mat.transpose(); // the 3x3 in nanovdb is transposed relative to openvdb's 3x3
|
||||
mat.setTranslation(openvdb::math::Vec3<double>(nanoMap.mVecD));
|
||||
dstGrid->setTransform(openvdb::math::Transform::createLinearTransform(mat)); // calls simplify!
|
||||
|
||||
// process root node and recursively call its child inner nodes
|
||||
auto &root = dstGrid->tree().root();
|
||||
auto *data = indexGrid.tree().root().data();
|
||||
for (uint32_t i=0; i<data->mTableSize; ++i) {
|
||||
auto *tile = data->tile(i);
|
||||
if (tile->isChild()) {
|
||||
auto *dstChild = new trait::OpenNodeT<NanoValueT, 2>();// un-initialized
|
||||
this->template process<2, NanoIndexT, NanoValueT>( dstChild, data->getChild(tile), sideCar );
|
||||
root.addChild( dstChild );
|
||||
} else {
|
||||
root.addTile(trait::mapCoord(tile->origin()), *trait::mapPtr(sideCar + tile->value), tile->state);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(util::is_same_v<NanoIndexT, ValueOnIndex>) {
|
||||
if (gridClass == GridClass::LevelSet) openvdb::tools::signedFloodFill(dstGrid->tree());
|
||||
}
|
||||
|
||||
return dstGrid;
|
||||
}// NanoToOpenVDB::operator()(const NanoGrid<ValueIndex>& grid, const NanoValueT*)
|
||||
|
||||
template<typename NanoIndexT>
|
||||
util::enable_if_t<BuildTraits<NanoIndexT>::is_index, openvdb::GridBase::Ptr>
|
||||
NanoToOpenVDB::operator()(const NanoGrid<NanoIndexT>& idxGrid, int blindDataID)
|
||||
{
|
||||
for (uint32_t i=0; i<idxGrid.blindDataCount(); ++i) {
|
||||
if (blindDataID >= 0 && int(i) != blindDataID) continue;
|
||||
const auto &metaData = idxGrid.blindMetaData(i);
|
||||
if (metaData.mDataClass != GridBlindDataClass::ChannelArray ||
|
||||
idxGrid.valueCount() != metaData.mValueCount) continue;
|
||||
|
||||
const GridClass gridClass = toGridClass(metaData.mSemantic);
|
||||
switch (metaData.mDataType){
|
||||
case GridType::Float:
|
||||
NANOVDB_ASSERT(metaData.mValueSize == sizeof(float));
|
||||
return (*this)(idxGrid, idxGrid.template getBlindData<float>(i), gridClass);
|
||||
case GridType::Double:
|
||||
NANOVDB_ASSERT(metaData.mValueSize == sizeof(double));
|
||||
return (*this)(idxGrid, idxGrid.template getBlindData<double>(i), gridClass);
|
||||
case GridType::Int32:
|
||||
NANOVDB_ASSERT(metaData.mValueSize == sizeof(int32_t));
|
||||
return (*this)(idxGrid, idxGrid.template getBlindData<int32_t>(i), gridClass);
|
||||
case GridType::Vec3f:
|
||||
NANOVDB_ASSERT(metaData.mValueSize == sizeof(Vec3f));
|
||||
return (*this)(idxGrid, idxGrid.template getBlindData<Vec3f>(i), gridClass);
|
||||
default:// required to avoid compiler warning
|
||||
break;
|
||||
}
|
||||
}
|
||||
OPENVDB_THROW(openvdb::RuntimeError, "No valid sidecar located in the blind data!");
|
||||
}// NanoToOpenVDB::operator()(const NanoGrid<NanoIndexT>& idxGrid)
|
||||
|
||||
// ================================================================================================
|
||||
|
||||
template<int LEVEL, typename NanoBuildT>
|
||||
util::disable_if_t<BuildTraits<NanoBuildT>::is_index || LEVEL == 0>
|
||||
NanoToOpenVDB::process(trait::OpenNodeT<NanoBuildT, LEVEL> *dstNode,
|
||||
const NanoNodeT<NanoBuildT, LEVEL> *srcNode)
|
||||
{
|
||||
using DstNodeT = trait::OpenNodeT<NanoBuildT, LEVEL>;
|
||||
using SrcNodeT = NanoNodeT<NanoBuildT, LEVEL>;
|
||||
dstNode->setOrigin(trait::mapCoord(srcNode->origin()));
|
||||
const auto &childMask = trait::mapMask(srcNode->childMask());
|
||||
const auto &valueMask = trait::mapMask(srcNode->valueMask());
|
||||
const_cast<typename DstNodeT::NodeMaskType&>(dstNode->getValueMask()) = valueMask;
|
||||
const_cast<typename DstNodeT::NodeMaskType&>(dstNode->getChildMask()) = childMask;
|
||||
auto* dstTable = const_cast<typename DstNodeT::UnionType*>(dstNode->getTable());
|
||||
auto* srcData = srcNode->data();
|
||||
@@ -198,13 +336,15 @@ NanoToOpenVDB<T>::processNode(const SrcNodeT *srcNode)
|
||||
if (childMask.isOn(n)) {
|
||||
childNodes.emplace_back(n, srcData->getChild(n));
|
||||
} else {
|
||||
dstTable[n].setValue(Convert(srcData->mTable[n].value));
|
||||
dstTable[n].setValue(*trait::mapPtr(&srcData->mTable[n].value));
|
||||
}
|
||||
}
|
||||
auto kernel = [&](const auto& r) {
|
||||
for (auto i = r.begin(); i != r.end(); ++i) {
|
||||
auto &p = childNodes[i];
|
||||
dstTable[p.first].setChild( this->process(p.second) );
|
||||
auto* dstChild = new typename DstNodeT::ChildNodeType();// un-initialized
|
||||
this->template process<LEVEL-1, NanoBuildT>(dstChild, p.second);
|
||||
dstTable[p.first].setChild( dstChild );
|
||||
}
|
||||
};
|
||||
|
||||
@@ -213,129 +353,179 @@ NanoToOpenVDB<T>::processNode(const SrcNodeT *srcNode)
|
||||
#else
|
||||
util::forEach(0, childCount, 1, kernel);
|
||||
#endif
|
||||
return dstNode;
|
||||
} // processNode
|
||||
} // NanoToOpenVDB::process(const SrcNodeT *srcNode, DstNodeT *dstNode)
|
||||
|
||||
template<typename T>
|
||||
template <typename NanoLeafT>
|
||||
inline typename std::enable_if<!std::is_same<bool, typename NanoLeafT::BuildType>::value &&
|
||||
!std::is_same<ValueMask, typename NanoLeafT::BuildType>::value &&
|
||||
!std::is_same<Fp4, typename NanoLeafT::BuildType>::value &&
|
||||
!std::is_same<Fp8, typename NanoLeafT::BuildType>::value &&
|
||||
!std::is_same<Fp16,typename NanoLeafT::BuildType>::value &&
|
||||
!std::is_same<FpN, typename NanoLeafT::BuildType>::value,
|
||||
typename NanoToOpenVDB<T>::OpenNode0*>::type
|
||||
NanoToOpenVDB<T>::process(const NanoLeafT *srcNode)
|
||||
template<int LEVEL, typename NanoIndexT, typename NanoValueT>
|
||||
util::enable_if_t<BuildTraits<NanoIndexT>::is_index && LEVEL != 0>
|
||||
NanoToOpenVDB::process(trait::OpenNodeT<NanoValueT, LEVEL> *dstNode,
|
||||
const NanoNodeT<NanoIndexT, LEVEL> *srcNode,
|
||||
const NanoValueT* sideCar)
|
||||
{
|
||||
static_assert(std::is_same<NanoLeafT, NanoNode0>::value, "NanoToOpenVDB<FpN>::process assert failed");
|
||||
OpenNode0* dstNode = new OpenNode0(); // un-initialized for fast construction
|
||||
dstNode->setOrigin(srcNode->origin());
|
||||
dstNode->setValueMask(srcNode->valueMask());
|
||||
|
||||
const auto* src = Convert(srcNode->data()->mValues);// doesn't work for compressed data, bool or ValueMask
|
||||
for (auto *dst = dstNode->buffer().data(), *end = dst + OpenNode0::SIZE; dst != end; dst += 4, src += 4) {
|
||||
dst[0] = src[0];
|
||||
dst[1] = src[1];
|
||||
dst[2] = src[2];
|
||||
dst[3] = src[3];
|
||||
using DstNodeT = trait::OpenNodeT<NanoValueT, LEVEL>;
|
||||
using SrcNodeT = NanoNodeT<NanoIndexT, LEVEL>;
|
||||
dstNode->setOrigin(trait::mapCoord(srcNode->origin()));
|
||||
const auto &childMask = trait::mapMask(srcNode->childMask());
|
||||
const auto &valueMask = trait::mapMask(srcNode->valueMask());
|
||||
const_cast<typename DstNodeT::NodeMaskType&>(dstNode->getValueMask()) = valueMask;
|
||||
const_cast<typename DstNodeT::NodeMaskType&>(dstNode->getChildMask()) = childMask;
|
||||
auto* dstTable = const_cast<typename DstNodeT::UnionType*>(dstNode->getTable());
|
||||
auto* srcData = srcNode->data();
|
||||
std::vector<std::pair<uint32_t, const typename SrcNodeT::ChildNodeType*>> childNodes;
|
||||
const auto childCount = childMask.countOn();
|
||||
childNodes.reserve(childCount);
|
||||
for (uint32_t n = 0; n < DstNodeT::NUM_VALUES; ++n) {
|
||||
if (childMask.isOn(n)) {
|
||||
childNodes.emplace_back(n, srcData->getChild(n));
|
||||
} else {
|
||||
dstTable[n].setValue(*trait::mapPtr(sideCar + srcData->mTable[n].value));
|
||||
}
|
||||
}
|
||||
auto kernel = [&](const auto& r) {
|
||||
for (auto i = r.begin(); i != r.end(); ++i) {
|
||||
auto &p = childNodes[i];
|
||||
auto *dstChild = new typename DstNodeT::ChildNodeType();// un-initialized
|
||||
this->template process<LEVEL-1, NanoIndexT, NanoValueT>(dstChild, p.second, sideCar);
|
||||
dstTable[p.first].setChild( dstChild );
|
||||
}
|
||||
};
|
||||
|
||||
return dstNode;
|
||||
} // process(NanoNode0)
|
||||
#if 0
|
||||
kernel(Range1D(0, childCount));
|
||||
#else
|
||||
util::forEach(0, childCount, 1, kernel);
|
||||
#endif
|
||||
} // NanoToOpenVDB::process(const SrcNodeT* srcNode, const ValueT* sideCar)
|
||||
|
||||
template<typename T>
|
||||
template <typename NanoLeafT>
|
||||
inline typename std::enable_if<std::is_same<Fp4, typename NanoLeafT::BuildType>::value ||
|
||||
std::is_same<Fp8, typename NanoLeafT::BuildType>::value ||
|
||||
std::is_same<Fp16,typename NanoLeafT::BuildType>::value ||
|
||||
std::is_same<FpN, typename NanoLeafT::BuildType>::value,
|
||||
typename NanoToOpenVDB<T>::OpenNode0*>::type
|
||||
NanoToOpenVDB<T>::process(const NanoLeafT *srcNode)
|
||||
// ================================================================================================
|
||||
|
||||
template<int LEVEL, typename NanoBuildT>
|
||||
util::disable_if_t<BuildTraits<NanoBuildT>::is_index || LEVEL != 0>
|
||||
NanoToOpenVDB::process(trait::OpenNodeT<NanoBuildT, LEVEL> *dstLeaf,
|
||||
const NanoNodeT<NanoBuildT, LEVEL> *srcLeaf)
|
||||
{
|
||||
static_assert(std::is_same<NanoLeafT, NanoNode0>::value, "NanoToOpenVDB<T>::process assert failed");
|
||||
OpenNode0* dstNode = new OpenNode0(); // un-initialized for fast construction
|
||||
dstNode->setOrigin(srcNode->origin());
|
||||
dstNode->setValueMask(srcNode->valueMask());
|
||||
float *dst = dstNode->buffer().data();
|
||||
for (int i=0; i!=512; i+=4) {
|
||||
*dst++ = srcNode->getValue(i);
|
||||
*dst++ = srcNode->getValue(i+1);
|
||||
*dst++ = srcNode->getValue(i+2);
|
||||
*dst++ = srcNode->getValue(i+3);
|
||||
dstLeaf->setOrigin(trait::mapCoord(srcLeaf->origin()));
|
||||
dstLeaf->setValueMask(trait::mapMask(srcLeaf->valueMask()));
|
||||
|
||||
if constexpr(!BuildTraits<NanoBuildT>::is_special) {
|
||||
const auto *src = trait::mapPtr(srcLeaf->data()->mValues);// doesn't work for compressed data, bool or ValueMask
|
||||
#if 1
|
||||
std::copy(src, src + 512, dstLeaf->buffer().data());
|
||||
#else
|
||||
for (auto *dst = dstLeaf->buffer().data(), *end = dst + 512; dst != end; dst += 4, src += 4) {
|
||||
dst[0] = src[0];
|
||||
dst[1] = src[1];
|
||||
dst[2] = src[2];
|
||||
dst[3] = src[3];
|
||||
}
|
||||
#endif
|
||||
} else if constexpr(BuildTraits<NanoBuildT>::is_Fp) {
|
||||
float *dst = dstLeaf->buffer().data();
|
||||
for (int i=0; i!=512; i+=4) {
|
||||
*dst++ = srcLeaf->getValue(i);
|
||||
*dst++ = srcLeaf->getValue(i+1);
|
||||
*dst++ = srcLeaf->getValue(i+2);
|
||||
*dst++ = srcLeaf->getValue(i+3);
|
||||
}
|
||||
} else if constexpr(util::is_same_v<NanoBuildT, bool>) {
|
||||
reinterpret_cast<openvdb::util::NodeMask<3>&>(dstLeaf->buffer()) = trait::mapMask(srcLeaf->data()->mValues);
|
||||
} else if constexpr(!util::is_same_v<NanoBuildT, ValueMask>) {
|
||||
OPENVDB_THROW(openvdb::RuntimeError, "Unsupported NanoBuildT in NanoToOpenVDB::process(NanoLeaf<NanoBuildT>)");
|
||||
}
|
||||
}// NanoToOpenVDB::process(const NanoLeaf<NanoBuildT>* srcLeaf)
|
||||
|
||||
return dstNode;
|
||||
} // process(NanoNode0)
|
||||
|
||||
template<typename T>
|
||||
template <typename NanoLeafT>
|
||||
inline typename std::enable_if<std::is_same<ValueMask, typename NanoLeafT::BuildType>::value,
|
||||
typename NanoToOpenVDB<T>::OpenNode0*>::type
|
||||
NanoToOpenVDB<T>::process(const NanoLeafT *srcNode)
|
||||
template<int LEVEL, typename NanoIndexT, typename NanoValueT>
|
||||
util::enable_if_t<BuildTraits<NanoIndexT>::is_index && LEVEL == 0>
|
||||
NanoToOpenVDB::process(trait::OpenNodeT<NanoValueT, LEVEL> *dstLeaf,
|
||||
const NanoNodeT<NanoIndexT, LEVEL> *srcLeaf,
|
||||
const NanoValueT *sideCar)
|
||||
{
|
||||
static_assert(std::is_same<NanoLeafT, NanoNode0>::value, "NanoToOpenVDB<ValueMask>::process assert failed");
|
||||
OpenNode0* dstNode = new OpenNode0(); // un-initialized for fast construction
|
||||
dstNode->setOrigin(srcNode->origin());
|
||||
dstNode->setValueMask(srcNode->valueMask());
|
||||
dstLeaf->setOrigin(trait::mapCoord(srcLeaf->origin()));
|
||||
dstLeaf->setValueMask(trait::mapMask(srcLeaf->valueMask()));
|
||||
|
||||
return dstNode;
|
||||
} // process(NanoNode0)
|
||||
if constexpr(!BuildTraits<NanoValueT>::is_special) {
|
||||
if constexpr(util::is_same_v<NanoIndexT, ValueIndex>) {
|
||||
const auto *src = trait::mapPtr(sideCar + srcLeaf->data()->mOffset);
|
||||
#if 1
|
||||
std::copy(src, src + 512, dstLeaf->buffer().data());
|
||||
#else
|
||||
for (auto *dst = dstLeaf->buffer().data(), *end = dst + 512; dst != end; dst += 4, src += 4) {
|
||||
dst[0] = src[0];
|
||||
dst[1] = src[1];
|
||||
dst[2] = src[2];
|
||||
dst[3] = src[3];
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
const auto* src = trait::mapPtr(sideCar);
|
||||
auto *dst = dstLeaf->buffer().data();
|
||||
for (int i = 0; i < 512; i += 4) {
|
||||
*dst++ = src[srcLeaf->getValue(i )];
|
||||
*dst++ = src[srcLeaf->getValue(i+1)];
|
||||
*dst++ = src[srcLeaf->getValue(i+2)];
|
||||
*dst++ = src[srcLeaf->getValue(i+3)];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
OPENVDB_THROW(openvdb::RuntimeError, "Unsupported NanoValueT in NanoToOpenVDB::process(NanoLeaf<NanoIndexT>, NanoValueT*)");
|
||||
}
|
||||
}// NanoToOpenVDB::process(const NanoLeaf<NanoIndexT>* srcLeaf, const NanoValueT *sideCar)
|
||||
|
||||
template<typename T>
|
||||
template <typename NanoLeafT>
|
||||
inline typename std::enable_if<std::is_same<bool, typename NanoLeafT::BuildType>::value,
|
||||
typename NanoToOpenVDB<T>::OpenNode0*>::type
|
||||
NanoToOpenVDB<T>::process(const NanoLeafT *srcNode)
|
||||
{
|
||||
static_assert(std::is_same<NanoLeafT, NanoNode0>::value, "NanoToOpenVDB<ValueMask>::process assert failed");
|
||||
OpenNode0* dstNode = new OpenNode0(); // un-initialized for fast construction
|
||||
dstNode->setOrigin(srcNode->origin());
|
||||
dstNode->setValueMask(srcNode->valueMask());
|
||||
reinterpret_cast<openvdb::util::NodeMask<3>&>(dstNode->buffer()) = srcNode->data()->mValues;
|
||||
|
||||
return dstNode;
|
||||
} // process(NanoNode0)
|
||||
// ================================================================================================
|
||||
|
||||
template<typename NanoBuildT>
|
||||
inline typename openvdb::Grid<typename openvdb::tree::Tree4<typename ConvertTrait<NanoBuildT>::Type>::Type>::Ptr
|
||||
nanoToOpenVDB(const NanoGrid<NanoBuildT>& grid, int verbose)
|
||||
auto nanoToOpenVDB(const NanoGrid<NanoBuildT>& grid)
|
||||
{
|
||||
NanoToOpenVDB<NanoBuildT> tmp;
|
||||
return tmp(grid, verbose);
|
||||
NanoToOpenVDB tmp;
|
||||
return tmp(grid);
|
||||
}
|
||||
|
||||
template<typename NanoBuildT, typename NanoValueT>
|
||||
util::enable_if_t<BuildTraits<NanoBuildT>::is_index, trait::OpenGridPtr<NanoValueT>>
|
||||
nanoToOpenVDB(const NanoGrid<NanoBuildT>& grid,
|
||||
const NanoValueT *sideCar,
|
||||
GridClass gridClass,
|
||||
const char *gridName)
|
||||
{
|
||||
NanoToOpenVDB tmp;
|
||||
return tmp(grid, sideCar, gridClass, gridName);
|
||||
}
|
||||
|
||||
template<typename BufferT>
|
||||
openvdb::GridBase::Ptr
|
||||
nanoToOpenVDB(const GridHandle<BufferT>& handle, int verbose, uint32_t n)
|
||||
nanoToOpenVDB(const GridHandle<BufferT>& handle, uint32_t n)
|
||||
{
|
||||
if (auto grid = handle.template grid<float>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<double>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<int32_t>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<int64_t>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<bool>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<nanovdb::Fp4>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<nanovdb::Fp8>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<nanovdb::Fp16>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<nanovdb::FpN>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<nanovdb::ValueMask>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<nanovdb::Vec3f>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<nanovdb::Vec3d>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<nanovdb::Vec4f>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<nanovdb::Vec4d>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<Fp4>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<Fp8>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<Fp16>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<FpN>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<ValueMask>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<Vec3f>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<Vec3d>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<Vec4f>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<Vec4d>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<ValueOnIndex>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else if (auto grid = handle.template grid<ValueIndex>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid);
|
||||
} else {
|
||||
OPENVDB_THROW(openvdb::RuntimeError, "Unsupported NanoVDB grid type!");
|
||||
}
|
||||
@@ -346,19 +536,17 @@ nanoToOpenVDB(const GridHandle<BufferT>& handle, int verbose, uint32_t n)
|
||||
/// @brief Forward declaration of free-standing function that de-serializes a typed NanoVDB grid into an OpenVDB Grid
|
||||
template<typename NanoBuildT>
|
||||
[[deprecated("Use nanovdb::tools::nanoToOpenVDB instead.")]]
|
||||
typename openvdb::Grid<typename openvdb::tree::Tree4<typename ConvertTrait<NanoBuildT>::Type>::Type>::Ptr
|
||||
nanoToOpenVDB(const NanoGrid<NanoBuildT>& grid, int verbose = 0)
|
||||
tools::trait::OpenGridPtr<NanoBuildT> nanoToOpenVDB(const NanoGrid<NanoBuildT>& grid)
|
||||
{
|
||||
return tools::nanoToOpenVDB(grid, verbose);
|
||||
return tools::nanoToOpenVDB(grid);
|
||||
}
|
||||
|
||||
/// @brief Forward declaration of free-standing function that de-serializes a NanoVDB GridHandle into an OpenVDB GridBase
|
||||
template<typename BufferT>
|
||||
[[deprecated("Use nanovdb::tools::nanoToOpenVDB instead.")]]
|
||||
openvdb::GridBase::Ptr
|
||||
nanoToOpenVDB(const GridHandle<BufferT>& handle, int verbose = 0, uint32_t n = 0)
|
||||
openvdb::GridBase::Ptr nanoToOpenVDB(const GridHandle<BufferT>& handle, uint32_t n = 0)
|
||||
{
|
||||
return tools::nanoToOpenVDB(handle, verbose, n);
|
||||
return tools::nanoToOpenVDB(handle, n);
|
||||
}
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/VoxelBlockManager.h
|
||||
|
||||
\author Eftychios Sifakis
|
||||
|
||||
\date July 24, 2025
|
||||
|
||||
\brief Implements a handle class holding the metadata involved in invocations
|
||||
of the VoxelBlockManager structure.
|
||||
The VoxelBlockManager is an acceleration structure for sequential access
|
||||
and stencil operations over solely the active voxels of an OnIndexGrid
|
||||
which enables SIMT parallelism independent of occupancy.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NANOVDB_VOXELBLOCKMANAGER_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_VOXELBLOCKMANAGER_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools {
|
||||
|
||||
/// @brief This class serves to manage the raw memory buffers for the metadata of a NanoVDB VoxelBlockManager
|
||||
template<typename BufferT>
|
||||
class VoxelBlockManagerHandle
|
||||
{
|
||||
BufferT mFirstLeafID;
|
||||
BufferT mJumpMap;
|
||||
uint64_t mBlockCount{0};
|
||||
uint64_t mFirstOffset{0};
|
||||
uint64_t mLastOffset{0};
|
||||
uint32_t mLowerCount{0};
|
||||
|
||||
public:
|
||||
/// @brief Move constructor from metadata buffers
|
||||
VoxelBlockManagerHandle(BufferT&& firstLeafID, BufferT&& jumpMap, const uint64_t blockCount,
|
||||
const uint64_t firstOffset, const uint64_t lastOffset, const uint64_t lowerCount) {
|
||||
mFirstLeafID = std::move(firstLeafID);
|
||||
mJumpMap = std::move(jumpMap);
|
||||
mBlockCount = blockCount;
|
||||
mFirstOffset = firstOffset;
|
||||
mLastOffset = lastOffset;
|
||||
mLowerCount = lowerCount;
|
||||
}
|
||||
|
||||
VoxelBlockManagerHandle() = default;
|
||||
VoxelBlockManagerHandle(const VoxelBlockManagerHandle&) = delete;
|
||||
VoxelBlockManagerHandle& operator=(const VoxelBlockManagerHandle&) = delete;
|
||||
|
||||
VoxelBlockManagerHandle& operator=(VoxelBlockManagerHandle&& other) noexcept {
|
||||
mFirstLeafID = std::move(other.mFirstLeafID);
|
||||
mJumpMap = std::move(other.mJumpMap);
|
||||
mBlockCount = other.mblockCount;
|
||||
mFirstOffset = other.mfirstOffset;
|
||||
mLastOffset = other.mlastOffset;
|
||||
mLowerCount = other.mlowerCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VoxelBlockManagerHandle(VoxelBlockManagerHandle&& other) noexcept {
|
||||
mFirstLeafID = std::move(other.mFirstLeafID);
|
||||
mJumpMap = std::move(other.mJumpMap);
|
||||
mBlockCount = other.mblockCount;
|
||||
mFirstOffset = other.mfirstOffset;
|
||||
mLastOffset = other.mlastOffset;
|
||||
mLowerCount = other.mlowerCount;
|
||||
}
|
||||
|
||||
~VoxelBlockManagerHandle() { this->reset(); }
|
||||
/// @brief clear the buffer
|
||||
void reset() { mFirstLeafID.clear(); mJumpMap.clear(); mBlockCount = 0; }
|
||||
|
||||
/// @brief Returns a non-const pointer to the firstLeafID device-hosted data
|
||||
///
|
||||
/// @warning Note that the return pointer can be NULL if the VoxelBlockManagerHandle was not initialized
|
||||
template<typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, uint32_t*>::type
|
||||
deviceFirstLeafID() { return static_cast<uint32_t*>(mFirstLeafID.deviceData()); }
|
||||
|
||||
/// @brief Returns a const pointer to the firstLeafID device-hosted data
|
||||
///
|
||||
/// @warning Note that the return pointer can be NULL if the VoxelBlockManagerHandle was not initialized
|
||||
template<typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, const uint32_t*>::type
|
||||
deviceFirstLeafID() const { return static_cast<const uint32_t*>(mFirstLeafID.deviceData()); }
|
||||
|
||||
/// @brief Returns a non-const pointer to the jumpMap device-hosted data
|
||||
///
|
||||
/// @warning Note that the return pointer can be NULL if the VoxelBlockManagerHandle was not initialized
|
||||
template<typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, uint64_t*>::type
|
||||
deviceJumpMap() { return static_cast<uint64_t*>(mJumpMap.deviceData()); }
|
||||
|
||||
/// @brief Returns a const pointer to the jumpMap device-hosted data
|
||||
///
|
||||
/// @warning Note that the return pointer can be NULL if the VoxelBlockManagerHandle was not initialized
|
||||
template<typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, const uint64_t*>::type
|
||||
deviceJumpMap() const { return static_cast<const uint64_t*>(mJumpMap.deviceData()); }
|
||||
|
||||
/// @brief Returns the number of voxel blocks in the VoxelBlockManager
|
||||
uint64_t blockCount() const { return mBlockCount; }
|
||||
|
||||
/// @brief Sets the first and last voxel indices (linear offsets) associated with this VoxelBlockManager
|
||||
void setOffsets(const uint64_t firstOffset, const uint64_t lastOffset) { mFirstOffset = firstOffset; mLastOffset = lastOffset; }
|
||||
|
||||
/// @brief Returns the first voxel index (linear offset) associated with this VoxelBlockManager
|
||||
uint64_t firstOffset() const { return mFirstOffset; }
|
||||
|
||||
/// @brief Returns the last voxel index (linear offset) associated with this VoxelBlockManager
|
||||
uint64_t lastOffset() const { return mLastOffset; }
|
||||
|
||||
/// @brief Sets the count of lower internal nodes in the IndexGrid associated with this VoxelBlockManager
|
||||
void setLowerCount(const uint32_t lowerCount) { mLowerCount = lowerCount; }
|
||||
|
||||
/// @brief Returns the count of lower internal nodes in the IndexGrid associated with this VoxelBlockManager
|
||||
uint32_t lowerCount() const { return mLowerCount; }
|
||||
|
||||
/// @brief Allocates new buffers for metadata, adequate for storing a VoxelBlockManager of the specified block size
|
||||
template<int BlockWidthLog2, typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, void>::type
|
||||
deviceResize(const uint64_t blockCount, cudaStream_t stream = 0) {
|
||||
static constexpr int BlockWidth = 1 << BlockWidthLog2;
|
||||
reset();
|
||||
int device = 0;
|
||||
cudaCheck(cudaGetDevice(&device));
|
||||
mBlockCount = blockCount;
|
||||
mFirstLeafID = BufferT::create(blockCount*sizeof(uint32_t), nullptr, device, stream);
|
||||
mJumpMap = BufferT::create(blockCount*(BlockWidth/8), nullptr, device, stream);
|
||||
}
|
||||
|
||||
}; // VoxelBlockManagerHandle
|
||||
|
||||
} // namespace tools
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#if defined(__CUDACC__)
|
||||
#include <nanovdb/tools/cuda/VoxelBlockManager.cuh>
|
||||
#endif// defined(__CUDACC__)
|
||||
|
||||
#endif // NANOVDB_VOXELBLOCKMANAGER_H_HAS_BEEN_INCLUDED
|
||||
@@ -14,8 +14,8 @@
|
||||
to only include it in .cu files (or other .cuh files)
|
||||
*/
|
||||
|
||||
#ifndef NVIDIA_TOOLS_CUDA_ADDBLINDDATA_CUH_HAS_BEEN_INCLUDED
|
||||
#define NVIDIA_TOOLS_CUDA_ADDBLINDDATA_CUH_HAS_BEEN_INCLUDED
|
||||
#ifndef NANOVDB_TOOLS_CUDA_ADDBLINDDATA_CUH_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_TOOLS_CUDA_ADDBLINDDATA_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
@@ -143,4 +143,4 @@ cudaAddBlindData(const NanoGrid<BuildT> *d_grid,
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NVIDIA_TOOLS_CUDA_ADDBLINDDATA_CUH_HAS_BEEN_INCLUDED
|
||||
#endif // NANOVDB_TOOLS_CUDA_ADDBLINDDATA_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/CoarsenGrid.cuh
|
||||
|
||||
\authors Efty Sifakis
|
||||
|
||||
\brief 2x Topological coarsening of NanoVDB indexGrids on the device
|
||||
|
||||
\warning The header file contains cuda device code so be sure
|
||||
to only include it in .cu files (or other .cuh files)
|
||||
*/
|
||||
|
||||
#ifndef NVIDIA_TOOLS_CUDA_COARSENGRID_CUH_HAS_BEEN_INCLUDED
|
||||
#define NVIDIA_TOOLS_CUDA_COARSENGRID_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <cub/cub.cuh>
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/tools/cuda/TopologyBuilder.cuh>
|
||||
#include <nanovdb/util/cuda/DeviceGridTraits.cuh>
|
||||
#include <nanovdb/util/cuda/Morphology.cuh>
|
||||
#include <nanovdb/util/cuda/Timer.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools::cuda {
|
||||
|
||||
template <typename BuildT>
|
||||
class CoarsenGrid
|
||||
{
|
||||
using GridT = NanoGrid<BuildT>;
|
||||
using TreeT = NanoTree<BuildT>;
|
||||
using RootT = NanoRoot<BuildT>;
|
||||
using UpperT = NanoUpper<BuildT>;
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructor
|
||||
/// @param deviceGrid source device grid to be coarsened
|
||||
/// @param stream optional CUDA stream (defaults to CUDA stream 0)
|
||||
CoarsenGrid(const GridT* d_srcGrid, cudaStream_t stream = 0)
|
||||
: mBuilder(stream), mStream(stream), mTimer(stream), mDeviceSrcGrid(d_srcGrid) {}
|
||||
|
||||
/// @brief Toggle on and off verbose mode
|
||||
/// @param level Verbose level: 0=quiet, 1=timing, 2=benchmarking
|
||||
void setVerbose(int level = 1) { mVerbose = level; }
|
||||
|
||||
/// @brief Set the mode for checksum computation, which is disabled by default
|
||||
/// @param mode Mode of checksum computation
|
||||
void setChecksum(CheckMode mode = CheckMode::Disable){mBuilder.mChecksum = mode;}
|
||||
|
||||
/// @brief Creates a handle to the coarsened grid
|
||||
/// @tparam BufferT Buffer type used for allocation of the grid handle
|
||||
/// @param buffer optional buffer (currently ignored)
|
||||
/// @return returns a handle with a grid of type NanoGrid<BuildT>
|
||||
template<typename BufferT = nanovdb::cuda::DeviceBuffer>
|
||||
GridHandle<BufferT>
|
||||
getHandle(const BufferT &buffer = BufferT());
|
||||
|
||||
private:
|
||||
void coarsenRoot();
|
||||
|
||||
void coarsenInternalNodes();
|
||||
|
||||
void processGridTreeRoot();
|
||||
|
||||
void coarsenLeafNodes();
|
||||
|
||||
static constexpr unsigned int mNumThreads = 128;// for kernels spawned via lambdaKernel (others may specialize)
|
||||
static unsigned int numBlocks(unsigned int n) {return (n + mNumThreads - 1) / mNumThreads;}
|
||||
|
||||
TopologyBuilder<BuildT> mBuilder;
|
||||
cudaStream_t mStream{0};
|
||||
util::cuda::Timer mTimer;
|
||||
int mVerbose{0};
|
||||
const GridT *mDeviceSrcGrid;
|
||||
TreeData mSrcTreeData;
|
||||
};// tools::cuda::CoarsenGrid<BuildT>
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
template<typename BufferT>
|
||||
GridHandle<BufferT>
|
||||
CoarsenGrid<BuildT>::getHandle(const BufferT &pool)
|
||||
{
|
||||
// Copy TreeData from GPU -> CPU
|
||||
cudaStreamSynchronize(mStream);
|
||||
mSrcTreeData = util::cuda::DeviceGridTraits<BuildT>::getTreeData(mDeviceSrcGrid);
|
||||
|
||||
// Ensure that the input grid contains no tile values
|
||||
if (mSrcTreeData.mTileCount[2] || mSrcTreeData.mTileCount[1] || mSrcTreeData.mTileCount[0])
|
||||
throw std::runtime_error("Topological operations not supported on grids with value tiles");
|
||||
|
||||
// Coarsen root node
|
||||
if (mVerbose==1) mTimer.start("\nCoarsening root node");
|
||||
coarsenRoot();
|
||||
|
||||
// Allocate memory for coarsened upper/lower masks
|
||||
if (mVerbose==1) mTimer.restart("Allocating internal node mask buffers");
|
||||
mBuilder.allocateInternalMaskBuffers(mStream);
|
||||
|
||||
// Coarsen masks of upper/lower nodes
|
||||
if (mVerbose==1) mTimer.restart("Coarsening internal nodes");
|
||||
coarsenInternalNodes();
|
||||
|
||||
// Enumerate tree nodes
|
||||
if (mVerbose==1) mTimer.restart("Count coarsened tree nodes");
|
||||
mBuilder.countNodes(mStream);
|
||||
|
||||
cudaStreamSynchronize(mStream);
|
||||
|
||||
// Allocate new device grid buffer for coarsened result
|
||||
if (mVerbose==1) mTimer.restart("Allocating coarsened grid buffer");
|
||||
auto buffer = mBuilder.getBuffer(pool, mStream);
|
||||
|
||||
// Process GridData/TreeData/RootData of coarsened result
|
||||
if (mVerbose==1) mTimer.restart("Processing grid/tree/root");
|
||||
processGridTreeRoot();
|
||||
|
||||
// Process upper nodes of coarsened result
|
||||
if (mVerbose==1) mTimer.restart("Processing upper nodes");
|
||||
mBuilder.processUpperNodes(mStream);
|
||||
|
||||
// Process lower nodes of coarsened result
|
||||
if (mVerbose==1) mTimer.restart("Processing lower nodes");
|
||||
mBuilder.processLowerNodes(mStream);
|
||||
|
||||
// Coarsen leaf node active masks into new topology
|
||||
if (mVerbose==1) mTimer.restart("Coarsen leaf nodes");
|
||||
coarsenLeafNodes();
|
||||
|
||||
// Process bounding boxes
|
||||
if (mVerbose==1) mTimer.restart("Processing bounding boxes");
|
||||
mBuilder.processBBox(mStream);
|
||||
|
||||
// Post-process Grid/Tree data
|
||||
if (mVerbose==1) mTimer.restart("Post-processing grid/tree data");
|
||||
mBuilder.postProcessGridTree(mStream);
|
||||
if (mVerbose==1) mTimer.stop();
|
||||
|
||||
cudaStreamSynchronize(mStream);
|
||||
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
}// CoarsenGrid<BuildT>::getHandle
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void CoarsenGrid<BuildT>::coarsenRoot()
|
||||
{
|
||||
// This method coarsens the root tiles, to accommodate for the overall downsamping operation.
|
||||
|
||||
int device = 0;
|
||||
cudaGetDevice(&device);
|
||||
|
||||
std::map<uint64_t, typename RootT::DataType::Tile> coarsenedTiles;
|
||||
|
||||
// This encoding scheme mirrors the one used in PointsToGrid; note that it is different from Tile::key
|
||||
auto coordToKey = [](const Coord &ijk)->uint64_t{
|
||||
// Note: int32_t has a range of -2^31 to 2^31 - 1 whereas uint32_t has a range of 0 to 2^32 - 1
|
||||
static constexpr int64_t kOffset = 1 << 31;
|
||||
return (uint64_t(uint32_t(int64_t(ijk[2]) + kOffset) >> 12) ) | // z is the lower 21 bits
|
||||
(uint64_t(uint32_t(int64_t(ijk[1]) + kOffset) >> 12) << 21) | // y is the middle 21 bits
|
||||
(uint64_t(uint32_t(int64_t(ijk[0]) + kOffset) >> 12) << 42); // x is the upper 21 bits
|
||||
};// coordToKey lambda functor
|
||||
|
||||
if (mSrcTreeData.mVoxelCount) { // If the input grid is not empty
|
||||
// Make a host copy of the Root topology
|
||||
auto deviceSrcRoot = static_cast<const RootT*>(util::PtrAdd(mDeviceSrcGrid, GridT::memUsage() + mSrcTreeData.mNodeOffset[3]));
|
||||
uint64_t rootSize = mSrcTreeData.mNodeOffset[2] - mSrcTreeData.mNodeOffset[3];
|
||||
auto srcRootBuffer = nanovdb::HostBuffer::create(rootSize);
|
||||
cudaCheck(cudaMemcpyAsync(srcRootBuffer.data(), deviceSrcRoot, rootSize, cudaMemcpyDeviceToHost, mStream));
|
||||
auto srcRoot = static_cast<RootT*>(srcRootBuffer.data());
|
||||
|
||||
// Add all root tiles, reordering if necessary
|
||||
for (uint32_t t = 0; t < srcRoot->tileCount(); t++) {
|
||||
auto coarsenedOrigin = util::morphology::coarsenCoord(srcRoot->tile(t)->origin());
|
||||
auto sortKey = coordToKey(coarsenedOrigin); // key used in the radix sort, in accordance with PointsToGrid
|
||||
auto tileKey = RootT::CoordToKey(coarsenedOrigin); // encoding used in the NanoVDB tile
|
||||
typename RootT::Tile coarsenedTile{tileKey}; // Only the key value is needed; child pointer & value will be unused
|
||||
coarsenedTiles.emplace(sortKey, coarsenedTile);
|
||||
}
|
||||
}
|
||||
|
||||
// Package the new root topology into a RootNode plus Tile list; upload to the GPU
|
||||
uint64_t rootSize = RootT::memUsage(coarsenedTiles.size());
|
||||
mBuilder.mProcessedRoot = nanovdb::cuda::DeviceBuffer::create(rootSize);
|
||||
auto coarsenedRootPtr = static_cast<RootT*>(mBuilder.mProcessedRoot.data());
|
||||
coarsenedRootPtr->mTableSize = coarsenedTiles.size();
|
||||
uint32_t t = 0;
|
||||
for (const auto& [key, tile] : coarsenedTiles)
|
||||
*coarsenedRootPtr->tile(t++) = tile;
|
||||
mBuilder.mProcessedRoot.deviceUpload(device, mStream, false);
|
||||
}// CoarsenGrid<BuildT>::coarsenRoot
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void CoarsenGrid<BuildT>::coarsenInternalNodes()
|
||||
{
|
||||
// Computes the masks of upper and (densified) lower internal nodes, as a result of the coarsening operation
|
||||
// Masks of lower internal nodes are densified in the sense that a serialized array of them is allocated,
|
||||
// as if every upper node had a full set of 32^3 lower children
|
||||
if (auto srcLeafCount = mSrcTreeData.mNodeCount[0]) { // Unless it's an empty grid
|
||||
util::cuda::lambdaKernel<<<numBlocks(srcLeafCount), mNumThreads, 0, mStream>>>(
|
||||
srcLeafCount, util::morphology::cuda::CoarsenInternalNodesFunctor<BuildT>(),
|
||||
mDeviceSrcGrid, mBuilder.deviceProcessedRoot(), mBuilder.mUpperMasks.deviceData(), mBuilder.mLowerMasks.deviceData() );
|
||||
}
|
||||
}// CoarsenGrid<BuildT>::coarsenInternalNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template <typename BuildT>
|
||||
void CoarsenGrid<BuildT>::processGridTreeRoot()
|
||||
{
|
||||
// Copy GridData from source grid
|
||||
// By convention: this will duplicate grid name and map. Others will be reset later
|
||||
cudaCheck(cudaMemcpyAsync(&mBuilder.data()->getGrid(), mDeviceSrcGrid->data(), GridT::memUsage(), cudaMemcpyDeviceToDevice, mStream));
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, mStream>>>(1, topology::detail::BuildGridTreeRootFunctor<BuildT>(), mBuilder.deviceData());
|
||||
cudaCheckError();
|
||||
}// CoarsenGrid<BuildT>::processGridTreeRoot
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void CoarsenGrid<BuildT>::coarsenLeafNodes()
|
||||
{
|
||||
// Coarsens the active masks of the source grid (as indicated at the leaf level), into a new grid that
|
||||
// has been already topologically coarsened to include all necessary leaf nodes.
|
||||
auto srcLeafCount = mSrcTreeData.mNodeCount[0];
|
||||
if (srcLeafCount) { // Unless grid is empty
|
||||
util::cuda::lambdaKernel<<<numBlocks(srcLeafCount), mNumThreads, 0, mStream>>>(
|
||||
srcLeafCount, util::morphology::cuda::CoarsenLeafMasksFunctor<BuildT>(), mDeviceSrcGrid, &mBuilder.data()->getGrid());
|
||||
}
|
||||
|
||||
// Update leaf offsets and prefix sums
|
||||
mBuilder.processLeafOffsets(mStream);
|
||||
}// CoarsenGrid<BuildT>::coarsenLeafNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NVIDIA_TOOLS_CUDA_COARSENGRID_CUH_HAS_BEEN_INCLUDED
|
||||
@@ -0,0 +1,296 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/DilateGrid.cuh
|
||||
|
||||
\authors Efty Sifakis
|
||||
|
||||
\brief Morphological dilation of NanoVDB indexGrids on the device
|
||||
|
||||
\warning The header file contains cuda device code so be sure
|
||||
to only include it in .cu files (or other .cuh files)
|
||||
*/
|
||||
|
||||
#ifndef NVIDIA_TOOLS_CUDA_DILATEGRID_CUH_HAS_BEEN_INCLUDED
|
||||
#define NVIDIA_TOOLS_CUDA_DILATEGRID_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <cub/cub.cuh>
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/tools/cuda/TopologyBuilder.cuh>
|
||||
#include <nanovdb/util/cuda/DeviceGridTraits.cuh>
|
||||
#include <nanovdb/util/cuda/Morphology.cuh>
|
||||
#include <nanovdb/util/cuda/Timer.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools::cuda {
|
||||
|
||||
template <typename BuildT>
|
||||
class DilateGrid
|
||||
{
|
||||
using GridT = NanoGrid<BuildT>;
|
||||
using TreeT = NanoTree<BuildT>;
|
||||
using RootT = NanoRoot<BuildT>;
|
||||
using UpperT = NanoUpper<BuildT>;
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructor
|
||||
/// @param deviceGrid source device grid to be dilated
|
||||
/// @param stream optional CUDA stream (defaults to CUDA stream 0)
|
||||
DilateGrid(const GridT* d_srcGrid, cudaStream_t stream = 0)
|
||||
: mBuilder(stream), mStream(stream), mTimer(stream), mDeviceSrcGrid(d_srcGrid) {}
|
||||
|
||||
/// @brief Toggle on and off verbose mode
|
||||
/// @param level Verbose level: 0=quiet, 1=timing, 2=benchmarking
|
||||
void setVerbose(int level = 1) { mVerbose = level; }
|
||||
|
||||
/// @brief Set the mode for checksum computation, which is disabled by default
|
||||
/// @param mode Mode of checksum computation
|
||||
void setChecksum(CheckMode mode = CheckMode::Disable){mBuilder.mChecksum = mode;}
|
||||
|
||||
/// @brief Set type of dilation operation
|
||||
/// @param op: NN_FACE=face neighbors, NN_FACE_EDGE=face and edge neibhros, NN_FACE_EDGE_VERTEX=26-connected neighbors
|
||||
void setOperation(morphology::NearestNeighbors op) { mOp = op; }
|
||||
|
||||
/// @brief Creates a handle to the dilated grid
|
||||
/// @tparam BufferT Buffer type used for allocation of the grid handle
|
||||
/// @param buffer optional buffer (currently ignored)
|
||||
/// @return returns a handle with a grid of type NanoGrid<BuildT>
|
||||
template<typename BufferT = nanovdb::cuda::DeviceBuffer>
|
||||
GridHandle<BufferT>
|
||||
getHandle(const BufferT &buffer = BufferT());
|
||||
|
||||
private:
|
||||
void dilateRoot();
|
||||
|
||||
void dilateInternalNodes();
|
||||
|
||||
void processGridTreeRoot();
|
||||
|
||||
void dilateLeafNodes();
|
||||
|
||||
static constexpr unsigned int mNumThreads = 128;// for kernels spawned via lambdaKernel (others may specialize)
|
||||
static unsigned int numBlocks(unsigned int n) {return (n + mNumThreads - 1) / mNumThreads;}
|
||||
|
||||
TopologyBuilder<BuildT> mBuilder;
|
||||
cudaStream_t mStream{0};
|
||||
util::cuda::Timer mTimer;
|
||||
int mVerbose{0};
|
||||
const GridT *mDeviceSrcGrid;
|
||||
morphology::NearestNeighbors mOp{morphology::NN_FACE_EDGE_VERTEX};
|
||||
TreeData mSrcTreeData;
|
||||
};// tools::cuda::DilateGrid<BuildT>
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
template<typename BufferT>
|
||||
GridHandle<BufferT>
|
||||
DilateGrid<BuildT>::getHandle(const BufferT &pool)
|
||||
{
|
||||
// Copy TreeData from GPU -> CPU
|
||||
cudaStreamSynchronize(mStream);
|
||||
mSrcTreeData = util::cuda::DeviceGridTraits<BuildT>::getTreeData(mDeviceSrcGrid);
|
||||
|
||||
// Ensure that the input grid contains no tile values
|
||||
if (mSrcTreeData.mTileCount[2] || mSrcTreeData.mTileCount[1] || mSrcTreeData.mTileCount[0])
|
||||
throw std::runtime_error("Topological operations not supported on grids with value tiles");
|
||||
|
||||
// Speculatively dilate root node
|
||||
if (mVerbose==1) mTimer.start("\nDilating root node");
|
||||
dilateRoot();
|
||||
|
||||
// Allocate memory for dilated upper/lower masks
|
||||
if (mVerbose==1) mTimer.restart("Allocating internal node mask buffers");
|
||||
mBuilder.allocateInternalMaskBuffers(mStream);
|
||||
|
||||
// Dilate masks of upper/lower nodes
|
||||
if (mVerbose==1) mTimer.restart("Dilate internal nodes");
|
||||
dilateInternalNodes();
|
||||
|
||||
// Enumerate tree nodes
|
||||
if (mVerbose==1) mTimer.restart("Count dilated tree nodes");
|
||||
mBuilder.countNodes(mStream);
|
||||
|
||||
cudaStreamSynchronize(mStream);
|
||||
|
||||
// Allocate new device grid buffer for dilated result
|
||||
if (mVerbose==1) mTimer.restart("Allocating dilated grid buffer");
|
||||
auto buffer = mBuilder.getBuffer(pool, mStream);
|
||||
|
||||
// Process GridData/TreeData/RootData of dilated result
|
||||
if (mVerbose==1) mTimer.restart("Processing grid/tree/root");
|
||||
processGridTreeRoot();
|
||||
|
||||
// Process upper nodes of dilated result
|
||||
if (mVerbose==1) mTimer.restart("Processing upper nodes");
|
||||
mBuilder.processUpperNodes(mStream);
|
||||
|
||||
// Process lower nodes of dilated result
|
||||
if (mVerbose==1) mTimer.restart("Processing lower nodes");
|
||||
mBuilder.processLowerNodes(mStream);
|
||||
|
||||
// Dilate leaf node active masks into new topology
|
||||
if (mVerbose==1) mTimer.restart("Dilating leaf nodes");
|
||||
dilateLeafNodes();
|
||||
|
||||
// Process bounding boxes
|
||||
if (mVerbose==1) mTimer.restart("Processing bounding boxes");
|
||||
mBuilder.processBBox(mStream);
|
||||
|
||||
// Post-process Grid/Tree data
|
||||
if (mVerbose==1) mTimer.restart("Post-processing grid/tree data");
|
||||
mBuilder.postProcessGridTree(mStream);
|
||||
if (mVerbose==1) mTimer.stop();
|
||||
|
||||
cudaStreamSynchronize(mStream);
|
||||
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
}// DilateGrid<BuildT>::getHandle
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void DilateGrid<BuildT>::dilateRoot()
|
||||
{
|
||||
// This method conservatively and speculatively dilates the root tiles, to accommodate
|
||||
// any new root nodes that might be introduced by the dilation operation.
|
||||
// The index-space bounding box of each tile is examined, and if it is within a 1-pixel of
|
||||
// intersecting any of the 26-connected neighboring root tiles, those are preemptively
|
||||
// introduced into the root topology.
|
||||
// (As of the present implementation this presumes a maximum of 1-voxel radius in dilation)
|
||||
// Root tiles that were preemptively introduced, but end up having no active contents will
|
||||
// be pruned in later stages of processing.
|
||||
|
||||
int device = 0;
|
||||
cudaGetDevice(&device);
|
||||
|
||||
std::map<uint64_t, typename RootT::DataType::Tile> dilatedTiles;
|
||||
|
||||
// This encoding scheme mirrors the one used in PointsToGrid; note that it is different from Tile::key
|
||||
auto coordToKey = [](const Coord &ijk)->uint64_t{
|
||||
// Note: int32_t has a range of -2^31 to 2^31 - 1 whereas uint32_t has a range of 0 to 2^32 - 1
|
||||
static constexpr int64_t kOffset = 1 << 31;
|
||||
return (uint64_t(uint32_t(int64_t(ijk[2]) + kOffset) >> 12) ) | // z is the lower 21 bits
|
||||
(uint64_t(uint32_t(int64_t(ijk[1]) + kOffset) >> 12) << 21) | // y is the middle 21 bits
|
||||
(uint64_t(uint32_t(int64_t(ijk[0]) + kOffset) >> 12) << 42); // x is the upper 21 bits
|
||||
};// coordToKey lambda functor
|
||||
|
||||
if (mSrcTreeData.mVoxelCount) { // If the input grid is not empty
|
||||
// Make a host copy of the source topology RootNode *and* the Upper Nodes (needed for BBox'es)
|
||||
// TODO: Consider avoiding to copy the entire set of upper nodes
|
||||
auto deviceSrcRoot = static_cast<const RootT*>(util::PtrAdd(mDeviceSrcGrid, GridT::memUsage() + mSrcTreeData.mNodeOffset[3]));
|
||||
uint64_t rootAndUpperSize = mSrcTreeData.mNodeOffset[1] - mSrcTreeData.mNodeOffset[3];
|
||||
auto srcRootAndUpperBuffer = nanovdb::HostBuffer::create(rootAndUpperSize);
|
||||
cudaCheck(cudaMemcpyAsync(srcRootAndUpperBuffer.data(), deviceSrcRoot, rootAndUpperSize, cudaMemcpyDeviceToHost, mStream));
|
||||
auto srcRootAndUpper = static_cast<RootT*>(srcRootAndUpperBuffer.data());
|
||||
|
||||
// For each original root tile, consider adding those tiles in its 26-connected neighborhood
|
||||
for (uint32_t t = 0; t < srcRootAndUpper->tileCount(); t++) {
|
||||
auto srcUpper = srcRootAndUpper->getChild(srcRootAndUpper->tile(t));
|
||||
const auto dilatedBBox = srcUpper->bbox().expandBy(1); // TODO: update/specialize if larger dilation neighborhoods are used
|
||||
|
||||
static constexpr int32_t rootTileDim = UpperT::DIM; // 4096
|
||||
for (int di = -rootTileDim; di <= rootTileDim; di += rootTileDim)
|
||||
for (int dj = -rootTileDim; dj <= rootTileDim; dj += rootTileDim)
|
||||
for (int dk = -rootTileDim; dk <= rootTileDim; dk += rootTileDim) {
|
||||
auto testBBox = nanovdb::CoordBBox::createCube(srcUpper->origin().offsetBy(di,dj,dk), rootTileDim);
|
||||
auto sortKey = coordToKey(testBBox.min()); // key used in the radix sort, in accordance with PointsToGrid
|
||||
auto tileKey = RootT::CoordToKey(testBBox.min()); // encoding used in the NanoVDB tile
|
||||
if (testBBox.hasOverlap(dilatedBBox) & (dilatedTiles.count(sortKey) == 0)) {
|
||||
typename RootT::Tile neighborTile{tileKey}; // Only the key value is needed; child pointer & value will be unused
|
||||
dilatedTiles.emplace(sortKey, neighborTile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Package the new root topology into a RootNode plus Tile list; upload to the GPU
|
||||
uint64_t rootSize = RootT::memUsage(dilatedTiles.size());
|
||||
mBuilder.mProcessedRoot = nanovdb::cuda::DeviceBuffer::create(rootSize);
|
||||
auto dilatedRootPtr = static_cast<RootT*>(mBuilder.mProcessedRoot.data());
|
||||
dilatedRootPtr->mTableSize = dilatedTiles.size();
|
||||
uint32_t t = 0;
|
||||
for (const auto& [key, tile] : dilatedTiles)
|
||||
*dilatedRootPtr->tile(t++) = tile;
|
||||
mBuilder.mProcessedRoot.deviceUpload(device, mStream, false);
|
||||
}// DilateGrid<BuildT>::dilateRoot
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void DilateGrid<BuildT>::dilateInternalNodes()
|
||||
{
|
||||
// Computes the masks of upper and (densified) lower internal nodes, as a result of the dilation operation
|
||||
// Masks of lower internal nodes are densified in the sense that a serialized array of them is allocated,
|
||||
// as if every upper node had a full set of 32^3 lower children
|
||||
if (mSrcTreeData.mNodeCount[1]) { // Unless it's an empty grid
|
||||
if (mOp == morphology::NN_FACE) {
|
||||
using Op = util::morphology::cuda::DilateInternalNodesFunctor<BuildT, morphology::NN_FACE>;
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<dim3(mSrcTreeData.mNodeCount[1],Op::SlicesPerLowerNode,1), Op::MaxThreadsPerBlock, 0, mStream>>>
|
||||
(mDeviceSrcGrid, mBuilder.deviceProcessedRoot(), mBuilder.deviceUpperMasks(), mBuilder.deviceLowerMasks()); }
|
||||
else if (mOp == morphology::NN_FACE_EDGE) {
|
||||
using Op = util::morphology::cuda::DilateInternalNodesFunctor<BuildT, morphology::NN_FACE_EDGE>;
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<dim3(mSrcTreeData.mNodeCount[1],Op::SlicesPerLowerNode,1), Op::MaxThreadsPerBlock, 0, mStream>>>
|
||||
(mDeviceSrcGrid, mBuilder.deviceProcessedRoot(), mBuilder.deviceUpperMasks(), mBuilder.deviceLowerMasks()); }
|
||||
else if (mOp == morphology::NN_FACE_EDGE_VERTEX) {
|
||||
using Op = util::morphology::cuda::DilateInternalNodesFunctor<BuildT, morphology::NN_FACE_EDGE_VERTEX>;
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<dim3(mSrcTreeData.mNodeCount[1],Op::SlicesPerLowerNode,1), Op::MaxThreadsPerBlock, 0, mStream>>>
|
||||
(mDeviceSrcGrid, mBuilder.deviceProcessedRoot(), mBuilder.deviceUpperMasks(), mBuilder.deviceLowerMasks()); }
|
||||
}
|
||||
}// DilateGrid<BuildT>::dilateInternalNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template <typename BuildT>
|
||||
void DilateGrid<BuildT>::processGridTreeRoot()
|
||||
{
|
||||
// Copy GridData from source grid
|
||||
// By convention: this will duplicate grid name and map. Others will be reset later
|
||||
cudaCheck(cudaMemcpyAsync(&mBuilder.data()->getGrid(), mDeviceSrcGrid->data(), GridT::memUsage(), cudaMemcpyDeviceToDevice, mStream));
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, mStream>>>(1, topology::detail::BuildGridTreeRootFunctor<BuildT>(), mBuilder.deviceData());
|
||||
cudaCheckError();
|
||||
}// DilateGrid<BuildT>::processGridTreeRoot
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void DilateGrid<BuildT>::dilateLeafNodes()
|
||||
{
|
||||
// Dilates the active masks of the source grid (as indicated at the leaf level), into a new grid that
|
||||
// has been already topologically dilated to include all necessary leaf nodes.
|
||||
if (mBuilder.data()->nodeCount[1]) { // Unless output grid is empty
|
||||
if (mOp == morphology::NN_FACE) {
|
||||
using Op = util::morphology::cuda::DilateLeafNodesFunctor<BuildT, morphology::NN_FACE>;
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<dim3(mBuilder.data()->nodeCount[1],Op::SlicesPerLowerNode,1), Op::MaxThreadsPerBlock, 0, mStream>>>
|
||||
(mDeviceSrcGrid, static_cast<GridT*>(mBuilder.data()->d_bufferPtr)); }
|
||||
else if (mOp == morphology::NN_FACE_EDGE)
|
||||
throw std::runtime_error("dilateLeafNodes() not implemented for NN_FACE_EDGE stencil");
|
||||
else if (mOp == morphology::NN_FACE_EDGE_VERTEX) {
|
||||
using Op = util::morphology::cuda::DilateLeafNodesFunctor<BuildT, morphology::NN_FACE_EDGE_VERTEX>;
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<dim3(mBuilder.data()->nodeCount[1],Op::SlicesPerLowerNode,1), Op::MaxThreadsPerBlock>>>
|
||||
(mDeviceSrcGrid, static_cast<GridT*>(mBuilder.data()->d_bufferPtr)); }
|
||||
}
|
||||
|
||||
// Update leaf offsets and prefix sums
|
||||
mBuilder.processLeafOffsets(mStream);
|
||||
}// DilateGrid<BuildT>::dilateLeafNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NVIDIA_TOOLS_CUDA_DILATEGRID_CUH_HAS_BEEN_INCLUDED
|
||||
File diff suppressed because it is too large
Load Diff
@@ -72,7 +72,7 @@ inline void updateChecksum(GridData *d_gridData, cudaStream_t stream = 0)
|
||||
namespace util::cuda {
|
||||
|
||||
/// @brief Cuda kernel that computes CRC32 checksums of blocks of data using a look-up-table
|
||||
/// @param d_data device pointer to raw data from wich to compute the CRC32 checksums
|
||||
/// @param d_data device pointer to raw data from which to compute the CRC32 checksums
|
||||
/// @param d_blockCRC device pointer to array of @c blockCount checksums for each block
|
||||
/// @param blockCount number of blocks and checksums
|
||||
/// @param blockSize size of each block in bytes
|
||||
@@ -85,7 +85,7 @@ __global__ void crc32Kernel(const T *d_data, uint32_t* d_blockCRC, uint32_t bloc
|
||||
}
|
||||
|
||||
/// @brief Cuda kernel that computes CRC32 checksums of blocks of data (without using a look-up-table)
|
||||
/// @param d_data device pointer to raw data from wich to compute the CRC32 checksums
|
||||
/// @param d_data device pointer to raw data from which to compute the CRC32 checksums
|
||||
/// @param d_blockCRC device pointer to array of @c blockCount checksums for each block
|
||||
/// @param blockCount number of blocks and checksums
|
||||
/// @param blockSize size of each block in bytes
|
||||
@@ -374,7 +374,7 @@ inline Checksum getChecksum(const GridData *d_gridData, cudaStream_t stream)
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
/// @return The actual mode used for checksum computation. Eg. if @c d_gridData is NULL (or @c mode = CheckMode::Empty)
|
||||
/// then CheckMode::Empty is always returned. Else if the grid has no nodes or blind data CheckMode::Partial
|
||||
/// is always returnd (even if @c mode = CheckMode::Full).
|
||||
/// is always returned (even if @c mode = CheckMode::Full).
|
||||
inline void updateChecksum(GridData *d_gridData, CheckMode mode, cudaStream_t stream)
|
||||
{
|
||||
NANOVDB_ASSERT(d_gridData);
|
||||
|
||||
@@ -32,7 +32,7 @@ void updateGridStats(NanoGrid<BuildT> *d_grid, StatsMode mode = StatsMode::Defau
|
||||
|
||||
//================================================================================================
|
||||
|
||||
/// @brief Allows for the construction of NanoVDB grids without any dependecy
|
||||
/// @brief Allows for the construction of NanoVDB grids without any dependency
|
||||
template<typename BuildT, typename StatsT = Stats<typename NanoGrid<BuildT>::ValueType>>
|
||||
class GridStats
|
||||
{
|
||||
@@ -84,20 +84,19 @@ template<typename BuildT, typename StatsT, int LEVEL>
|
||||
__global__ void processInternal(NodeManager<BuildT> *d_nodeMgr, StatsT *d_stats)
|
||||
{
|
||||
using ChildT = typename NanoNode<BuildT,LEVEL-1>::type;
|
||||
const uint32_t tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid >= d_nodeMgr->nodeCount(LEVEL)) return;
|
||||
auto &d_node = d_nodeMgr->template node<LEVEL>(tid);
|
||||
uint32_t nodeID = blockIdx.x * blockDim.x + threadIdx.x;// thread id (reused below to avoid compiler warning)
|
||||
if (nodeID >= d_nodeMgr->nodeCount(LEVEL)) return;
|
||||
auto &d_node = d_nodeMgr->template node<LEVEL>(nodeID);
|
||||
auto &bbox = d_node.mBBox;
|
||||
bbox = CoordBBox();// empty bbox
|
||||
StatsT stats;
|
||||
uint32_t childID = 0u;
|
||||
|
||||
for (auto it = d_node.beginChild(); it; ++it) {
|
||||
auto &child = *it;
|
||||
bbox.expand( child.bbox() );
|
||||
if constexpr(StatsT::hasAverage()) {
|
||||
childID = *reinterpret_cast<uint32_t*>(&child.mMinimum);
|
||||
StatsT &s = d_stats[childID];
|
||||
nodeID = *reinterpret_cast<uint32_t*>(&child.mMinimum);
|
||||
StatsT &s = d_stats[nodeID];
|
||||
s.setStats(child);
|
||||
stats.add(s);
|
||||
} else if constexpr(StatsT::hasMinMax()) {
|
||||
@@ -112,8 +111,8 @@ __global__ void processInternal(NodeManager<BuildT> *d_nodeMgr, StatsT *d_stats)
|
||||
if constexpr(StatsT::hasStats()) stats.add(*it, ChildT::NUM_VALUES);
|
||||
}
|
||||
if constexpr(StatsT::hasAverage()) {
|
||||
d_stats[childID] = stats;
|
||||
*reinterpret_cast<uint32_t*>(&d_node.mMinimum) = childID;
|
||||
d_stats[nodeID] = stats;
|
||||
*reinterpret_cast<uint32_t*>(&d_node.mMinimum) = nodeID;
|
||||
} else if constexpr(StatsT::hasMinMax()) {
|
||||
stats.setStats(d_node);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
to only include it in .cu files (or other .cuh files)
|
||||
*/
|
||||
|
||||
#ifndef NVIDIA_TOOLS_CUDA_INDEXTOGRID_CUH_HAS_BEEN_INCLUDED
|
||||
#define NVIDIA_TOOLS_CUDA_INDEXTOGRID_CUH_HAS_BEEN_INCLUDED
|
||||
#ifndef NANOVDB_TOOLS_CUDA_INDEXTOGRID_CUH_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_TOOLS_CUDA_INDEXTOGRID_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
@@ -31,7 +31,7 @@ namespace tools::cuda {// ======================================================
|
||||
/// @tparam DstBuildT Build time of the destination/output Grid
|
||||
/// @tparam SrcBuildT Build type of the source/input IndexGrid
|
||||
/// @tparam BufferT Type of the buffer used for allocation of the destination Grid
|
||||
/// @param d_srcGrid Device pointer to source/input IndexGrid, i.e. SrcBuildT={ValueIndex,ValueOnIndex,ValueIndexMask,ValueOnIndexMask}
|
||||
/// @param d_srcGrid Device pointer to source/input IndexGrid, i.e. SrcBuildT={ValueIndex,ValueOnIndex}
|
||||
/// @param d_srcValues Device pointer to an array of values
|
||||
/// @param pool Memory pool used to create a buffer for the destination/output Grid
|
||||
/// @param stream optional CUDA stream (defaults to CUDA stream 0
|
||||
@@ -233,7 +233,6 @@ __global__ void processNodesKernel(typename IndexToGrid<SrcBuildT>::NodeAccessor
|
||||
if (srcGrid.hasStdDeviation()) dstNode.mStdDevi = srcValues[srcNode.mStdDevi];
|
||||
}
|
||||
}
|
||||
const uint64_t nodeSkip = nodeAcc->nodeCount[LEVEL] - blockIdx.x, srcOff = sizeof(SrcNodeT)*nodeSkip, dstOff = sizeof(DstNodeT)*nodeSkip;// offset to first node of child type
|
||||
const int off = blockDim.x*blockDim.y*threadIdx.x + blockDim.x*threadIdx.y;
|
||||
for (int threadIdx_z=0; threadIdx_z<blockDim.x; ++threadIdx_z) {
|
||||
const int i = off + threadIdx_z;
|
||||
@@ -241,6 +240,7 @@ __global__ void processNodesKernel(typename IndexToGrid<SrcBuildT>::NodeAccessor
|
||||
if constexpr(sizeof(SrcNodeT)==sizeof(DstNodeT) && sizeof(SrcChildT)==sizeof(DstChildT)) {
|
||||
dstNode.mTable[i].child = srcNode.mTable[i].child;
|
||||
} else {
|
||||
const uint64_t nodeSkip = nodeAcc->nodeCount[LEVEL] - blockIdx.x, srcOff = sizeof(SrcNodeT)*nodeSkip, dstOff = sizeof(DstNodeT)*nodeSkip;// offset to first node of child type
|
||||
const uint64_t childID = (srcNode.mTable[i].child - srcOff)/sizeof(SrcChildT);
|
||||
dstNode.mTable[i].child = dstOff + childID*sizeof(DstChildT);
|
||||
}
|
||||
@@ -415,4 +415,4 @@ cudaCreateNanoGrid(const NanoGrid<SrcBuildT> *d_srcGrid, const typename BuildToV
|
||||
|
||||
}// nanovdb namespace ===================================================================
|
||||
|
||||
#endif // NVIDIA_TOOLS_CUDA_INDEXTOGRID_CUH_HAS_BEEN_INCLUDED
|
||||
#endif // NANOVDB_TOOLS_CUDA_INDEXTOGRID_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/MergeGrids.cuh
|
||||
|
||||
\authors Efty Sifakis
|
||||
|
||||
\brief Morphological union of NanoVDB indexGrids on the device
|
||||
|
||||
\warning The header file contains cuda device code so be sure
|
||||
to only include it in .cu files (or other .cuh files)
|
||||
*/
|
||||
|
||||
#ifndef NVIDIA_TOOLS_CUDA_MERGEGRIDS_CUH_HAS_BEEN_INCLUDED
|
||||
#define NVIDIA_TOOLS_CUDA_MERGEGRIDS_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <cub/cub.cuh>
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/tools/cuda/TopologyBuilder.cuh>
|
||||
#include <nanovdb/util/cuda/DeviceGridTraits.cuh>
|
||||
#include <nanovdb/util/cuda/Morphology.cuh>
|
||||
#include <nanovdb/util/cuda/Timer.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools::cuda {
|
||||
|
||||
template <typename BuildT>
|
||||
class MergeGrids
|
||||
{
|
||||
using GridT = NanoGrid<BuildT>;
|
||||
using TreeT = NanoTree<BuildT>;
|
||||
using RootT = NanoRoot<BuildT>;
|
||||
using UpperT = NanoUpper<BuildT>;
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructor
|
||||
/// @param d_srcGrid1 first source device grid to be merged
|
||||
/// @param d_srcGrid2 second source device grid to be merged
|
||||
/// @param stream optional CUDA stream (defaults to CUDA stream 0)
|
||||
MergeGrids(const GridT* d_srcGrid1, const GridT* d_srcGrid2, cudaStream_t stream = 0)
|
||||
: mBuilder(stream), mStream(stream), mTimer(stream), mDeviceSrcGrid1(d_srcGrid1), mDeviceSrcGrid2(d_srcGrid2) {}
|
||||
|
||||
/// @brief Toggle on and off verbose mode
|
||||
/// @param level Verbose level: 0=quiet, 1=timing, 2=benchmarking
|
||||
void setVerbose(int level = 1) { mVerbose = level; }
|
||||
|
||||
/// @brief Set the mode for checksum computation, which is disabled by default
|
||||
/// @param mode Mode of checksum computation
|
||||
void setChecksum(CheckMode mode = CheckMode::Disable){mBuilder.mChecksum = mode;}
|
||||
|
||||
/// @brief Creates a handle to the merged grid
|
||||
/// @tparam BufferT Buffer type used for allocation of the grid handle
|
||||
/// @param buffer optional buffer (currently ignored)
|
||||
/// @return returns a handle with a grid of type NanoGrid<BuildT>
|
||||
template<typename BufferT = nanovdb::cuda::DeviceBuffer>
|
||||
GridHandle<BufferT>
|
||||
getHandle(const BufferT &buffer = BufferT());
|
||||
|
||||
private:
|
||||
void mergeRoot();
|
||||
|
||||
void mergeInternalNodes();
|
||||
|
||||
void processGridTreeRoot();
|
||||
|
||||
void mergeLeafNodes();
|
||||
|
||||
static constexpr unsigned int mNumThreads = 128;// for kernels spawned via lambdaKernel (others may specialize)
|
||||
static unsigned int numBlocks(unsigned int n) {return (n + mNumThreads - 1) / mNumThreads;}
|
||||
|
||||
TopologyBuilder<BuildT> mBuilder;
|
||||
cudaStream_t mStream{0};
|
||||
util::cuda::Timer mTimer;
|
||||
int mVerbose{0};
|
||||
const GridT *mDeviceSrcGrid1;
|
||||
const GridT *mDeviceSrcGrid2;
|
||||
TreeData mSrcTreeData1;
|
||||
TreeData mSrcTreeData2;
|
||||
};// tools::cuda::MergeGrids<BuildT>
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
template<typename BufferT>
|
||||
GridHandle<BufferT>
|
||||
MergeGrids<BuildT>::getHandle(const BufferT &pool)
|
||||
{
|
||||
// Copy TreeData from GPU -> CPU
|
||||
cudaStreamSynchronize(mStream);
|
||||
mSrcTreeData1 = util::cuda::DeviceGridTraits<BuildT>::getTreeData(mDeviceSrcGrid1);
|
||||
mSrcTreeData2 = util::cuda::DeviceGridTraits<BuildT>::getTreeData(mDeviceSrcGrid2);
|
||||
|
||||
// Ensure that the input grid contains no tile values
|
||||
if (mSrcTreeData1.mTileCount[2] || mSrcTreeData1.mTileCount[1] || mSrcTreeData1.mTileCount[0] ||
|
||||
mSrcTreeData2.mTileCount[2] || mSrcTreeData2.mTileCount[1] || mSrcTreeData2.mTileCount[0])
|
||||
throw std::runtime_error("Topological operations not supported on grids with value tiles");
|
||||
|
||||
// Merge root nodes
|
||||
if (mVerbose==1) mTimer.start("\nMerging root nodes");
|
||||
mergeRoot();
|
||||
|
||||
// Allocate memory for merged upper/lower masks
|
||||
if (mVerbose==1) mTimer.restart("Allocating internal node mask buffers");
|
||||
mBuilder.allocateInternalMaskBuffers(mStream);
|
||||
|
||||
// Merge masks of upper/lower nodes
|
||||
if (mVerbose==1) mTimer.restart("Merge internal nodes");
|
||||
mergeInternalNodes();
|
||||
|
||||
// Enumerate tree nodes
|
||||
if (mVerbose==1) mTimer.restart("Count merged tree nodes");
|
||||
mBuilder.countNodes(mStream);
|
||||
|
||||
cudaStreamSynchronize(mStream);
|
||||
|
||||
// Allocate new device grid buffer for merged result
|
||||
if (mVerbose==1) mTimer.restart("Allocating merged grid buffer");
|
||||
auto buffer = mBuilder.getBuffer(pool, mStream);
|
||||
|
||||
// Process GridData/TreeData/RootData of merged result
|
||||
if (mVerbose==1) mTimer.restart("Processing grid/tree/root");
|
||||
processGridTreeRoot();
|
||||
|
||||
// Process upper nodes of merged result
|
||||
if (mVerbose==1) mTimer.restart("Processing upper nodes");
|
||||
mBuilder.processUpperNodes(mStream);
|
||||
|
||||
// Process lower nodes of merged result
|
||||
if (mVerbose==1) mTimer.restart("Processing lower nodes");
|
||||
mBuilder.processLowerNodes(mStream);
|
||||
|
||||
// Merge leaf node active masks into new topology
|
||||
if (mVerbose==1) mTimer.restart("Merging leaf nodes");
|
||||
mergeLeafNodes();
|
||||
|
||||
// Process bounding boxes
|
||||
if (mVerbose==1) mTimer.restart("Processing bounding boxes");
|
||||
mBuilder.processBBox(mStream);
|
||||
|
||||
// Post-process Grid/Tree data
|
||||
if (mVerbose==1) mTimer.restart("Post-processing grid/tree data");
|
||||
mBuilder.postProcessGridTree(mStream);
|
||||
if (mVerbose==1) mTimer.stop();
|
||||
|
||||
cudaStreamSynchronize(mStream);
|
||||
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
}// MergeGrids<BuildT>::getHandle
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void MergeGrids<BuildT>::mergeRoot()
|
||||
{
|
||||
// Creates a new merged tree root with the merged tiles of the two input root topologies
|
||||
|
||||
int device = 0;
|
||||
cudaGetDevice(&device);
|
||||
|
||||
std::map<uint64_t, typename RootT::DataType::Tile> mergedTiles;
|
||||
|
||||
// This encoding scheme mirrors the one used in PointsToGrid; note that it is different from Tile::key
|
||||
auto coordToKey = [](const Coord &ijk)->uint64_t{
|
||||
// Note: int32_t has a range of -2^31 to 2^31 - 1 whereas uint32_t has a range of 0 to 2^32 - 1
|
||||
static constexpr int64_t kOffset = 1 << 31;
|
||||
return (uint64_t(uint32_t(int64_t(ijk[2]) + kOffset) >> 12) ) | // z is the lower 21 bits
|
||||
(uint64_t(uint32_t(int64_t(ijk[1]) + kOffset) >> 12) << 21) | // y is the middle 21 bits
|
||||
(uint64_t(uint32_t(int64_t(ijk[0]) + kOffset) >> 12) << 42); // x is the upper 21 bits
|
||||
};// coordToKey lambda functor
|
||||
|
||||
// Make a host copy of the source root topology RootNode for both inputs
|
||||
// Then, merge tiles of two sources in a sorted container
|
||||
|
||||
if (mSrcTreeData1.mVoxelCount) { // If the first input is not a null grid
|
||||
// Make a host copy of the Root topology
|
||||
auto deviceSrcRoot1 = static_cast<const RootT*>(util::PtrAdd(mDeviceSrcGrid1, GridT::memUsage() + mSrcTreeData1.mNodeOffset[3]));
|
||||
uint64_t rootSize1 = mSrcTreeData1.mNodeOffset[2] - mSrcTreeData1.mNodeOffset[3];
|
||||
auto srcRootBuffer1 = nanovdb::HostBuffer::create(rootSize1);
|
||||
cudaCheck(cudaMemcpyAsync(srcRootBuffer1.data(), deviceSrcRoot1, rootSize1, cudaMemcpyDeviceToHost, mStream));
|
||||
auto srcRoot1 = static_cast<RootT*>(srcRootBuffer1.data());
|
||||
|
||||
// Add all root tiles, reordering if necessary
|
||||
for (uint32_t t = 0; t < srcRoot1->tileCount(); t++) {
|
||||
auto tile = srcRoot1->tile(t);
|
||||
auto sortKey = coordToKey(tile->origin());
|
||||
mergedTiles.emplace(sortKey, *tile);
|
||||
}
|
||||
}
|
||||
|
||||
if (mSrcTreeData2.mVoxelCount) { // If the first input is not a null grid
|
||||
// Make a host copy of the Root topology
|
||||
auto deviceSrcRoot2 = static_cast<const RootT*>(util::PtrAdd(mDeviceSrcGrid2, GridT::memUsage() + mSrcTreeData2.mNodeOffset[3]));
|
||||
uint64_t rootSize2 = mSrcTreeData2.mNodeOffset[2] - mSrcTreeData2.mNodeOffset[3];
|
||||
auto srcRootBuffer2 = nanovdb::HostBuffer::create(rootSize2);
|
||||
cudaCheck(cudaMemcpyAsync(srcRootBuffer2.data(), deviceSrcRoot2, rootSize2, cudaMemcpyDeviceToHost, mStream));
|
||||
auto srcRoot2 = static_cast<RootT*>(srcRootBuffer2.data());
|
||||
|
||||
// Add all root tiles, reordering if necessary
|
||||
for (uint32_t t = 0; t < srcRoot2->tileCount(); t++) {
|
||||
auto tile = srcRoot2->tile(t);
|
||||
auto sortKey = coordToKey(tile->origin());
|
||||
mergedTiles.emplace(sortKey, *tile);
|
||||
}
|
||||
}
|
||||
|
||||
// Package the new root topology into a RootNode plus Tile list; upload to the GPU
|
||||
uint64_t rootSize = RootT::memUsage(mergedTiles.size());
|
||||
mBuilder.mProcessedRoot = nanovdb::cuda::DeviceBuffer::create(rootSize);
|
||||
auto mergedRootPtr = static_cast<RootT*>(mBuilder.mProcessedRoot.data());
|
||||
mergedRootPtr->mTableSize = mergedTiles.size();
|
||||
uint32_t t = 0;
|
||||
for (const auto& [key, tile] : mergedTiles)
|
||||
*mergedRootPtr->tile(t++) = tile;
|
||||
mBuilder.mProcessedRoot.deviceUpload(device, mStream, false);
|
||||
}// MergeGrids<BuildT>::mergeRoot
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void MergeGrids<BuildT>::mergeInternalNodes()
|
||||
{
|
||||
// Merges the masks of upper and lower nodes from both input topologies into the
|
||||
// densified, pre-allocated mask arrays of the merged result
|
||||
using Op = util::morphology::cuda::MergeInternalNodesFunctor<BuildT>;
|
||||
if (mSrcTreeData1.mNodeCount[1]) { // Unless the first grid to merge is empty
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<mSrcTreeData1.mNodeCount[1], Op::MaxThreadsPerBlock, 0, mStream>>>
|
||||
(mDeviceSrcGrid1, mBuilder.deviceProcessedRoot(), mBuilder.deviceUpperMasks(), mBuilder.deviceLowerMasks());
|
||||
}
|
||||
if (mSrcTreeData2.mNodeCount[1]) { // Unless the second grid to merge is empty
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<mSrcTreeData2.mNodeCount[1], Op::MaxThreadsPerBlock, 0, mStream>>>
|
||||
(mDeviceSrcGrid2, mBuilder.deviceProcessedRoot(), mBuilder.deviceUpperMasks(), mBuilder.deviceLowerMasks());
|
||||
}
|
||||
}// MergeGrids<BuildT>::mergeInternalNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template <typename BuildT>
|
||||
void MergeGrids<BuildT>::processGridTreeRoot()
|
||||
{
|
||||
// Copy GridData from first source grid
|
||||
// TODO: Check for instances where extra processing is needed
|
||||
// TODO: check that the second grid input has consistent GridData, too
|
||||
cudaCheck(cudaMemcpyAsync(&mBuilder.data()->getGrid(), mDeviceSrcGrid1->data(), GridT::memUsage(), cudaMemcpyDeviceToDevice, mStream));
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, mStream>>>(1, topology::detail::BuildGridTreeRootFunctor<BuildT>(), mBuilder.deviceData());
|
||||
cudaCheckError();
|
||||
}// MergeGrids<BuildT>::processGridTreeRoot
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void MergeGrids<BuildT>::mergeLeafNodes()
|
||||
{
|
||||
using Op = util::morphology::cuda::MergeLeafNodesFunctor<BuildT>;
|
||||
if (mSrcTreeData1.mNodeCount[1]) { // Unless first input grid is empty
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<dim3(mSrcTreeData1.mNodeCount[1],Op::SlicesPerLowerNode,1), Op::MaxThreadsPerBlock, 0, mStream>>>
|
||||
(mDeviceSrcGrid1, static_cast<GridT*>(mBuilder.data()->d_bufferPtr));
|
||||
}
|
||||
if (mSrcTreeData2.mNodeCount[1]) { // Unless second input grid is empty
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<dim3(mSrcTreeData2.mNodeCount[1],Op::SlicesPerLowerNode,1), Op::MaxThreadsPerBlock, 0, mStream>>>
|
||||
(mDeviceSrcGrid2, static_cast<GridT*>(mBuilder.data()->d_bufferPtr));
|
||||
}
|
||||
|
||||
// Update leaf offsets and prefix sums
|
||||
mBuilder.processLeafOffsets(mStream);
|
||||
}// MergeGrids<BuildT>::mergeLeafNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NVIDIA_TOOLS_CUDA_MERGEGRIDS_CUH_HAS_BEEN_INCLUDED
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,255 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/PruneGrid.cuh
|
||||
|
||||
\authors Efty Sifakis
|
||||
|
||||
\brief Morphological pruning of NanoVDB indexGrids using leaf-indexed mask
|
||||
|
||||
\warning The header file contains cuda device code so be sure
|
||||
to only include it in .cu files (or other .cuh files)
|
||||
*/
|
||||
|
||||
#ifndef NVIDIA_TOOLS_CUDA_PRUNEGRID_CUH_HAS_BEEN_INCLUDED
|
||||
#define NVIDIA_TOOLS_CUDA_PRUNEGRID_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <cub/cub.cuh>
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/tools/cuda/TopologyBuilder.cuh>
|
||||
#include <nanovdb/util/cuda/DeviceGridTraits.cuh>
|
||||
#include <nanovdb/util/cuda/Morphology.cuh>
|
||||
#include <nanovdb/util/cuda/Timer.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools::cuda {
|
||||
|
||||
template <typename BuildT>
|
||||
class PruneGrid
|
||||
{
|
||||
using GridT = NanoGrid<BuildT>;
|
||||
using TreeT = NanoTree<BuildT>;
|
||||
using RootT = NanoRoot<BuildT>;
|
||||
using UpperT = NanoUpper<BuildT>;
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructor
|
||||
/// @param d_srcGrid source device grid to be pruned
|
||||
/// @param d_srcLeafMask sidecar array of leaf masks for voxels to retain
|
||||
/// @param stream optional CUDA stream (defaults to CUDA stream 0)
|
||||
PruneGrid(const GridT* d_srcGrid, const Mask<3>* d_srcLeafMask, cudaStream_t stream = 0)
|
||||
: mBuilder(stream), mStream(stream), mTimer(stream), mDeviceSrcGrid(d_srcGrid), mDeviceSrcLeafMask(d_srcLeafMask) {}
|
||||
|
||||
/// @brief Toggle on and off verbose mode
|
||||
/// @param level Verbose level: 0=quiet, 1=timing, 2=benchmarking
|
||||
void setVerbose(int level = 1) { mVerbose = level; }
|
||||
|
||||
/// @brief Set the mode for checksum computation, which is disabled by default
|
||||
/// @param mode Mode of checksum computation
|
||||
void setChecksum(CheckMode mode = CheckMode::Disable){mBuilder.mChecksum = mode;}
|
||||
|
||||
/// @brief Creates a handle to the pruned grid
|
||||
/// @tparam BufferT Buffer type used for allocation of the grid handle
|
||||
/// @param buffer optional buffer (currently ignored)
|
||||
/// @return returns a handle with a grid of type NanoGrid<BuildT>
|
||||
template<typename BufferT = nanovdb::cuda::DeviceBuffer>
|
||||
GridHandle<BufferT>
|
||||
getHandle(const BufferT &buffer = BufferT());
|
||||
|
||||
private:
|
||||
void pruneRoot();
|
||||
|
||||
void pruneInternalNodes();
|
||||
|
||||
void processGridTreeRoot();
|
||||
|
||||
void pruneLeafNodes();
|
||||
|
||||
static constexpr unsigned int mNumThreads = 128;// for kernels spawned via lambdaKernel (others may specialize)
|
||||
static unsigned int numBlocks(unsigned int n) {return (n + mNumThreads - 1) / mNumThreads;}
|
||||
|
||||
TopologyBuilder<BuildT> mBuilder;
|
||||
cudaStream_t mStream{0};
|
||||
util::cuda::Timer mTimer;
|
||||
int mVerbose{0};
|
||||
const GridT *mDeviceSrcGrid;
|
||||
const Mask<3> *mDeviceSrcLeafMask;
|
||||
TreeData mSrcTreeData;
|
||||
};// tools::cuda::PruneGrid<BuildT>
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
template<typename BufferT>
|
||||
GridHandle<BufferT>
|
||||
PruneGrid<BuildT>::getHandle(const BufferT &pool)
|
||||
{
|
||||
// Copy TreeData from GPU -> CPU
|
||||
cudaStreamSynchronize(mStream);
|
||||
mSrcTreeData = util::cuda::DeviceGridTraits<BuildT>::getTreeData(mDeviceSrcGrid);
|
||||
|
||||
// Ensure that the input grid contains no tile values
|
||||
if (mSrcTreeData.mTileCount[2] || mSrcTreeData.mTileCount[1] || mSrcTreeData.mTileCount[0])
|
||||
throw std::runtime_error("Topological operations not supported on grids with value tiles");
|
||||
|
||||
|
||||
// Speculatively prune root node
|
||||
if (mVerbose==1) mTimer.start("\nPrune root node");
|
||||
pruneRoot();
|
||||
|
||||
// Allocate memory for pruned upper/lower masks
|
||||
if (mVerbose==1) mTimer.restart("Allocating internal node mask buffers");
|
||||
mBuilder.allocateInternalMaskBuffers(mStream);
|
||||
|
||||
// Prune masks of upper/lower nodes
|
||||
if (mVerbose==1) mTimer.restart("Prune internal nodes");
|
||||
pruneInternalNodes();
|
||||
|
||||
// Enumerate tree nodes
|
||||
if (mVerbose==1) mTimer.restart("Count pruned tree nodes");
|
||||
mBuilder.countNodes(mStream);
|
||||
|
||||
cudaStreamSynchronize(mStream);
|
||||
|
||||
// Allocate new device grid buffer for pruned result
|
||||
if (mVerbose==1) mTimer.restart("Allocating pruned grid buffer");
|
||||
auto buffer = mBuilder.getBuffer(pool, mStream);
|
||||
|
||||
// Process GridData/TreeData/RootData of pruned result
|
||||
if (mVerbose==1) mTimer.restart("Processing grid/tree/root");
|
||||
processGridTreeRoot();
|
||||
|
||||
// Process upper nodes of pruned result
|
||||
if (mVerbose==1) mTimer.restart("Processing upper nodes");
|
||||
mBuilder.processUpperNodes(mStream);
|
||||
|
||||
// Process lower nodes of pruned result
|
||||
if (mVerbose==1) mTimer.restart("Processing lower nodes");
|
||||
mBuilder.processLowerNodes(mStream);
|
||||
|
||||
// Prune active masks of leaf nodes and rebuild offsets
|
||||
if (mVerbose==1) mTimer.restart("Pruning leaf nodes");
|
||||
pruneLeafNodes();
|
||||
|
||||
// Process bounding boxes
|
||||
if (mVerbose==1) mTimer.restart("Processing bounding boxes");
|
||||
mBuilder.processBBox(mStream);
|
||||
|
||||
// Post-process Grid/Tree data
|
||||
if (mVerbose==1) mTimer.restart("Post-processing grid/tree data");
|
||||
mBuilder.postProcessGridTree(mStream);
|
||||
if (mVerbose==1) mTimer.stop();
|
||||
|
||||
cudaStreamSynchronize(mStream);
|
||||
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
}// PruneGrid<BuildT>::getHandle
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void PruneGrid<BuildT>::pruneRoot()
|
||||
{
|
||||
// This method conservatively (and trivially) prunes the root tile table.
|
||||
// For this simple approximation, it is assumed that all root tiles currently present will presist,
|
||||
// and they will be pruned at a later stage if deemed empty.
|
||||
|
||||
int device = 0;
|
||||
cudaGetDevice(&device);
|
||||
|
||||
std::map<uint64_t, typename RootT::DataType::Tile> prunedTiles;
|
||||
|
||||
// This encoding scheme mirrors the one used in PointsToGrid; note that it is different from Tile::key
|
||||
auto coordToKey = [](const Coord &ijk)->uint64_t{
|
||||
// Note: int32_t has a range of -2^31 to 2^31 - 1 whereas uint32_t has a range of 0 to 2^32 - 1
|
||||
static constexpr int64_t kOffset = 1 << 31;
|
||||
return (uint64_t(uint32_t(int64_t(ijk[2]) + kOffset) >> 12) ) | // z is the lower 21 bits
|
||||
(uint64_t(uint32_t(int64_t(ijk[1]) + kOffset) >> 12) << 21) | // y is the middle 21 bits
|
||||
(uint64_t(uint32_t(int64_t(ijk[0]) + kOffset) >> 12) << 42); // x is the upper 21 bits
|
||||
};// coordToKey lambda functor
|
||||
|
||||
if (mSrcTreeData.mVoxelCount) { // If the input grid is not empty
|
||||
// Make a host copy of the source topology RootNode
|
||||
auto deviceSrcRoot = static_cast<const RootT*>(util::PtrAdd(mDeviceSrcGrid, GridT::memUsage() + mSrcTreeData.mNodeOffset[3]));
|
||||
uint64_t rootSize = mSrcTreeData.mNodeOffset[2] - mSrcTreeData.mNodeOffset[3];
|
||||
auto srcRootBuffer = nanovdb::HostBuffer::create(rootSize);
|
||||
cudaCheck(cudaMemcpyAsync(srcRootBuffer.data(), deviceSrcRoot, rootSize, cudaMemcpyDeviceToHost, mStream));
|
||||
auto srcRoot = static_cast<RootT*>(srcRootBuffer.data());
|
||||
|
||||
// Add all root tiles, reordering if necessary
|
||||
for (uint32_t t = 0; t < srcRoot->tileCount(); t++) {
|
||||
auto tile = srcRoot->tile(t);
|
||||
auto sortKey = coordToKey(tile->origin());
|
||||
prunedTiles.emplace(sortKey, *tile);
|
||||
}
|
||||
}
|
||||
|
||||
// Package the duplicated root topology into a RootNode plus Tile list; upload to the GPU
|
||||
uint64_t rootSize = RootT::memUsage(prunedTiles.size());
|
||||
mBuilder.mProcessedRoot = nanovdb::cuda::DeviceBuffer::create(rootSize);
|
||||
auto prunedRootPtr = static_cast<RootT*>(mBuilder.mProcessedRoot.data());
|
||||
prunedRootPtr->mTableSize = prunedTiles.size();
|
||||
uint32_t t = 0;
|
||||
for (const auto& [key, tile] : prunedTiles)
|
||||
*prunedRootPtr->tile(t++) = tile;
|
||||
mBuilder.mProcessedRoot.deviceUpload(device, mStream, false);
|
||||
}// PruneGrid<BuildT>::pruneRoot
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void PruneGrid<BuildT>::pruneInternalNodes()
|
||||
{
|
||||
// Computes the masks of upper and (densified) lower internal nodes, as a result of the pruning operation
|
||||
// Masks of lower internal nodes are densified in the sense that a serialized array of them is allocated,
|
||||
// as if every upper node had a full set of 32^3 lower children
|
||||
if (auto srcLeafCount = mSrcTreeData.mNodeCount[0]) { // Unless it's an empty grid
|
||||
util::cuda::lambdaKernel<<<numBlocks(srcLeafCount), mNumThreads, 0, mStream>>>(
|
||||
srcLeafCount, util::morphology::cuda::PruneInternalNodesFunctor<BuildT>(),
|
||||
mDeviceSrcGrid, mBuilder.deviceProcessedRoot(), mDeviceSrcLeafMask, mBuilder.mUpperMasks.deviceData(), mBuilder.mLowerMasks.deviceData() );
|
||||
}
|
||||
}// PruneGrid<BuildT>::pruneInternalNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template <typename BuildT>
|
||||
void PruneGrid<BuildT>::processGridTreeRoot()
|
||||
{
|
||||
// Copy GridData from source grid
|
||||
// By convention: this will duplicate grid name and map. Others will be reset later
|
||||
cudaCheck(cudaMemcpyAsync(&mBuilder.data()->getGrid(), mDeviceSrcGrid->data(), GridT::memUsage(), cudaMemcpyDeviceToDevice, mStream));
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, mStream>>>(1, topology::detail::BuildGridTreeRootFunctor<BuildT>(), mBuilder.deviceData());
|
||||
cudaCheckError();
|
||||
}// PruneGrid<BuildT>::processGridTreeRoot
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void PruneGrid<BuildT>::pruneLeafNodes()
|
||||
{
|
||||
// Prunes the active masks of the source grid to the intersection with the leaf-mask sidecar
|
||||
// followed by rebuilding the leaf offsets
|
||||
auto srcLeafCount = mSrcTreeData.mNodeCount[0];
|
||||
if (srcLeafCount) { // Unless grid is empty
|
||||
util::cuda::lambdaKernel<<<numBlocks(srcLeafCount), mNumThreads, 0, mStream>>>(
|
||||
srcLeafCount, util::morphology::cuda::PruneLeafMasksFunctor<BuildT>(), mDeviceSrcGrid, &mBuilder.data()->getGrid(), mDeviceSrcLeafMask);
|
||||
}
|
||||
|
||||
// Update leaf offsets and prefix sums
|
||||
mBuilder.processLeafOffsets(mStream);
|
||||
}// PruneGrid<BuildT>::pruneLeafNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NVIDIA_TOOLS_CUDA_PRUNEGRID_CUH_HAS_BEEN_INCLUDED
|
||||
@@ -0,0 +1,267 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/RefineGrid.cuh
|
||||
|
||||
\authors Efty Sifakis
|
||||
|
||||
\brief 2x Topological refinement of NanoVDB indexGrids on the device
|
||||
|
||||
\warning The header file contains cuda device code so be sure
|
||||
to only include it in .cu files (or other .cuh files)
|
||||
*/
|
||||
|
||||
#ifndef NVIDIA_TOOLS_CUDA_REFINEGRID_CUH_HAS_BEEN_INCLUDED
|
||||
#define NVIDIA_TOOLS_CUDA_REFINEGRID_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <cub/cub.cuh>
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/tools/cuda/TopologyBuilder.cuh>
|
||||
#include <nanovdb/util/cuda/DeviceGridTraits.cuh>
|
||||
#include <nanovdb/util/cuda/Morphology.cuh>
|
||||
#include <nanovdb/util/cuda/Timer.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools::cuda {
|
||||
|
||||
template <typename BuildT>
|
||||
class RefineGrid
|
||||
{
|
||||
using GridT = NanoGrid<BuildT>;
|
||||
using TreeT = NanoTree<BuildT>;
|
||||
using RootT = NanoRoot<BuildT>;
|
||||
using UpperT = NanoUpper<BuildT>;
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructor
|
||||
/// @param deviceGrid source device grid to be refined
|
||||
/// @param stream optional CUDA stream (defaults to CUDA stream 0)
|
||||
RefineGrid(const GridT* d_srcGrid, cudaStream_t stream = 0)
|
||||
: mBuilder(stream), mStream(stream), mTimer(stream), mDeviceSrcGrid(d_srcGrid) {}
|
||||
|
||||
/// @brief Toggle on and off verbose mode
|
||||
/// @param level Verbose level: 0=quiet, 1=timing, 2=benchmarking
|
||||
void setVerbose(int level = 1) { mVerbose = level; }
|
||||
|
||||
/// @brief Set the mode for checksum computation, which is disabled by default
|
||||
/// @param mode Mode of checksum computation
|
||||
void setChecksum(CheckMode mode = CheckMode::Disable){mBuilder.mChecksum = mode;}
|
||||
|
||||
/// @brief Creates a handle to the refined grid
|
||||
/// @tparam BufferT Buffer type used for allocation of the grid handle
|
||||
/// @param buffer optional buffer (currently ignored)
|
||||
/// @return returns a handle with a grid of type NanoGrid<BuildT>
|
||||
template<typename BufferT = nanovdb::cuda::DeviceBuffer>
|
||||
GridHandle<BufferT>
|
||||
getHandle(const BufferT &buffer = BufferT());
|
||||
|
||||
private:
|
||||
void refineRoot();
|
||||
|
||||
void refineInternalNodes();
|
||||
|
||||
void processGridTreeRoot();
|
||||
|
||||
void refineLeafNodes();
|
||||
|
||||
static constexpr unsigned int mNumThreads = 128;// for kernels spawned via lambdaKernel (others may specialize)
|
||||
static unsigned int numBlocks(unsigned int n) {return (n + mNumThreads - 1) / mNumThreads;}
|
||||
|
||||
TopologyBuilder<BuildT> mBuilder;
|
||||
cudaStream_t mStream{0};
|
||||
util::cuda::Timer mTimer;
|
||||
int mVerbose{0};
|
||||
const GridT *mDeviceSrcGrid;
|
||||
TreeData mSrcTreeData;
|
||||
};// tools::cuda::RefineGrid<BuildT>
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
template<typename BufferT>
|
||||
GridHandle<BufferT>
|
||||
RefineGrid<BuildT>::getHandle(const BufferT &pool)
|
||||
{
|
||||
// Copy TreeData from GPU -> CPU
|
||||
cudaStreamSynchronize(mStream);
|
||||
mSrcTreeData = util::cuda::DeviceGridTraits<BuildT>::getTreeData(mDeviceSrcGrid);
|
||||
|
||||
// Ensure that the input grid contains no tile values
|
||||
if (mSrcTreeData.mTileCount[2] || mSrcTreeData.mTileCount[1] || mSrcTreeData.mTileCount[0])
|
||||
throw std::runtime_error("Topological operations not supported on grids with value tiles");
|
||||
|
||||
// Speculatively refine root node
|
||||
if (mVerbose==1) mTimer.start("\nRefining root node");
|
||||
refineRoot();
|
||||
|
||||
// Allocate memory for refined upper/lower masks
|
||||
if (mVerbose==1) mTimer.restart("Allocating internal node mask buffers");
|
||||
mBuilder.allocateInternalMaskBuffers(mStream);
|
||||
|
||||
// Refine masks of upper/lower nodes
|
||||
if (mVerbose==1) mTimer.restart("Refining internal nodes");
|
||||
refineInternalNodes();
|
||||
|
||||
// Enumerate tree nodes
|
||||
if (mVerbose==1) mTimer.restart("Count refined tree nodes");
|
||||
mBuilder.countNodes(mStream);
|
||||
|
||||
cudaStreamSynchronize(mStream);
|
||||
|
||||
// Allocate new device grid buffer for refined result
|
||||
if (mVerbose==1) mTimer.restart("Allocating refined grid buffer");
|
||||
auto buffer = mBuilder.getBuffer(pool, mStream);
|
||||
|
||||
// Process GridData/TreeData/RootData of refined result
|
||||
if (mVerbose==1) mTimer.restart("Processing grid/tree/root");
|
||||
processGridTreeRoot();
|
||||
|
||||
// Process upper nodes of refined result
|
||||
if (mVerbose==1) mTimer.restart("Processing upper nodes");
|
||||
mBuilder.processUpperNodes(mStream);
|
||||
|
||||
// Process lower nodes of refined result
|
||||
if (mVerbose==1) mTimer.restart("Processing lower nodes");
|
||||
mBuilder.processLowerNodes(mStream);
|
||||
|
||||
// Refine leaf node active masks into new topology
|
||||
if (mVerbose==1) mTimer.restart("Refining leaf nodes");
|
||||
refineLeafNodes();
|
||||
|
||||
// Process bounding boxes
|
||||
if (mVerbose==1) mTimer.restart("Processing bounding boxes");
|
||||
mBuilder.processBBox(mStream);
|
||||
|
||||
// Post-process Grid/Tree data
|
||||
if (mVerbose==1) mTimer.restart("Post-processing grid/tree data");
|
||||
mBuilder.postProcessGridTree(mStream);
|
||||
if (mVerbose==1) mTimer.stop();
|
||||
|
||||
cudaStreamSynchronize(mStream);
|
||||
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
}// RefineGrid<BuildT>::getHandle
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void RefineGrid<BuildT>::refineRoot()
|
||||
{
|
||||
// This method conservatively and speculatively refines the root tiles, to accommodate
|
||||
// any new root nodes that might be introduced by the upsampling operation.
|
||||
// The index-space bounding box of each tile is examined, and if it overlaps any of the 2048^3-sized octants
|
||||
// of the tile, a corresponding new tile is preemptively introduced into the root topology.
|
||||
// Root tiles that were preemptively introduced, but end up having no active contents will
|
||||
// be pruned in later stages of processing.
|
||||
|
||||
int device = 0;
|
||||
cudaGetDevice(&device);
|
||||
|
||||
std::map<uint64_t, typename RootT::DataType::Tile> refinedTiles;
|
||||
|
||||
// This encoding scheme mirrors the one used in PointsToGrid; note that it is different from Tile::key
|
||||
auto coordToKey = [](const Coord &ijk)->uint64_t{
|
||||
// Note: int32_t has a range of -2^31 to 2^31 - 1 whereas uint32_t has a range of 0 to 2^32 - 1
|
||||
static constexpr int64_t kOffset = 1 << 31;
|
||||
return (uint64_t(uint32_t(int64_t(ijk[2]) + kOffset) >> 12) ) | // z is the lower 21 bits
|
||||
(uint64_t(uint32_t(int64_t(ijk[1]) + kOffset) >> 12) << 21) | // y is the middle 21 bits
|
||||
(uint64_t(uint32_t(int64_t(ijk[0]) + kOffset) >> 12) << 42); // x is the upper 21 bits
|
||||
};// coordToKey lambda functor
|
||||
|
||||
if (mSrcTreeData.mVoxelCount) { // If the input grid is not empty
|
||||
// Make a host copy of the source topology RootNode *and* the Upper Nodes (needed for BBox'es)
|
||||
// TODO: Consider avoiding to copy the entire set of upper nodes
|
||||
auto deviceSrcRoot = static_cast<const RootT*>(util::PtrAdd(mDeviceSrcGrid, GridT::memUsage() + mSrcTreeData.mNodeOffset[3]));
|
||||
uint64_t rootAndUpperSize = mSrcTreeData.mNodeOffset[1] - mSrcTreeData.mNodeOffset[3];
|
||||
auto srcRootAndUpperBuffer = nanovdb::HostBuffer::create(rootAndUpperSize);
|
||||
cudaCheck(cudaMemcpyAsync(srcRootAndUpperBuffer.data(), deviceSrcRoot, rootAndUpperSize, cudaMemcpyDeviceToHost, mStream));
|
||||
auto srcRootAndUpper = static_cast<RootT*>(srcRootAndUpperBuffer.data());
|
||||
|
||||
// For each original root tile, consider adding those tiles in its 26-connected neighborhood
|
||||
for (uint32_t t = 0; t < srcRootAndUpper->tileCount(); t++) {
|
||||
auto srcUpper = srcRootAndUpper->getChild(srcRootAndUpper->tile(t));
|
||||
const auto tileBBox = srcUpper->bbox();
|
||||
for (int di = 0; di <= 2048; di += 2048)
|
||||
for (int dj = 0; dj <= 2048; dj += 2048)
|
||||
for (int dk = 0; dk <= 2048; dk += 2048) {
|
||||
const auto octantBBox = nanovdb::CoordBBox::createCube(srcUpper->origin().offsetBy(di,dj,dk), 2048);
|
||||
if (tileBBox.hasOverlap(octantBBox)) {
|
||||
auto refinedOrigin = octantBBox.min()+octantBBox.min();
|
||||
auto sortKey = coordToKey(refinedOrigin); // key used in the radix sort, in accordance with PointsToGrid
|
||||
auto tileKey = RootT::CoordToKey(refinedOrigin); // encoding used in the NanoVDB tile
|
||||
typename RootT::Tile refinedTile{tileKey}; // Only the key value is needed; child pointer & value will be unused
|
||||
refinedTiles.emplace(sortKey, refinedTile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Package the new root topology into a RootNode plus Tile list; upload to the GPU
|
||||
uint64_t rootSize = RootT::memUsage(refinedTiles.size());
|
||||
mBuilder.mProcessedRoot = nanovdb::cuda::DeviceBuffer::create(rootSize);
|
||||
auto refinedRootPtr = static_cast<RootT*>(mBuilder.mProcessedRoot.data());
|
||||
refinedRootPtr->mTableSize = refinedTiles.size();
|
||||
uint32_t t = 0;
|
||||
for (const auto& [key, tile] : refinedTiles)
|
||||
*refinedRootPtr->tile(t++) = tile;
|
||||
mBuilder.mProcessedRoot.deviceUpload(device, mStream, false);
|
||||
}// RefineGrid<BuildT>::refineRoot
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void RefineGrid<BuildT>::refineInternalNodes()
|
||||
{
|
||||
// Computes the masks of upper and (densified) lower internal nodes, as a result of the refinement operation
|
||||
// Masks of lower internal nodes are densified in the sense that a serialized array of them is allocated,
|
||||
// as if every upper node had a full set of 32^3 lower children
|
||||
if (auto srcLeafCount = mSrcTreeData.mNodeCount[0]) { // Unless it's an empty grid
|
||||
util::cuda::lambdaKernel<<<numBlocks(srcLeafCount), mNumThreads, 0, mStream>>>(
|
||||
srcLeafCount, util::morphology::cuda::RefineInternalNodesFunctor<BuildT>(),
|
||||
mDeviceSrcGrid, mBuilder.deviceProcessedRoot(), mBuilder.mUpperMasks.deviceData(), mBuilder.mLowerMasks.deviceData() );
|
||||
}
|
||||
}// RefineGrid<BuildT>::refineInternalNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template <typename BuildT>
|
||||
void RefineGrid<BuildT>::processGridTreeRoot()
|
||||
{
|
||||
// Copy GridData from source grid
|
||||
// By convention: this will duplicate grid name and map. Others will be reset later
|
||||
cudaCheck(cudaMemcpyAsync(&mBuilder.data()->getGrid(), mDeviceSrcGrid->data(), GridT::memUsage(), cudaMemcpyDeviceToDevice, mStream));
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, mStream>>>(1, topology::detail::BuildGridTreeRootFunctor<BuildT>(), mBuilder.deviceData());
|
||||
cudaCheckError();
|
||||
}// RefineGrid<BuildT>::processGridTreeRoot
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void RefineGrid<BuildT>::refineLeafNodes()
|
||||
{
|
||||
// Refines the active masks of the source grid (as indicated at the leaf level), into a new grid that
|
||||
// has been already topologically refined to include all necessary leaf nodes.
|
||||
auto srcLeafCount = mSrcTreeData.mNodeCount[0];
|
||||
if (srcLeafCount) { // Unless grid is empty
|
||||
util::cuda::lambdaKernel<<<numBlocks(srcLeafCount), mNumThreads, 0, mStream>>>(
|
||||
srcLeafCount, util::morphology::cuda::RefineLeafMasksFunctor<BuildT>(), mDeviceSrcGrid, &mBuilder.data()->getGrid());
|
||||
}
|
||||
|
||||
// Update leaf offsets and prefix sums
|
||||
mBuilder.processLeafOffsets(mStream);
|
||||
}// RefineGrid<BuildT>::refineLeafNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NVIDIA_TOOLS_CUDA_REFINEGRID_CUH_HAS_BEEN_INCLUDED
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
\brief Performs signed flood-fill operation on the hierarchical tree structure on the device
|
||||
|
||||
\todo This tools needs to handle the (extremely) rare case when root node
|
||||
\todo This tools needs to handle the (extremely) rare case when the root node
|
||||
needs to be modified during the signed flood fill operation. This happens
|
||||
when the root-table needs to be expanded with tile values (of size 4096^3)
|
||||
that are completely inside the implicit surface.
|
||||
@@ -24,9 +24,11 @@
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/cuda/UnifiedBuffer.h>
|
||||
#include <nanovdb/util/cuda/Timer.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
#include <nanovdb/tools/cuda/GridChecksum.cuh>
|
||||
#include <nanovdb/io/IO.h>// debugging needs io of Coord
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
@@ -41,8 +43,6 @@ template<typename BuildT>
|
||||
typename util::enable_if<BuildTraits<BuildT>::is_float, void>::type
|
||||
signedFloodFill(NanoGrid<BuildT> *d_grid, bool verbose = false, cudaStream_t stream = 0);
|
||||
|
||||
namespace {// anonymous namespace
|
||||
|
||||
template<typename BuildT>
|
||||
class SignedFloodFill
|
||||
{
|
||||
@@ -65,47 +65,77 @@ private:
|
||||
|
||||
//================================================================================================
|
||||
|
||||
namespace kernels {// kernels namespace
|
||||
|
||||
template <typename ValueT>
|
||||
struct RootChild {
|
||||
Coord ijk;// origin of the child node
|
||||
uint32_t idx;// linear offset for the child node
|
||||
ValueT val[2];// first and last values as defined by child->getFirstValue/getLastValue
|
||||
RootChild(Coord i=Coord(), uint32_t j=0) : ijk(i), idx(j) {};// c-tor
|
||||
bool operator()(const RootChild &a, const RootChild &b) const { return a.ijk < b.ijk; }// for sorting
|
||||
};
|
||||
|
||||
// CPU kernel!
|
||||
template<typename BuildT>
|
||||
__global__ void processRootKernel(NanoTree<BuildT> *tree)
|
||||
{
|
||||
// auto &root = tree->root();
|
||||
/*
|
||||
using ChildT = typename RootT::ChildNodeType;
|
||||
// Insert the child nodes into a map sorted according to their origin
|
||||
std::map<Coord, ChildT*> nodeKeys;
|
||||
typename RootT::ChildOnIter it = root.beginChildOn();
|
||||
for (; it; ++it) nodeKeys.insert(std::pair<Coord, ChildT*>(it.getCoord(), &(*it)));
|
||||
static const Index DIM = RootT::ChildNodeType::DIM;
|
||||
void processRoot(NanoTree<BuildT> *d_tree)
|
||||
{// the root needs special care since unlike other nodes it's sparse and not dense!
|
||||
using TreeT = NanoTree<BuildT>;
|
||||
using RootT = NanoRoot<BuildT>;
|
||||
using TileT = typename RootT::Tile;
|
||||
using ValueT = typename RootT::ValueType;
|
||||
using ChildT = RootChild<ValueT>;
|
||||
static const int dim = int(RootT::ChildNodeType::DIM);
|
||||
|
||||
// First copy the tree and root and then its tiles, which is of unknown size
|
||||
nanovdb::cuda::UnifiedBuffer uBuffer(sizeof(TreeT) + sizeof(RootT), sizeof(TreeT) + sizeof(RootT) + 64*sizeof(TileT));
|
||||
cudaCheck(cudaMemcpy(uBuffer.data(), d_tree, uBuffer.size(), cudaMemcpyDeviceToHost));// copy Tree and Root (minus tiles)
|
||||
if (!uBuffer.data<TreeT>()->isRootNext()) throw std::runtime_error("ERROR: expected no padding between tree and root!");
|
||||
if ( uBuffer.data<TreeT>()->root().tileCount() == 0) return;// empty root node so nothing to do
|
||||
uBuffer.resize(sizeof(TreeT) + uBuffer.data<TreeT>()->root().memUsage());// likely does nothing since we reserved 64 tiles
|
||||
RootT *root = &uBuffer.data<TreeT>()->root();
|
||||
cudaCheck(cudaMemcpy(root + 1, (char*)(d_tree + 1) + sizeof(RootT), root->tileCount()*sizeof(TileT), cudaMemcpyDeviceToHost));// copy tiles
|
||||
|
||||
// Sort the child nodes of the root in lexicographic order
|
||||
nanovdb::cuda::UnifiedBuffer nodeBuffer(root->tileCount()*sizeof(ChildT));// potential over-allocation
|
||||
auto *first = nodeBuffer.data<ChildT>(), *last = first;
|
||||
for (auto it=root->beginChild(); it; ++it) *last++ = ChildT(it.getCoord(), it.pos());
|
||||
if (last - first < 2) return;// zero or one child node so nothing to do!
|
||||
std::sort(first, last, ChildT());// lexicographic ordering
|
||||
|
||||
// We employ a simple z-scanline algorithm that inserts inactive tiles with
|
||||
// the inside value if they are sandwiched between inside child nodes only!
|
||||
typename std::map<Coord, ChildT*>::const_iterator b = nodeKeys.begin(), e = nodeKeys.end();
|
||||
if ( b == e ) return;
|
||||
for (typename std::map<Coord, ChildT*>::const_iterator a = b++; b != e; ++a, ++b) {
|
||||
Coord d = b->first - a->first; // delta of neighboring coordinates
|
||||
if (d[0]!=0 || d[1]!=0 || d[2]==Int32(DIM)) continue;// not same z-scanline or neighbors
|
||||
const ValueT fill[] = { a->second->getLastValue(), b->second->getFirstValue() };
|
||||
if (!(fill[0] < 0) || !(fill[1] < 0)) continue; // scanline isn't inside
|
||||
Coord c = a->first + Coord(0u, 0u, DIM);
|
||||
for (; c[2] != b->first[2]; c[2] += DIM) root.addTile(c, mInside, false);
|
||||
for (ChildT *a = first, *b = a+1; b!=last; ++a, ++b) {// loop over pairs of adjacent child nodes
|
||||
const Coord d = b->ijk - a->ijk;// coord delta of adjacent child nodes
|
||||
if (d[0]!=0 || d[1]!=0 || d[2]==dim) continue;// not same z-scanline or they are neighbors
|
||||
util::cuda::lambdaKernel<<<1, 1>>>(1, [=] __device__(size_t) {
|
||||
a->val[1] = root->getChild(root->tile(a->idx))->getLastValue();
|
||||
b->val[0] = root->getChild(root->tile(b->idx))->getFirstValue();
|
||||
});
|
||||
cudaCheck(cudaDeviceSynchronize());// required for host access to RootChild::val[2]
|
||||
if (a->val[1] > 0 || b->val[0] > 0) continue; // scanline is not inside a surface
|
||||
for (Coord c = a->ijk.offsetBy(0,0,dim); c[2] != b->ijk[2]; c[2] += dim) {
|
||||
TileT *tile = root->probeTile(c);
|
||||
if (!tile) throw std::runtime_error("ERROR: missing internal tile! Please add them and try again!");
|
||||
tile->setValue(c, false, -root->background());
|
||||
}
|
||||
}
|
||||
*/
|
||||
//root.setBackground(mOutside, /*updateChildNodes=*/false);
|
||||
}// processRootKernel
|
||||
cudaCheck(cudaMemcpy(d_tree + 1, root, root->memUsage(), cudaMemcpyHostToDevice));// copy tiles back to device
|
||||
}// processRoot
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename BuildT, int LEVEL>
|
||||
__global__ void processNodeKernel(NanoTree<BuildT> *tree, size_t count)
|
||||
__global__ void processNode(NanoTree<BuildT> *d_tree, size_t count)
|
||||
{
|
||||
using NodeT = typename NanoNode<BuildT, LEVEL>::type;
|
||||
const int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid >= count) return;
|
||||
const uint32_t nValue = tid & (NodeT::SIZE - 1u);
|
||||
auto &node = *(tree->template getFirstNode<LEVEL>() + (tid >> (3*NodeT::LOG2DIM)));
|
||||
auto &node = *(d_tree->template getFirstNode<LEVEL>() + (tid >> (3*NodeT::LOG2DIM)));
|
||||
const auto &mask = node.childMask();
|
||||
if (mask.isOn(nValue)) return;// ignore if child
|
||||
auto value = tree->background();// initiate to outside value
|
||||
auto value = d_tree->background();// initiate to outside value
|
||||
auto n = mask.template findNext<true>(nValue);
|
||||
if (n < NodeT::SIZE) {
|
||||
if (node.getChild(n)->getFirstValue() < 0) value = -value;
|
||||
@@ -115,25 +145,25 @@ __global__ void processNodeKernel(NanoTree<BuildT> *tree, size_t count)
|
||||
value = -value;
|
||||
}
|
||||
node.setValue(nValue, value);
|
||||
}// processNodeKernel
|
||||
}// processNode
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename BuildT>
|
||||
__global__ void processLeafKernel(NanoTree<BuildT> *tree, size_t count)
|
||||
__global__ void processLeaf(NanoTree<BuildT> *d_tree, size_t count)
|
||||
{
|
||||
using LeafT = NanoLeaf<BuildT>;
|
||||
const size_t tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid >= count) return;
|
||||
const uint32_t nVoxel = tid & (LeafT::SIZE - 1u);
|
||||
auto *leaf = tree->getFirstLeaf() + (tid >> (3*LeafT::LOG2DIM));
|
||||
auto *leaf = d_tree->getFirstLeaf() + (tid >> (3*LeafT::LOG2DIM));
|
||||
const auto &mask = leaf->valueMask();
|
||||
if (mask.isOn(nVoxel)) return;
|
||||
auto *buffer = leaf->mValues;
|
||||
auto n = mask.template findNext<true>(nVoxel);
|
||||
if (n == LeafT::SIZE && (n = mask.template findPrev<true>(nVoxel)) == LeafT::SIZE) n = 0u;
|
||||
buffer[nVoxel] = buffer[n]<0 ? -tree->background() : tree->background();
|
||||
}// processLeafKernel
|
||||
buffer[nVoxel] = buffer[n]<0 ? -d_tree->background() : d_tree->background();
|
||||
}// processLeaf
|
||||
|
||||
//================================================================================================
|
||||
|
||||
@@ -145,7 +175,7 @@ __global__ void cpyNodeCountKernel(NanoGrid<BuildT> *d_grid, uint64_t *d_count)
|
||||
*d_count = d_grid->tree().root().tileCount();
|
||||
}
|
||||
|
||||
}// anonymous namespace
|
||||
}// kernels namespace
|
||||
|
||||
//================================================================================================
|
||||
|
||||
@@ -156,29 +186,29 @@ void SignedFloodFill<BuildT>::operator()(NanoGrid<BuildT> *d_grid)
|
||||
NANOVDB_ASSERT(d_grid);
|
||||
uint64_t count[4], *d_count = nullptr;
|
||||
cudaCheck(util::cuda::mallocAsync((void**)&d_count, 4*sizeof(uint64_t), mStream));
|
||||
cpyNodeCountKernel<BuildT><<<1, 1, 0, mStream>>>(d_grid, d_count);
|
||||
kernels::cpyNodeCountKernel<BuildT><<<1, 1, 0, mStream>>>(d_grid, d_count);
|
||||
cudaCheckError();
|
||||
cudaCheck(cudaMemcpyAsync(&count, d_count, 4*sizeof(uint64_t), cudaMemcpyDeviceToHost, mStream));
|
||||
cudaCheck(util::cuda::freeAsync(d_count, mStream));
|
||||
|
||||
static const int threadsPerBlock = 128;
|
||||
auto blocksPerGrid = [&](size_t count)->uint32_t{return (count + (threadsPerBlock - 1)) / threadsPerBlock;};
|
||||
auto *tree = reinterpret_cast<NanoTree<BuildT>*>(d_grid + 1);
|
||||
auto *d_tree = reinterpret_cast<NanoTree<BuildT>*>(d_grid + 1);
|
||||
|
||||
if (mVerbose) mTimer.start("\nProcess leaf nodes");
|
||||
processLeafKernel<BuildT><<<blocksPerGrid(count[0]<<9), threadsPerBlock, 0, mStream>>>(tree, count[0]<<9);
|
||||
kernels::processLeaf<BuildT><<<blocksPerGrid(count[0]<<9), threadsPerBlock, 0, mStream>>>(d_tree, count[0]<<9);
|
||||
cudaCheckError();
|
||||
|
||||
if (mVerbose) mTimer.restart("Process lower internal nodes");
|
||||
processNodeKernel<BuildT,1><<<blocksPerGrid(count[1]<<12), threadsPerBlock, 0, mStream>>>(tree, count[1]<<12);
|
||||
kernels::processNode<BuildT,1><<<blocksPerGrid(count[1]<<12), threadsPerBlock, 0, mStream>>>(d_tree, count[1]<<12);
|
||||
cudaCheckError();
|
||||
|
||||
if (mVerbose) mTimer.restart("Process upper internal nodes");
|
||||
processNodeKernel<BuildT,2><<<blocksPerGrid(count[2]<<15), threadsPerBlock, 0, mStream>>>(tree, count[2]<<15);
|
||||
kernels::processNode<BuildT,2><<<blocksPerGrid(count[2]<<15), threadsPerBlock, 0, mStream>>>(d_tree, count[2]<<15);
|
||||
cudaCheckError();
|
||||
|
||||
//if (mVerbose) mTimer.restart("Process root node");
|
||||
//processRootKernel<BuildT><<<1, 1, 0, mStream>>>(tree);
|
||||
if (mVerbose) mTimer.restart("Process root node");
|
||||
kernels::processRoot<BuildT>(d_tree);
|
||||
if (mVerbose) mTimer.stop();
|
||||
cudaCheckError();
|
||||
}// SignedFloodFill::operator()
|
||||
@@ -196,6 +226,7 @@ signedFloodFill(NanoGrid<BuildT> *d_grid, bool verbose, cudaStream_t stream)
|
||||
if (cs.isFull()) {// CheckMode::Partial checksum is unaffected
|
||||
updateChecksum(d_gridData, CheckMode::Full, stream);
|
||||
}
|
||||
cudaCheck(cudaStreamSynchronize(stream));
|
||||
}
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
@@ -0,0 +1,563 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/TopologyBuilder.cuh
|
||||
|
||||
\authors Efty Sifakis
|
||||
|
||||
\brief Shared functionality of (mostly morphology) operators that alter the voxel content of grids
|
||||
|
||||
\warning The header file contains cuda device code so be sure
|
||||
to only include it in .cu files (or other .cuh files)
|
||||
*/
|
||||
|
||||
#ifndef NVIDIA_TOOLS_CUDA_TOPOLOGYBUILDER_CUH_HAS_BEEN_INCLUDED
|
||||
#define NVIDIA_TOOLS_CUDA_TOPOLOGYBUILDER_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/cuda/TempPool.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/util/cuda/Morphology.cuh>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools::cuda {
|
||||
|
||||
template <typename BuildT>
|
||||
class TopologyBuilder
|
||||
{
|
||||
static_assert(nanovdb::BuildTraits<BuildT>::is_onindex);// For now, only OnIndexGrids supported
|
||||
|
||||
using GridT = NanoGrid<BuildT>;
|
||||
using TreeT = NanoTree<BuildT>;
|
||||
using RootT = NanoRoot<BuildT>;
|
||||
using UpperT = NanoUpper<BuildT>;
|
||||
using LowerT = NanoLower<BuildT>;
|
||||
using LeafT = NanoLeaf<BuildT>;
|
||||
|
||||
public:
|
||||
|
||||
TopologyBuilder(cudaStream_t stream)
|
||||
{
|
||||
mData = nanovdb::cuda::DeviceBuffer::create(sizeof(Data));
|
||||
}
|
||||
|
||||
struct Data {
|
||||
void *d_bufferPtr;
|
||||
uint64_t grid, tree, root, upper, lower, leaf, size;// byte offsets to nodes in buffer
|
||||
uint32_t nodeCount[3];// 0=leaf,1=lower, 2=upper
|
||||
uint32_t *d_upperOffsets;
|
||||
__hostdev__ GridT& getGrid() const {return *util::PtrAdd<GridT>(d_bufferPtr, grid);}
|
||||
__hostdev__ TreeT& getTree() const {return *util::PtrAdd<TreeT>(d_bufferPtr, tree);}
|
||||
__hostdev__ RootT& getRoot() const {return *util::PtrAdd<RootT>(d_bufferPtr, root);}
|
||||
__hostdev__ UpperT& getUpper(int i) const {return *(util::PtrAdd<UpperT>(d_bufferPtr, upper)+i);}
|
||||
__hostdev__ LowerT& getLower(int i) const {return *(util::PtrAdd<LowerT>(d_bufferPtr, lower)+i);}
|
||||
__hostdev__ LeafT& getLeaf(int i) const {return *(util::PtrAdd<LeafT>(d_bufferPtr, leaf)+i);}
|
||||
};// Data
|
||||
|
||||
void allocateInternalMaskBuffers(cudaStream_t stream);
|
||||
|
||||
void countNodes(cudaStream_t stream);
|
||||
|
||||
template<typename BufferT>
|
||||
BufferT getBuffer(const BufferT &buffer, cudaStream_t stream);
|
||||
|
||||
void processUpperNodes(cudaStream_t stream);
|
||||
|
||||
void processLowerNodes(cudaStream_t stream);
|
||||
|
||||
void processLeafOffsets(cudaStream_t stream);
|
||||
|
||||
void processBBox(cudaStream_t stream);
|
||||
|
||||
void postProcessGridTree(cudaStream_t stream);
|
||||
|
||||
nanovdb::cuda::DeviceBuffer mProcessedRoot;
|
||||
nanovdb::cuda::DeviceBuffer mUpperMasks;
|
||||
nanovdb::cuda::DeviceBuffer mLowerMasks;
|
||||
nanovdb::cuda::DeviceBuffer mUpperOffsets;
|
||||
nanovdb::cuda::DeviceBuffer mLowerOffsets;
|
||||
nanovdb::cuda::DeviceBuffer mLeafOffsets;
|
||||
nanovdb::cuda::DeviceBuffer mVoxelOffsets;
|
||||
nanovdb::cuda::DeviceBuffer mLowerParents;
|
||||
nanovdb::cuda::DeviceBuffer mLeafParents;
|
||||
nanovdb::cuda::DeviceBuffer mData;
|
||||
CheckMode mChecksum{CheckMode::Disable};
|
||||
|
||||
auto deviceProcessedRoot() { return static_cast<RootT*>(mProcessedRoot.deviceData()); }
|
||||
auto hostProcessedRoot() { return static_cast<RootT*>(mProcessedRoot.data()); }
|
||||
void* deviceUpperMasks() { return mUpperMasks.deviceData(); }
|
||||
void* deviceLowerMasks() { return mLowerMasks.deviceData(); }
|
||||
Data* data() { return static_cast<Data*>(mData.data()); }
|
||||
Data* deviceData() { return static_cast<Data*>(mData.deviceData()); }
|
||||
|
||||
private:
|
||||
static constexpr unsigned int mNumThreads = 128;// for kernels spawned via lambdaKernel (others may specialize)
|
||||
static unsigned int numBlocks(unsigned int n) {return (n + mNumThreads - 1) / mNumThreads;}
|
||||
|
||||
nanovdb::cuda::TempDevicePool mTempDevicePool;
|
||||
};// tools::cuda::TopologyBuilder<BuildT>
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Define utility macro used to call cub functions that use dynamic temporary storage
|
||||
#ifndef CALL_CUBS
|
||||
#ifdef _WIN32
|
||||
#define CALL_CUBS(func, ...) \
|
||||
cudaCheck(cub::func(nullptr, mTempDevicePool.requestedSize(), __VA_ARGS__, stream)); \
|
||||
mTempDevicePool.reallocate(stream); \
|
||||
cudaCheck(cub::func(mTempDevicePool.data(), mTempDevicePool.size(), __VA_ARGS__, stream));
|
||||
#else// ndef _WIN32
|
||||
#define CALL_CUBS(func, args...) \
|
||||
cudaCheck(cub::func(nullptr, mTempDevicePool.requestedSize(), args, stream)); \
|
||||
mTempDevicePool.reallocate(stream); \
|
||||
cudaCheck(cub::func(mTempDevicePool.data(), mTempDevicePool.size(), args, stream));
|
||||
#endif// ifdef _WIN32
|
||||
#endif// ifndef CALL_CUBS
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void TopologyBuilder<BuildT>::allocateInternalMaskBuffers(cudaStream_t stream)
|
||||
{
|
||||
if (hostProcessedRoot()->tileCount() == 0) return; // Processing empty grid(s); nothing to allocate
|
||||
|
||||
// Allocate (and zero-fill) buffers large enough to hold:
|
||||
// (a) The serialized masks of all upper nodes, for all tiles in the updated root node, and
|
||||
// (b) The serialized masks of all densified lower nodes, as if every upper node had a full set of 32^3 lower children
|
||||
int device = 0;
|
||||
cudaGetDevice(&device);
|
||||
uint64_t upperSize = hostProcessedRoot()->tileCount() * sizeof(Mask<5>);
|
||||
uint64_t lowerSize = hostProcessedRoot()->tileCount() * Mask<5>::SIZE * sizeof(Mask<4>);
|
||||
mUpperMasks = nanovdb::cuda::DeviceBuffer::create(upperSize, nullptr, device, stream);
|
||||
if (mUpperMasks.deviceData() == nullptr) throw std::runtime_error("Failed to allocate upper mask buffer on device");
|
||||
cudaCheck(cudaMemsetAsync(mUpperMasks.deviceData(), 0, upperSize, stream));
|
||||
mLowerMasks = nanovdb::cuda::DeviceBuffer::create( lowerSize, nullptr, device, stream );
|
||||
if (mLowerMasks.deviceData() == nullptr) throw std::runtime_error("Failed to allocate lower mask buffer on device");
|
||||
cudaCheck(cudaMemsetAsync(mLowerMasks.deviceData(), 0, lowerSize, stream));
|
||||
}// TopologyBuilder<BuildT>::allocateInternalMaskBuffers
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename BuildT>
|
||||
void TopologyBuilder<BuildT>::countNodes(cudaStream_t stream)
|
||||
{
|
||||
auto processedTileCount = hostProcessedRoot()->tileCount();
|
||||
if (processedTileCount == 0) { // Processing empty grid(s); zero nodes at all levels
|
||||
data()->nodeCount[0] = data()->nodeCount[1] = data()->nodeCount[2] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Computes prefix sums of (a) non-empty lower nodes, (b) counts of their leaf children,
|
||||
// and (c) count of the speculatively updated root tiles that have actually been used.
|
||||
// These are used to reconstruct child offsets for the internal nodes of the updated tree,
|
||||
// as well as the tile table at the root.
|
||||
std::size_t size = processedTileCount*Mask<5>::SIZE;
|
||||
|
||||
int device = 0;
|
||||
cudaGetDevice(&device);
|
||||
nanovdb::cuda::DeviceBuffer upperCountsBuffer = nanovdb::cuda::DeviceBuffer::create(processedTileCount*sizeof(uint32_t), nullptr, device, stream);
|
||||
nanovdb::cuda::DeviceBuffer lowerCountsBuffer = nanovdb::cuda::DeviceBuffer::create(size*sizeof(uint32_t), nullptr, device, stream);
|
||||
nanovdb::cuda::DeviceBuffer leafCountsBuffer = nanovdb::cuda::DeviceBuffer::create(size*sizeof(uint32_t), nullptr, device, stream);
|
||||
|
||||
using CountType = uint32_t (*)[Mask<5>::SIZE];
|
||||
auto lowerCounts = reinterpret_cast<CountType>( lowerCountsBuffer.deviceData() );
|
||||
auto leafCounts = reinterpret_cast<CountType>( leafCountsBuffer.deviceData() );
|
||||
|
||||
using Op = util::morphology::cuda::EnumerateNodesFunctor;
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<dim3(processedTileCount, Op::SlicesPerUpperNode, 1), Op::MaxThreadsPerBlock, 0, stream>>>
|
||||
(deviceUpperMasks(), deviceLowerMasks(), lowerCounts, leafCounts);
|
||||
|
||||
mUpperOffsets = nanovdb::cuda::DeviceBuffer::create((processedTileCount+1)*sizeof(uint32_t), nullptr, device, stream);
|
||||
mLowerOffsets = nanovdb::cuda::DeviceBuffer::create((size+1)*sizeof(uint32_t), nullptr, device, stream);
|
||||
mLeafOffsets = nanovdb::cuda::DeviceBuffer::create((size+1)*sizeof(uint32_t), nullptr, device, stream);
|
||||
|
||||
cudaCheck(cudaMemsetAsync(mLowerOffsets.deviceData(), 0, sizeof(uint32_t), stream));
|
||||
CALL_CUBS(DeviceScan::InclusiveSum,
|
||||
static_cast<uint32_t*>(lowerCountsBuffer.deviceData()),
|
||||
static_cast<uint32_t*>(mLowerOffsets.deviceData())+1,
|
||||
size);
|
||||
cudaCheck(cudaMemcpyAsync(&data()->nodeCount[1], static_cast<uint32_t*>(mLowerOffsets.deviceData())+size, sizeof(uint32_t), cudaMemcpyDeviceToHost, stream));
|
||||
|
||||
cudaCheck(cudaMemsetAsync(mLeafOffsets.deviceData(), 0, sizeof(uint32_t), stream));
|
||||
CALL_CUBS(DeviceScan::InclusiveSum,
|
||||
static_cast<uint32_t*>(leafCountsBuffer.deviceData()),
|
||||
static_cast<uint32_t*>(mLeafOffsets.deviceData())+1,
|
||||
size);
|
||||
cudaCheck(cudaMemcpyAsync(&data()->nodeCount[0], static_cast<uint32_t*>(mLeafOffsets.deviceData())+size, sizeof(uint32_t), cudaMemcpyDeviceToHost, stream));
|
||||
|
||||
util::cuda::lambdaKernel<<<numBlocks(processedTileCount), mNumThreads, 0, stream>>>(
|
||||
processedTileCount,
|
||||
[] __device__(size_t tileID, CountType lowerOffsets, uint32_t* upperCounts)
|
||||
{ upperCounts[tileID] = (lowerOffsets[tileID+1][0] > lowerOffsets[tileID][0]) ? 1 : 0; },
|
||||
static_cast<CountType>(mLowerOffsets.deviceData()),
|
||||
static_cast<uint32_t*>(upperCountsBuffer.deviceData()));
|
||||
|
||||
cudaCheck(cudaMemsetAsync( mUpperOffsets.deviceData(), 0, sizeof(uint32_t), stream));
|
||||
CALL_CUBS(DeviceScan::InclusiveSum,
|
||||
static_cast<uint32_t*>(upperCountsBuffer.deviceData()),
|
||||
static_cast<uint32_t*>(mUpperOffsets.deviceData())+1,
|
||||
processedTileCount);
|
||||
cudaCheck(cudaMemcpyAsync(&data()->nodeCount[2], static_cast<uint32_t*>(mUpperOffsets.deviceData())+processedTileCount, sizeof(uint32_t), cudaMemcpyDeviceToHost, stream));
|
||||
}// TopologyBuilder<BuildT>::countNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template <typename BuildT>
|
||||
template <typename BufferT>
|
||||
BufferT TopologyBuilder<BuildT>::getBuffer(const BufferT &pool, cudaStream_t stream)
|
||||
{
|
||||
// Allocates a device buffer for the destination grid, once the topology/size of the tree is known
|
||||
data()->grid = 0;// grid is always stored at the start of the buffer!
|
||||
data()->tree = GridT::memUsage();// grid ends and tree begins
|
||||
data()->root = data()->tree + TreeT::memUsage(); // tree ends and root node begins
|
||||
data()->upper = data()->root + RootT::memUsage(data()->nodeCount[2]);// root node ends and upper internal nodes begin
|
||||
data()->lower = data()->upper + UpperT::memUsage()*data()->nodeCount[2];// upper internal nodes ends and lower internal nodes begin
|
||||
data()->leaf = data()->lower + LowerT::memUsage()*data()->nodeCount[1];// lower internal nodes ends and leaf nodes begin
|
||||
data()->size = data()->leaf + LeafT::DataType::memUsage()*data()->nodeCount[0];// leaf nodes end and blind meta data begins
|
||||
|
||||
int device = 0;
|
||||
cudaGetDevice(&device);
|
||||
auto buffer = BufferT::create(data()->size, &pool, device, stream);// only allocate buffer on the device
|
||||
cudaCheck(cudaMemsetAsync(buffer.deviceData(), 0, data()->size, stream));
|
||||
|
||||
data()->d_bufferPtr = buffer.deviceData();
|
||||
if (data()->d_bufferPtr == nullptr) throw std::runtime_error("Failed to allocate grid buffer on the device");
|
||||
if (data()->nodeCount[2] != 0) // Unless the result is an empty grid
|
||||
data()->d_upperOffsets = static_cast<uint32_t*>(mUpperOffsets.deviceData());
|
||||
mData.deviceUpload(device, stream, false);
|
||||
|
||||
return buffer;
|
||||
}// TopologyBuilder<BuildT>::getBuffer
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace topology::detail {
|
||||
|
||||
template <typename BuildT>
|
||||
struct BuildGridTreeRootFunctor
|
||||
{
|
||||
__device__
|
||||
void operator()(size_t, typename TopologyBuilder<BuildT>::Data *d_data) {
|
||||
|
||||
// process Root
|
||||
auto &root = d_data->getRoot();
|
||||
root.mTableSize = d_data->nodeCount[2];
|
||||
root.mBackground = NanoRoot<BuildT>::ValueType(0);// background_value
|
||||
root.mMinimum = root.mMaximum = NanoRoot<BuildT>::ValueType(0);
|
||||
root.mAverage = root.mStdDevi = NanoRoot<BuildT>::FloatType(0);
|
||||
root.mBBox = CoordBBox(); // To be further updated after the leaf-level voxel update
|
||||
|
||||
// process Tree
|
||||
auto &tree = d_data->getTree();
|
||||
tree.setRoot(&root);
|
||||
if (d_data->nodeCount[2]) {
|
||||
tree.setFirstNode(&d_data->getUpper(0));
|
||||
tree.setFirstNode(&d_data->getLower(0));
|
||||
tree.setFirstNode(&d_data->getLeaf(0));
|
||||
}
|
||||
else {
|
||||
tree.template setFirstNode<NanoUpper<BuildT>>(nullptr);
|
||||
tree.template setFirstNode<NanoLower<BuildT>>(nullptr);
|
||||
tree.template setFirstNode<NanoLeaf<BuildT>>(nullptr);
|
||||
}
|
||||
tree.mNodeCount[2] = d_data->nodeCount[2];
|
||||
tree.mNodeCount[1] = d_data->nodeCount[1];
|
||||
tree.mNodeCount[0] = d_data->nodeCount[0];
|
||||
tree.mVoxelCount = 0; // Actual voxel count (for non-empty grids) will only be known
|
||||
// once leaf masks have been processed
|
||||
tree.mTileCount[2] = tree.mTileCount[1] = tree.mTileCount[0] = 0;
|
||||
|
||||
// process Grid
|
||||
// The GridData header has already been copied from the input;
|
||||
// reset what is necessary, and assert that others are at the expected values
|
||||
auto &grid = d_data->getGrid();
|
||||
|
||||
#ifdef NANOVDB_USE_NEW_MAGIC_NUMBERS
|
||||
NANOVDB_ASSERT(grid.mMagic == NANOVDB_MAGIC_GRID);
|
||||
#else
|
||||
NANOVDB_ASSERT(grid.mMagic == NANOVDB_MAGIC_NUMB);
|
||||
#endif
|
||||
grid.mChecksum.disable(); // all 64 bits ON means checksum is disabled
|
||||
NANOVDB_ASSERT(grid.mVersion == Version());
|
||||
NANOVDB_ASSERT(grid.mFlags.isMaskOn(GridFlags::IsBreadthFirst));
|
||||
grid.mFlags.initMask({GridFlags::IsBreadthFirst}); // expected flags (HasBBox will be set later if grid is non-empty)
|
||||
grid.mGridIndex = 0u; // Possibly overwriting input; returned grid has batch size 1
|
||||
grid.mGridCount = 1u; // Possibly overwriting input; returned grid has batch size 1
|
||||
grid.mGridSize = d_data->size;
|
||||
// grid.mGridName expected to have been copied verbatim from input
|
||||
// grid.mMap expected to have been copied verbatim from input
|
||||
grid.mWorldBBox = Vec3dBBox();// invalid bbox
|
||||
grid.mVoxelSize = grid.mMap.getVoxelSize();
|
||||
NANOVDB_ASSERT(grid.mGridClass == GridClass::IndexGrid);
|
||||
NANOVDB_ASSERT(grid.mGridType == toGridType<BuildT>());
|
||||
grid.mBlindMetadataOffset = d_data->size; // i.e. no blind data, even if the input grid had any
|
||||
grid.mBlindMetadataCount = 0u; // i.e. no blind data
|
||||
NANOVDB_ASSERT(grid.mData0 == 0u); // zero padding
|
||||
grid.mData1 = 1u; // This will be updated (unless this is an empty grid) after voxels have been processed
|
||||
#ifdef NANOVDB_USE_NEW_MAGIC_NUMBERS
|
||||
NANOVDB_ASSERT(grid.mData2 == 0u);
|
||||
#else
|
||||
NANOVDB_ASSERT(grid.mData2 == NANOVDB_MAGIC_GRID);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
}// namespace topology::detail
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace topology::detail {
|
||||
|
||||
template <typename BuildT>
|
||||
struct BuildUpperNodesFunctor
|
||||
{
|
||||
__device__
|
||||
void operator()(size_t processedTileID, typename TopologyBuilder<BuildT>::Data *d_data, NanoRoot<BuildT> *d_processedRoot) {
|
||||
uint32_t tileID = d_data->d_upperOffsets[processedTileID];
|
||||
if (tileID != d_data->d_upperOffsets[processedTileID+1]) // if the offsets are the same, this was a speculatively introduced tile which was not necessary
|
||||
{
|
||||
auto &root = d_data->getRoot();
|
||||
auto &dstUpper = d_data->getUpper(tileID);
|
||||
auto &processedTile = *d_processedRoot->tile(processedTileID);
|
||||
root.tile(tileID)->setChild( processedTile.origin(), &dstUpper, &root );
|
||||
dstUpper.mBBox = CoordBBox(); // To be further updated after the operation has been applied at leaf-level
|
||||
// TODO: Is this accurate? Any other flags that should be set?
|
||||
dstUpper.mFlags = (uint64_t)GridFlags::HasBBox;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}// namespace topology::detail
|
||||
|
||||
template <typename BuildT>
|
||||
inline void TopologyBuilder<BuildT>::processUpperNodes(cudaStream_t stream)
|
||||
{
|
||||
// Connect all newly allocated upper nodes to their respective tiles
|
||||
// Also fill in any necessary part of the preamble (in InternalData) of upper nodes
|
||||
auto processedTileCount = hostProcessedRoot()->tileCount();
|
||||
|
||||
if (processedTileCount) { // Unless output grid is empty
|
||||
util::cuda::lambdaKernel<<<numBlocks(processedTileCount), mNumThreads, 0, stream>>>(
|
||||
processedTileCount, topology::detail::BuildUpperNodesFunctor<BuildT>(), deviceData(), deviceProcessedRoot());
|
||||
cudaCheckError();
|
||||
}
|
||||
}// TopologyBuilder<BuildT>::processUpperNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template <typename BuildT>
|
||||
inline void TopologyBuilder<BuildT>::processLowerNodes(cudaStream_t stream)
|
||||
{
|
||||
// Fill out the contents of all newly allocated lower nodes (using the densified upper/lower mask arrays)
|
||||
// Also fill in the preamble (most of LeafData) for their leaf children
|
||||
auto processedTileCount = hostProcessedRoot()->tileCount();
|
||||
using CountType = uint32_t (*)[Mask<5>::SIZE];
|
||||
|
||||
if (processedTileCount) { // Unless output grid is empty
|
||||
int device = 0;
|
||||
cudaGetDevice(&device);
|
||||
std::size_t lowerCount = data()->nodeCount[1];
|
||||
mLowerParents = nanovdb::cuda::DeviceBuffer::create(lowerCount*sizeof(uint32_t), nullptr, device, stream);
|
||||
std::size_t leafCount = data()->nodeCount[0];
|
||||
mLeafParents = nanovdb::cuda::DeviceBuffer::create(leafCount*sizeof(uint32_t), nullptr, device, stream);
|
||||
|
||||
using Op = util::morphology::cuda::ProcessLowerNodesFunctor<BuildT>;
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<dim3(processedTileCount, Op::SlicesPerUpperNode, 1), Op::MaxThreadsPerBlock, 0, stream>>>(
|
||||
deviceUpperMasks(),
|
||||
deviceLowerMasks(),
|
||||
static_cast<uint32_t*>(mUpperOffsets.deviceData()),
|
||||
static_cast<CountType>(mLowerOffsets.deviceData()),
|
||||
static_cast<CountType>(mLeafOffsets.deviceData()),
|
||||
static_cast<GridT*>(data()->d_bufferPtr),
|
||||
static_cast<uint32_t*>(mLowerParents.deviceData()),
|
||||
static_cast<uint32_t*>(mLeafParents.deviceData())
|
||||
);
|
||||
cudaCheckError();
|
||||
}
|
||||
|
||||
mProcessedRoot.clear(stream);
|
||||
mUpperMasks.clear(stream);
|
||||
mLowerMasks.clear(stream);
|
||||
mLowerOffsets.clear(stream);
|
||||
mLeafOffsets.clear(stream);
|
||||
}// TopologyBuilder<BuildT>::processLowerNodes
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace topology::detail {
|
||||
|
||||
template <typename BuildT>
|
||||
struct UpdateLeafVoxelCountsAndPrefixSumFunctor
|
||||
{
|
||||
__device__
|
||||
void operator()(size_t leafID, typename TopologyBuilder<BuildT>::Data *d_data, uint64_t *d_voxelCounts) {
|
||||
auto &leaf = d_data->getGrid().tree().template getFirstNode<0>()[leafID];
|
||||
const uint64_t *w = leaf.mValueMask.words();
|
||||
uint64_t prefixSum = 0, sum = util::countOn(*w++);
|
||||
prefixSum = sum;
|
||||
for (int n = 9; n < 55; n += 9) {// n=i*9 where i=1,2,..6
|
||||
sum += util::countOn(*w++);
|
||||
prefixSum |= sum << n; }// each pre-fixed sum is encoded in 9 bits
|
||||
sum += util::countOn(*w);
|
||||
d_voxelCounts[leafID] = sum;
|
||||
leaf.mPrefixSum = prefixSum; }
|
||||
};
|
||||
|
||||
template <typename BuildT>
|
||||
struct UpdateLeafVoxelOffsetsFunctor
|
||||
{
|
||||
__device__
|
||||
void operator()(size_t leafID, typename TopologyBuilder<BuildT>::Data *d_data, uint64_t *d_voxelOffsets) {
|
||||
auto &leaf = d_data->getGrid().tree().template getFirstNode<0>()[leafID];
|
||||
leaf.mOffset = d_voxelOffsets[leafID]+1; }
|
||||
};
|
||||
|
||||
}// namespace topology::detail
|
||||
|
||||
template<typename BuildT>
|
||||
inline void TopologyBuilder<BuildT>::processLeafOffsets(cudaStream_t stream)
|
||||
{
|
||||
int device = 0;
|
||||
cudaGetDevice(&device);
|
||||
std::size_t leafCount = data()->nodeCount[0];
|
||||
if (leafCount) { // Unless output grid is empty
|
||||
mVoxelOffsets = nanovdb::cuda::DeviceBuffer::create((leafCount+1)*sizeof(uint64_t), nullptr, device, stream);
|
||||
cudaCheck(cudaMemsetAsync(mVoxelOffsets.deviceData(), 0, sizeof(uint64_t), stream));
|
||||
util::cuda::lambdaKernel<<<numBlocks(leafCount), mNumThreads, 0, stream>>>(
|
||||
leafCount, topology::detail::UpdateLeafVoxelCountsAndPrefixSumFunctor<BuildT>(), deviceData(), static_cast<uint64_t*>(mVoxelOffsets.deviceData())+1);
|
||||
CALL_CUBS(DeviceScan::InclusiveSum,
|
||||
static_cast<uint64_t*>(mVoxelOffsets.deviceData())+1,
|
||||
static_cast<uint64_t*>(mVoxelOffsets.deviceData())+1,
|
||||
leafCount);
|
||||
util::cuda::lambdaKernel<<<numBlocks(leafCount), mNumThreads, 0, stream>>>(
|
||||
leafCount, topology::detail::UpdateLeafVoxelOffsetsFunctor<BuildT>(), deviceData(), static_cast<uint64_t*>(mVoxelOffsets.deviceData()));
|
||||
}
|
||||
}// TopologyBuilder<BuildT>::processLeafOffsets
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Undefine utility macro for cub functions
|
||||
#ifdef CALL_CUBS
|
||||
#undef CALL_CUBS
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace topology::detail {
|
||||
|
||||
template <typename BuildT>
|
||||
struct UpdateAndPropagateLeafBBoxFunctor
|
||||
{
|
||||
__device__
|
||||
void operator()(size_t tid, typename TopologyBuilder<BuildT>::Data *d_data, const uint32_t* leafParents) {
|
||||
auto &lower = d_data->getLower(leafParents[tid]);
|
||||
auto &leaf = d_data->getLeaf(tid);
|
||||
leaf.updateBBox();
|
||||
lower.mBBox.expandAtomic(leaf.bbox());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename BuildT>
|
||||
struct PropagateLowerBBoxFunctor
|
||||
{
|
||||
__device__
|
||||
void operator()(size_t tid, typename TopologyBuilder<BuildT>::Data *d_data, const uint32_t* lowerParents) {
|
||||
auto &upper = d_data->getUpper(lowerParents[tid]);
|
||||
auto &lower = d_data->getLower(tid);
|
||||
upper.mBBox.expandAtomic(lower.bbox()); }
|
||||
};
|
||||
|
||||
template <typename BuildT>
|
||||
struct PropagateUpperBBoxFunctor
|
||||
{
|
||||
__device__
|
||||
void operator()(size_t tid, typename TopologyBuilder<BuildT>::Data *d_data) {
|
||||
d_data->getRoot().mBBox.expandAtomic(d_data->getUpper(tid).bbox());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename BuildT>
|
||||
struct UpdateRootWorldBBoxFunctor
|
||||
{
|
||||
__device__
|
||||
void operator()(size_t tid, typename TopologyBuilder<BuildT>::Data *d_data) {
|
||||
// TODO: check that the correct semantics are followed in this transformation
|
||||
auto BBox = d_data->getRoot().mBBox;
|
||||
BBox.max() += 1;
|
||||
d_data->getGrid().mFlags.setMaskOn(GridFlags::HasBBox);
|
||||
d_data->getGrid().mWorldBBox = BBox.transform(d_data->getGrid().data()->mMap);
|
||||
}
|
||||
};
|
||||
|
||||
}// namespace topology::detail
|
||||
|
||||
template <typename BuildT>
|
||||
inline void TopologyBuilder<BuildT>::processBBox(cudaStream_t stream)
|
||||
{
|
||||
if (data()->nodeCount[0] == 0) return; // Output grid is empty; retain empty bounding box
|
||||
|
||||
// TODO: Do we need a special case when flags indicates no bounding box?
|
||||
|
||||
// update and propagate bbox from leaf -> lower/parent nodes
|
||||
util::cuda::lambdaKernel<<<numBlocks(data()->nodeCount[0]), mNumThreads, 0, stream>>>(
|
||||
data()->nodeCount[0], topology::detail::UpdateAndPropagateLeafBBoxFunctor<BuildT>(), deviceData(), static_cast<uint32_t*>(mLeafParents.deviceData()));
|
||||
mLeafParents.clear(stream);
|
||||
cudaCheckError();
|
||||
|
||||
// propagate bbox from lower -> upper/parent node
|
||||
util::cuda::lambdaKernel<<<numBlocks(data()->nodeCount[1]), mNumThreads, 0, stream>>>(
|
||||
data()->nodeCount[1], topology::detail::PropagateLowerBBoxFunctor<BuildT>(), deviceData(), static_cast<uint32_t*>(mLowerParents.deviceData()));
|
||||
mLowerParents.clear(stream);
|
||||
cudaCheckError();
|
||||
|
||||
// propagate bbox from upper -> root/parent node
|
||||
util::cuda::lambdaKernel<<<numBlocks(data()->nodeCount[2]), mNumThreads, 0, stream>>>(data()->nodeCount[2], topology::detail::PropagateUpperBBoxFunctor<BuildT>(), deviceData());
|
||||
cudaCheckError();
|
||||
|
||||
// update the world-bbox in the root node
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, stream>>>(1, topology::detail::UpdateRootWorldBBoxFunctor<BuildT>(), deviceData());
|
||||
cudaCheckError();
|
||||
}// TopologyBuilder<BuildT>::processBBox
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace topology::detail {
|
||||
|
||||
template <typename BuildT>
|
||||
struct PostProcessGridTreeFunctor
|
||||
{
|
||||
__device__
|
||||
void operator()(size_t tid, typename TopologyBuilder<BuildT>::Data *d_data, uint64_t* d_voxelOffsets) {
|
||||
auto& grid = d_data->getGrid();
|
||||
auto& tree = grid.tree();
|
||||
auto leafCount = tree.mNodeCount[0];
|
||||
tree.mVoxelCount = d_voxelOffsets[leafCount];
|
||||
grid.mData1 = tree.mVoxelCount+1;
|
||||
}
|
||||
};
|
||||
|
||||
}// namespace topology::detail
|
||||
|
||||
template <typename BuildT>
|
||||
inline void TopologyBuilder<BuildT>::postProcessGridTree(cudaStream_t stream)
|
||||
{
|
||||
// Finish updates to GridData/TreeData and (optionally) update checksum
|
||||
if (data()->nodeCount[0]) // if grid is empty, the default values are correct
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, stream>>>(1, topology::detail::PostProcessGridTreeFunctor<BuildT>(), deviceData(), static_cast<uint64_t*>(mVoxelOffsets.deviceData()));
|
||||
cudaCheckError();
|
||||
mVoxelOffsets.clear(stream);
|
||||
|
||||
tools::cuda::updateChecksum((GridData*)data()->d_bufferPtr, mChecksum, stream);
|
||||
}// TopologyBuilder<BuildT>::postProcessGridTree
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NVIDIA_TOOLS_CUDA_TOPOLOGYBUILDER_CUH_HAS_BEEN_INCLUDED
|
||||
@@ -0,0 +1,250 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/VoxelBlockManager.cuh
|
||||
|
||||
\author Eftychios Sifakis
|
||||
|
||||
\date January 27, 2025
|
||||
|
||||
\brief Implements device functions to build and query the VoxelBlockManager.
|
||||
The VoxelBlockManager is an acceleration structure for sequential access
|
||||
and stencil operations over solely the active voxels of an OnIndexGrid
|
||||
which enables SIMT parallelism independent of occupancy.
|
||||
*/
|
||||
|
||||
#include <cuda/atomic>
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
#ifndef NANOVDB_VOXELBLOCKMANAGER_CUH_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_VOXELBLOCKMANAGER_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools::cuda {
|
||||
|
||||
/// @brief This class allows for sequential access to contiguous spans (i.e.
|
||||
/// blocks) of active voxels in an IndexGrid on the device, and efficient
|
||||
/// computation of linear offsets of stencils (e.g. 3-wide box stencil).
|
||||
/// @tparam BlockWidth Number of active voxels in a contiguous span
|
||||
template <int BlockWidth>
|
||||
struct VoxelBlockManager
|
||||
{
|
||||
// The efficiency of the functions in this class are contingent on
|
||||
// threadblock-level coordination, which manifests either as using shared
|
||||
// memory for synchronization, or warp-level shift operations.
|
||||
|
||||
// The jumpMap is packed in uint64_t's, one per 64 consecutive bits. Hence
|
||||
// JumpMapLength is the number of uint64_t that are needed to straddle the
|
||||
// total BlockWidth bits of the jumpMap.
|
||||
static constexpr int JumpMapLength = BlockWidth/64;
|
||||
|
||||
static constexpr uint32_t UnusedLeafIndex = 0xffffffff;
|
||||
static constexpr uint16_t UnusedVoxelOffset = 0xffff;
|
||||
|
||||
/// @brief Given a grid and the associated jumpMap in global memory, compute
|
||||
/// the leaf indices and voxel offsets in shared memory. This function
|
||||
/// writes to shared memory and synchronizes threads and thus should not be
|
||||
/// called from divergent threads within a thread block.
|
||||
/// @tparam BuildT Build type of the grid
|
||||
/// @param grid
|
||||
/// @param firstLeafID
|
||||
/// @param jumpMap
|
||||
/// @param blockFirstOffset
|
||||
/// @param smem_leafIndex Leaf indices stored in shared memory
|
||||
/// @param smem_voxelOffset Voxel offsets stored in shared memory
|
||||
template <class BuildT>
|
||||
__device__
|
||||
static typename util::enable_if<BuildTraits<BuildT>::is_index, void>::type
|
||||
decodeInverseMaps(
|
||||
NanoGrid<BuildT> *grid,
|
||||
const uint32_t firstLeafID,
|
||||
const uint64_t *jumpMap,
|
||||
const uint64_t blockFirstOffset,
|
||||
uint32_t *smem_leafIndex,
|
||||
uint16_t *smem_voxelOffset)
|
||||
{
|
||||
// Verify that the nodes can be accessed linearly
|
||||
NANOVDB_ASSERT(grid->isSequential());
|
||||
|
||||
int tID = threadIdx.x;
|
||||
|
||||
// Count how many additional leaves (following the one indicated by firstLeafID)
|
||||
// overlap with this voxel block
|
||||
int nExtraLeaves = 0;
|
||||
for (int i = 0; i < JumpMapLength; i++)
|
||||
nExtraLeaves += util::countOn(jumpMap[i]);
|
||||
|
||||
// Initialize leafIndex & voxelOffset to sentinel values
|
||||
// for blocks that extend beyond the last active voxel in the grid
|
||||
if (tID < BlockWidth)
|
||||
#pragma cuda unroll
|
||||
for (int i = 0; i < BlockWidth; i += blockDim.x) {
|
||||
smem_leafIndex[i+tID] = UnusedLeafIndex;
|
||||
smem_voxelOffset[i+tID] = UnusedVoxelOffset;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
NANOVDB_ASSERT(blockDim.x <= 512);
|
||||
const auto& tree = grid->tree();
|
||||
// Loop through all leafNodes overlapping the voxel block
|
||||
// with all threads in threadblock working collaboratively within each leafNode
|
||||
for (int leafID = firstLeafID; leafID <= firstLeafID + nExtraLeaves; leafID++) {
|
||||
const auto& leaf = tree.template getFirstNode<0>()[leafID];
|
||||
const Coord origin = leaf.origin();
|
||||
for (int threadOffset = 0; threadOffset < 512; threadOffset += blockDim.x) {
|
||||
int localOffset = threadOffset + tID;
|
||||
int index = leaf.data()->getValue(localOffset);
|
||||
if ((index >= blockFirstOffset) && (index < blockFirstOffset + BlockWidth)) {
|
||||
int blockOffset = index - blockFirstOffset;
|
||||
// Write inverse map to shared memory; no collisions
|
||||
smem_leafIndex[blockOffset] = leafID;
|
||||
smem_voxelOffset[blockOffset] = localOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
/// @brief Given a grid and its decoded voxel map, compute the stencil.
|
||||
/// This function accesses shared memory but does not synchronize threads
|
||||
/// so it may be called from divergent threads within a thread block.
|
||||
/// offsets for a 3x3x3 box stencil.
|
||||
/// @tparam BuildT Build type of the grid
|
||||
/// @param grid
|
||||
/// @param smem_leafIndex Leaf indices stored in shared memory
|
||||
/// @param smem_voxelOffset Voxel offsets stored in shared memory
|
||||
/// @param stencilIndices Pointer to output stencil indices. Must have
|
||||
/// length of at least 27 (corresponding to the 3x3x3 stencil)
|
||||
template <class BuildT>
|
||||
__device__
|
||||
static typename util::enable_if<BuildTraits<BuildT>::is_index, void>::type
|
||||
computeBoxStencil(
|
||||
const NanoGrid<BuildT> *grid,
|
||||
const uint32_t *smem_leafIndex,
|
||||
const uint16_t *smem_voxelOffset,
|
||||
uint64_t *stencilIndices)
|
||||
{
|
||||
// Verify that the nodes can be accessed linearly
|
||||
NANOVDB_ASSERT(grid->isSequential());
|
||||
|
||||
int tID = threadIdx.x;
|
||||
const auto& tree = grid->tree();
|
||||
if (smem_leafIndex[tID] != UnusedLeafIndex) {
|
||||
// This presumes that leaf nodes are fixed-size and sequentially accessible in memory
|
||||
const auto& leaf = tree.template getFirstNode<0>()[ smem_leafIndex[tID] ];
|
||||
const Coord coord = leaf.offsetToGlobalCoord( smem_voxelOffset[tID] );
|
||||
const auto index = leaf.getValue( smem_voxelOffset[tID] );
|
||||
for (int di = -1; di <= 1; di++)
|
||||
for (int dj = -1; dj <= 1; dj++)
|
||||
for (int dk = -1; dk <= 1; dk++) {
|
||||
int spokeID = ( di + 1 ) * 9 + ( dj + 1 ) * 3 + dk + 1;
|
||||
const auto neighbor = coord.offsetBy( di, dj, dk );
|
||||
stencilIndices[spokeID] = tree.getValue( neighbor );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief This functor calculates the firstLeafID and jumpMap for the
|
||||
/// VoxelBlockManager over the subset of the Tree nodes specified by
|
||||
/// firstOffset, lastOffset, and nBlocks.
|
||||
template<int BlockWidthLog2, int NumThreads>
|
||||
struct BuildVoxelBlockManagerFunctor
|
||||
{
|
||||
static constexpr int BlockWidth = 1 << BlockWidthLog2;
|
||||
static constexpr int JumpMapLength = BlockWidth/64;
|
||||
static constexpr int SlicesPerLowerNode = 8;
|
||||
static constexpr int LeafNodesPerSlice = 4096/SlicesPerLowerNode;
|
||||
|
||||
static constexpr int MaxThreadsPerBlock = NumThreads;
|
||||
static constexpr int MinBlocksPerMultiprocessor = 1;
|
||||
|
||||
void __device__
|
||||
operator()(
|
||||
uint64_t firstOffset,
|
||||
uint64_t lastOffset,
|
||||
int nBlocks,
|
||||
const NanoGrid<ValueOnIndex> *grid,
|
||||
uint32_t *firstLeafID,
|
||||
uint64_t *jumpMap)
|
||||
{
|
||||
// Verify that the nodes can be accessed linearly
|
||||
NANOVDB_ASSERT(grid->isSequential());
|
||||
|
||||
using JumpMapType = uint64_t (&)[][JumpMapLength];
|
||||
|
||||
int tID = threadIdx.x;
|
||||
int blockID = blockIdx.x;
|
||||
int sliceID = blockIdx.y;
|
||||
|
||||
const auto& tree = grid->tree();
|
||||
|
||||
const auto& lower = tree.getFirstNode<1>()[blockID];
|
||||
for ( std::size_t jj = sliceID*LeafNodesPerSlice; jj < (sliceID+1)*LeafNodesPerSlice; jj += MaxThreadsPerBlock )
|
||||
if ( lower.childMask().isOn(jj+tID) )
|
||||
{
|
||||
auto& leaf = *lower.getChild(jj+tID);
|
||||
const auto leafFirstOffset = leaf.data()->firstOffset();
|
||||
const auto leafValueCount = leaf.data()->valueCount();
|
||||
const auto leafLastOffset = leafFirstOffset + leafValueCount - 1;
|
||||
|
||||
auto leafIndex = util::PtrDiff(&leaf,tree.getFirstNode<0>()) / sizeof(NanoLeaf<ValueOnIndex>);
|
||||
|
||||
if ( ( leafFirstOffset > lastOffset ) || (leafLastOffset < firstOffset) ) continue;
|
||||
|
||||
int lastBlock = (leafLastOffset - firstOffset) >> BlockWidthLog2;
|
||||
lastBlock = min(lastBlock, nBlocks-1);
|
||||
uint64_t firstBlock = (leafFirstOffset < firstOffset) ? 0 :
|
||||
(leafFirstOffset - firstOffset) >> BlockWidthLog2;
|
||||
|
||||
// For all but the first block touched, mark the firstLeaf as being this one
|
||||
for ( uint64_t b = lastBlock; b > firstBlock; --b )
|
||||
firstLeafID[b] = leafIndex;
|
||||
if (leafFirstOffset < firstOffset) { firstLeafID[0] = leafIndex; continue; }
|
||||
|
||||
const auto offsetInBlock = (leafFirstOffset - 1) & (BlockWidth - 1);
|
||||
if ( !offsetInBlock ) {
|
||||
// If the first leaf starts exactly at the beginning of a
|
||||
// block, register it in mFirstLeaf too
|
||||
firstLeafID[firstBlock] = leafIndex;
|
||||
} else {
|
||||
// Otherwise, mark it in the jumpMap
|
||||
// The specific uint64_t in the jumpMap to be marked is at element offset (offsetInBlock>>6), i.e. offsetBlock/64
|
||||
// and bit offset (offsetInBlock & 0x3f), i.e. offsetInBlock%64
|
||||
::cuda::atomic_ref<uint64_t> ref(jumpMap[firstBlock * JumpMapLength + (offsetInBlock>>6)]);
|
||||
ref.fetch_or(0x1ul << (offsetInBlock & 0x3f));
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// @brief Helper function to build a VoxelBlockManager on the device
|
||||
template<int BlockWidthLog2, int NumThreads = 128>
|
||||
void buildVoxelBlockManager(
|
||||
uint64_t firstOffset,
|
||||
uint64_t lastOffset,
|
||||
int nBlocks,
|
||||
int lowerCount,
|
||||
const NanoGrid<ValueOnIndex> *grid,
|
||||
uint32_t *firstLeafID,
|
||||
uint64_t *jumpMap,
|
||||
cudaStream_t stream = 0)
|
||||
{
|
||||
using Op = BuildVoxelBlockManagerFunctor<BlockWidthLog2, NumThreads>;
|
||||
util::cuda::operatorKernel<Op>
|
||||
<<<dim3(lowerCount,Op::SlicesPerLowerNode,1), NumThreads, 0, stream>>>(
|
||||
firstOffset, lastOffset, nBlocks, grid, firstLeafID, jumpMap);
|
||||
}
|
||||
|
||||
} // namespace tools::cuda
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_VOXELBLOCKMANAGER_CUH_HAS_BEEN_INCLUDED
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(NanoVDBTests LANGUAGES CXX)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
@@ -33,6 +33,11 @@ endif()
|
||||
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/data")
|
||||
|
||||
if (MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler /bigobj")
|
||||
endif()
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
add_executable(nanovdb_test_nanovdb "TestNanoVDB.cc")
|
||||
@@ -44,7 +49,13 @@ add_test(nanovdb_unit_test nanovdb_test_nanovdb)
|
||||
if(NANOVDB_USE_CUDA)
|
||||
add_executable(nanovdb_test_cuda "TestNanoVDB.cu")
|
||||
target_link_libraries(nanovdb_test_cuda PRIVATE nanovdb GTest::GTest GTest::Main)
|
||||
set_target_properties(nanovdb_test_cuda PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
add_test(nanovdb_cuda_unit_test nanovdb_test_cuda)
|
||||
|
||||
add_executable(nanovdb_test_mgpu "TestMultiGPU.cu")
|
||||
target_link_libraries(nanovdb_test_mgpu PRIVATE nanovdb GTest::GTest GTest::Main)
|
||||
set_target_properties(nanovdb_test_mgpu PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
add_test(nanovdb_mgpu_unit_test nanovdb_test_mgpu)
|
||||
endif()
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/tools/cuda/DistributedPointsToGrid.cuh>
|
||||
#include <nanovdb/util/Timer.h>
|
||||
#include <nanovdb/cuda/DeviceMesh.h>
|
||||
|
||||
#include <cuda.h>
|
||||
#include <cuda_runtime_api.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <thread> // for std::thread
|
||||
|
||||
#include <thrust/fill.h>
|
||||
#include <thrust/universal_vector.h>
|
||||
#include <thrust/execution_policy.h>
|
||||
|
||||
/// @brief Tests the correctness of multi-GPU radix sort
|
||||
TEST(TestNanoVDBMultiGPU, RadixSort)
|
||||
{
|
||||
nanovdb::cuda::DeviceMesh deviceMesh;
|
||||
|
||||
std::vector<cudaEvent_t> preEvents(deviceMesh.deviceCount());
|
||||
std::vector<cudaEvent_t> postEvents(deviceMesh.deviceCount());
|
||||
std::vector<nanovdb::cuda::TempDevicePool> tempDevicePools(deviceMesh.deviceCount());
|
||||
std::vector<ptrdiff_t> deviceOffsets(deviceMesh.deviceCount());
|
||||
std::vector<size_t> deviceSizes(deviceMesh.deviceCount());
|
||||
|
||||
using KeyT = int;
|
||||
using ValueT = double;
|
||||
|
||||
std::srand(444);
|
||||
const size_t numItems = 1571;
|
||||
thrust::universal_vector<KeyT> keysIn(numItems);
|
||||
thrust::universal_vector<ValueT> valuesIn(numItems);
|
||||
for (size_t i = 0; i < numItems; ++i)
|
||||
{
|
||||
keysIn[i] = rand();
|
||||
valuesIn[i] = -static_cast<ValueT>(keysIn[i]);
|
||||
}
|
||||
|
||||
thrust::universal_vector<KeyT> keysOut(numItems);
|
||||
thrust::universal_vector<ValueT> valuesOut(numItems);
|
||||
|
||||
for (const auto& [deviceId, stream] : deviceMesh) {
|
||||
cudaSetDevice(deviceId);
|
||||
cudaEventCreateWithFlags(&preEvents[deviceId], cudaEventDisableTiming);
|
||||
cudaEventCreateWithFlags(&postEvents[deviceId], cudaEventDisableTiming);
|
||||
|
||||
auto deviceSize = (numItems + deviceMesh.deviceCount() - 1) / deviceMesh.deviceCount();
|
||||
deviceOffsets[deviceId] = deviceSize * deviceId;
|
||||
deviceSizes[deviceId] = std::min(deviceSize, numItems - deviceOffsets[deviceId]);
|
||||
}
|
||||
|
||||
nanovdb::tools::cuda::radixSortAsync(deviceMesh, tempDevicePools.data(), keysIn.data().get(), keysOut.data().get(), valuesIn.data().get(), valuesOut.data().get(), numItems, deviceOffsets.data(), deviceSizes.data(), preEvents.data(), postEvents.data());
|
||||
|
||||
for (const auto& [deviceId, stream] : deviceMesh) {
|
||||
cudaSetDevice(deviceId);
|
||||
cudaEventSynchronize(postEvents[deviceId]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < numItems -1; ++i) {
|
||||
EXPECT_LE(keysOut[i], keysOut[i + 1]);
|
||||
EXPECT_GE(valuesOut[i], valuesOut[i + 1]);
|
||||
}
|
||||
|
||||
for (const auto& [deviceId, stream] : deviceMesh) {
|
||||
cudaSetDevice(deviceId);
|
||||
cudaEventDestroy(postEvents[deviceId]);
|
||||
cudaEventDestroy(preEvents[deviceId]);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Tests the correctness of multi-GPU exclusive sums against an equivalent CPU implementation
|
||||
TEST(TestNanoVDBMultiGPU, ExclusiveSum)
|
||||
{
|
||||
nanovdb::cuda::DeviceMesh deviceMesh;
|
||||
|
||||
std::vector<cudaEvent_t> preEvents(deviceMesh.deviceCount());
|
||||
std::vector<cudaEvent_t> postEvents(deviceMesh.deviceCount());
|
||||
std::vector<nanovdb::cuda::TempDevicePool> tempDevicePools(deviceMesh.deviceCount());
|
||||
std::vector<size_t> deviceSizes(deviceMesh.deviceCount());
|
||||
|
||||
thrust::universal_vector<int> input(937);
|
||||
thrust::universal_vector<int> output(input.size());
|
||||
|
||||
for (const auto& [deviceId, stream] : deviceMesh) {
|
||||
cudaSetDevice(deviceId);
|
||||
cudaEventCreateWithFlags(&preEvents[deviceId], cudaEventDisableTiming);
|
||||
cudaEventCreateWithFlags(&postEvents[deviceId], cudaEventDisableTiming);
|
||||
|
||||
auto deviceSize = (input.size() + deviceMesh.deviceCount() - 1) / deviceMesh.deviceCount();
|
||||
const ptrdiff_t deviceOffset = deviceSize * deviceId;
|
||||
deviceSizes[deviceId] = std::min(deviceSize, input.size() - deviceOffset);
|
||||
|
||||
thrust::fill(thrust::cuda::par.on(stream), input.begin() + deviceOffset, input.begin() + deviceOffset + deviceSizes[deviceId], 0);
|
||||
thrust::fill(thrust::cuda::par.on(stream), output.begin() + deviceOffset, output.begin() + deviceOffset + deviceSizes[deviceId], 0);
|
||||
}
|
||||
|
||||
for (const auto& [deviceId, stream] : deviceMesh) {
|
||||
cudaSetDevice(deviceId);
|
||||
cudaStreamSynchronize(stream);
|
||||
}
|
||||
|
||||
{
|
||||
// Set the input indices corresponding to the Fibbonacci sequence to be 1, rest 0
|
||||
input[0] = 1;
|
||||
size_t i = 0;
|
||||
size_t j = 1;
|
||||
size_t k = i + j;
|
||||
while(k < input.size()) {
|
||||
input[k] = 1;
|
||||
i = j;
|
||||
j = k;
|
||||
k = (i + j);
|
||||
}
|
||||
}
|
||||
|
||||
nanovdb::tools::cuda::exclusiveSumAsync(deviceMesh, tempDevicePools.data(), input.data(), output.data(), deviceSizes.begin(), preEvents.data(), postEvents.data());
|
||||
|
||||
for (const auto& [deviceId, stream] : deviceMesh) {
|
||||
cudaSetDevice(deviceId);
|
||||
cudaStreamSynchronize(stream);
|
||||
}
|
||||
|
||||
int accumulator = 0;
|
||||
for (size_t i = 0; i < output.size(); ++i) {
|
||||
EXPECT_EQ(output[i], accumulator);
|
||||
accumulator += input[i];
|
||||
}
|
||||
|
||||
for (const auto& [deviceId, stream] : deviceMesh) {
|
||||
cudaSetDevice(deviceId);
|
||||
cudaEventDestroy(postEvents[deviceId]);
|
||||
cudaEventDestroy(preEvents[deviceId]);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Tests the correctness of multi-GPU inclusive sums against an equivalent CPU implementation
|
||||
TEST(TestNanoVDBMultiGPU, InclusiveSum)
|
||||
{
|
||||
nanovdb::cuda::DeviceMesh deviceMesh;
|
||||
|
||||
std::vector<cudaEvent_t> preEvents(deviceMesh.deviceCount());
|
||||
std::vector<cudaEvent_t> postEvents(deviceMesh.deviceCount());
|
||||
std::vector<nanovdb::cuda::TempDevicePool> tempDevicePools(deviceMesh.deviceCount());
|
||||
std::vector<size_t> deviceSizes(deviceMesh.deviceCount());
|
||||
|
||||
thrust::universal_vector<int> input(937);
|
||||
thrust::universal_vector<int> output(input.size());
|
||||
|
||||
for (const auto& [deviceId, stream] : deviceMesh) {
|
||||
cudaSetDevice(deviceId);
|
||||
cudaEventCreateWithFlags(&preEvents[deviceId], cudaEventDisableTiming);
|
||||
cudaEventCreateWithFlags(&postEvents[deviceId], cudaEventDisableTiming);
|
||||
|
||||
auto deviceSize = (input.size() + deviceMesh.deviceCount() - 1) / deviceMesh.deviceCount();
|
||||
const ptrdiff_t deviceOffset = deviceSize * deviceId;
|
||||
deviceSizes[deviceId] = std::min(deviceSize, input.size() - deviceOffset);
|
||||
|
||||
thrust::fill(thrust::cuda::par.on(stream), input.begin() + deviceOffset, input.begin() + deviceOffset + deviceSizes[deviceId], 0);
|
||||
thrust::fill(thrust::cuda::par.on(stream), output.begin() + deviceOffset, output.begin() + deviceOffset + deviceSizes[deviceId], 0);
|
||||
}
|
||||
|
||||
for (const auto& [deviceId, stream] : deviceMesh) {
|
||||
cudaSetDevice(deviceId);
|
||||
cudaStreamSynchronize(stream);
|
||||
}
|
||||
|
||||
{
|
||||
// Set the input indices corresponding to the Fibbonacci sequence to be 1, rest 0
|
||||
input[0] = 1;
|
||||
size_t i = 0;
|
||||
size_t j = 1;
|
||||
size_t k = i + j;
|
||||
while(k < input.size()) {
|
||||
input[k] = 1;
|
||||
i = j;
|
||||
j = k;
|
||||
k = (i + j);
|
||||
}
|
||||
}
|
||||
|
||||
nanovdb::tools::cuda::inclusiveSumAsync(deviceMesh, tempDevicePools.data(), input.data(), output.data(), deviceSizes.begin(), preEvents.data(), postEvents.data());
|
||||
|
||||
for (const auto& [deviceId, stream] : deviceMesh) {
|
||||
cudaSetDevice(deviceId);
|
||||
cudaStreamSynchronize(stream);
|
||||
}
|
||||
|
||||
int accumulator = 0;
|
||||
for (size_t i = 0; i < output.size(); ++i) {
|
||||
accumulator += input[i];
|
||||
EXPECT_EQ(output[i], accumulator);
|
||||
}
|
||||
|
||||
for (const auto& [deviceId, stream] : deviceMesh) {
|
||||
cudaSetDevice(deviceId);
|
||||
cudaEventDestroy(postEvents[deviceId]);
|
||||
cudaEventDestroy(preEvents[deviceId]);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Tests multi-GPU creation of grids for a single dense leaf
|
||||
TEST(TestNanoVDBMultiGPU, DenseLeaf_DistributedCudaPointsToGrid_UnifiedBuffer)
|
||||
{
|
||||
int current = 0;
|
||||
cudaCheck(cudaGetDevice(¤t));
|
||||
|
||||
using BufferT = nanovdb::cuda::UnifiedBuffer;
|
||||
using BuildT = nanovdb::ValueOnIndex;
|
||||
// Initialize coordinates corresponding to a single dense leaf. In
|
||||
// DistributedPointsToGrid, individual leaf nodes are resident and
|
||||
// processed entirely on a single GPU. Thus, the single leaf case results
|
||||
// in the edge case where one GPU constructs a leaf while the others idle.
|
||||
const size_t voxelCount = 8 * 8 * 8;
|
||||
nanovdb::Coord* voxels = nullptr;
|
||||
const size_t voxelSize = voxelCount * sizeof(nanovdb::Coord);
|
||||
cudaCheck(cudaMallocManaged(&voxels, voxelSize));
|
||||
for (int32_t i = 0; i < 8; ++i)
|
||||
for (int32_t j = 0; j < 8; ++j)
|
||||
for (int32_t k = 0; k < 8; ++k)
|
||||
voxels[i * 8 * 8 + j * 8 + k] = nanovdb::Coord(i, j, k);
|
||||
|
||||
nanovdb::cuda::DeviceMesh deviceMesh;
|
||||
nanovdb::tools::cuda::DistributedPointsToGrid<BuildT> converter(deviceMesh);
|
||||
auto handle = converter.getHandle(voxels, voxelCount);
|
||||
|
||||
EXPECT_TRUE(handle.deviceData());// grid exists on the GPU
|
||||
EXPECT_TRUE(handle.deviceGrid<BuildT>());
|
||||
EXPECT_FALSE(handle.deviceGrid<int>(0));
|
||||
EXPECT_TRUE(handle.deviceGrid<BuildT>(0));
|
||||
EXPECT_FALSE(handle.deviceGrid<BuildT>(1));
|
||||
EXPECT_TRUE(handle.data());// grid also exists on the CPU
|
||||
|
||||
//timer.start("Allocating and copying grid from GPU to CPU");
|
||||
auto *grid = handle.grid<BuildT>();// grid also exists on the CPU
|
||||
EXPECT_TRUE(grid);
|
||||
handle.deviceDownload();// creates a copy on the CPU
|
||||
EXPECT_TRUE(handle.deviceData());
|
||||
EXPECT_TRUE(handle.data());
|
||||
auto *data = handle.gridData();
|
||||
EXPECT_TRUE(data);
|
||||
grid = handle.grid<BuildT>();
|
||||
EXPECT_TRUE(grid);
|
||||
EXPECT_TRUE(grid->activeVoxelCount() == 512);
|
||||
EXPECT_EQ(nanovdb::Vec3d(1.0), grid->voxelSize());
|
||||
|
||||
cudaCheck(cudaFree(voxels));
|
||||
cudaSetDevice(current); // restore device so subsequent tests don't fail
|
||||
}// Large_DistributedCudaPointsToGrid_UnifiedBuffer
|
||||
|
||||
/// @brief Tests multi-GPU creation of grids for a large number of randomly sampled voxels
|
||||
TEST(TestNanoVDBMultiGPU, Large_DistributedCudaPointsToGrid_UnifiedBuffer)
|
||||
{
|
||||
int current = 0;
|
||||
cudaCheck(cudaGetDevice(¤t));
|
||||
|
||||
using BufferT = nanovdb::cuda::UnifiedBuffer;
|
||||
using BuildT = nanovdb::ValueOnIndex;
|
||||
nanovdb::util::Timer timer;
|
||||
const size_t voxelCount = 1 << 20;// 1048576
|
||||
nanovdb::Coord* voxels = nullptr;
|
||||
const size_t voxelSize = voxelCount * sizeof(nanovdb::Coord);
|
||||
cudaCheck(cudaMallocManaged(&voxels, voxelSize));
|
||||
{//generate random voxels
|
||||
std::srand(98765);
|
||||
const int max = 512, min = -max;
|
||||
auto op = [&](){return rand() % (max - min) + min;};
|
||||
for (size_t i = 0; i < voxelCount; ++i)
|
||||
voxels[i] = nanovdb::Coord(op(), op(), op());
|
||||
}
|
||||
|
||||
nanovdb::cuda::DeviceMesh deviceMesh;
|
||||
nanovdb::tools::cuda::DistributedPointsToGrid<BuildT> converter(deviceMesh);
|
||||
auto handle = converter.getHandle(voxels, voxelCount);
|
||||
// auto handle = nanovdb::tools::cuda::voxelsToGrid<BuildT, nanovdb::Coord*, BufferT>(voxels, voxelCount);
|
||||
|
||||
EXPECT_TRUE(handle.deviceData());// grid exists on the GPU
|
||||
EXPECT_TRUE(handle.deviceGrid<BuildT>());
|
||||
EXPECT_FALSE(handle.deviceGrid<int>(0));
|
||||
EXPECT_TRUE(handle.deviceGrid<BuildT>(0));
|
||||
EXPECT_FALSE(handle.deviceGrid<BuildT>(1));
|
||||
EXPECT_TRUE(handle.data());// grid also exists on the CPU
|
||||
|
||||
//timer.start("Allocating and copying grid from GPU to CPU");
|
||||
auto *grid = handle.grid<BuildT>();// grid also exists on the CPU
|
||||
EXPECT_TRUE(grid);
|
||||
handle.deviceDownload();// creates a copy on the CPU
|
||||
EXPECT_TRUE(handle.deviceData());
|
||||
EXPECT_TRUE(handle.data());
|
||||
auto *data = handle.gridData();
|
||||
EXPECT_TRUE(data);
|
||||
grid = handle.grid<BuildT>();
|
||||
EXPECT_TRUE(grid);
|
||||
EXPECT_TRUE(grid->valueCount()>0);
|
||||
EXPECT_EQ(nanovdb::Vec3d(1.0), grid->voxelSize());
|
||||
|
||||
//timer.restart("Parallel unit-testing on CPU");
|
||||
nanovdb::util::forEach(0, voxelCount, 1, [&](const nanovdb::util::Range1D &r){
|
||||
auto acc = grid->getAccessor();
|
||||
for (size_t i=r.begin(); i!=r.end(); ++i) {
|
||||
const nanovdb::Coord &ijk = voxels[i];
|
||||
EXPECT_TRUE(acc.probeLeaf(ijk)!=nullptr);
|
||||
EXPECT_TRUE(acc.isActive(ijk));
|
||||
EXPECT_TRUE(acc.getValue(ijk) > 0u);
|
||||
const auto *leaf = acc.get<nanovdb::GetLeaf<BuildT>>(ijk);
|
||||
EXPECT_TRUE(leaf);
|
||||
const auto offset = leaf->CoordToOffset(ijk);
|
||||
EXPECT_EQ(ijk, leaf->offsetToGlobalCoord(offset));
|
||||
}
|
||||
});
|
||||
|
||||
cudaCheck(cudaFree(voxels));
|
||||
cudaSetDevice(current); // restore device so subsequent tests don't fail
|
||||
}// Large_DistributedCudaPointsToGrid_UnifiedBuffer
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <algorithm> // for std::is_sorted
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <iomanip> // for std::setw, std::setfill
|
||||
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
@@ -231,8 +232,8 @@ using MyTypes = ::testing::Types<float,// GridType::Float =
|
||||
// GridType::Vec4d = 18
|
||||
nanovdb::ValueIndex,// GridType::Index = 19
|
||||
nanovdb::ValueOnIndex,// GridType::OnIndex = 20
|
||||
nanovdb::ValueIndexMask,// GridType::IndexMask = 21
|
||||
nanovdb::ValueOnIndexMask,// GridType::OnIndexMask = 22
|
||||
// nanovdb::ValueIndexMask,// GridType::IndexMask = 21
|
||||
// nanovdb::ValueOnIndexMask,// GridType::OnIndexMask = 22
|
||||
nanovdb::Point,// GridType::PointIndex = 23
|
||||
nanovdb::Vec3u8,// GridType::Vec3u8 = 24
|
||||
nanovdb::Vec3u16,// GridType::Vec3u16 = 25
|
||||
@@ -372,8 +373,8 @@ TEST_F(TestNanoVDB, Basic)
|
||||
TEST_F(TestNanoVDB, toStr)
|
||||
{
|
||||
{// toStr(GridType)
|
||||
EXPECT_EQ(12, nanovdb::strlen<nanovdb::GridType>());
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::Unknown ), "?"), 0 );
|
||||
EXPECT_EQ(11, nanovdb::strlen<nanovdb::GridType>());
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::Unknown ), "Unknown"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::Float ), "float"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::Double ), "double"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::Int16 ), "int16"), 0 );
|
||||
@@ -394,8 +395,6 @@ TEST_F(TestNanoVDB, toStr)
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::Vec4d ), "Vec4d"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::Index ), "Index"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::OnIndex ), "OnIndex"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::IndexMask ), "IndexMask"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::OnIndexMask ), "OnIndexMask"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::PointIndex ), "PointIndex"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::Vec3u8 ), "Vec3u8"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::GridType::Vec3u16 ), "Vec3u16"), 0 );
|
||||
@@ -437,14 +436,12 @@ TEST_F(TestNanoVDB, toStr)
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::Version(12,34,56) ), "12.34.56"), 0 );
|
||||
}
|
||||
{// toStr(MagicType)
|
||||
EXPECT_EQ(25, nanovdb::strlen<nanovdb::MagicType>());
|
||||
EXPECT_EQ(14, nanovdb::strlen<nanovdb::MagicType>());
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::MagicType::Unknown ), "unknown"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::MagicType::OpenVDB ), "openvdb"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::MagicType::NanoVDB ), "nanovdb"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::MagicType::NanoGrid ), "nanovdb::Grid"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::MagicType::NanoFile ), "nanovdb::File"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::MagicType::NanoNode ), "nanovdb::NodeManager"), 0 );
|
||||
EXPECT_EQ( strcmp(nanovdb::toStr(mStr, nanovdb::MagicType::NanoFrag ), "fragmented nanovdb::Grid"), 0 );
|
||||
}
|
||||
}// toStr
|
||||
|
||||
@@ -920,12 +917,8 @@ TEST_F(TestNanoVDB, Traits)
|
||||
EXPECT_FALSE(test);
|
||||
test = nanovdb::BuildTraits<nanovdb::ValueIndex>::is_index;
|
||||
EXPECT_TRUE(test);
|
||||
test = nanovdb::BuildTraits<nanovdb::ValueIndexMask>::is_index;
|
||||
EXPECT_TRUE(test);
|
||||
test = nanovdb::BuildTraits<nanovdb::ValueOnIndex>::is_index;
|
||||
EXPECT_TRUE(test);
|
||||
test = nanovdb::BuildTraits<nanovdb::ValueOnIndexMask>::is_index;
|
||||
EXPECT_TRUE(test);
|
||||
test = nanovdb::BuildTraits<nanovdb::Fp4>::is_Fp;
|
||||
EXPECT_TRUE(test);
|
||||
test = nanovdb::BuildTraits<nanovdb::Fp8>::is_Fp;
|
||||
@@ -2198,6 +2191,51 @@ TEST_F(TestNanoVDB, RootNode)
|
||||
}
|
||||
} // RootNode
|
||||
|
||||
TEST_F(TestNanoVDB, GridBlindMetaData)
|
||||
{
|
||||
nanovdb::GridBlindMetaData meta0;
|
||||
EXPECT_TRUE(meta0.isValid());
|
||||
EXPECT_EQ(0u, meta0.mDataOffset);
|
||||
EXPECT_TRUE(std::string(meta0.mName).empty());
|
||||
EXPECT_EQ(nullptr, meta0.getBlindData<float>());
|
||||
EXPECT_EQ(nullptr, meta0.getBlindData<int>());
|
||||
|
||||
const std::string name("this is a test");
|
||||
nanovdb::GridBlindMetaData meta(0, 10, sizeof(float),
|
||||
nanovdb::GridBlindDataSemantic::Unknown,
|
||||
nanovdb::GridBlindDataClass::AttributeArray,
|
||||
nanovdb::GridType::Float);
|
||||
EXPECT_TRUE(meta.isValid());
|
||||
EXPECT_TRUE(std::string(meta.mName).empty());
|
||||
meta.setName("this is a test");
|
||||
EXPECT_EQ(std::string(meta.mName), name);
|
||||
|
||||
float *data = new float[10];
|
||||
EXPECT_TRUE(data);
|
||||
meta.setBlindData(data);
|
||||
EXPECT_TRUE(meta.isValid());
|
||||
EXPECT_EQ(std::string(meta.mName), name);
|
||||
EXPECT_EQ((const void*)data, meta.blindData());
|
||||
EXPECT_EQ(data, meta.getBlindData<float>());
|
||||
EXPECT_EQ(nullptr, meta.getBlindData<int>());
|
||||
|
||||
auto meta2 = meta;
|
||||
EXPECT_TRUE(meta2.isValid());
|
||||
EXPECT_EQ(std::string(meta2.mName), name);
|
||||
EXPECT_EQ((const void*)data, meta2.blindData());
|
||||
EXPECT_EQ(data, meta2.getBlindData<float>());
|
||||
EXPECT_EQ(nullptr, meta2.getBlindData<int>());
|
||||
|
||||
nanovdb::GridBlindMetaData meta3(meta);
|
||||
EXPECT_TRUE(meta3.isValid());
|
||||
EXPECT_EQ(std::string(meta3.mName), name);
|
||||
EXPECT_EQ((const void*)data, meta3.blindData());
|
||||
EXPECT_EQ(data, meta3.getBlindData<float>());
|
||||
EXPECT_EQ(nullptr, meta3.getBlindData<int>());
|
||||
|
||||
delete [] data;
|
||||
}// GridBlindMetaData
|
||||
|
||||
TEST_F(TestNanoVDB, Offsets)
|
||||
{
|
||||
{// check GridBlindMetaData
|
||||
@@ -2520,8 +2558,6 @@ TYPED_TEST(TestOffsets, NanoVDB)
|
||||
LeafData<Mask> is padded: no
|
||||
LeafData<Index> is padded: no
|
||||
LeafData<OnIndex> is padded: no
|
||||
LeafData<IndexMask> is padded: no
|
||||
LeafData<OnIndexMask> is padded: no
|
||||
LeafData<bool> is padded: no
|
||||
LeafData<PointIndex> is padded: no
|
||||
LeafData<Vec3u8> is padded: yes
|
||||
@@ -2583,18 +2619,6 @@ void checkLeaf<nanovdb::ValueIndex>(int &offset)
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
template<>
|
||||
void checkLeaf<nanovdb::ValueIndexMask>(int &offset)
|
||||
{
|
||||
using DataT = typename nanovdb::LeafNode<nanovdb::ValueIndexMask>::DataType;
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mOffset), offset);
|
||||
offset += 8;
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mPrefixSum), offset);
|
||||
offset += 8;
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMask), offset);
|
||||
offset += 64;
|
||||
}
|
||||
|
||||
template<>
|
||||
void checkLeaf<nanovdb::ValueOnIndex>(int &offset)
|
||||
{
|
||||
@@ -2605,18 +2629,6 @@ void checkLeaf<nanovdb::ValueOnIndex>(int &offset)
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
template<>
|
||||
void checkLeaf<nanovdb::ValueOnIndexMask>(int &offset)
|
||||
{
|
||||
using DataT = typename nanovdb::LeafNode<nanovdb::ValueOnIndexMask>::DataType;
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mOffset), offset);
|
||||
offset += 8;
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mPrefixSum), offset);
|
||||
offset += 8;
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMask), offset);
|
||||
offset += 64;
|
||||
}
|
||||
|
||||
template<>
|
||||
void checkLeaf<nanovdb::Fp4>(int &offset)
|
||||
{
|
||||
@@ -3146,30 +3158,82 @@ TEST_F(TestNanoVDB, CreateNanoGrid_Basic1)
|
||||
EXPECT_EQ("", std::string(meta->shortGridName()));
|
||||
EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
|
||||
EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
|
||||
auto* dstGrid = handle.grid<float>();
|
||||
const auto* dstGrid = handle.grid<float>();
|
||||
EXPECT_TRUE(dstGrid);
|
||||
EXPECT_EQ("", std::string(dstGrid->gridName()));
|
||||
EXPECT_EQ(nanovdb::Vec3d(1.0), dstGrid->voxelSize());
|
||||
EXPECT_EQ(1u, dstGrid->activeVoxelCount());
|
||||
EXPECT_EQ(1.0f, dstGrid->tree().getValue(ijk));
|
||||
const auto &dstTree = dstGrid->tree();
|
||||
EXPECT_EQ(1.0f, dstTree.getValue(ijk));
|
||||
auto dstAcc = dstGrid->getAccessor();
|
||||
EXPECT_EQ(1.0f, dstAcc.getValue(ijk));
|
||||
EXPECT_TRUE(srcAcc.isActive(ijk));
|
||||
EXPECT_EQ(nanovdb::Coord(1, 2, 3), dstGrid->indexBBox()[0]);
|
||||
EXPECT_EQ(nanovdb::Coord(1, 2, 3), dstGrid->indexBBox()[1]);
|
||||
EXPECT_EQ(dstGrid->tree().root().minimum(), 1.0f);// minimum active value
|
||||
EXPECT_EQ(dstGrid->tree().root().maximum(), 1.0f);// maximum active value
|
||||
EXPECT_NEAR(dstGrid->tree().root().average(), 1.0f, 1e-6);
|
||||
EXPECT_NEAR(dstGrid->tree().root().variance(), 0.0f,1e-6);
|
||||
EXPECT_NEAR(dstGrid->tree().root().stdDeviation(), 0.0f, 1e-6);
|
||||
EXPECT_EQ(dstTree.root().minimum(), 1.0f);// minimum active value
|
||||
EXPECT_EQ(dstTree.root().maximum(), 1.0f);// maximum active value
|
||||
EXPECT_NEAR(dstTree.root().average(), 1.0f, 1e-6);
|
||||
EXPECT_NEAR(dstTree.root().variance(), 0.0f,1e-6);
|
||||
EXPECT_NEAR(dstTree.root().stdDeviation(), 0.0f, 1e-6);
|
||||
EXPECT_FALSE(dstGrid->isEmpty());
|
||||
EXPECT_FALSE(dstGrid->tree().isEmpty());
|
||||
EXPECT_FALSE(dstGrid->tree().root().isEmpty());
|
||||
EXPECT_EQ(1u, dstGrid->tree().nodeCount(0));
|
||||
EXPECT_EQ(1u, dstGrid->tree().nodeCount(1));
|
||||
EXPECT_EQ(1u, dstGrid->tree().nodeCount(2));
|
||||
EXPECT_FALSE(dstTree.isEmpty());
|
||||
EXPECT_FALSE(dstTree.root().isEmpty());
|
||||
EXPECT_EQ(1u, dstTree.nodeCount(0));
|
||||
EXPECT_EQ(1u, dstTree.nodeCount(1));
|
||||
EXPECT_EQ(1u, dstTree.nodeCount(2));
|
||||
|
||||
// Test GetLeaf, GetLower, GetUpper, GetTile, and GetDim random access structs
|
||||
|
||||
{// hitting tile and all nodes
|
||||
const nanovdb::Coord ijk(0,0,0);
|
||||
EXPECT_EQ(dstTree.getFirstNode<0>(), dstAcc.get<nanovdb::GetLeaf<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.getFirstNode<1>(), dstAcc.get<nanovdb::GetLower<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.getFirstNode<2>(), dstAcc.get<nanovdb::GetUpper<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.root().probeTile(ijk), dstAcc.get<nanovdb::GetTile<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.getFirstNode<0>(), dstTree.get<nanovdb::GetLeaf<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.getFirstNode<1>(), dstTree.get<nanovdb::GetLower<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.getFirstNode<2>(), dstTree.get<nanovdb::GetUpper<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.root().probeTile(ijk), dstTree.get<nanovdb::GetTile<float>>(ijk));
|
||||
EXPECT_EQ(1u, dstTree.get<nanovdb::GetDim<float>>(ijk));
|
||||
}
|
||||
{// missing leaf node
|
||||
const nanovdb::Coord ijk(8, 8, 8);
|
||||
EXPECT_EQ(nullptr, dstAcc.get<nanovdb::GetLeaf<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.getFirstNode<1>(), dstAcc.get<nanovdb::GetLower<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.getFirstNode<2>(), dstAcc.get<nanovdb::GetUpper<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.root().probeTile(ijk), dstAcc.get<nanovdb::GetTile<float>>(ijk));
|
||||
EXPECT_EQ(nullptr, dstTree.get<nanovdb::GetLeaf<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.getFirstNode<1>(), dstTree.get<nanovdb::GetLower<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.getFirstNode<2>(), dstTree.get<nanovdb::GetUpper<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.root().probeTile(ijk), dstTree.get<nanovdb::GetTile<float>>(ijk));
|
||||
EXPECT_EQ(8u, dstTree.get<nanovdb::GetDim<float>>(ijk));
|
||||
}
|
||||
{// missing leaf and lower node
|
||||
const nanovdb::Coord ijk(8*16, 8*16, 8*16);
|
||||
EXPECT_EQ(nullptr, dstAcc.get<nanovdb::GetLeaf<float>>(ijk));
|
||||
EXPECT_EQ(nullptr, dstAcc.get<nanovdb::GetLower<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.getFirstNode<2>(), dstAcc.get<nanovdb::GetUpper<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.root().probeTile(ijk), dstAcc.get<nanovdb::GetTile<float>>(ijk));
|
||||
EXPECT_EQ(nullptr, dstTree.get<nanovdb::GetLeaf<float>>(ijk));
|
||||
EXPECT_EQ(nullptr, dstTree.get<nanovdb::GetLower<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.getFirstNode<2>(), dstTree.get<nanovdb::GetUpper<float>>(ijk));
|
||||
EXPECT_EQ(dstTree.root().probeTile(ijk), dstTree.get<nanovdb::GetTile<float>>(ijk));
|
||||
EXPECT_EQ(128u, dstTree.get<nanovdb::GetDim<float>>(ijk));
|
||||
}
|
||||
{// missing tile and all nodes
|
||||
const nanovdb::Coord ijk(-1, -1,-1);
|
||||
EXPECT_EQ(nullptr, dstAcc.get<nanovdb::GetLeaf<float>>(ijk));
|
||||
EXPECT_EQ(nullptr, dstAcc.get<nanovdb::GetLower<float>>(ijk));
|
||||
EXPECT_EQ(nullptr, dstAcc.get<nanovdb::GetUpper<float>>(ijk));
|
||||
EXPECT_EQ(nullptr, dstAcc.get<nanovdb::GetTile<float>>(ijk));
|
||||
EXPECT_EQ(nullptr, dstTree.get<nanovdb::GetLeaf<float>>(ijk));
|
||||
EXPECT_EQ(nullptr, dstTree.get<nanovdb::GetLower<float>>(ijk));
|
||||
EXPECT_EQ(nullptr, dstTree.get<nanovdb::GetUpper<float>>(ijk));
|
||||
EXPECT_EQ(nullptr, dstTree.get<nanovdb::GetTile<float>>(ijk));
|
||||
EXPECT_EQ(0u, dstTree.get<nanovdb::GetDim<float>>(ijk));
|
||||
}
|
||||
}
|
||||
} // GridBuilderBasic1
|
||||
} // CreateNanoGrid_Basic1
|
||||
|
||||
TEST_F(TestNanoVDB, CreateNanoGrid_addTile)
|
||||
{
|
||||
@@ -4145,9 +4209,9 @@ TEST_F(TestNanoVDB, NodeManager)
|
||||
EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node1>());
|
||||
EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node0>());
|
||||
|
||||
EXPECT_EQ(48u, sizeof(nanovdb::NodeManagerData));
|
||||
EXPECT_EQ(40u, sizeof(nanovdb::NodeManagerData));
|
||||
auto nodeMgrHandle = nanovdb::createNodeManager(*dstGrid);
|
||||
EXPECT_EQ(48u, nodeMgrHandle.size());// since its linear
|
||||
EXPECT_EQ(40u, nodeMgrHandle.size());// since its linear
|
||||
auto *nodeMgr = nodeMgrHandle.mgr<float>();
|
||||
EXPECT_TRUE(nodeMgr);
|
||||
EXPECT_TRUE(nanovdb::isAligned(nodeMgr));
|
||||
@@ -4899,8 +4963,6 @@ TEST_F(TestNanoVDB, PNanoVDB_Basic)
|
||||
EXPECT_EQ((int)nanovdb::GridType::Vec4d, PNANOVDB_GRID_TYPE_VEC4D);
|
||||
EXPECT_EQ((int)nanovdb::GridType::Index, PNANOVDB_GRID_TYPE_INDEX);
|
||||
EXPECT_EQ((int)nanovdb::GridType::OnIndex, PNANOVDB_GRID_TYPE_ONINDEX);
|
||||
EXPECT_EQ((int)nanovdb::GridType::IndexMask, PNANOVDB_GRID_TYPE_INDEXMASK);
|
||||
EXPECT_EQ((int)nanovdb::GridType::OnIndexMask, PNANOVDB_GRID_TYPE_ONINDEXMASK);
|
||||
EXPECT_EQ((int)nanovdb::GridType::PointIndex, PNANOVDB_GRID_TYPE_POINTINDEX);
|
||||
EXPECT_EQ((int)nanovdb::GridType::End, PNANOVDB_GRID_TYPE_END);
|
||||
|
||||
@@ -5024,17 +5086,6 @@ void validateLeaf<nanovdb::ValueIndex>(pnanovdb_grid_type_t grid_type)
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(leaf_t, mPrefixSum), PNANOVDB_LEAF_OFF_VALUE_MASK + 64 + 8);
|
||||
}
|
||||
|
||||
// template specializations for nanovdb::ValueIndexMask types
|
||||
template <>
|
||||
void validateLeaf<nanovdb::ValueIndexMask>(pnanovdb_grid_type_t grid_type)
|
||||
{
|
||||
using leaf_t = typename nanovdb::LeafNode<nanovdb::ValueIndexMask>;
|
||||
EXPECT_EQ(sizeof(leaf_t), (pnanovdb_grid_type_constants[grid_type].leaf_size));
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(leaf_t, mOffset), PNANOVDB_LEAF_OFF_VALUE_MASK + 64);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(leaf_t, mPrefixSum), PNANOVDB_LEAF_OFF_VALUE_MASK + 64 + 8);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(leaf_t, mMask), PNANOVDB_LEAF_OFF_VALUE_MASK + 64 + 8 + 8);
|
||||
}
|
||||
|
||||
// template specializations for nanovdb::ValueOnIndex types
|
||||
template <>
|
||||
void validateLeaf<nanovdb::ValueOnIndex>(pnanovdb_grid_type_t grid_type)
|
||||
@@ -5045,17 +5096,6 @@ void validateLeaf<nanovdb::ValueOnIndex>(pnanovdb_grid_type_t grid_type)
|
||||
EXPECT_EQ(sizeof(leaf_t), (pnanovdb_grid_type_constants[grid_type].leaf_size));
|
||||
}
|
||||
|
||||
// template specializations for nanovdb::ValueOnIndexMask types
|
||||
template <>
|
||||
void validateLeaf<nanovdb::ValueOnIndexMask>(pnanovdb_grid_type_t grid_type)
|
||||
{
|
||||
using leaf_t = typename nanovdb::LeafNode<nanovdb::ValueOnIndexMask>;
|
||||
EXPECT_EQ(sizeof(leaf_t), (pnanovdb_grid_type_constants[grid_type].leaf_size));
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(leaf_t, mOffset), PNANOVDB_LEAF_OFF_VALUE_MASK + 64);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(leaf_t, mPrefixSum), PNANOVDB_LEAF_OFF_VALUE_MASK + 64 + 8);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(leaf_t, mMask), PNANOVDB_LEAF_OFF_VALUE_MASK + 64 + 8 + 8);
|
||||
}
|
||||
|
||||
// template specializations for nanovdb::Point types
|
||||
template <>
|
||||
void validateLeaf<nanovdb::Point>(pnanovdb_grid_type_t grid_type)
|
||||
@@ -5093,10 +5133,6 @@ TYPED_TEST(TestOffsets, PNanoVDB)
|
||||
grid_type = PNANOVDB_GRID_TYPE_INDEX;
|
||||
} else if (std::is_same<nanovdb::ValueOnIndex, TypeParam>::value) {
|
||||
grid_type = PNANOVDB_GRID_TYPE_ONINDEX;
|
||||
} else if (std::is_same<nanovdb::ValueIndexMask, TypeParam>::value) {
|
||||
grid_type = PNANOVDB_GRID_TYPE_INDEXMASK;
|
||||
} else if (std::is_same<nanovdb::ValueOnIndexMask, TypeParam>::value) {
|
||||
grid_type = PNANOVDB_GRID_TYPE_ONINDEXMASK;
|
||||
} else if (std::is_same<bool, TypeParam>::value) {
|
||||
grid_type = PNANOVDB_GRID_TYPE_BOOLEAN;
|
||||
} else if (std::is_same<nanovdb::Fp4, TypeParam>::value) {
|
||||
@@ -5190,9 +5226,9 @@ TYPED_TEST(TestOffsets, PNanoVDB)
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mNodeCount[0]), PNANOVDB_TREE_OFF_NODE_COUNT_LEAF);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mNodeCount[1]), PNANOVDB_TREE_OFF_NODE_COUNT_LOWER);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mNodeCount[2]), PNANOVDB_TREE_OFF_NODE_COUNT_UPPER);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mTileCount[0]), PNANOVDB_TREE_OFF_TILE_COUNT_LEAF);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mTileCount[1]), PNANOVDB_TREE_OFF_TILE_COUNT_LOWER);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mTileCount[2]), PNANOVDB_TREE_OFF_TILE_COUNT_UPPER);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mTileCount[0]), PNANOVDB_TREE_OFF_TILE_COUNT_LOWER);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mTileCount[1]), PNANOVDB_TREE_OFF_TILE_COUNT_UPPER);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mTileCount[2]), PNANOVDB_TREE_OFF_TILE_COUNT_ROOT);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mVoxelCount), PNANOVDB_TREE_OFF_VOXEL_COUNT);
|
||||
|
||||
EXPECT_EQ((int)sizeof(pnanovdb_tree_t), PNANOVDB_TREE_SIZE);
|
||||
@@ -5203,9 +5239,9 @@ TYPED_TEST(TestOffsets, PNanoVDB)
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, node_count_leaf), PNANOVDB_TREE_OFF_NODE_COUNT_LEAF);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, node_count_lower), PNANOVDB_TREE_OFF_NODE_COUNT_LOWER);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, node_count_upper), PNANOVDB_TREE_OFF_NODE_COUNT_UPPER);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, tile_count_leaf), PNANOVDB_TREE_OFF_TILE_COUNT_LEAF);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, tile_count_lower), PNANOVDB_TREE_OFF_TILE_COUNT_LOWER);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, tile_count_upper), PNANOVDB_TREE_OFF_TILE_COUNT_UPPER);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, tile_count_root), PNANOVDB_TREE_OFF_TILE_COUNT_ROOT);
|
||||
EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, voxel_count), PNANOVDB_TREE_OFF_VOXEL_COUNT);
|
||||
|
||||
// background value can start at pad1
|
||||
@@ -6751,40 +6787,55 @@ TEST_F(TestNanoVDB, NodeIterators)
|
||||
{// check RootNode::ValueOnInterator
|
||||
nanovdb::NanoRoot<float>::ValueOnIterator it;
|
||||
EXPECT_FALSE(it);
|
||||
it = fltTree.root().beginValueOn();
|
||||
it = fltRoot.beginValueOn();
|
||||
EXPECT_FALSE(it);// no active tiles
|
||||
auto it2 = it;
|
||||
EXPECT_FALSE(it2);
|
||||
}
|
||||
|
||||
{// check RootNode::ValueOnInterator
|
||||
{// check RootNode::ChildInterator
|
||||
nanovdb::NanoRoot<float>::ChildIterator it;
|
||||
EXPECT_FALSE(it);
|
||||
it = fltTree.root().beginChild();
|
||||
it = fltRoot.beginChild();
|
||||
EXPECT_TRUE(it);
|
||||
auto it2 = it;
|
||||
EXPECT_TRUE(it2);
|
||||
}
|
||||
}
|
||||
|
||||
{// RootNode::TileIterator
|
||||
nanovdb::NanoRoot<float>::TileIterator it;
|
||||
EXPECT_FALSE(it);
|
||||
it = fltRoot.beginTile();
|
||||
EXPECT_TRUE(it);
|
||||
EXPECT_EQ(0, it.pos());
|
||||
EXPECT_EQ(nanovdb::Coord(-4096), it->origin());
|
||||
auto it2 = it;
|
||||
EXPECT_TRUE(it2);
|
||||
EXPECT_EQ(0, it2.pos());
|
||||
EXPECT_EQ(nanovdb::Coord(-4096), it2->origin());
|
||||
EXPECT_TRUE(it2.isChild());
|
||||
EXPECT_FALSE(it2.isValue());
|
||||
EXPECT_FALSE(it2.isValueOn());
|
||||
auto it3 = fltRoot.probe(nanovdb::Coord(0));
|
||||
EXPECT_TRUE(it3);
|
||||
EXPECT_EQ(8, fltRoot.tileCount());
|
||||
EXPECT_EQ(7, it3.pos());
|
||||
EXPECT_EQ(nanovdb::Coord(0), it3->origin());
|
||||
EXPECT_TRUE(it3.isChild());
|
||||
EXPECT_FALSE(it3.isValue());
|
||||
EXPECT_FALSE(it3.isValueOn());
|
||||
}
|
||||
}// NodeIterators
|
||||
|
||||
// make testNanoVDB && ./unittest/testNanoVDB --gtest_filter="*BasicValueIndexStats*" --gtest_break_on_failure --gtest_repeat=5
|
||||
TEST_F(TestNanoVDB, BasicValueIndexStats)
|
||||
{
|
||||
{
|
||||
using ValueIndexT = typename nanovdb::NanoLeaf<nanovdb::ValueIndex>::DataType;
|
||||
using ValueIndexMaskT = typename nanovdb::NanoLeaf<nanovdb::ValueIndexMask>::DataType;
|
||||
using ValueOnIndexT = typename nanovdb::NanoLeaf<nanovdb::ValueOnIndex>::DataType;
|
||||
using ValueOnIndexMaskT = typename nanovdb::NanoLeaf<nanovdb::ValueOnIndexMask>::DataType;
|
||||
const size_t size1 = sizeof(ValueOnIndexT),
|
||||
size2 = sizeof(ValueOnIndexMaskT),
|
||||
size3 = sizeof(ValueIndexT),
|
||||
size4 = sizeof(ValueIndexMaskT);
|
||||
const size_t size1 = sizeof(ValueOnIndexT), size2 = sizeof(ValueIndexT);
|
||||
EXPECT_EQ(size1, ValueOnIndexT::memUsage());
|
||||
EXPECT_EQ(size2, ValueOnIndexMaskT::memUsage());
|
||||
EXPECT_EQ(size3, ValueIndexT::memUsage());
|
||||
EXPECT_EQ(size4, ValueIndexMaskT::memUsage());
|
||||
EXPECT_EQ(64u, size2 - size1);// 512 bits = 64 bytes
|
||||
EXPECT_EQ(64u, size4 - size3);// 512 bits = 64 bytes
|
||||
EXPECT_EQ(size2, ValueIndexT::memUsage());
|
||||
}
|
||||
EXPECT_TRUE(nanovdb::Version() >= nanovdb::Version(32,3,4));
|
||||
using SrcGridT = nanovdb::tools::build::Grid<float>;
|
||||
@@ -7585,7 +7636,7 @@ TEST_F(TestNanoVDB, ChannelIndexGridBuilder)
|
||||
EXPECT_EQ(idxGrid->valueCount(), idxGrid->blindMetaData(i).mValueCount);
|
||||
EXPECT_EQ(nanovdb::GridType::Float, idxGrid->blindMetaData(i).mDataType);
|
||||
EXPECT_EQ(nanovdb::GridBlindDataClass::ChannelArray, idxGrid->blindMetaData(i).mDataClass);
|
||||
EXPECT_EQ(nanovdb::GridBlindDataSemantic::Unknown, idxGrid->blindMetaData(i).mSemantic);
|
||||
EXPECT_EQ(nanovdb::GridBlindDataSemantic::LevelSet, idxGrid->blindMetaData(i).mSemantic);
|
||||
const std::string name = std::string("channel_") + std::to_string(i);
|
||||
EXPECT_EQ(0, std::strcmp(idxGrid->blindMetaData(i).mName, name.c_str() ));
|
||||
//mTimer.start("Parallel leaf iterator test of active voxels in channel");
|
||||
@@ -7606,7 +7657,7 @@ TEST_F(TestNanoVDB, ChannelIndexGridBuilder)
|
||||
EXPECT_EQ(idxGrid->valueCount(), idxGrid->blindMetaData(i).mValueCount);
|
||||
EXPECT_EQ(nanovdb::GridType::Float, idxGrid->blindMetaData(i).mDataType);
|
||||
EXPECT_EQ(nanovdb::GridBlindDataClass::ChannelArray, idxGrid->blindMetaData(i).mDataClass);
|
||||
EXPECT_EQ(nanovdb::GridBlindDataSemantic::Unknown, idxGrid->blindMetaData(i).mSemantic);
|
||||
EXPECT_EQ(nanovdb::GridBlindDataSemantic::LevelSet, idxGrid->blindMetaData(i).mSemantic);
|
||||
const std::string name = std::string("channel_") + std::to_string(i);
|
||||
EXPECT_EQ(0, std::strcmp(idxGrid->blindMetaData(i).mName, name.c_str() ));
|
||||
//mTimer.start("Parallel leaf iterator test of active voxels in channel");
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <nanovdb/tools/GridBuilder.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/tools/VoxelBlockManager.h>
|
||||
#include <nanovdb/NodeManager.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
#include <nanovdb/tools/cuda/SignedFloodFill.cuh>
|
||||
@@ -16,22 +17,30 @@
|
||||
#include <nanovdb/tools/cuda/GridChecksum.cuh>
|
||||
#include <nanovdb/tools/cuda/GridValidator.cuh>
|
||||
#include <nanovdb/tools/cuda/GridStats.cuh>
|
||||
//#include <nanovdb/tools/cuda/DilateVoxels.cuh>
|
||||
#include <nanovdb/tools/cuda/DilateGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/MergeGrids.cuh>
|
||||
#include <nanovdb/tools/cuda/PruneGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/CoarsenGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/RefineGrid.cuh>
|
||||
#include <nanovdb/util/cuda/Injection.cuh>
|
||||
#include <nanovdb/util/cuda/Timer.h>
|
||||
#include <nanovdb/util/Timer.h>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/cuda/UnifiedBuffer.h>
|
||||
#include <nanovdb/cuda/DeviceStreamMap.h>
|
||||
#include <nanovdb/cuda/DeviceMesh.h>
|
||||
|
||||
#include <cuda.h>
|
||||
#include <cuda_runtime_api.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <algorithm>// for std::sort
|
||||
#include <iomanip> // for std::setw, std::setfill
|
||||
#include <thread> // for std::thread
|
||||
|
||||
namespace nanovdb {// this namespace is required by gtest
|
||||
|
||||
namespace test {
|
||||
|
||||
// used for testing cuda::DeviceBuffer
|
||||
void device2host(size_t count)
|
||||
{
|
||||
@@ -42,7 +51,7 @@ void device2host(size_t count)
|
||||
EXPECT_TRUE(buffer.deviceData());
|
||||
float *d_array = reinterpret_cast<float*>(buffer.deviceData());
|
||||
constexpr unsigned int num_threads = 256;
|
||||
unsigned int num_blocks = num_blocks = (static_cast<unsigned int>(count) + num_threads - 1) / num_threads;
|
||||
unsigned int num_blocks = (static_cast<unsigned int>(count) + num_threads - 1) / num_threads;
|
||||
nanovdb::util::cuda::lambdaKernel<<<num_blocks, num_threads>>>(count, [=] __device__ (size_t i) {d_array[i] = float(i);});
|
||||
buffer.deviceDownload();// copy device -> host
|
||||
EXPECT_EQ(size, buffer.size());
|
||||
@@ -51,6 +60,46 @@ void device2host(size_t count)
|
||||
float *array = reinterpret_cast<float*>(buffer.data());
|
||||
for (size_t i=0; i<count; ++i) EXPECT_EQ(array[i], float(i));
|
||||
}// device2host
|
||||
|
||||
void host2device(size_t count)
|
||||
{
|
||||
const size_t size = count * sizeof(float);
|
||||
auto hostBuffer = nanovdb::HostBuffer(size);
|
||||
for (size_t i=0; i<count; ++i) *hostBuffer.data<float>(i) = float(i);
|
||||
|
||||
int dev;
|
||||
cudaError_t err = cudaGetDevice(&dev);
|
||||
if (err != cudaSuccess) printf("kernel cuda error: %d\n", (int)err);
|
||||
|
||||
auto devBuffer = nanovdb::cuda::DeviceBuffer::create(hostBuffer, dev);// on device only
|
||||
EXPECT_EQ(size, devBuffer.size());
|
||||
EXPECT_FALSE(devBuffer.data());
|
||||
EXPECT_TRUE(devBuffer.deviceData());
|
||||
float *d_array = reinterpret_cast<float*>(devBuffer.deviceData());
|
||||
constexpr unsigned int num_threads = 256;
|
||||
unsigned int num_blocks = (static_cast<unsigned int>(count) + num_threads - 1) / num_threads;
|
||||
|
||||
bool *test, *d_test;
|
||||
cudaCheck(cudaMallocHost((void**)&test, sizeof(bool)));
|
||||
cudaCheck(cudaMalloc((void**)&d_test, sizeof(bool)));
|
||||
*test = true;
|
||||
cudaCheck(cudaMemcpyAsync(d_test, test, sizeof(bool), cudaMemcpyHostToDevice));// on host only
|
||||
|
||||
nanovdb::util::cuda::lambdaKernel<<<num_blocks, num_threads>>>(count, [=] __device__ (size_t i) {
|
||||
if (d_array[i] != float(i)) *d_test = false;
|
||||
d_array[i] = float(i) + 1.0f;
|
||||
});
|
||||
cudaCheck(cudaMemcpy(test, d_test, sizeof(bool), cudaMemcpyDeviceToHost));
|
||||
EXPECT_TRUE(*test);
|
||||
cudaCheck(cudaFreeHost(test));
|
||||
cudaCheck(cudaFree(d_test));
|
||||
devBuffer.deviceDownload();// copy device -> host
|
||||
EXPECT_EQ(size, devBuffer.size());
|
||||
EXPECT_TRUE(devBuffer.data());
|
||||
EXPECT_TRUE(devBuffer.deviceData());
|
||||
for (size_t i=0; i<count; ++i) EXPECT_EQ(*hostBuffer.data<float>(i) + 1.0f, *devBuffer.data<float>(i));
|
||||
}// host2device
|
||||
|
||||
// used for testing cuda::DeviceBuffer
|
||||
void host2device2host(size_t count)
|
||||
{
|
||||
@@ -73,7 +122,7 @@ void host2device2host(size_t count)
|
||||
EXPECT_TRUE(buffer.deviceData());
|
||||
float *d_array = reinterpret_cast<float*>(buffer.deviceData());
|
||||
constexpr unsigned int num_threads = 256;
|
||||
unsigned int num_blocks = num_blocks = (static_cast<unsigned int>(count) + num_threads - 1) / num_threads;
|
||||
unsigned int num_blocks = (static_cast<unsigned int>(count) + num_threads - 1) / num_threads;
|
||||
nanovdb::util::cuda::lambdaKernel<<<num_blocks, num_threads>>>(count, [=] __device__ (size_t i) {
|
||||
if (d_array[i] != float(i)) *d_test = false;
|
||||
d_array[i] = float(i) + 1.0f;
|
||||
@@ -88,11 +137,13 @@ void host2device2host(size_t count)
|
||||
EXPECT_TRUE(buffer.deviceData());
|
||||
for (size_t i=0; i<count; ++i) EXPECT_EQ(array[i], float(i) + 1.0f);
|
||||
}// host2device2host
|
||||
|
||||
// used to test cudaStr methods
|
||||
int signum(int val)
|
||||
{
|
||||
return (0 < val) - (val < 0);
|
||||
}// signum
|
||||
|
||||
void cudaStr()
|
||||
{
|
||||
const size_t size = 50;
|
||||
@@ -130,7 +181,9 @@ void cudaStr()
|
||||
cudaCheck(cudaFree(d_n));
|
||||
cudaCheck(cudaFree(d_str));
|
||||
}// cudaStr
|
||||
|
||||
}// namespace test
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
TEST(TestNanoVDBCUDA, CudaDeviceBuffer)
|
||||
@@ -152,6 +205,7 @@ TEST(TestNanoVDBCUDA, CudaDeviceBuffer)
|
||||
EXPECT_FALSE(buffer.empty());
|
||||
}
|
||||
nanovdb::test::device2host(1000);
|
||||
nanovdb::test::host2device(1000);
|
||||
nanovdb::test::host2device2host(1000);
|
||||
}
|
||||
|
||||
@@ -164,7 +218,7 @@ __global__ void testKernel(int device)
|
||||
{
|
||||
int dev;
|
||||
cudaError_t err = cudaGetDevice(&dev);
|
||||
if (err != cudaSuccess) printf("kernel cuda error: %d\n", (int)err);
|
||||
//if (err != cudaSuccess) printf("kernel cuda error: %d\n", (int)err);
|
||||
if (dev != device) printf("Error: expected device ID = %i but was called with %i\n", dev, device);
|
||||
}
|
||||
|
||||
@@ -178,7 +232,11 @@ TEST(TestNanoVDBCUDA, DeviceStreamMap)
|
||||
EXPECT_EQ(count, nanovdb::util::cuda::deviceCount());
|
||||
EXPECT_EQ(current, nanovdb::util::cuda::currentDevice());
|
||||
float *ptr = new float;
|
||||
EXPECT_EQ(cudaInvalidDeviceId, nanovdb::util::cuda::ptrToDevice(ptr));
|
||||
const int deviceID = nanovdb::util::cuda::ptrToDevice(ptr);
|
||||
EXPECT_TRUE(cudaInvalidDeviceId == deviceID || cudaCpuDeviceId == deviceID);
|
||||
EXPECT_GT(0, deviceID);
|
||||
//EXPECT_EQ(cudaInvalidDeviceId, deviceID);
|
||||
//EXPECT_EQ(cudaCpuDeviceId, deviceID);
|
||||
delete ptr;
|
||||
cudaCheck(cudaMalloc((void**)&ptr, sizeof(float)));
|
||||
EXPECT_EQ(current, nanovdb::util::cuda::ptrToDevice(ptr));
|
||||
@@ -228,6 +286,52 @@ TEST(TestNanoVDBCUDA, DeviceStreamMap)
|
||||
cudaSetDevice(current); // restore device so subsequent tests don't fail
|
||||
}
|
||||
|
||||
TEST(TestNanoVDBCUDA, DeviceMesh)
|
||||
{
|
||||
int count = 0, current = 0;
|
||||
{
|
||||
cudaCheck(cudaGetDeviceCount(&count));
|
||||
cudaCheck(cudaGetDevice(¤t));
|
||||
EXPECT_EQ(count, nanovdb::util::cuda::deviceCount());
|
||||
EXPECT_EQ(current, nanovdb::util::cuda::currentDevice());
|
||||
}
|
||||
//std::cout << "Total device count = " << count << std::endl;
|
||||
nanovdb::cuda::DeviceMesh serialMesh;
|
||||
EXPECT_EQ(count, serialMesh.deviceCount());
|
||||
EXPECT_GT(nanovdb::cuda::minDevicePageSize(serialMesh), 0);
|
||||
// Construction of deviceMesh (and destruction) should not modify current device
|
||||
EXPECT_EQ(current, nanovdb::util::cuda::currentDevice());
|
||||
// Serial kernel dispatch with iterator
|
||||
for (const auto& [device, stream] : serialMesh) {
|
||||
cudaSetDevice(device);
|
||||
testKernel<<<1, 1, 0, stream>>>(device);
|
||||
}
|
||||
// Wait on each kernel to finish using indexing operation
|
||||
for (size_t i = 0; i < serialMesh.deviceCount(); ++i) {
|
||||
cudaSetDevice(serialMesh[i].id);
|
||||
cudaStreamSynchronize(serialMesh[i].stream);
|
||||
}
|
||||
|
||||
// Test move constructor
|
||||
nanovdb::cuda::DeviceMesh parallelMesh(std::move(serialMesh));
|
||||
EXPECT_EQ(count, parallelMesh.deviceCount());
|
||||
// After move constructor, serialMesh should have no devices
|
||||
EXPECT_EQ(0, serialMesh.deviceCount());
|
||||
// Parallel kernel dispatch with iterator
|
||||
std::vector<std::thread> threads;
|
||||
for (const auto& [device, stream] : parallelMesh) {
|
||||
threads.emplace_back([](int device, cudaStream_t stream) {
|
||||
cudaSetDevice(device);
|
||||
testKernel<<<1, 1, 0, stream>>>(device);
|
||||
cudaStreamSynchronize(stream);
|
||||
}, device, stream);
|
||||
}
|
||||
std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
|
||||
threads.clear();
|
||||
|
||||
cudaSetDevice(current); // Restore device so subsequent tests don't fail
|
||||
}
|
||||
|
||||
TEST(TestNanoVDBCUDA, Basic_CudaPointsToGrid_float)
|
||||
{
|
||||
using BuildT = float;
|
||||
@@ -286,6 +390,8 @@ struct ProbeValueNew {
|
||||
ValueT value;
|
||||
operator bool() const { return state; }
|
||||
};
|
||||
static constexpr int LEVEL = 0;// minimum level for the descent during top-down traversal
|
||||
using Type = Probe;
|
||||
__hostdev__ static Probe get(const NanoRoot<BuildT> &root) {
|
||||
return Probe{false, root.mBackground};
|
||||
}
|
||||
@@ -303,24 +409,6 @@ struct ProbeValueNew {
|
||||
}
|
||||
};// ProbeValueNew<BuildT>
|
||||
|
||||
template <typename BuildT>
|
||||
struct AccessLeafMask;
|
||||
|
||||
// template specialization of AccessLeafMask wrt ValueOnIndexMask
|
||||
template <>
|
||||
struct AccessLeafMask<ValueOnIndexMask>{
|
||||
__hostdev__ static bool get(const NanoRoot<ValueOnIndexMask>&) {return false;}
|
||||
__hostdev__ static bool get(const typename NanoRoot<ValueOnIndexMask>::Tile&) {return false;}
|
||||
__hostdev__ static bool get(const NanoUpper<ValueOnIndexMask>&, uint32_t) {return false;}
|
||||
__hostdev__ static bool get(const NanoLower<ValueOnIndexMask>&, uint32_t) {return false;}
|
||||
__hostdev__ static bool get(const NanoLeaf<ValueOnIndexMask> &leaf, uint32_t n) {return leaf.mMask.isOn(n);}
|
||||
__hostdev__ static void set(NanoRoot<ValueOnIndexMask>&) {}
|
||||
__hostdev__ static void set(typename NanoRoot<ValueOnIndexMask>::Tile&) {}
|
||||
__hostdev__ static void set(NanoUpper<ValueOnIndexMask>&, uint32_t) {}
|
||||
__hostdev__ static void set(NanoLower<ValueOnIndexMask>&, uint32_t) {}
|
||||
__hostdev__ static void set(NanoLeaf<ValueOnIndexMask> &leaf, uint32_t n) {leaf.mMask.setOn(n);}
|
||||
};// AccessLeafMask<BuildT>
|
||||
|
||||
}// end of test namespace
|
||||
}// end of nanovdb namespace
|
||||
|
||||
@@ -383,8 +471,15 @@ TEST(TestNanoVDBCUDA, Basic_CudaPointsToGrid_ValueIndex)
|
||||
const nanovdb::Coord ijk = coords[i];
|
||||
const auto *leaf = acc.get<nanovdb::GetLeaf<BuildT>>(ijk);
|
||||
EXPECT_TRUE(leaf);
|
||||
EXPECT_EQ(0u, leaf->LEVEL);
|
||||
const auto offset = leaf->CoordToOffset(ijk);
|
||||
EXPECT_EQ(ijk, leaf->offsetToGlobalCoord(offset));
|
||||
const auto *lower = acc.get<nanovdb::GetLower<BuildT>>(ijk);
|
||||
EXPECT_TRUE(lower);
|
||||
EXPECT_EQ(1u, lower->LEVEL);
|
||||
const auto *upper = acc.get<nanovdb::GetUpper<BuildT>>(ijk);
|
||||
EXPECT_TRUE(upper);
|
||||
EXPECT_EQ(2u, upper->LEVEL);
|
||||
}
|
||||
}// Basic_CudaPointsToGrid_ValueIndex
|
||||
|
||||
@@ -393,7 +488,6 @@ TEST(TestNanoVDBCUDA, Basic_CudaPointsToGrid_ValueOnIndex)
|
||||
using BuildT = nanovdb::ValueOnIndex;
|
||||
using GridT = nanovdb::NanoGrid<BuildT>;
|
||||
EXPECT_TRUE(nanovdb::BuildTraits<BuildT>::is_index);
|
||||
EXPECT_FALSE(nanovdb::BuildTraits<BuildT>::is_indexmask);
|
||||
EXPECT_TRUE(nanovdb::BuildTraits<BuildT>::is_onindex);
|
||||
EXPECT_FALSE(nanovdb::BuildTraits<BuildT>::is_offindex);
|
||||
const size_t num_points = 3;
|
||||
@@ -486,117 +580,6 @@ TEST(TestNanoVDBCUDA, Basic_CudaPointsToGrid_ValueOnIndex)
|
||||
}
|
||||
}// Basic_CudaPointsToGrid_ValueOnIndex
|
||||
|
||||
TEST(TestNanoVDBCUDA, Basic_CudaPointsToGrid_ValueOnIndexMask)
|
||||
{
|
||||
using BuildT = nanovdb::ValueOnIndexMask;
|
||||
using GridT = nanovdb::NanoGrid<BuildT>;
|
||||
EXPECT_TRUE(nanovdb::BuildTraits<BuildT>::is_index);
|
||||
EXPECT_TRUE(nanovdb::BuildTraits<BuildT>::is_indexmask);
|
||||
EXPECT_TRUE(nanovdb::BuildTraits<BuildT>::is_onindex);
|
||||
EXPECT_FALSE(nanovdb::BuildTraits<BuildT>::is_offindex);
|
||||
const size_t num_points = 3;
|
||||
nanovdb::Coord coords[num_points] = {nanovdb::Coord(1, 2, 3),
|
||||
nanovdb::Coord(1, 2, 4),
|
||||
nanovdb::Coord(8, 2, 3)}, *d_coords = nullptr;
|
||||
cudaCheck(cudaMalloc(&d_coords, num_points * sizeof(nanovdb::Coord)));
|
||||
cudaCheck(cudaMemcpy(d_coords, coords, num_points * sizeof(nanovdb::Coord), cudaMemcpyHostToDevice));// CPU -> GPU
|
||||
|
||||
#if 0
|
||||
nanovdb::tools::cuda::PointsToGrid converter;
|
||||
auto handle = converter.getHandle<BuildT>(d_coords, num_points);
|
||||
#else
|
||||
auto handle = nanovdb::tools::cuda::voxelsToGrid<BuildT>(d_coords, num_points);
|
||||
#endif
|
||||
|
||||
cudaCheck(cudaFree(d_coords));
|
||||
EXPECT_TRUE(handle.deviceData());// grid only exists on the GPU
|
||||
EXPECT_FALSE(handle.data());// no grid was yet allocated on the CPU
|
||||
|
||||
const uint64_t size = sizeof(GridT) +
|
||||
sizeof(GridT::TreeType) +
|
||||
GridT::RootType::memUsage(1) +
|
||||
sizeof(GridT::UpperNodeType) +
|
||||
sizeof(GridT::LowerNodeType) +
|
||||
2*sizeof(GridT::LeafNodeType);
|
||||
EXPECT_EQ(handle.size(), size);
|
||||
|
||||
GridT *grid = handle.grid<BuildT>();// no grid on the CPU
|
||||
EXPECT_FALSE(grid);
|
||||
handle.deviceDownload();// creates a copy up the CPU
|
||||
EXPECT_TRUE(handle.deviceData());
|
||||
EXPECT_TRUE(handle.data());
|
||||
auto *data = handle.gridData();
|
||||
EXPECT_TRUE(data);
|
||||
grid = handle.grid<BuildT>();
|
||||
EXPECT_TRUE(grid);
|
||||
EXPECT_EQ(4u, grid->valueCount());
|
||||
|
||||
auto acc = grid->getAccessor();
|
||||
EXPECT_FALSE( acc.isActive(nanovdb::Coord(0,2,3)));
|
||||
EXPECT_TRUE( acc.isActive(nanovdb::Coord(1,2,3)));
|
||||
EXPECT_TRUE( acc.isActive(nanovdb::Coord(1,2,4)));
|
||||
EXPECT_TRUE( acc.isActive(nanovdb::Coord(8,2,3)));
|
||||
EXPECT_EQ(0u, acc.getValue(nanovdb::Coord(0,2,3)));
|
||||
EXPECT_EQ(1u, acc.getValue(nanovdb::Coord(1,2,3)));
|
||||
EXPECT_EQ(2u, acc.getValue(nanovdb::Coord(1,2,4)));
|
||||
EXPECT_EQ(3u, acc.getValue(nanovdb::Coord(8,2,3)));
|
||||
|
||||
using GetT = nanovdb::GetValue<BuildT>;
|
||||
EXPECT_EQ(0u, acc.get<GetT>(nanovdb::Coord(0,2,3)));
|
||||
EXPECT_EQ(1u, acc.get<GetT>(nanovdb::Coord(1,2,3)));
|
||||
EXPECT_EQ(2u, acc.get<GetT>(nanovdb::Coord(1,2,4)));
|
||||
EXPECT_EQ(3u, acc.get<GetT>(nanovdb::Coord(8,2,3)));
|
||||
|
||||
using OpT = nanovdb::test::AccessLeafMask<BuildT>;
|
||||
EXPECT_EQ(false, acc.get<OpT>(nanovdb::Coord(0,2,3)));
|
||||
EXPECT_EQ(true, acc.get<OpT>(nanovdb::Coord(1,2,3)));
|
||||
EXPECT_EQ(true, acc.get<OpT>(nanovdb::Coord(1,2,4)));
|
||||
EXPECT_EQ(true, acc.get<OpT>(nanovdb::Coord(8,2,3)));
|
||||
|
||||
acc.set<OpT>(nanovdb::Coord(1,2,3));
|
||||
acc.set<OpT>(nanovdb::Coord(8,2,3));
|
||||
|
||||
EXPECT_EQ(false, acc.get<OpT>(nanovdb::Coord(0,2,3)));
|
||||
EXPECT_EQ(true , acc.get<OpT>(nanovdb::Coord(1,2,3)));
|
||||
EXPECT_EQ(true, acc.get<OpT>(nanovdb::Coord(1,2,4)));
|
||||
EXPECT_EQ(true, acc.get<OpT>(nanovdb::Coord(8,2,3)));
|
||||
|
||||
{
|
||||
using T = nanovdb::ProbeValue<BuildT>;
|
||||
uint64_t value = 0;
|
||||
EXPECT_EQ(false, acc.get<T>(nanovdb::Coord(0,2,3), value) );
|
||||
EXPECT_EQ(0u, value);
|
||||
EXPECT_EQ(true, acc.get<T>(nanovdb::Coord(1,2,3), value) );
|
||||
EXPECT_EQ(1u, value);
|
||||
EXPECT_EQ(true, acc.get<T>(nanovdb::Coord(1,2,4), value) );
|
||||
EXPECT_EQ(2u, value);
|
||||
EXPECT_EQ(true, acc.get<T>(nanovdb::Coord(8,2,3), value) );
|
||||
EXPECT_EQ(3u, value);
|
||||
EXPECT_EQ(false, acc.get<T>(nanovdb::Coord(-18,2,3), value) );
|
||||
EXPECT_EQ(0u, value);
|
||||
|
||||
EXPECT_EQ(false, grid->tree().get<T>(nanovdb::Coord(0,2,3), value) );
|
||||
EXPECT_EQ(0u, value);
|
||||
EXPECT_EQ(true, grid->tree().get<T>(nanovdb::Coord(1,2,3), value) );
|
||||
EXPECT_EQ(1u, value);
|
||||
EXPECT_EQ(true, grid->tree().get<T>(nanovdb::Coord(1,2,4), value) );
|
||||
EXPECT_EQ(2u, value);
|
||||
EXPECT_EQ(true, grid->tree().get<T>(nanovdb::Coord(8,2,3), value) );
|
||||
EXPECT_EQ(3u, value);
|
||||
EXPECT_EQ(false, grid->tree().get<T>(nanovdb::Coord(-18,2,3), value) );
|
||||
EXPECT_EQ(0u, value);
|
||||
}
|
||||
|
||||
for (size_t i=0; i<num_points; ++i) {
|
||||
const nanovdb::Coord ijk = coords[i];
|
||||
const auto *leaf = acc.get<nanovdb::GetLeaf<BuildT>>(ijk);
|
||||
EXPECT_TRUE(leaf);
|
||||
const auto offset = leaf->CoordToOffset(ijk);
|
||||
EXPECT_EQ(ijk, leaf->offsetToGlobalCoord(offset));
|
||||
EXPECT_EQ(leaf->mValueMask, leaf->mMask);
|
||||
}
|
||||
}// Basic_CudaPointsToGrid_ValueOnIndexMask
|
||||
|
||||
TEST(TestNanoVDBCUDA, Large_CudaPointsToGrid_DeviceBuffer)
|
||||
{
|
||||
using BuildT = nanovdb::ValueOnIndex;
|
||||
@@ -674,7 +657,8 @@ TEST(TestNanoVDBCUDA, Large_CudaPointsToGrid_DeviceBuffer)
|
||||
});
|
||||
|
||||
//timer.stop();
|
||||
}// Large_CudaPointsToGrid_old
|
||||
cudaCheck(cudaFree(d_coords));
|
||||
}// Large_CudaPointsToGrid_DeviceBuffer
|
||||
|
||||
TEST(TestNanoVDBCUDA, Large_CudaPointsToGrid_UnifiedBuffer)
|
||||
{
|
||||
@@ -753,7 +737,8 @@ TEST(TestNanoVDBCUDA, Large_CudaPointsToGrid_UnifiedBuffer)
|
||||
});
|
||||
|
||||
//timer.stop();
|
||||
}// Large_CudaPointsToGrid_new
|
||||
cudaCheck(cudaFree(d_coords));
|
||||
}// Large_CudaPointsToGrid_UnifiedBuffer
|
||||
|
||||
TEST(TestNanoVDBCUDA, mergeSplitGrids)
|
||||
{
|
||||
@@ -934,14 +919,12 @@ TEST(TestNanoVDBCUDA, CudaIndexGridToGrid_basic)
|
||||
EXPECT_EQ(1u, idxHdl.buffer().bufferCount());
|
||||
EXPECT_TRUE(idxHdl.buffer().data(0, cudaCpuDeviceId));
|
||||
EXPECT_FALSE(idxHdl.buffer().data(0, 0));
|
||||
EXPECT_FALSE(idxHdl.buffer().data(0, 1));
|
||||
EXPECT_FALSE(idxHdl.deviceGrid<nanovdb::ValueIndex>());
|
||||
idxHdl.deviceUpload();
|
||||
EXPECT_TRUE(idxHdl.deviceGrid<nanovdb::ValueIndex>());
|
||||
EXPECT_EQ(2u, idxHdl.buffer().bufferCount());
|
||||
EXPECT_TRUE(idxHdl.buffer().data(0, cudaCpuDeviceId));
|
||||
EXPECT_TRUE(idxHdl.buffer().data(0, 0));
|
||||
EXPECT_FALSE(idxHdl.buffer().data(0, 1));
|
||||
auto *idxGrid = idxHdl.grid<nanovdb::ValueIndex>();
|
||||
EXPECT_TRUE(idxGrid);
|
||||
//timer.restart("Create value list on CPU");
|
||||
@@ -1114,22 +1097,22 @@ TEST(TestNanoVDBCUDA, CudaSignedFloodFill)
|
||||
{
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
//nanovdb::util::Timer timer("Create FloatGrid on CPU");
|
||||
auto floatHdl = nanovdb::tools::createLevelSetSphere<float, BufferT>(100);
|
||||
auto floatHdl = nanovdb::tools::createLevelSetSphere<float, BufferT>(1000);
|
||||
auto *floatGrid = floatHdl.grid<float>();
|
||||
EXPECT_TRUE(floatGrid);
|
||||
auto acc = floatGrid->getAccessor();
|
||||
EXPECT_FALSE(acc.isActive(nanovdb::Coord(103,0,0)));
|
||||
EXPECT_TRUE( acc.isActive(nanovdb::Coord(100,0,0)));
|
||||
EXPECT_FALSE(acc.isActive(nanovdb::Coord( 97,0,0)));
|
||||
EXPECT_EQ( 3.0f, acc(103,0,0));
|
||||
EXPECT_EQ( 0.0f, acc(100,0,0));
|
||||
EXPECT_EQ(-3.0f, acc( 97,0,0));
|
||||
EXPECT_FALSE(acc.isActive(nanovdb::Coord(1003,0,0)));
|
||||
EXPECT_TRUE( acc.isActive(nanovdb::Coord(1000,0,0)));
|
||||
EXPECT_FALSE(acc.isActive(nanovdb::Coord( 997,0,0)));
|
||||
EXPECT_EQ( 3.0f, acc(1003,0,0));
|
||||
EXPECT_EQ( 0.0f, acc(1000,0,0));
|
||||
EXPECT_EQ(-3.0f, acc( 997,0,0));
|
||||
using OpT = nanovdb::SetVoxel<float>;// only set the voxel value
|
||||
acc.set<OpT>(nanovdb::Coord(103,0,0),-1.0f);// flip sign and value of inactive voxel
|
||||
acc.set<OpT>(nanovdb::Coord( 97,0,0), 1.0f);// flip sign and value of inactive voxel
|
||||
EXPECT_EQ(-1.0f, acc(103,0,0));
|
||||
EXPECT_EQ( 0.0f, acc(100,0,0));
|
||||
EXPECT_EQ( 1.0f, acc( 97,0,0));
|
||||
acc.set<OpT>(nanovdb::Coord(1003,0,0),-1.0f);// flip sign and value of inactive voxel
|
||||
acc.set<OpT>(nanovdb::Coord( 997,0,0), 1.0f);// flip sign and value of inactive voxel
|
||||
EXPECT_EQ(-1.0f, acc(1003,0,0));
|
||||
EXPECT_EQ( 0.0f, acc(1000,0,0));
|
||||
EXPECT_EQ( 1.0f, acc( 997,0,0));
|
||||
//timer.restart("Copy FloatGrid from CPU to GPU");
|
||||
floatHdl.deviceUpload();// CPU -> GPU
|
||||
auto *d_floatGrid = floatHdl.deviceGrid<float>();
|
||||
@@ -1143,9 +1126,9 @@ TEST(TestNanoVDBCUDA, CudaSignedFloodFill)
|
||||
floatGrid = floatHdl.grid<float>();
|
||||
EXPECT_TRUE(floatGrid);
|
||||
acc = floatGrid->getAccessor();
|
||||
EXPECT_EQ( 3.0f, acc(103,0,0));
|
||||
EXPECT_EQ( 0.0f, acc(100,0,0));
|
||||
EXPECT_EQ(-3.0f, acc( 97,0,0));
|
||||
EXPECT_EQ( 3.0f, acc(1003,0,0));
|
||||
EXPECT_EQ( 0.0f, acc(1000,0,0));
|
||||
EXPECT_EQ(-3.0f, acc( 997,0,0));
|
||||
//EXPECT_FALSE(floatGrid->isLexicographic());
|
||||
EXPECT_TRUE(floatGrid->isBreadthFirst());
|
||||
}// CudaSignedFloodFill
|
||||
@@ -2643,7 +2626,7 @@ TEST(TestNanoVDBCUDA, compareNodeOrdering)
|
||||
auto *upper1 = grid1->tree().getFirstUpper(), *upper2 = grid2->tree().getFirstUpper();
|
||||
EXPECT_TRUE(upper1);
|
||||
EXPECT_TRUE(upper2);
|
||||
for (int i=0; i<grid1->tree().nodeCount(2); ++i) {
|
||||
for (uint32_t i=0; i<grid1->tree().nodeCount(2); ++i) {
|
||||
//std::cerr << "#" << i << " origin(CPU)=" << upper1[i].origin() << " origin(GPU)=" << upper2[i].origin() << std::endl;
|
||||
EXPECT_EQ(upper1[i].origin(), upper2[i].origin());
|
||||
EXPECT_EQ(upper1[i].valueMask(), upper2[i].valueMask());
|
||||
@@ -2655,7 +2638,7 @@ TEST(TestNanoVDBCUDA, compareNodeOrdering)
|
||||
auto *lower1 = grid1->tree().getFirstLower(), *lower2 = grid2->tree().getFirstLower();
|
||||
EXPECT_TRUE(lower1);
|
||||
EXPECT_TRUE(lower2);
|
||||
for (int i=0; i<grid1->tree().nodeCount(1); ++i) {
|
||||
for (uint32_t i=0; i<grid1->tree().nodeCount(1); ++i) {
|
||||
EXPECT_EQ(lower1[i].origin(), lower2[i].origin());
|
||||
EXPECT_EQ(lower1[i].valueMask(), lower2[i].valueMask());
|
||||
EXPECT_EQ(lower1[i].childMask(), lower2[i].childMask());
|
||||
@@ -2666,7 +2649,7 @@ TEST(TestNanoVDBCUDA, compareNodeOrdering)
|
||||
auto *leaf1 = grid1->tree().getFirstLeaf(), *leaf2 = grid2->tree().getFirstLeaf();
|
||||
EXPECT_TRUE(leaf1);
|
||||
EXPECT_TRUE(leaf2);
|
||||
for (int i=0; i<grid1->tree().nodeCount(0); ++i) {
|
||||
for (uint32_t i=0; i<grid1->tree().nodeCount(0); ++i) {
|
||||
EXPECT_EQ(leaf1[i].origin(), leaf2[i].origin());
|
||||
EXPECT_EQ(leaf1[i].valueMask(), leaf2[i].valueMask());
|
||||
}
|
||||
@@ -3258,7 +3241,8 @@ TEST(TestNanoVDBCUDA, UnifiedBuffer_basic)
|
||||
//buffer.deviceDownload(stream);
|
||||
buffer.prefetch(0, size, cudaCpuDeviceId, stream);
|
||||
nanovdb::util::Timer timer("Setting values on CPU with unified memory");
|
||||
for (int i = 0, *x = buffer.data<int>(); i < N; i++) *x++ = 1;
|
||||
int *x = buffer.data<int>();
|
||||
for (size_t i = 0; i < N; i++) *x++ = 1;
|
||||
timer.stop();
|
||||
}
|
||||
{// resize unified buffers
|
||||
@@ -3279,8 +3263,13 @@ TEST(TestNanoVDBCUDA, UnifiedBuffer_basic)
|
||||
|
||||
EXPECT_EQ(CUDA_SUCCESS, cudaStreamSynchronize(stream));
|
||||
int *x = buffer.data<int>();
|
||||
for (int i = 0; i < N; ++i) EXPECT_EQ(1, *x++);
|
||||
for (int i = 0; i < N; ++i) EXPECT_EQ(2, *x++);
|
||||
for (size_t i = 0; i < N; ++i) EXPECT_EQ(1, *x++);
|
||||
for (size_t i = 0; i < N; ++i) EXPECT_EQ(2, *x++);
|
||||
|
||||
nanovdb::cuda::UnifiedBuffer otherBuffer(std::move(buffer));
|
||||
int *y = otherBuffer.data<int>();
|
||||
for (size_t i = 0; i < N; ++i) EXPECT_EQ(1, *y++);
|
||||
for (size_t i = 0; i < N; ++i) EXPECT_EQ(2, *y++);
|
||||
}// UnifiedBuffer_basic
|
||||
|
||||
TEST(TestNanoVDBCUDA, UnifiedBuffer_IO)
|
||||
@@ -3360,3 +3349,306 @@ TEST(TestNanoVDBCUDA, UnifiedBuffer_createLevelSetSphere)
|
||||
EXPECT_EQ(nanovdb::Coord(center - radius - 2), dstGrid->indexBBox()[0]);
|
||||
EXPECT_EQ(nanovdb::Coord(center + radius + 2), dstGrid->indexBBox()[1]);
|
||||
} // UnifiedBuffer_createLevelSetSphere
|
||||
|
||||
template<class BuildT>
|
||||
__global__ void testComputeStencilNeighborsKernel(
|
||||
nanovdb::NanoGrid<BuildT> *grid,
|
||||
uint32_t *firstLeafIDArray,
|
||||
uint64_t *jumpMapArray,
|
||||
uint64_t *stencilNeighborsArray)
|
||||
{
|
||||
static constexpr int BlockWidth = 128;
|
||||
static constexpr int JumpMapLength = BlockWidth >> 6;
|
||||
int bID = blockIdx.x;
|
||||
int tID = threadIdx.x;
|
||||
|
||||
uint32_t &firstLeafID = firstLeafIDArray[bID];
|
||||
uint64_t *jumpMap = jumpMapArray + JumpMapLength * bID;
|
||||
int firstOffset = 1;
|
||||
int blockFirstOffset = firstOffset + bID * BlockWidth;
|
||||
__shared__ uint32_t leafIndex[BlockWidth];
|
||||
__shared__ uint16_t voxelOffset[BlockWidth];
|
||||
|
||||
nanovdb::tools::cuda::VoxelBlockManager<BlockWidth>::decodeInverseMaps(
|
||||
grid, firstLeafID, jumpMap, blockFirstOffset, &leafIndex[0], &voxelOffset[0]);
|
||||
|
||||
uint64_t localNeighbors[27] = {};
|
||||
nanovdb::tools::cuda::VoxelBlockManager<BlockWidth>::computeBoxStencil(
|
||||
grid, &leafIndex[0], &voxelOffset[0], localNeighbors);
|
||||
__syncthreads();
|
||||
|
||||
using StencilNeighborsType = uint64_t (*)[27];
|
||||
auto stencilNeighbors = reinterpret_cast<StencilNeighborsType>(stencilNeighborsArray+27*BlockWidth*bID);
|
||||
for (int i = 0; i < 27; i++)
|
||||
stencilNeighbors[tID][i] = localNeighbors[i];
|
||||
}
|
||||
|
||||
template<class BuildT, class BufferT>
|
||||
void testVoxelBlockManager()
|
||||
{
|
||||
// Create a test domain (21^3 box)
|
||||
std::vector<nanovdb::Coord> voxels;
|
||||
nanovdb::CoordBBox bbox = nanovdb::CoordBBox::createCube(125,145); // Coordinates chosen to span more than 1 lower node
|
||||
for (auto it = bbox.begin(); it; it++) voxels.push_back(*it);
|
||||
constexpr std::size_t BlockWidthLog2 = 7;
|
||||
constexpr std::size_t BlockWidth = 1 << BlockWidthLog2; // 128
|
||||
const std::size_t nVoxels = voxels.size();
|
||||
const std::size_t nBlocks = (nVoxels + BlockWidth - 1) >> BlockWidthLog2;
|
||||
nanovdb::Coord *deviceVoxels;
|
||||
cudaCheck(cudaMalloc(&deviceVoxels, nVoxels * sizeof(nanovdb::Coord)));
|
||||
cudaCheck(cudaMemcpy(deviceVoxels, voxels.data(), nVoxels * sizeof(nanovdb::Coord), cudaMemcpyHostToDevice)); // CPU -> GPU
|
||||
auto handle = nanovdb::tools::cuda::voxelsToGrid<BuildT>(deviceVoxels, nVoxels);
|
||||
cudaCheck(cudaFree(deviceVoxels));
|
||||
EXPECT_TRUE((handle.deviceData())); // grid only exists on the GPU
|
||||
auto deviceGrid = handle.template deviceGrid<BuildT>();
|
||||
EXPECT_FALSE(handle.data()); // no grid was yet allocated on the CPU
|
||||
handle.deviceDownload();
|
||||
EXPECT_TRUE(handle.data()); // no grid was yet allocated on the CPU
|
||||
auto grid = handle.template grid<BuildT>();
|
||||
EXPECT_TRUE(grid);
|
||||
std::size_t nLowerNodes = grid->tree().nodeCount(1);
|
||||
|
||||
// Construct VBM structure
|
||||
nanovdb::tools::VoxelBlockManagerHandle<BufferT> vbmHandle;
|
||||
vbmHandle.template deviceResize<BlockWidthLog2>(nBlocks);
|
||||
vbmHandle.setOffsets(1, nVoxels);
|
||||
vbmHandle.setLowerCount(nLowerNodes);
|
||||
|
||||
nanovdb::tools::cuda::buildVoxelBlockManager<BlockWidthLog2>(
|
||||
vbmHandle.firstOffset(), vbmHandle.lastOffset(),
|
||||
vbmHandle.blockCount(), vbmHandle.lowerCount(),
|
||||
deviceGrid, vbmHandle.deviceFirstLeafID(), vbmHandle.deviceJumpMap() );
|
||||
|
||||
// Compute stencil neighbors
|
||||
const size_t neighborStencilSize = nBlocks * BlockWidth * 27 * sizeof(uint64_t);
|
||||
auto neighborStencilBuffer = nanovdb::cuda::DeviceBuffer::create(neighborStencilSize, nullptr, false);
|
||||
EXPECT_TRUE(neighborStencilBuffer.deviceData());
|
||||
testComputeStencilNeighborsKernel<<<nBlocks,BlockWidth>>>(deviceGrid,
|
||||
vbmHandle.deviceFirstLeafID(), vbmHandle.deviceJumpMap(),
|
||||
reinterpret_cast<uint64_t*>(neighborStencilBuffer.deviceData()));
|
||||
cudaCheck(cudaGetLastError());
|
||||
|
||||
// Verify stencil neighbors against results of standard accessors
|
||||
neighborStencilBuffer.deviceDownload();
|
||||
EXPECT_TRUE(neighborStencilBuffer.data());
|
||||
using StencilNeighborsType = uint64_t (*)[27];
|
||||
auto stencilNeighbors = reinterpret_cast<StencilNeighborsType>(neighborStencilBuffer.data());
|
||||
auto acc = grid->getAccessor();
|
||||
for (const auto& coord : voxels) {
|
||||
const auto index = acc.getValue(coord);
|
||||
for (int di = -1; di <= 1; di++)
|
||||
for (int dj = -1; dj <= 1; dj++)
|
||||
for (int dk = -1; dk <= 1; dk++) {
|
||||
int spokeID = ( di + 1 ) * 9 + ( dj + 1 ) * 3 + dk + 1;
|
||||
const auto neighbor = coord.offsetBy( di, dj, dk );
|
||||
const auto neighborIndex = acc.getValue(neighbor);
|
||||
EXPECT_EQ( neighborIndex, stencilNeighbors[index-1][spokeID] ); } }
|
||||
}
|
||||
|
||||
TEST(TestNanoVDBCUDA, VoxelBlockManager_ValueOnIndex)
|
||||
{
|
||||
testVoxelBlockManager<nanovdb::ValueOnIndex,nanovdb::cuda::DeviceBuffer>();
|
||||
}// VoxelBlockManager_ValueOnIndex
|
||||
|
||||
TEST(TestNanoVDBCUDA, VoxelBlockManager_ValueOnIndex_UnifiedBuffer)
|
||||
{
|
||||
testVoxelBlockManager<nanovdb::ValueOnIndex,nanovdb::cuda::UnifiedBuffer>();
|
||||
}// VoxelBlockManager_ValueOnIndex_UnifiedBuffer
|
||||
|
||||
TEST(TestNanoVDBCUDA, DilateInjectPrune_ValueOnIndex)
|
||||
{
|
||||
using BuildT = nanovdb::ValueOnIndex;
|
||||
|
||||
// Create an input (original) grid to use as input to dilation op
|
||||
std::vector<nanovdb::Coord> inputPoints;
|
||||
inputPoints.emplace_back(1,0,0); // Added nodes after dilation: 3 root, 3 upper, 3 lower, 3 leaf
|
||||
inputPoints.emplace_back(0,1,1); // Added nodes after dilation: 1 root, 1 upper, 1 lower, 1 leaf
|
||||
inputPoints.emplace_back(127,127,127); // Added nodes after dilation: 7 lower, 7 leaf
|
||||
auto inputBuffer = nanovdb::cuda::DeviceBuffer::create( inputPoints.size() * sizeof(nanovdb::Coord), nullptr, false);
|
||||
EXPECT_FALSE(inputBuffer.data());
|
||||
EXPECT_TRUE(inputBuffer.deviceData());
|
||||
cudaCheck(cudaMemcpy(inputBuffer.deviceData(), inputPoints.data(), inputPoints.size() * sizeof(nanovdb::Coord), cudaMemcpyHostToDevice));
|
||||
nanovdb::tools::cuda::PointsToGrid<BuildT> converter;
|
||||
converter.setChecksum(nanovdb::CheckMode::Default);
|
||||
auto inputHandle = converter.getHandle(static_cast<nanovdb::Coord*>(inputBuffer.deviceData()), inputPoints.size());
|
||||
EXPECT_TRUE(inputHandle.deviceGrid<BuildT>());
|
||||
auto inputGrid = inputHandle.deviceGrid<BuildT>();
|
||||
EXPECT_FALSE(inputHandle.grid<BuildT>());
|
||||
|
||||
// Perform dilation
|
||||
nanovdb::tools::cuda::DilateGrid<BuildT> dilator( inputGrid );
|
||||
dilator.setOperation(nanovdb::tools::morphology::NN_FACE_EDGE_VERTEX);
|
||||
dilator.setChecksum(nanovdb::CheckMode::Default);
|
||||
dilator.setVerbose(0);
|
||||
auto dilatedHandle = dilator.getHandle();
|
||||
auto dilatedGrid = dilatedHandle.deviceGrid<BuildT>();
|
||||
EXPECT_TRUE(dilatedGrid);
|
||||
auto dilatedTreeData = nanovdb::util::cuda::DeviceGridTraits<BuildT>::getTreeData(dilatedGrid);
|
||||
EXPECT_EQ(dilatedTreeData.mNodeCount[0], 13);
|
||||
EXPECT_EQ(dilatedTreeData.mNodeCount[1], 12);
|
||||
EXPECT_EQ(dilatedTreeData.mNodeCount[2], 5);
|
||||
EXPECT_EQ(dilatedTreeData.mVoxelCount, 73);
|
||||
|
||||
// Create a prune mask (set bits correspond to retained voxels) from the occupancy of the original grid
|
||||
auto maskBuffer = nanovdb::cuda::DeviceBuffer::create( dilatedTreeData.mNodeCount[0] * sizeof(nanovdb::Mask<3>), nullptr, false);
|
||||
EXPECT_TRUE(maskBuffer.deviceData());
|
||||
auto leafMasks = static_cast<nanovdb::Mask<3>*>(maskBuffer.deviceData());
|
||||
constexpr unsigned int num_threads = 128;
|
||||
unsigned int num_blocks = (static_cast<unsigned int>(dilatedTreeData.mNodeCount[0]) + num_threads - 1) / num_threads;
|
||||
nanovdb::util::cuda::lambdaKernel<<<num_blocks, num_threads>>>(dilatedTreeData.mNodeCount[0],
|
||||
nanovdb::util::cuda::InjectGridMaskFunctor<BuildT>(),
|
||||
inputGrid, dilatedGrid, leafMasks);
|
||||
|
||||
// Prune with computed mask
|
||||
nanovdb::tools::cuda::PruneGrid<BuildT> pruner( dilatedGrid, leafMasks );
|
||||
pruner.setChecksum(nanovdb::CheckMode::Default);
|
||||
pruner.setVerbose(0);
|
||||
auto prunedHandle = pruner.getHandle();
|
||||
auto prunedGrid = prunedHandle.deviceGrid<BuildT>();
|
||||
EXPECT_TRUE(prunedGrid);
|
||||
|
||||
// The pruned grid should be identical to the original input
|
||||
EXPECT_FALSE(prunedHandle.grid<BuildT>());
|
||||
inputHandle.deviceDownload();
|
||||
prunedHandle.deviceDownload();
|
||||
EXPECT_TRUE(inputHandle.grid<BuildT>());
|
||||
EXPECT_TRUE(prunedHandle.grid<BuildT>());
|
||||
EXPECT_EQ(inputHandle.grid<BuildT>()->mChecksum.full(), prunedHandle.grid<BuildT>()->mChecksum.full());
|
||||
}// DilateInjectPrune_ValueOnIndex
|
||||
|
||||
TEST(TestNanoVDBCUDA, RefineCoarsen_ValueOnIndex)
|
||||
{
|
||||
using BuildT = nanovdb::ValueOnIndex;
|
||||
|
||||
// Create an input (original) grid to use as input to refinement op
|
||||
std::vector<nanovdb::Coord> inputPoints;
|
||||
inputPoints.emplace_back(0,0,0); // Refinement doesn't introduce extra nodes
|
||||
inputPoints.emplace_back(4,0,0); // Refinement will introduce extra leaf
|
||||
inputPoints.emplace_back(0,64,0); // Refinement will introduce extra lower node
|
||||
inputPoints.emplace_back(0,0,2048); // Refinement will introduce extra upper node (and root tile)
|
||||
auto inputBuffer = nanovdb::cuda::DeviceBuffer::create( inputPoints.size() * sizeof(nanovdb::Coord), nullptr, false);
|
||||
EXPECT_FALSE(inputBuffer.data());
|
||||
EXPECT_TRUE(inputBuffer.deviceData());
|
||||
cudaCheck(cudaMemcpy(inputBuffer.deviceData(), inputPoints.data(), inputPoints.size() * sizeof(nanovdb::Coord), cudaMemcpyHostToDevice));
|
||||
nanovdb::tools::cuda::PointsToGrid<BuildT> converter;
|
||||
converter.setChecksum(nanovdb::CheckMode::Default);
|
||||
auto inputHandle = converter.getHandle(static_cast<nanovdb::Coord*>(inputBuffer.deviceData()), inputPoints.size());
|
||||
EXPECT_TRUE(inputHandle.deviceGrid<BuildT>());
|
||||
auto inputGrid = inputHandle.deviceGrid<BuildT>();
|
||||
EXPECT_FALSE(inputHandle.grid<BuildT>());
|
||||
|
||||
// Perform refinement
|
||||
nanovdb::tools::cuda::RefineGrid<BuildT> refiner( inputGrid );
|
||||
refiner.setChecksum(nanovdb::CheckMode::Default);
|
||||
refiner.setVerbose(0);
|
||||
auto refinedHandle = refiner.getHandle();
|
||||
auto refinedGrid = refinedHandle.deviceGrid<BuildT>();
|
||||
EXPECT_TRUE(refinedGrid);
|
||||
auto refinedTreeData = nanovdb::util::cuda::DeviceGridTraits<BuildT>::getTreeData(refinedGrid);
|
||||
EXPECT_EQ(refinedTreeData.mNodeCount[0], 4);
|
||||
EXPECT_EQ(refinedTreeData.mNodeCount[1], 3);
|
||||
EXPECT_EQ(refinedTreeData.mNodeCount[2], 2);
|
||||
EXPECT_EQ(refinedTreeData.mVoxelCount, 32);
|
||||
|
||||
// Coarsen back to original
|
||||
nanovdb::tools::cuda::CoarsenGrid<BuildT> coarsener( refinedGrid );
|
||||
coarsener.setChecksum(nanovdb::CheckMode::Default);
|
||||
coarsener.setVerbose(0);
|
||||
auto coarsenedHandle = coarsener.getHandle();
|
||||
auto coarsenedGrid = coarsenedHandle.deviceGrid<BuildT>();
|
||||
EXPECT_TRUE(coarsenedGrid);
|
||||
|
||||
// The coarsened grid should be identical to the original input
|
||||
EXPECT_FALSE(coarsenedHandle.grid<BuildT>());
|
||||
inputHandle.deviceDownload();
|
||||
coarsenedHandle.deviceDownload();
|
||||
EXPECT_TRUE(inputHandle.grid<BuildT>());
|
||||
EXPECT_TRUE(coarsenedHandle.grid<BuildT>());
|
||||
EXPECT_EQ(inputHandle.grid<BuildT>()->mChecksum.full(), coarsenedHandle.grid<BuildT>()->mChecksum.full());
|
||||
}// RefineCoarsen_ValueOnIndex
|
||||
|
||||
TEST(TestNanoVDBCUDA, MergeGrids_ValueOnIndex)
|
||||
{
|
||||
using BuildT = nanovdb::ValueOnIndex;
|
||||
|
||||
// Create the first input grid for the merge op
|
||||
std::vector<nanovdb::Coord> inputPointsA;
|
||||
for (int i = 0; i <= 2; i++)
|
||||
for (int j = 0; j <= 2; j++)
|
||||
for (int k = 0; k <= 2; k++)
|
||||
inputPointsA.emplace_back(i-1, j*8-1, k*128); // 4 upper, 12 lower, 18 leaf nodes
|
||||
auto inputBufferA = nanovdb::cuda::DeviceBuffer::create( inputPointsA.size() * sizeof(nanovdb::Coord), nullptr, false);
|
||||
EXPECT_FALSE(inputBufferA.data());
|
||||
EXPECT_TRUE(inputBufferA.deviceData());
|
||||
cudaCheck(cudaMemcpy(inputBufferA.deviceData(), inputPointsA.data(), inputPointsA.size() * sizeof(nanovdb::Coord), cudaMemcpyHostToDevice));
|
||||
auto inputHandleA = nanovdb::tools::cuda::voxelsToGrid<BuildT>(static_cast<nanovdb::Coord*>(inputBufferA.deviceData()), inputPointsA.size());
|
||||
EXPECT_TRUE(inputHandleA.deviceGrid<BuildT>());
|
||||
auto inputGridA = inputHandleA.deviceGrid<BuildT>();
|
||||
EXPECT_FALSE(inputHandleA.grid<BuildT>());
|
||||
|
||||
// Create the second input grid for the merge op
|
||||
std::vector<nanovdb::Coord> inputPointsB;
|
||||
for (int i = 0; i <= 2; i++)
|
||||
for (int j = 0; j <= 2; j++)
|
||||
for (int k = 0; k <= 2; k++)
|
||||
inputPointsB.emplace_back(i, j*8-1, (k-1)*128); // 4 upper, 6 lower, 9 leaf nodes
|
||||
auto inputBufferB = nanovdb::cuda::DeviceBuffer::create( inputPointsB.size() * sizeof(nanovdb::Coord), nullptr, false);
|
||||
EXPECT_FALSE(inputBufferB.data());
|
||||
EXPECT_TRUE(inputBufferB.deviceData());
|
||||
cudaCheck(cudaMemcpy(inputBufferB.deviceData(), inputPointsB.data(), inputPointsB.size() * sizeof(nanovdb::Coord), cudaMemcpyHostToDevice));
|
||||
auto inputHandleB = nanovdb::tools::cuda::voxelsToGrid<BuildT>(static_cast<nanovdb::Coord*>(inputBufferB.deviceData()), inputPointsB.size());
|
||||
EXPECT_TRUE(inputHandleB.deviceGrid<BuildT>());
|
||||
auto inputGridB = inputHandleB.deviceGrid<BuildT>();
|
||||
EXPECT_FALSE(inputHandleB.grid<BuildT>());
|
||||
|
||||
// Perform the merge operation
|
||||
nanovdb::tools::cuda::MergeGrids<BuildT> merger( inputGridA, inputGridB );
|
||||
merger.setChecksum(nanovdb::CheckMode::Disable);
|
||||
merger.setVerbose(0);
|
||||
auto mergedHandle = merger.getHandle();
|
||||
auto mergedGrid = mergedHandle.deviceGrid<BuildT>();
|
||||
EXPECT_TRUE(mergedGrid);
|
||||
auto mergedTreeData = nanovdb::util::cuda::DeviceGridTraits<BuildT>::getTreeData(mergedGrid);
|
||||
EXPECT_EQ(mergedTreeData.mNodeCount[0], 21);
|
||||
EXPECT_EQ(mergedTreeData.mNodeCount[1], 14);
|
||||
EXPECT_EQ(mergedTreeData.mNodeCount[2], 6);
|
||||
EXPECT_EQ(mergedTreeData.mVoxelCount, 42); // Each input grid has 27 active voxels, 12 shared between the two
|
||||
}// DilateInjectPrune_ValueOnIndex
|
||||
|
||||
TEST(TestNanoVDBCUDA, GridHandle_from_HostBuffer)
|
||||
{
|
||||
using namespace nanovdb;
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
auto hostHandle = tools::createLevelSetSphere<float>(100, Vec3d(0),1,3, Vec3d(0), "test");
|
||||
|
||||
int dev;
|
||||
cudaError_t err = cudaGetDevice(&dev);
|
||||
EXPECT_EQ(err, cudaSuccess);
|
||||
cudaStream_t stream;
|
||||
cudaCheck(cudaStreamCreate(&stream));
|
||||
|
||||
{// longer version
|
||||
auto devBuffer = BufferT::create(hostHandle.buffer(), dev, stream);
|
||||
EXPECT_EQ(hostHandle.bufferSize(), devBuffer.size());
|
||||
auto devHandle = GridHandle<BufferT>(std::move(devBuffer));
|
||||
|
||||
// testing
|
||||
EXPECT_EQ(hostHandle.bufferSize(), devHandle.bufferSize());
|
||||
EXPECT_EQ(devBuffer.size(), 0);
|
||||
devHandle.deviceDownload(stream);
|
||||
for (uint64_t i=0; i<hostHandle.bufferSize(); ++i) {
|
||||
EXPECT_EQ(*hostHandle.buffer().data<char>(i), *devHandle.buffer().data<char>(i));
|
||||
}
|
||||
}
|
||||
{// compact version
|
||||
auto devHandle = GridHandle<BufferT>(BufferT::create(hostHandle.buffer(), dev, stream));
|
||||
|
||||
// testing
|
||||
EXPECT_EQ(hostHandle.bufferSize(), devHandle.bufferSize());
|
||||
devHandle.deviceDownload(stream);
|
||||
for (uint64_t i=0; i<hostHandle.bufferSize(); ++i) {
|
||||
EXPECT_EQ(*hostHandle.buffer().data<char>(i), *devHandle.buffer().data<char>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user