Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae0875bb06 | ||
|
|
13aa50ac1f | ||
|
|
77a424312e | ||
|
|
7624beab0a | ||
|
|
7262f8b113 | ||
|
|
a6c7af8109 | ||
|
|
f5f92dcde1 |
@@ -14,7 +14,6 @@ on:
|
||||
- 'nanovdb/**'
|
||||
- 'openvdb_maya/**'
|
||||
- 'openvdb_houdini/**'
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
pull_request:
|
||||
@@ -27,7 +26,6 @@ on:
|
||||
- 'nanovdb/**'
|
||||
- 'openvdb_maya/**'
|
||||
- 'openvdb_houdini/**'
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
schedule:
|
||||
@@ -72,12 +70,10 @@ jobs:
|
||||
- { image: '2023-clang15', cxx: 'clang++', build: 'Debug', cmake: '' }
|
||||
- { image: '2022-clang11', cxx: 'clang++', build: 'Release', cmake: '' }
|
||||
- { image: '2022-clang11', cxx: 'g++', build: 'Release', cmake: '' }
|
||||
- { image: '2021-clang10', cxx: 'clang++', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' }
|
||||
- { image: '2021-clang10', cxx: 'g++', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Enable Node 16
|
||||
if: contains(matrix.config.image, '2022')
|
||||
run: |
|
||||
echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
- name: pybind11
|
||||
#if: contains(matrix.config.image, '2023') == false
|
||||
@@ -127,16 +123,14 @@ jobs:
|
||||
matrix:
|
||||
config:
|
||||
#@note llvm10 never got its own brew formula...
|
||||
# Last macos runner befor M1 (macos-14)
|
||||
- { runner: 'macos-13', cxx: 'clang++', build: 'Release', llvm: '12' }
|
||||
- { runner: 'macos-13', cxx: 'clang++', build: 'Release', llvm: '13' }
|
||||
- { runner: 'macos-latest', cxx: 'clang++', build: 'Release', llvm: '11' }
|
||||
- { runner: 'macos-latest', cxx: 'clang++', build: 'Release', llvm: '12' }
|
||||
- { runner: 'macos-latest', cxx: 'clang++', build: 'Release', llvm: '13' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install_deps
|
||||
run: |
|
||||
./ci/install_macos.sh ${{ matrix.config.llvm }}
|
||||
./ci/install_tbb_macos.sh
|
||||
run: ./ci/install_macos.sh ${{ matrix.config.llvm }}
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
@@ -145,8 +139,7 @@ jobs:
|
||||
--cargs=\"
|
||||
-DOPENVDB_AX_TEST_CMD_DOWNLOADS=ON
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF
|
||||
-DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install
|
||||
-DLLVM_DIR=/opt/homebrew/opt/llvm@${{ matrix.config.llvm }}/lib/cmake/llvm
|
||||
-DLLVM_DIR=/usr/local/opt/llvm@${{ matrix.config.llvm }}/lib/cmake/llvm
|
||||
\"
|
||||
- name: test
|
||||
run: cd build && ctest -V
|
||||
|
||||
+38
-35
@@ -15,7 +15,6 @@ on:
|
||||
- 'openvdb_houdini/**'
|
||||
- 'openvdb_ax/**'
|
||||
- 'nanovdb/**'
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
pull_request:
|
||||
@@ -29,7 +28,6 @@ on:
|
||||
- 'openvdb_houdini/**'
|
||||
- 'openvdb_ax/**'
|
||||
- 'nanovdb/**'
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
schedule:
|
||||
@@ -76,27 +74,22 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { cxx: clang++, image: '2024', abi: '11', build: 'Release', cmake: '' }
|
||||
- { cxx: g++, image: '2024', abi: '11', build: 'Release', cmake: '' }
|
||||
- { cxx: clang++, image: '2024', abi: '11', build: 'Debug', cmake: '' }
|
||||
- { cxx: clang++, image: '2023', abi: '11', build: 'Release', cmake: '' }
|
||||
- { cxx: g++, image: '2023', abi: '11', build: 'Release', cmake: '' }
|
||||
- { cxx: clang++, image: '2022', abi: '10', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' }
|
||||
- { cxx: g++, image: '2022', abi: '10', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' }
|
||||
# @note we specifically use clang15.0 (not clang15) here as the newest
|
||||
# versions of the clang15.X containers have some issues with the GLFW
|
||||
# installation
|
||||
- { cxx: clang++, image: '2023-clang15.0', abi: '11', build: 'Release', cmake: '' }
|
||||
- { cxx: g++, image: '2023-clang15.0', abi: '11', build: 'Release', cmake: '' }
|
||||
- { cxx: clang++, image: '2022-clang11', abi: '11', build: 'Debug', cmake: '' }
|
||||
- { cxx: clang++, image: '2022-clang11', abi: '10', build: 'Release', cmake: '' }
|
||||
- { cxx: g++, image: '2022-clang11', abi: '10', build: 'Release', cmake: '' }
|
||||
- { cxx: clang++, image: '2022-clang11', abi: '9', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' }
|
||||
- { cxx: g++, image: '2022-clang11', abi: '9', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Enable Node 16
|
||||
# Solution taken from https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default
|
||||
if: contains(matrix.config.image, '2022')
|
||||
run: |
|
||||
echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
- name: pybind11
|
||||
#if: contains(matrix.config.image, '2023') == false
|
||||
run: ./ci/install_pybind11.sh 2.10.0
|
||||
- name: glfw
|
||||
if: contains(matrix.config.image, '2023') == true
|
||||
run: ./ci/install_glfw.sh 3.3.10
|
||||
- name: timestamp
|
||||
id: timestamp
|
||||
run: echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
@@ -132,16 +125,29 @@ jobs:
|
||||
run: ccache --evict-older-than 1d
|
||||
|
||||
windows:
|
||||
# Windows CI. Tests a dynamic build with MD.
|
||||
# Windows CI. Tests static and dynamic builds with MT and MD respectively.
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'win'
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'windows-2022-8c-32g-300h') || 'windows-latest' }}
|
||||
name: windows
|
||||
name: windows-vc:${{ matrix.config.vc }}-type:${{ matrix.config.build }}
|
||||
env:
|
||||
VCPKG_DEFAULT_TRIPLET: x64-windows
|
||||
VCPKG_DEFAULT_TRIPLET: ${{ matrix.config.vc }}
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
# static build of blosc from vcpkg does not build internal sources.
|
||||
# USE_STATIC_DEPENDENCIES is required for IlmBase/OpenEXR defines and
|
||||
# Boost as both shared and static libs are installed.
|
||||
# USE_EXPLICIT_INSTANTIATION is disabled for debug static libraries
|
||||
# due to disk space constraints
|
||||
# @note Commented out the static debug build due to linker OOM LNK1102
|
||||
- { 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: 'Debug', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON -DUSE_EXPLICIT_INSTANTIATION=OFF' }
|
||||
- { 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', build: 'Release', cmake: '-G \"MinGW Makefiles\" -DOPENVDB_CORE_STATIC=OFF' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -149,18 +155,17 @@ jobs:
|
||||
shell: pwsh
|
||||
run: |
|
||||
# note: system path must be modified in a previous step to it's use
|
||||
echo "$Env:VCPKG_INSTALLATION_ROOT\installed\x64-windows\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "${{github.workspace}}\build\openvdb\openvdb\Release" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "$Env:VCPKG_INSTALLATION_ROOT\installed\${{ matrix.config.vc }}\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "${{github.workspace}}\build\openvdb\openvdb\${{ matrix.config.build }}" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- name: install
|
||||
shell: powershell
|
||||
run: .\ci\install_windows.ps1
|
||||
run: ./ci/install_windows.sh
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--config='Release'
|
||||
--components='core,bin,view,render,python,test'
|
||||
--config=${{ matrix.config.build }}
|
||||
--components=${{ matrix.config.components }}
|
||||
--cargs=\'
|
||||
-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF
|
||||
${{ matrix.config.cmake }}
|
||||
-DMSVC_COMPRESS_PDB=ON
|
||||
-DUSE_EXR=ON
|
||||
-DUSE_PNG=ON
|
||||
@@ -171,29 +176,27 @@ jobs:
|
||||
# Print the build directy size (monitor if we're hitting runner limits)
|
||||
run: du -h build
|
||||
- name: test
|
||||
run: cd build && ctest -V -C Release
|
||||
# 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'
|
||||
run: cd build && ctest -V -C ${{ matrix.config.build }}
|
||||
|
||||
macos:
|
||||
if: |
|
||||
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: macos-latest
|
||||
env:
|
||||
CXX: clang++
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install
|
||||
run: |
|
||||
./ci/install_macos.sh
|
||||
./ci/install_tbb_macos.sh
|
||||
run: ./ci/install_macos.sh
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--build-type=Release
|
||||
--components=\"core,python,bin,view,render,test\"
|
||||
--cargs=\'
|
||||
-DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install
|
||||
\'
|
||||
- name: test
|
||||
run: cd build && ctest -V
|
||||
|
||||
@@ -42,9 +42,6 @@ jobs:
|
||||
# need to re-write the python docs to use sphinx
|
||||
image: aswf/ci-openvdb:2022
|
||||
steps:
|
||||
- name: Enable Node 16
|
||||
run: |
|
||||
echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
- name: install_doxygen
|
||||
run: ./ci/install_doxygen.sh 1_8_11
|
||||
@@ -54,12 +51,7 @@ jobs:
|
||||
# - name: install_epydoc
|
||||
# run: pip install epydoc
|
||||
- name: install_latex
|
||||
run: |
|
||||
# Fix error: Cannot prepare internal mirrorlist: No URLs in mirrorlist. CentOS 8 reached EOL means need to replace the official mirror to vault.centos.org
|
||||
# Comment out mirrorlist and replace #baseurl=...mirror.centos.org with baseurl=...vault.centos.org in files starting with CentOS- in /etc/yum.repos.d folder
|
||||
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
|
||||
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
|
||||
yum -y install texlive-latex-bin texlive-dvips texlive-collection-fontsrecommended texlive-collection-latexrecommended
|
||||
run: yum -y install texlive-latex-bin texlive-dvips texlive-collection-fontsrecommended texlive-collection-latexrecommended
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
|
||||
@@ -13,7 +13,6 @@ on:
|
||||
- 'doc/**'
|
||||
- 'nanovdb/**'
|
||||
- 'openvdb_maya/**'
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
pull_request:
|
||||
@@ -25,7 +24,6 @@ on:
|
||||
- 'doc/**'
|
||||
- 'nanovdb/**'
|
||||
- 'openvdb_maya/**'
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
schedule:
|
||||
@@ -75,32 +73,22 @@ 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: '2023.0', hou_hash: '20_0-newabi', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
- { cxx: clang++, image: '2022', hou_hash: '20_0-oldabi', build: 'Release', components: 'core,hou' }
|
||||
- { cxx: clang++, image: '2021', hou_hash: '19_5', build: 'Release', components: 'core,hou' }
|
||||
- { cxx: clang++, image: '2023.0', hou_hash: '20_0-newabi', build: 'Debug', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
- { cxx: g++, image: '2023.0', hou_hash: '20_0-newabi', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' }
|
||||
- { cxx: g++, image: '2022', hou_hash: '20_0-oldabi', build: 'Release', components: 'core,hou' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
# See note on this step in the Houdini weekly.yml job
|
||||
# We can remove this when we no longer use < 2023 images
|
||||
- name: Enable Node 16
|
||||
run: |
|
||||
echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV
|
||||
- name: remove zstd
|
||||
run: yum -y remove zstd
|
||||
- uses: actions/checkout@v3
|
||||
- name: pybind11
|
||||
#if: contains(matrix.config.image, '2023') == false
|
||||
run: ./ci/install_pybind11.sh 2.10.0
|
||||
- name: glfw
|
||||
if: contains(matrix.config.image, '2023') == true
|
||||
run: ./ci/install_glfw.sh 3.3.10
|
||||
- name: cppunit
|
||||
if: contains(matrix.config.image, '2022') == false
|
||||
run: ./ci/install_cppunit.sh 1.15.1
|
||||
- name: timestamp
|
||||
id: timestamp
|
||||
run: echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
@@ -171,12 +159,15 @@ 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: |
|
||||
brew update
|
||||
brew install bash gnu-getopt cmake boost glfw googletest openexr pybind11 llvm@15 cppunit
|
||||
echo "/usr/local/opt/gnu-getopt/bin" >> $GITHUB_PATH
|
||||
- name: build
|
||||
run: |
|
||||
./ci/build.sh -v \
|
||||
--build-type=Release \
|
||||
--components="core,hou,bin,view,render,python,test,axcore,axbin" \
|
||||
--components="core,hou,bin,view,render,python,test,axcore,axbin,axtest" \
|
||||
--cargs=\" \
|
||||
-DHOUDINI_ROOT=$HOME/houdini_install/hou \
|
||||
-DOPENVDB_BUILD_HOUDINI_ABITESTS=OFF \
|
||||
@@ -184,9 +175,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 \
|
||||
-DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install \
|
||||
-DLLVM_DIR=/usr/local/opt/llvm@15/lib/cmake/llvm \
|
||||
\"
|
||||
- name: test
|
||||
run: cd build && ctest -V
|
||||
|
||||
@@ -14,7 +14,6 @@ on:
|
||||
- 'openvdb_ax/**'
|
||||
- 'openvdb_maya/**'
|
||||
- 'openvdb_houdini/**'
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
pull_request:
|
||||
@@ -27,7 +26,6 @@ on:
|
||||
- 'openvdb_ax/**'
|
||||
- 'openvdb_maya/**'
|
||||
- 'openvdb_houdini/**'
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
schedule:
|
||||
@@ -67,17 +65,9 @@ jobs:
|
||||
- { cxx: clang++, image: '2022-clang11', build: 'Debug' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Enable Node 16
|
||||
if: contains(matrix.config.image, '2022')
|
||||
run: |
|
||||
echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
- name: install_cuda_11
|
||||
run: |
|
||||
# Fix error: Cannot prepare internal mirrorlist: No URLs in mirrorlist. CentOS 8 reached EOL means need to replace the official mirror to vault.centos.org
|
||||
# Comment out mirrorlist and replace #baseurl=...mirror.centos.org with baseurl=...vault.centos.org in files starting with CentOS- in /etc/yum.repos.d folder
|
||||
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
|
||||
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
|
||||
yum -y install yum-utils
|
||||
yum-config-manager --add-repo http://developer.download.nvidia.com/compute/cuda/repos/rhel7/x86_64/cuda-rhel7.repo
|
||||
echo "Installing cuda toolkit"
|
||||
@@ -106,32 +96,43 @@ jobs:
|
||||
github.event.inputs.type == 'win'
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'windows-2022-8c-32g-300h') || 'windows-latest' }}
|
||||
env:
|
||||
VCPKG_DEFAULT_TRIPLET: 'x64-windows'
|
||||
VCPKG_DEFAULT_TRIPLET: ${{ matrix.config.vc }}
|
||||
visual_studio: "Visual Studio 17 2022"
|
||||
cuda: "12.4.0"
|
||||
cuda: "11.6.2"
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
# static build of blosc from vcpkg does not build internal sources.
|
||||
# USE_STATIC_DEPENDENCIES is required for IlmBase/OpenEXR defines and
|
||||
# Boost as both shared and static libs are installed.
|
||||
- { vc: 'x64-windows-static', build: 'Release', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded' }
|
||||
- { vc: 'x64-windows-static', build: 'Debug', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug' }
|
||||
- { vc: 'x64-windows', build: 'Release', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF' }
|
||||
- { vc: 'x64-windows', build: 'Debug', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: path
|
||||
run: |
|
||||
# note: system path must be modified in a previous step to it's use
|
||||
echo "$Env:VCPKG_INSTALLATION_ROOT\installed\x64-windows\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "${{github.workspace}}\build\openvdb\openvdb\Release" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "$Env:VCPKG_INSTALLATION_ROOT\installed\${{ matrix.config.vc }}\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "${{github.workspace}}\build\openvdb\openvdb\${{ matrix.config.build }}" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- name: install_cuda
|
||||
shell: powershell
|
||||
run: .\ci\install_windows_cuda.ps1
|
||||
- name: install
|
||||
shell: powershell
|
||||
run: .\ci\install_windows.ps1
|
||||
- name: install_deps
|
||||
shell: bash
|
||||
run: |
|
||||
vcpkg update
|
||||
vcpkg install zlib tbb gtest blosc boost-iostreams boost-system boost-any boost-uuid boost-interprocess boost-algorithm
|
||||
- name: build
|
||||
shell: bash
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--config=Release
|
||||
--config=${{ matrix.config.build }}
|
||||
--components=core,nano,nanotest,nanoexam,nanobench,nanotool
|
||||
--cargs=\'
|
||||
-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF
|
||||
${{ matrix.config.cmake }}
|
||||
-DMSVC_COMPRESS_PDB=ON
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF
|
||||
-DNANOVDB_USE_CUDA=ON
|
||||
@@ -154,15 +155,13 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { runner: 'macos-12', cxx: 'clang++', build: 'Release' }
|
||||
- { runner: 'macos-12', cxx: 'clang++', build: 'Debug' }
|
||||
- { runner: 'macos-11', cxx: 'clang++', build: 'Release' }
|
||||
- { runner: 'macos-11', cxx: 'clang++', build: 'Debug' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install_deps
|
||||
run: |
|
||||
./ci/install_macos.sh
|
||||
./ci/install_tbb_macos.sh
|
||||
run: ./ci/install_macos.sh
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
@@ -189,6 +188,6 @@ jobs:
|
||||
cd nanovdb/nanovdb
|
||||
sudo mkdir .build
|
||||
cd .build
|
||||
sudo cmake -DUSE_EXPLICIT_INSTANTIATION=OFF -DNANOVDB_BUILD_UNITTESTS=ON -DNANOVDB_USE_OPENVDB=OFF -DNANOVDB_USE_CUDA=OFF ../
|
||||
sudo cmake -DUSE_EXPLICIT_INSTANTIATION=OFF -DNANOVDB_BUILD_UNITTESTS=ON ../
|
||||
sudo make -j8 install
|
||||
sudo ctest -V
|
||||
|
||||
+16
-149
@@ -73,14 +73,10 @@ jobs:
|
||||
- { houdini_version: '19.5', platform: 'linux_x86_64_gcc9.3', hou_hash: '19_5' }
|
||||
- { houdini_version: '20.0', platform: 'linux_x86_64_gcc9.3', hou_hash: '20_0-oldabi' }
|
||||
- { houdini_version: '20.0', platform: 'linux_x86_64_gcc11.2', hou_hash: '20_0-newabi' }
|
||||
- { houdini_version: '20.5', platform: 'linux_x86_64_gcc11.2', hou_hash: '20_5' }
|
||||
fail-fast: false
|
||||
container:
|
||||
image: aswf/ci-base:2024
|
||||
image: aswf/ci-base:2023
|
||||
steps:
|
||||
- name: Enable Node 16
|
||||
run: |
|
||||
echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV
|
||||
- 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
|
||||
@@ -115,8 +111,6 @@ jobs:
|
||||
(github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'houdini')
|
||||
# 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
|
||||
env:
|
||||
@@ -128,7 +122,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 20.0 macosx_x86_64_clang12.0_11 --prod
|
||||
- name: install_houdini
|
||||
run: |
|
||||
mkdir $HOME/houdini_install
|
||||
@@ -154,7 +148,10 @@ jobs:
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-20.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
name: linux-extra:${{ matrix.config.name }}
|
||||
container:
|
||||
image: aswf/ci-openvdb:2024
|
||||
# @note we specifically use clang15.0 (not clang15) here as the newest
|
||||
# versions of the clang15.X containers have some issues with the GLFW
|
||||
# installation
|
||||
image: aswf/ci-openvdb:2023-clang15.0
|
||||
env:
|
||||
CXX: clang++
|
||||
strategy:
|
||||
@@ -167,14 +164,11 @@ jobs:
|
||||
- { 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,axcore,axtest', 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,axcore,axtest', cmake: '-DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" ' }
|
||||
- { name: 'ubsan', build: 'ubsan', components: 'core,test,axcore,axtest', cmake: '' }
|
||||
- { name: 'c++20', build: 'Release', components: 'core,test,axcore,axtest', cmake: '-DCMAKE_CXX_STANDARD=20' }
|
||||
- { name: 'conf', build: 'Release', components: 'core,python,bin,view,render,test,axcore,axtest', cmake: '-DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Enable Node 16
|
||||
run: |
|
||||
echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
- name: pybind11
|
||||
#if: contains(container.image, '2023') == false
|
||||
@@ -204,7 +198,7 @@ jobs:
|
||||
# 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: '-D CMAKE_CXX_COMPILER=/usr/local/opt/llvm@15/bin/clang++' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -214,7 +208,6 @@ jobs:
|
||||
sudo apt-get -q install -y libboost-dev libboost-iostreams-dev libtbb-dev libblosc-dev llvm-dev libgtest-dev libcppunit-dev pybind11-dev
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
./ci/install_macos.sh 15
|
||||
./ci/install_tbb_macos.sh
|
||||
else
|
||||
echo "$RUNNER_OS not supported"; exit 1
|
||||
fi
|
||||
@@ -222,66 +215,11 @@ jobs:
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--build-type=Release
|
||||
--components=\"core,axcore,python,bin,render,test,axbin\"
|
||||
--components=\"core,axcore,python,bin,render,test,axtest,axbin\"
|
||||
--cargs=\"-DCMAKE_CXX_STANDARD=20 -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install ${{ matrix.config.cmake }}\"
|
||||
- name: test
|
||||
run: cd build && ctest -V
|
||||
|
||||
windows:
|
||||
# Windows CI. Tests static and dynamic builds with MT and MD respectively.
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'win'
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'windows-2022-8c-32g-300h') || 'windows-latest' }}
|
||||
name: windows-vc:${{ matrix.config.vc }}-type:${{ matrix.config.build }}
|
||||
env:
|
||||
VCPKG_DEFAULT_TRIPLET: ${{ matrix.config.vc }}
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
# static build of blosc from vcpkg does not build internal sources.
|
||||
# USE_STATIC_DEPENDENCIES is required for IlmBase/OpenEXR defines and
|
||||
# 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', 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' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: path
|
||||
shell: pwsh
|
||||
run: |
|
||||
# note: system path must be modified in a previous step to it's use
|
||||
echo "$Env:VCPKG_INSTALLATION_ROOT\installed\${{ matrix.config.vc }}\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "${{github.workspace}}\build\openvdb\openvdb\${{ matrix.config.build }}" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- name: install
|
||||
shell: powershell
|
||||
run: .\ci\install_windows.ps1
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--config=${{ matrix.config.build }}
|
||||
--components=${{ matrix.config.components }}
|
||||
--cargs=\'
|
||||
${{ matrix.config.cmake }}
|
||||
-DMSVC_COMPRESS_PDB=ON
|
||||
-DUSE_EXR=ON
|
||||
-DUSE_PNG=ON
|
||||
-DVCPKG_TARGET_TRIPLET=${VCPKG_DEFAULT_TRIPLET}
|
||||
-DCMAKE_TOOLCHAIN_FILE=\"${VCPKG_INSTALLATION_ROOT}\\scripts\\buildsystems\\vcpkg.cmake\"
|
||||
\'
|
||||
- name: size
|
||||
# Print the build directy size (monitor if we're hitting runner limits)
|
||||
run: du -h build
|
||||
- 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'
|
||||
run: cd build && ctest -V -C ${{ matrix.config.build }}
|
||||
|
||||
#############################################################################
|
||||
############################ AX Library Extras ##############################
|
||||
#############################################################################
|
||||
@@ -302,20 +240,17 @@ jobs:
|
||||
matrix:
|
||||
config:
|
||||
# Unified
|
||||
- { image: '2023-clang14', cxx: 'clang++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' }
|
||||
- { image: '2023-clang14', cxx: 'g++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' }
|
||||
- { image: '2022-clang14', cxx: 'clang++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' }
|
||||
- { image: '2022-clang14', cxx: 'g++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' }
|
||||
- { image: '2022-clang13', cxx: 'clang++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' }
|
||||
- { image: '2022-clang13', cxx: 'g++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' }
|
||||
# Standalone
|
||||
- { image: '2021-clang10', cxx: 'clang++', build: 'Release', components: 'core', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' }
|
||||
- { image: '2022-clang11', cxx: 'clang++', build: 'Debug', components: 'core', cmake: '' }
|
||||
- { image: '2022-clang11', cxx: 'clang++', build: 'Release', components: 'core', cmake: '' }
|
||||
- { image: '2022-clang11', cxx: 'g++', build: 'Release', components: 'core', cmake: '' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Enable Node 16
|
||||
if: contains(matrix.config.image, '2022')
|
||||
run: |
|
||||
echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
- name: pybind11
|
||||
#f: contains(matrix.config.image, '2023') == false
|
||||
@@ -356,7 +291,7 @@ jobs:
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'ax'
|
||||
runs-on: macos-13
|
||||
runs-on: macos-latest
|
||||
name: macos-cxx:${{ matrix.config.cxx }}-llvm:${{ matrix.config.llvm }}-${{ matrix.config.build }}
|
||||
env:
|
||||
CXX: ${{ matrix.config.cxx }}
|
||||
@@ -370,9 +305,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install_deps
|
||||
run: |
|
||||
./ci/install_macos.sh ${{ matrix.config.llvm }}
|
||||
./ci/install_tbb_macos.sh
|
||||
run: ./ci/install_macos.sh ${{ matrix.config.llvm }}
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
@@ -381,8 +314,7 @@ jobs:
|
||||
--cargs=\"
|
||||
-DOPENVDB_AX_TEST_CMD_DOWNLOADS=ON
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF
|
||||
-DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install
|
||||
-DLLVM_DIR=/opt/homebrew/opt/llvm@${{ matrix.config.llvm }}/lib/cmake/llvm
|
||||
-DLLVM_DIR=/usr/local/opt/llvm@${{ matrix.config.llvm }}/lib/cmake/llvm
|
||||
\"
|
||||
- name: test
|
||||
run: cd build && ctest -V
|
||||
@@ -418,8 +350,7 @@ jobs:
|
||||
- name: llvm
|
||||
run: ./ci/install_llvm_windows.sh ${{ matrix.config.crt }}
|
||||
- name: install
|
||||
shell: powershell
|
||||
run: .\ci\install_windows.ps1
|
||||
run: ./ci/install_windows.sh
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
@@ -449,64 +380,6 @@ jobs:
|
||||
################################## Blosc ####################################
|
||||
#############################################################################
|
||||
|
||||
windows-nanovdb:
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'win'
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'windows-2022-8c-32g-300h') || 'windows-latest' }}
|
||||
env:
|
||||
VCPKG_DEFAULT_TRIPLET: ${{ matrix.config.vc }}
|
||||
visual_studio: "Visual Studio 17 2022"
|
||||
cuda: "12.4.0"
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
# static build of blosc from vcpkg does not build internal sources.
|
||||
# USE_STATIC_DEPENDENCIES is required for IlmBase/OpenEXR defines and
|
||||
# Boost as both shared and static libs are installed.
|
||||
- { vc: 'x64-windows-static', build: 'Release', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded' }
|
||||
- { vc: 'x64-windows-static', build: 'Debug', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug' }
|
||||
- { vc: 'x64-windows', build: 'Release', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF' }
|
||||
- { vc: 'x64-windows', build: 'Debug', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: path
|
||||
shell: powershell
|
||||
run: |
|
||||
# note: system path must be modified in a previous step to it's use
|
||||
echo "$Env:VCPKG_INSTALLATION_ROOT\installed\${{ matrix.config.vc }}\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "${{github.workspace}}\build\openvdb\openvdb\${{ matrix.config.build }}" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- name: install_cuda
|
||||
shell: powershell
|
||||
run: .\ci\install_windows_cuda.ps1
|
||||
- name: install
|
||||
shell: powershell
|
||||
run: .\ci\install_windows.ps1
|
||||
- name: build
|
||||
shell: bash
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--config=${{ matrix.config.build }}
|
||||
--components=core,nano,nanotest,nanoexam,nanobench,nanotool
|
||||
--cargs=\'
|
||||
${{ matrix.config.cmake }}
|
||||
-DMSVC_COMPRESS_PDB=ON
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF
|
||||
-DNANOVDB_USE_CUDA=ON
|
||||
-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.*"
|
||||
|
||||
#############################################################################
|
||||
################################## Blosc ####################################
|
||||
#############################################################################
|
||||
|
||||
linux-blosc:
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
@@ -521,9 +394,6 @@ jobs:
|
||||
blosc: ['1.18.0','1.19.0','1.20.0','1.21.0']
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Enable Node 16
|
||||
run: |
|
||||
echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
- name: install_blosc
|
||||
run: sudo ./ci/install_blosc.sh ${{ matrix.blosc }}
|
||||
@@ -550,9 +420,6 @@ jobs:
|
||||
# may not have this build type. See OpenVDBCXX.cmake
|
||||
CXXFLAGS: "-gdwarf-4 -g3 -ggdb -Og"
|
||||
steps:
|
||||
- name: Enable Node 16
|
||||
run: |
|
||||
echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -36,5 +36,5 @@ jobs:
|
||||
- name: test
|
||||
run: |
|
||||
set +e
|
||||
git grep -n " " -- ':!*/whitespace.yml' ':!tsc/meetings/*' ':!*.svg' ':!*.cmd' ':!*.png' ':!pendingchanges/*' ':!*.wlt' ':!*.jpg' ':!*.gif' ':!*.mp4' ':!*.pt' ':!*.pth' ':!*.nvdb' ':!*.npz'
|
||||
git grep -n " " -- ':!*/whitespace.yml' ':!tsc/meetings/*' ':!*.svg' ':!*.cmd' ':!*.png' ':!pendingchanges/*' ':!*.wlt'
|
||||
test $? -eq 1
|
||||
|
||||
+14
-17
@@ -57,20 +57,18 @@ code and has the right to release it under the
|
||||
[Mozilla Public License, version 2.0](LICENSE.md)
|
||||
license. See the [TAC documentation on contribution sign off](https://github.com/AcademySoftwareFoundation/tac/blob/master/process/contributing.md#contribution-sign-off) for more information on this requirement.
|
||||
|
||||
## Maintainer
|
||||
## Committer
|
||||
|
||||
The maintainer role is the equivalent of the "Committer" role in the charter.
|
||||
The committer role enables the participant to commit code directly to the repository, but also comes with the obligation to be a responsible leader in the community.
|
||||
|
||||
This role enables the participant to commit code directly to the repository, but also comes with the obligation to be a responsible leader in the community.
|
||||
|
||||
### Process for becoming a maintainer
|
||||
### Process for becoming a committer
|
||||
|
||||
* Show your experience with the codebase through contributions and engagement on the community channels.
|
||||
* Request to become a maintainer.
|
||||
* Have the majority of maintainers approve you becoming a maintainer.
|
||||
* Request to become a committer.
|
||||
* Have the majority of committers approve you becoming a committer.
|
||||
* Your name and email is added to the MAINTAINERS.md file for the project.
|
||||
|
||||
### Maintainer responsibilities
|
||||
### Committer responsibilities
|
||||
|
||||
* Monitor email aliases.
|
||||
* Monitor Slack (delayed response is perfectly acceptable).
|
||||
@@ -78,29 +76,28 @@ This role enables the participant to commit code directly to the repository, but
|
||||
* Make sure that ongoing PRs are moving forward at the right pace or close them.
|
||||
* Remain an active contributor to the project in general and the code base in particular.
|
||||
|
||||
### When does a maintainer lose maintainer status?
|
||||
### When does a committer lose committer status?
|
||||
|
||||
If a maintainer is no longer interested or cannot perform the maintainer duties listed above, they
|
||||
If a committer is no longer interested or cannot perform the committer duties listed above, they
|
||||
should volunteer to be moved to emeritus status. In extreme cases this can also occur by a vote of
|
||||
the maintainers per the voting process below.
|
||||
the committers per the voting process below.
|
||||
|
||||
## Technical Steering Committee (TSC) member
|
||||
|
||||
The Technical Steering Committee (TSC) oversees the overall technical direction of OpenVDB, as defined in the [charter](charter.md).
|
||||
|
||||
TSC voting members consist of maintainers that have been nominated by the TSC, with a supermajority of voting members required to have a maintainer elected to be a TSC voting member. TSC voting members term and succession is defined in the [charter](charter.md).
|
||||
TSC voting members consist of committers that have been nominated by the committers, with a supermajority of voting members required to have a committer elected to be a TSC voting member. TSC voting members term and succession is defined in the [charter](charter.md).
|
||||
|
||||
All meetings of the TSC are open to participation by any member of the OpenVDB community. Meeting times are listed in the [ASWF technical community calendar](https://lists.aswf.io/g/tac/calendar).
|
||||
|
||||
## Current TSC members
|
||||
|
||||
* Ken Museth (Chair), Nvidia
|
||||
* Andre Pradhana, Nvidia
|
||||
* Ken Museth, Chair / NVIDIA
|
||||
* Andre Pradhana, DreamWorks
|
||||
* Jeff Lait, SideFX
|
||||
* Nick Avramoussis, WETA FX
|
||||
* Nick Avramoussis, WETA
|
||||
* Dan Bailey, ILM
|
||||
* Richard Jones, ILM
|
||||
* Gregory Hurst, United Therapeutics
|
||||
* Richard Jones, DNEG
|
||||
|
||||
# Release Process
|
||||
|
||||
|
||||
@@ -14,5 +14,3 @@ The current OpenVDB maintainers are:
|
||||
| Ken Museth | ken.museth@gmail.com
|
||||
| Andre Pradhana | andre.pradhana@gmail.com
|
||||
| Richard Jones | richardj@ilm.com
|
||||
| Gregory Hurst | tbd
|
||||
| Jonathan Swartz | tbd
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
The following copyright holders agree that all of their contributions
|
||||
originally submitted to this project under the Mozilla Public License
|
||||
Version 2.0, are hereby relicensed to the Apache License, Version 2.0,
|
||||
and are submitted pursuant to the Developer Certificate of Origin, version 1.1:
|
||||
|
||||
Ken Museth
|
||||
Mehdi Chinoune
|
||||
DreamWorks Animation
|
||||
Side Effects Software Inc.
|
||||
Blender Foundation
|
||||
NVIDIA Corporation
|
||||
United Therapeutics Corporation
|
||||
Digital Domain 3.0, Inc.
|
||||
Double Negative
|
||||
Ubisoft Entertainment SA
|
||||
Adobe Inc.
|
||||
Mathieu Malaterre
|
||||
Brecht Sanders
|
||||
Ignacio Vizzo
|
||||
Ben FrantzDale
|
||||
Sebastian Gaida
|
||||
Alessio Quaglino
|
||||
Benedikt Mersch
|
||||
David Aguilar
|
||||
Brian Sharpe
|
||||
Kartik Shrivastava
|
||||
Michael Lackner
|
||||
Lucas Baraya
|
||||
Kuba Roth
|
||||
Tom Cnops
|
||||
Walt Disney Pictures (Walt Disney Animation Studios)
|
||||
The Linux Foundation
|
||||
Industrial Light & Magic (ILM)
|
||||
@@ -35,8 +35,6 @@ Mozilla Foundation.
|
||||
The trademarks of any contributor to this project may not be used in
|
||||
association with the project without the contributor's express permission.
|
||||
|
||||
NOTE: OpenVDB is in the process of changing its license from [Mozilla Public License Version 2.0](https://www.mozilla.org/MPL/2.0/) to [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)! Please see the file RE-LICENSE_NOTE.txt for more details.
|
||||
|
||||
### Contributing
|
||||
|
||||
OpenVDB welcomes contributions to the OpenVDB project. Please refer to the
|
||||
|
||||
@@ -184,6 +184,7 @@ set -x
|
||||
# - 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_9=ON \
|
||||
-DOPENVDB_USE_DEPRECATED_ABI_10=ON \
|
||||
-DOPENVDB_BUILD_VDB_PRINT=ON \
|
||||
-DOPENVDB_BUILD_VDB_LOD=ON \
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
CURL_VERSION="$1"
|
||||
|
||||
wget -O cppunit.tar.gz https://dev-www.libreoffice.org/src/cppunit-${CURL_VERSION}.tar.gz
|
||||
|
||||
tar -xzf cppunit.tar.gz
|
||||
|
||||
cd cppunit-${CURL_VERSION}
|
||||
|
||||
./configure
|
||||
|
||||
make -j8
|
||||
make install
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
GLFW_VERSION="$1"
|
||||
|
||||
git clone https://github.com/glfw/glfw.git
|
||||
cd glfw
|
||||
|
||||
if [ "$GLFW_VERSION" != "latest" ]; then
|
||||
git checkout tags/${GLFW_VERSION} -b ${GLFW_VERSION}
|
||||
fi
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake ..
|
||||
|
||||
make -j8
|
||||
make install
|
||||
+1
-12
@@ -2,13 +2,6 @@
|
||||
|
||||
set -x
|
||||
|
||||
# Remove Python3 symlinks in /usr/local/bin as workaround to brew update issues
|
||||
# https://github.com/actions/setup-python/issues/577
|
||||
rm /usr/local/bin/2to3* || :
|
||||
rm /usr/local/bin/idle3* || :
|
||||
rm /usr/local/bin/pydoc* || :
|
||||
rm /usr/local/bin/python3* || :
|
||||
|
||||
brew update
|
||||
brew install bash gnu-getopt # for CI scripts
|
||||
brew install boost
|
||||
@@ -19,6 +12,7 @@ brew install googletest
|
||||
brew install jq # for trivial parsing of brew json
|
||||
brew install openexr
|
||||
brew install pybind11 # also installs the dependent python version
|
||||
brew install tbb
|
||||
brew install zlib
|
||||
brew install jemalloc
|
||||
|
||||
@@ -30,13 +24,8 @@ echo "Using python $py_version"
|
||||
echo "Python_ROOT_DIR=/usr/local/opt/$py_version" >> $GITHUB_ENV
|
||||
echo "/usr/local/opt/$py_version/bin" >> $GITHUB_PATH
|
||||
|
||||
# use bash
|
||||
echo "/usr/local/opt/bash/bin" >> $GITHUB_PATH
|
||||
echo "/opt/homebrew/opt/bash/bin" >> $GITHUB_PATH
|
||||
|
||||
# use gnu-getopt
|
||||
echo "/usr/local/opt/gnu-getopt/bin" >> $GITHUB_PATH
|
||||
echo "/opt/homebrew/opt/gnu-getopt/bin" >> $GITHUB_PATH
|
||||
|
||||
LLVM_VERSION=$1
|
||||
if [ ! -z "$LLVM_VERSION" ]; then
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
brew update
|
||||
brew install tbb
|
||||
@@ -1,40 +0,0 @@
|
||||
# Enable verbose and stop on error
|
||||
$ErrorActionPreference = "Stop"
|
||||
$VerbosePreference = "Continue"
|
||||
|
||||
# Required dependencies
|
||||
$vcpkgPackages = @(
|
||||
"zlib",
|
||||
"libpng",
|
||||
"openexr",
|
||||
"tbb",
|
||||
"gtest",
|
||||
"cppunit",
|
||||
"blosc",
|
||||
"glfw3",
|
||||
"glew",
|
||||
"python3",
|
||||
"jemalloc",
|
||||
"boost-iostreams",
|
||||
"boost-interprocess",
|
||||
"boost-algorithm",
|
||||
"pybind11"
|
||||
)
|
||||
|
||||
# Update vcpkg
|
||||
vcpkg update
|
||||
|
||||
# Allow the vcpkg command to fail once so we can retry with the latest
|
||||
try {
|
||||
vcpkg install $vcpkgPackages
|
||||
} catch {
|
||||
Write-Host "vcpkg install failed, retrying with latest ports..."
|
||||
# Retry the installation with updated ports
|
||||
Push-Location $env:VCPKG_INSTALLATION_ROOT
|
||||
git pull
|
||||
Pop-Location
|
||||
vcpkg update
|
||||
vcpkg install $vcpkgPackages
|
||||
}
|
||||
|
||||
Write-Host "vcpkg install completed successfully"
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
vcpkg update
|
||||
vcpkg install zlib libpng openexr tbb gtest cppunit blosc glfw3 glew python3 jemalloc \
|
||||
boost-iostreams boost-interprocess boost-algorithm pybind11 \
|
||||
--clean-after-build
|
||||
@@ -26,13 +26,12 @@ $CUDA_KNOWN_URLS = @{
|
||||
"11.2.1" = "https://developer.download.nvidia.com/compute/cuda/11.2.1/network_installers/cuda_11.2.1_win10_network.exe";
|
||||
"11.2.2" = "https://developer.download.nvidia.com/compute/cuda/11.2.2/network_installers/cuda_11.2.2_win10_network.exe";
|
||||
"11.3.0" = "https://developer.download.nvidia.com/compute/cuda/11.3.0/network_installers/cuda_11.3.0_win10_network.exe";
|
||||
"11.6.2" = "https://developer.download.nvidia.com/compute/cuda/11.6.2/network_installers/cuda_11.6.2_windows_network.exe";
|
||||
"12.4.0" = "https://developer.download.nvidia.com/compute/cuda/12.4.0/network_installers/cuda_12.4.0_windows_network.exe"
|
||||
"11.6.2" = "https://developer.download.nvidia.com/compute/cuda/11.6.2/network_installers/cuda_11.6.2_windows_network.exe"
|
||||
}
|
||||
|
||||
# @todo - change this to be based on _MSC_VER intead, or invert it to be CUDA keyed instead?
|
||||
$VISUAL_STUDIO_MIN_CUDA = @{
|
||||
"2022" = "12.4";
|
||||
"2022" = "11.6";
|
||||
"2019" = "10.1";
|
||||
"2017" = "10.0"; # Depends on which version of 2017! 9.0 to 10.0 depending on version
|
||||
"2015" = "8.0"; # might support older, unsure.
|
||||
|
||||
@@ -188,6 +188,22 @@ list(APPEND _BLOSC_LIBRARYDIR_SEARCH_DIRS
|
||||
${SYSTEM_LIBRARY_PATHS}
|
||||
)
|
||||
|
||||
# Library suffix handling
|
||||
|
||||
set(_BLOSC_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
set(_BLOSC_ORIG_CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES})
|
||||
|
||||
if(MSVC)
|
||||
if(BLOSC_USE_STATIC_LIBS)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
|
||||
set(CMAKE_FIND_LIBRARY_PREFIXES "${CMAKE_FIND_LIBRARY_PREFIXES};lib")
|
||||
endif()
|
||||
else()
|
||||
if(BLOSC_USE_STATIC_LIBS)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(Blosc_LIB_COMPONENTS "")
|
||||
# NOTE: Search for debug version first (see vcpkg hack)
|
||||
list(APPEND BLOSC_BUILD_TYPES DEBUG RELEASE)
|
||||
@@ -230,6 +246,13 @@ foreach(BUILD_TYPE ${BLOSC_BUILD_TYPES})
|
||||
set(CMAKE_IGNORE_PATH ${_BLOSC_CMAKE_IGNORE_PATH})
|
||||
endforeach()
|
||||
|
||||
# Reset library suffix
|
||||
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_BLOSC_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
set(CMAKE_FIND_LIBRARY_PREFIXES ${_BLOSC_ORIG_CMAKE_FIND_LIBRARY_PREFIXES})
|
||||
unset(_BLOSC_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES)
|
||||
unset(_BLOSC_ORIG_CMAKE_FIND_LIBRARY_PREFIXES)
|
||||
|
||||
if(Blosc_LIBRARY_DEBUG AND Blosc_LIBRARY_RELEASE)
|
||||
# if the generator is multi-config or if CMAKE_BUILD_TYPE is set for
|
||||
# single-config generators, set optimized and debug libraries
|
||||
|
||||
+27
-5
@@ -491,11 +491,37 @@ endif()
|
||||
# Add standard dependencies
|
||||
|
||||
find_package(TBB REQUIRED COMPONENTS tbb)
|
||||
find_package(Boost REQUIRED COMPONENTS iostreams)
|
||||
|
||||
# Add deps for pyopenvdb
|
||||
# @todo track for numpy
|
||||
|
||||
if(pyopenvdb IN_LIST OpenVDB_FIND_COMPONENTS)
|
||||
find_package(Python REQUIRED)
|
||||
|
||||
# Boost python handling - try and find both python and pythonXx (version suffixed).
|
||||
# Prioritize the version suffixed library, failing if neither exist.
|
||||
|
||||
find_package(Boost ${MINIMUM_BOOST_VERSION}
|
||||
QUIET COMPONENTS python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}
|
||||
)
|
||||
|
||||
if(TARGET Boost::python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR})
|
||||
set(BOOST_PYTHON_LIB "python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}")
|
||||
message(STATUS "Found boost_python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}")
|
||||
else()
|
||||
find_package(Boost ${MINIMUM_BOOST_VERSION} QUIET COMPONENTS python)
|
||||
if(TARGET Boost::python)
|
||||
set(BOOST_PYTHON_LIB "python")
|
||||
message(STATUS "Found non-suffixed boost_python, assuming to be python version "
|
||||
"\"${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}\" compatible"
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "Unable to find boost_python or "
|
||||
"boost_python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}."
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Add deps for openvdb_ax
|
||||
@@ -630,10 +656,6 @@ if(OpenVDB_USES_IMATH_HALF)
|
||||
find_package(Imath REQUIRED CONFIG)
|
||||
endif()
|
||||
|
||||
if(OpenVDB_USES_DELAYED_LOADING)
|
||||
find_package(Boost REQUIRED COMPONENTS iostreams)
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
@@ -744,7 +766,7 @@ if(OpenVDB_pyopenvdb_LIBRARY)
|
||||
set_target_properties(OpenVDB::pyopenvdb PROPERTIES
|
||||
IMPORTED_LOCATION "${OpenVDB_pyopenvdb_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OpenVDB_pyopenvdb_INCLUDE_DIR};${PYTHON_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES "OpenVDB::openvdb;${PYTHON_LIBRARIES}"
|
||||
INTERFACE_LINK_LIBRARIES "OpenVDB::openvdb;Boost::${BOOST_PYTHON_LIB};${PYTHON_LIBRARIES}"
|
||||
INTERFACE_COMPILE_FEATURES cxx_std_17
|
||||
)
|
||||
endif()
|
||||
|
||||
+1
-1
@@ -267,7 +267,7 @@ PyBind11 | C++/python bindings
|
||||
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
|
||||
At a minimum, boost, a matching C++17 compiler and CMake will be required. See
|
||||
the full [dependency list](@ref dependencies) for help with downloading and
|
||||
installing the above software. Note that as Blosc and ZLib are provided as part
|
||||
of the Houdini installation `USE_BLOSC` and `USE_ZLIB` should be left `ON`.
|
||||
|
||||
@@ -36,7 +36,7 @@ Reference Platform, but for those that do, their specified versions are
|
||||
|
||||
Component | Requirements | Optional
|
||||
----------------------- | ----------------------------------------------- | --------
|
||||
OpenVDB Core Library | CMake, C++17 compiler, TBB::tbb | Blosc, ZLib, Log4cplus, Imath::Imath, Boost::iostream
|
||||
OpenVDB Core Library | CMake, C++17 compiler, TBB::tbb, Boost::headers | Blosc, ZLib, Log4cplus, Imath::Imath, Boost::iostream
|
||||
OpenVDB Print | Core Library dependencies | -
|
||||
OpenVDB LOD | Core Library dependencies | -
|
||||
OpenVDB Render | Core Library dependencies | OpenEXR, Imath::Imath, libpng
|
||||
@@ -65,7 +65,7 @@ Imath | 3.1 | Latest | Half precision floating points
|
||||
OpenEXR | 3.1 | Latest | EXR serialization support | Y | Y | http://www.openexr.com
|
||||
TBB | 2020.2 | 2020.3 | 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.73 | 1.80 | Components: iostreams | Y | Y | https://www.boost.org
|
||||
Boost | 1.73 | 1.80 | Components: headers, iostreams | Y | Y | https://www.boost.org
|
||||
LLVM | 10.0.0 | 13.0.0* | Target-independent code generation | Y | Y | https://llvm.org/
|
||||
Bison | 3.0.0 | 3.7.0 | General-purpose parser generator | Y | Y | https://www.gnu.org/software/gcc
|
||||
Flex | 2.6.0 | 2.6.4 | Fast lexical analyzer generator | Y | Y | https://github.com/westes/flex
|
||||
|
||||
+23
-52
@@ -5,12 +5,12 @@
|
||||
* [NanoVDB.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/NanoVDB.h) C++11 implementation of the core data structure and its access methods.
|
||||
* [CNanoVDB.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/CNanoVDB.h) Incomplete but useable C99 implementation of the core data structure and its access methods. Designed in particular for use in OpenCL kernels. Note that this relies on zero-sized arrays for the _reserved padding, so will not work on all compilers (with MSVC being a particular example)
|
||||
* [PNanoVDB.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/PNanoVDB.h) C99 implementation of the core data structure and its access methods. More complete coverage than CNanoVDB. This version is pointer-less and supports virtually all graphics APIs.
|
||||
* [GridHandle.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/GridHandle.h) defines a handler for the memory allocated to a NanoVDB grid.
|
||||
* [io/IO.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/io/IO.h) implements I/O support.
|
||||
* [tools/CreateNanoGrid.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/tools/CreateNanoGrid.h) defines the converter from OpenVDB to NanoVDB and obviously depends on the OpenVDB library (as the only header file).
|
||||
* [math/Ray.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/math/Ray.h) Ray class.
|
||||
* [math/HDDA.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/math/HDDA.h) HDDA related.
|
||||
* [math/SampleFromVoxels.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/math/SampleFromVoxels.h) interpolation.
|
||||
* [util/GridHandle.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/util/GridHandle.h) defines a handler for the memory allocated to a NanoVDB grid.
|
||||
* [util/IO.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/util/IO.h) implements I/O support.
|
||||
* [util/OpenToNanoVDB.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/util/OpenToNanoVDB.h) defines the converter from OpenVDB to NanoVDB and obviously depends on the OpenVDB library (as the only header file).
|
||||
* [Ray.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/util/Ray.h) Ray class.
|
||||
* [HDDA.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/util/HDDA.h) HDDA related.
|
||||
* [SampleFromVoxels.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/util/SampleFromVoxels.h) interpolation.
|
||||
|
||||
```bash
|
||||
foo@bar:~$ tree
|
||||
@@ -22,19 +22,25 @@ foo@bar:~$ tree
|
||||
│ │ └── nanovdb_convert.cc
|
||||
│ ├── print
|
||||
│ │ └── nanovdb_print.cc
|
||||
│ ├── updateFiles.sh
|
||||
│ └── validate
|
||||
│ └── nanovdb_validate.cc
|
||||
├── CNanoVDB.h
|
||||
├── cuda
|
||||
│ ├── DeviceBuffer.h
|
||||
│ ├── GridHandle.cuh
|
||||
│ └── NodeManager.cuh
|
||||
├── docs
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── codingstyle.txt
|
||||
│ └── doxygen-config
|
||||
├── examples
|
||||
│ ├── benchmark
|
||||
│ │ ├── BenchKernels_dense.cu
|
||||
│ │ ├── BenchKernels_nano.cu
|
||||
│ │ ├── Benchmark_dense.cu
|
||||
│ │ ├── Benchmark_nano.cu
|
||||
│ │ ├── Camera.h
|
||||
│ │ ├── CMakeLists.txt
|
||||
│ │ ├── DenseGrid.h
|
||||
│ │ ├── Image.h
|
||||
│ │ ├── TestBenchmark.cc
|
||||
│ │ └── TestBenchmark.cu
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── ex_bump_pool_buffer
|
||||
│ │ └── bump_pool_buffer.cc
|
||||
@@ -44,7 +50,7 @@ foo@bar:~$ tree
|
||||
│ │ ├── nanovdb.cu
|
||||
│ │ └── openvdb.cc
|
||||
│ ├── ex_index_grid_cuda
|
||||
│ │ ├── index_grid_cuda.cc
|
||||
│ │ ├── index_grid_cuda.cu
|
||||
│ │ └── index_grid_cuda_kernel.cu
|
||||
│ ├── ex_make_custom_nanovdb
|
||||
│ │ └── make_custom_nanovdb.cc
|
||||
@@ -60,7 +66,6 @@ foo@bar:~$ tree
|
||||
│ ├── ex_map_pool_buffer
|
||||
│ │ └── map_pool_buffer.cc
|
||||
│ ├── ex_modify_nanovdb_thrust
|
||||
│ │ ├── modify_nanovdb_thrust.cc
|
||||
│ │ └── modify_nanovdb_thrust.cu
|
||||
│ ├── ex_nodemanager_cuda
|
||||
│ │ ├── nodemanager_cuda.cc
|
||||
@@ -98,38 +103,9 @@ foo@bar:~$ tree
|
||||
│ │ └── VoxToNanoVDB.h
|
||||
│ └── ex_write_nanovdb_grids
|
||||
│ └── write_nanovdb_grids.cc
|
||||
├── GridHandle.h
|
||||
├── HostBuffer.h
|
||||
├── io
|
||||
│ └── IO.h
|
||||
├── math
|
||||
│ ├── CSampleFromVoxels.h
|
||||
│ ├── DitherLUT.h
|
||||
│ ├── HDDA.h
|
||||
│ ├── Math.h
|
||||
│ ├── Ray.h
|
||||
│ ├── SampleFromVoxels.h
|
||||
│ └── Stencils.h
|
||||
├── NanoVDB.h
|
||||
├── NodeManager.h
|
||||
├── PNanoVDB.h
|
||||
├── Readme.md
|
||||
├── tools
|
||||
│ ├── CreateNanoGrid.h
|
||||
│ ├── CreatePrimitives.h
|
||||
│ ├── cuda
|
||||
│ │ ├── AddBlindData.cuh
|
||||
│ │ ├── GridChecksum.cuh
|
||||
│ │ ├── GridStats.cuh
|
||||
│ │ ├── GridValidator.cuh
|
||||
│ │ ├── IndexToGrid.cuh
|
||||
│ │ ├── PointsToGrid.cuh
|
||||
│ │ └── SignedFloodFill.cuh
|
||||
│ ├── GridBuilder.h
|
||||
│ ├── GridChecksum.h
|
||||
│ ├── GridStats.h
|
||||
│ ├── GridValidator.h
|
||||
│ └── NanoToOpenVDB.h
|
||||
├── unittest
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── pnanovdb_validate_strides.h
|
||||
@@ -139,25 +115,21 @@ foo@bar:~$ tree
|
||||
└── util
|
||||
├── CpuTimer.h
|
||||
├── CreateNanoGrid.h
|
||||
├── CSampleFromVoxels.h
|
||||
├── cuda
|
||||
│ ├── CudaAddBlindData.cuh
|
||||
│ ├── CudaDeviceBuffer.h
|
||||
│ ├── CudaGridChecksum.cuh
|
||||
│ ├── CudaGridHandle.cuh
|
||||
│ ├── CudaGridStats.cuh
|
||||
│ ├── CudaGridValidator.cuh
|
||||
│ ├── CudaIndexToGrid.cuh
|
||||
│ ├── CudaNodeManager.cuh
|
||||
│ ├── CudaPointsToGrid.cuh
|
||||
│ ├── CudaSignedFloodFill.cuh
|
||||
│ ├── CudaUtils.h
|
||||
│ ├── GpuTimer.h
|
||||
│ ├── Timer.h
|
||||
│ └── Util.h
|
||||
│ └── GpuTimer.cuh
|
||||
├── DitherLUT.h
|
||||
├── ForEach.h
|
||||
├── GridBuilder.h
|
||||
├── GridChecksum.h
|
||||
├── GridHandle.h
|
||||
├── GridStats.h
|
||||
├── GridValidator.h
|
||||
├── HDDA.h
|
||||
@@ -173,6 +145,5 @@ foo@bar:~$ tree
|
||||
├── Ray.h
|
||||
├── Reduce.h
|
||||
├── SampleFromVoxels.h
|
||||
├── Stencils.h
|
||||
├── Timer.h
|
||||
└── Util.h
|
||||
└── Stencils.h
|
||||
```
|
||||
|
||||
@@ -160,66 +160,31 @@ endif()
|
||||
# NanoVDB header files
|
||||
set(NANOVDB_INCLUDE_FILES
|
||||
CNanoVDB.h
|
||||
GridHandle.h
|
||||
HostBuffer.h
|
||||
NanoVDB.h
|
||||
NodeManager.h
|
||||
PNanoVDB.h
|
||||
)
|
||||
|
||||
# NanoVDB cuda header files
|
||||
set(NANOVDB_INCLUDE_CUDA_FILES
|
||||
cuda/DeviceBuffer.h
|
||||
cuda/GridHandle.cuh
|
||||
cuda/NodeManager.cuh
|
||||
)
|
||||
|
||||
# NanoVDB io header files
|
||||
set(NANOVDB_INCLUDE_IO_FILES
|
||||
io/IO.h
|
||||
)
|
||||
|
||||
# NanoVDB math header files
|
||||
set(NANOVDB_INCLUDE_MATH_FILES
|
||||
math/CSampleFromVoxels.h
|
||||
math/DitherLUT.h
|
||||
math/HDDA.h
|
||||
math/Math.h
|
||||
math/Ray.h
|
||||
math/SampleFromVoxels.h
|
||||
math/Stencils.h
|
||||
)
|
||||
|
||||
# NanoVDB tools header files
|
||||
set(NANOVDB_INCLUDE_TOOLS_FILES
|
||||
tools/CreateNanoGrid.h
|
||||
tools/CreatePrimitives.h
|
||||
tools/GridBuilder.h
|
||||
tools/GridChecksum.h
|
||||
tools/GridStats.h
|
||||
tools/GridValidator.h
|
||||
tools/NanoToOpenVDB.h
|
||||
)
|
||||
|
||||
# NanoVDB tools/cuda header files
|
||||
set(NANOVDB_INCLUDE_TOOLS_CUDA_FILES
|
||||
tools/cuda/AddBlindData.cuh
|
||||
tools/cuda/GridChecksum.cuh
|
||||
tools/cuda/GridStats.cuh
|
||||
tools/cuda/GridValidator.cuh
|
||||
tools/cuda/IndexToGrid.cuh
|
||||
tools/cuda/PointsToGrid.cuh
|
||||
tools/cuda/SignedFloodFill.cuh
|
||||
)
|
||||
|
||||
# NanoVDB util header files
|
||||
set(NANOVDB_INCLUDE_UTIL_FILES
|
||||
set(NANOVDB_INCLUDE_UTILFILES
|
||||
util/CpuTimer.h
|
||||
util/CreateNanoGrid.h
|
||||
util/CSampleFromVoxels.h
|
||||
util/cuda/CudaAddBlindData.cuh
|
||||
util/cuda/CudaDeviceBuffer.h
|
||||
util/cuda/CudaGridChecksum.cuh
|
||||
util/cuda/CudaGridHandle.cuh
|
||||
util/cuda/CudaGridStats.cuh
|
||||
util/cuda/CudaIndexToGrid.cuh
|
||||
util/cuda/CudaNodeManager.cuh
|
||||
util/cuda/CudaPointsToGrid.cuh
|
||||
util/cuda/CudaSignedFloodFill.cuh
|
||||
util/cuda/CudaUtils.h
|
||||
util/cuda/GpuTimer.h
|
||||
util/DitherLUT.h
|
||||
util/ForEach.h
|
||||
util/GridBuilder.h
|
||||
util/GridChecksum.h
|
||||
util/GridHandle.h
|
||||
util/GridStats.h
|
||||
util/GridValidator.h
|
||||
util/HDDA.h
|
||||
@@ -236,26 +201,6 @@ set(NANOVDB_INCLUDE_UTIL_FILES
|
||||
util/Reduce.h
|
||||
util/SampleFromVoxels.h
|
||||
util/Stencils.h
|
||||
util/Timer.h
|
||||
util/Util.h
|
||||
)
|
||||
|
||||
# NanoVDB util/cuda header files
|
||||
set(NANOVDB_INCLUDE_UTIL_CUDA_FILES
|
||||
util/cuda/CudaAddBlindData.cuh
|
||||
util/cuda/CudaGridHandle.cuh
|
||||
util/cuda/CudaIndexToGrid.cuh
|
||||
util/cuda/CudaSignedFloodFill.cuh
|
||||
util/cuda/Timer.h
|
||||
util/cuda/CudaDeviceBuffer.h
|
||||
util/cuda/CudaGridStats.cuh
|
||||
util/cuda/CudaNodeManager.cuh
|
||||
util/cuda/CudaUtils.h
|
||||
util/cuda/Util.h
|
||||
util/cuda/CudaGridChecksum.cuh
|
||||
util/cuda/CudaGridValidator.cuh
|
||||
util/cuda/CudaPointsToGrid.cuh
|
||||
util/cuda/GpuTimer.h
|
||||
)
|
||||
|
||||
add_library(nanovdb INTERFACE)
|
||||
@@ -321,23 +266,11 @@ if(TARGET Threads::Threads)
|
||||
target_link_libraries(nanovdb INTERFACE Threads::Threads)
|
||||
endif()
|
||||
|
||||
set(NANOVDB_INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}/nanovdb)
|
||||
set(NANOVDB_INSTALL_CUDA_DIR ${NANOVDB_INSTALL_INCLUDE_DIR}/cuda)
|
||||
set(NANOVDB_INSTALL_IO_DIR ${NANOVDB_INSTALL_INCLUDE_DIR}/io)
|
||||
set(NANOVDB_INSTALL_MATH_DIR ${NANOVDB_INSTALL_INCLUDE_DIR}/math)
|
||||
set(NANOVDB_INSTALL_TOOLS_DIR ${NANOVDB_INSTALL_INCLUDE_DIR}/tools)
|
||||
set(NANOVDB_INSTALL_TOOLS_CUDA_DIR ${NANOVDB_INSTALL_TOOLS_DIR}/cuda)
|
||||
set(NANOVDB_INSTALL_UTIL_DIR ${NANOVDB_INSTALL_INCLUDE_DIR}/util)
|
||||
set(NANOVDB_INSTALL_UTIL_CUDA_DIR ${NANOVDB_INSTALL_UTIL_DIR}/cuda)
|
||||
set(NANOVDB_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR}/nanovdb)
|
||||
set(NANOVDB_INSTALL_UTILDIR ${NANOVDB_INSTALL_INCLUDEDIR}/util)
|
||||
|
||||
install(FILES ${NANOVDB_INCLUDE_FILES} DESTINATION ${NANOVDB_INSTALL_INCLUDE_DIR})
|
||||
install(FILES ${NANOVDB_INCLUDE_CUDA_FILES} DESTINATION ${NANOVDB_INSTALL_CUDA_DIR})
|
||||
install(FILES ${NANOVDB_INCLUDE_IO_FILES} DESTINATION ${NANOVDB_INSTALL_IO_DIR})
|
||||
install(FILES ${NANOVDB_INCLUDE_MATH_FILES} DESTINATION ${NANOVDB_INSTALL_MATH_DIR})
|
||||
install(FILES ${NANOVDB_INCLUDE_TOOLS_FILES} DESTINATION ${NANOVDB_INSTALL_TOOLS_DIR})
|
||||
install(FILES ${NANOVDB_INCLUDE_TOOLS_CUDA_FILES} DESTINATION ${NANOVDB_INSTALL_TOOLS_CUDA_DIR})
|
||||
install(FILES ${NANOVDB_INCLUDE_UTIL_FILES} DESTINATION ${NANOVDB_INSTALL_UTIL_DIR})
|
||||
install(FILES ${NANOVDB_INCLUDE_UTIL_CUDA_FILES} DESTINATION ${NANOVDB_INSTALL_UTIL_CUDA_DIR})
|
||||
install(FILES ${NANOVDB_INCLUDE_FILES} DESTINATION ${NANOVDB_INSTALL_INCLUDEDIR})
|
||||
install(FILES ${NANOVDB_INCLUDE_UTILFILES} DESTINATION ${NANOVDB_INSTALL_UTILDIR})
|
||||
|
||||
###############################################################################
|
||||
# Options
|
||||
|
||||
@@ -1,590 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
@file nanovdb/HostBuffer.h
|
||||
|
||||
@date April 20, 2021
|
||||
|
||||
@brief HostBuffer - a buffer that contains a shared or private bump
|
||||
pool to either externally or internally managed host memory.
|
||||
|
||||
@details This HostBuffer can be used in multiple ways, most of which are
|
||||
demonstrated in the examples below. Memory in the pool can
|
||||
be managed or unmanged (e.g. internal or external) and can
|
||||
be shared between multiple buffers or belong to a single buffer.
|
||||
|
||||
Example that uses HostBuffer::create inside io::readGrids to create a
|
||||
full self-managed buffer, i.e. not shared and without padding, per grid in the file.
|
||||
@code
|
||||
auto handles = nanovdb::io::readGrids("file.nvdb");
|
||||
@endcode
|
||||
|
||||
Example that uses HostBuffer::createFull. Assuming you have a raw pointer
|
||||
to a NanoVDB grid of unknown type, this examples shows how to create its
|
||||
GridHandle which can be used to enquire about the grid type and meta data.
|
||||
@code
|
||||
void *data;// pointer to a NanoVDB grid of unknown type
|
||||
uint64_t size;// byte size of NanoVDB grid of unknown type
|
||||
auto buffer = nanovdb::HostBuffer::createFull(size, data);
|
||||
nanovdb::GridHandle<> gridHandle(std::move(buffer));
|
||||
@endcode
|
||||
|
||||
Example that uses HostBuffer::createPool for internally managed host memory.
|
||||
Suppose you want to read multiple grids in multiple files, but reuse the same
|
||||
fixed sized memory buffer to both avoid memory fragmentation as well as
|
||||
exceeding the fixed memory ceiling!
|
||||
@code
|
||||
auto pool = nanovdb::HostBuffer::createPool(1 << 30);// 1 GB memory pool
|
||||
std::vector<std::string>> frames;// vector of grid names
|
||||
for (int i=0; i<frames.size(); ++i) {
|
||||
auto handles = nanovdb::io::readGrids(frames[i], 0, pool);// throws if grids in file exceed 1 GB
|
||||
...
|
||||
pool.reset();// clears all handles and resets the memory pool for reuse
|
||||
}
|
||||
@endcode
|
||||
|
||||
Example that uses HostBuffer::createPool for externally managed host memory.
|
||||
Note that in this example @c handles are allowed to outlive @c pool since
|
||||
they internally store a shared pointer to the memory pool. However @c data
|
||||
MUST outlive @c handles since the pool does not own its memory in this example.
|
||||
@code
|
||||
const size_t poolSize = 1 << 30;// 1 GB
|
||||
void *data = std::malloc(size + NANOVDB_DATA_ALIGNMENT);// 1 GB pool with padding
|
||||
void *buffer = nanovdb::alignPtr(data);// 32B aligned buffer
|
||||
//void *buffer = std::aligned_alloc(NANOVDB_DATA_ALIGNMENT, poolSize);// in C++17
|
||||
auto pool = nanovdb::HostBuffer::createPool(poolSize, buffer);
|
||||
auto handles1 = nanovdb::io::readGrids("file1.nvdb", 0, pool);
|
||||
auto handles2 = nanovdb::io::readGrids("file2.nvdb", 0, pool);
|
||||
....
|
||||
std::free(data);
|
||||
//std::free(buffer);
|
||||
@endcode
|
||||
|
||||
Example that uses HostBuffer::createPool for externally managed host memory.
|
||||
Note that in this example @c handles are allowed to outlive @c pool since
|
||||
they internally store a shared pointer to the memory pool. However @c array
|
||||
MUST outlive @c handles since the pool does not own its memory in this example.
|
||||
@code
|
||||
const size_t poolSize = 1 << 30;// 1 GB
|
||||
std::unique_ptr<char[]> array(new char[size + NANOVDB_DATA_ALIGNMENT]);// scoped pool of 1 GB with padding
|
||||
void *buffer = nanovdb::alignPtr(array.get());// 32B aligned buffer
|
||||
auto pool = nanovdb::HostBuffer::createPool(poolSize, buffer);
|
||||
auto handles = nanovdb::io::readGrids("file.nvdb", 0, pool);
|
||||
@endcode
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_HOSTBUFFER_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_HOSTBUFFER_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>// for NANOVDB_DATA_ALIGNMENT;
|
||||
#include <stdint.h> // for types like int32_t etc
|
||||
#include <cstdio> // for fprintf
|
||||
#include <cstdlib> // for std::malloc/std::realloc/std::free
|
||||
#include <memory>// for std::make_shared
|
||||
#include <mutex>// for std::mutex
|
||||
#include <unordered_set>// for std::unordered_set
|
||||
#include <cassert>// for assert
|
||||
#include <sstream>// for std::stringstream
|
||||
#include <cstring>// for memcpy
|
||||
|
||||
#define checkPtr(ptr, msg) \
|
||||
{ \
|
||||
ptrAssert((ptr), (msg), __FILE__, __LINE__); \
|
||||
}
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
template<typename BufferT>
|
||||
struct BufferTraits
|
||||
{
|
||||
static constexpr bool hasDeviceDual = false;
|
||||
};
|
||||
|
||||
// ----------------------------> HostBuffer <--------------------------------------
|
||||
|
||||
/// @brief This is a buffer that contains a shared or private pool
|
||||
/// to either externally or internally managed host memory.
|
||||
///
|
||||
/// @note Terminology:
|
||||
/// Pool: 0 = buffer.size() < buffer.poolSize()
|
||||
/// Buffer: 0 < buffer.size() < buffer.poolSize()
|
||||
/// Full: 0 < buffer.size() = buffer.poolSize()
|
||||
/// Empty: 0 = buffer.size() = buffer.poolSize()
|
||||
class HostBuffer
|
||||
{
|
||||
struct Pool;// forward declaration of private pool struct
|
||||
std::shared_ptr<Pool> mPool;
|
||||
uint64_t mSize; // total number of bytes for the NanoVDB grid.
|
||||
void* mData; // raw buffer for the NanoVDB grid.
|
||||
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
static inline void ptrAssert(void* ptr, const char* msg, const char* file, int line, bool abort = true)
|
||||
{
|
||||
if (ptr == nullptr) {
|
||||
fprintf(stderr, "NULL pointer error: %s %s %d\n", msg, file, line);
|
||||
if (abort)
|
||||
exit(1);
|
||||
}
|
||||
if (uint64_t(ptr) % NANOVDB_DATA_ALIGNMENT) {
|
||||
fprintf(stderr, "Alignment pointer error: %s %s %d\n", msg, file, line);
|
||||
if (abort)
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void ptrAssert(void*, const char*, const char*, int, bool = true)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// @brief Return a full buffer or an empty buffer
|
||||
HostBuffer(uint64_t bufferSize = 0);
|
||||
|
||||
/// @brief Move copy-constructor
|
||||
HostBuffer(HostBuffer&& other);
|
||||
|
||||
/// @brief Custom descructor
|
||||
~HostBuffer() { this->clear(); }
|
||||
|
||||
/// @brief Move copy assignment operation
|
||||
HostBuffer& operator=(HostBuffer&& other);
|
||||
|
||||
/// @brief Disallow copy-construction
|
||||
HostBuffer(const HostBuffer&) = delete;
|
||||
|
||||
/// @brief Disallow copy assignment operation
|
||||
HostBuffer& operator=(const HostBuffer&) = delete;
|
||||
|
||||
/// @brief Return a pool buffer which satisfies: buffer.size == 0,
|
||||
/// buffer.poolSize() == poolSize, and buffer.data() == nullptr.
|
||||
/// If data==nullptr, memory for the pool will be allocated.
|
||||
///
|
||||
/// @throw If poolSize is zero.
|
||||
static HostBuffer createPool(uint64_t poolSize, void *data = nullptr);
|
||||
|
||||
/// @brief Return a full buffer which satisfies: buffer.size == bufferSize,
|
||||
/// buffer.poolSize() == bufferSize, and buffer.data() == data.
|
||||
/// If data==nullptr, memory for the pool will be allocated.
|
||||
///
|
||||
/// @throw If bufferSize is zero.
|
||||
static HostBuffer createFull(uint64_t bufferSize, void *data = nullptr);
|
||||
|
||||
/// @brief Return a buffer with @c bufferSize bytes managed by
|
||||
/// the specified memory @c pool. If none is provided, i.e.
|
||||
/// @c pool == nullptr or @c pool->poolSize() == 0, one is
|
||||
/// created with size @c bufferSize, i.e. a full buffer is returned.
|
||||
///
|
||||
/// @throw If the specified @c pool has insufficient memory for
|
||||
/// the requested buffer size.
|
||||
static HostBuffer create(uint64_t bufferSize, const HostBuffer* pool = nullptr);
|
||||
|
||||
/// @brief Initialize as a full buffer with the specified size. If data is NULL
|
||||
/// the memory is internally allocated.
|
||||
void init(uint64_t bufferSize, void *data = nullptr);
|
||||
|
||||
//@{
|
||||
/// @brief Retuns a pointer to the raw memory buffer managed by this allocator.
|
||||
///
|
||||
/// @warning Note that the pointer can be NULL if the allocator was not initialized!
|
||||
const void* data() const { return mData; }
|
||||
void* data() { return mData; }
|
||||
//@}
|
||||
|
||||
//@{
|
||||
/// @brief Returns the size in bytes associated with this buffer.
|
||||
uint64_t bufferSize() const { return mSize; }
|
||||
uint64_t size() const { return this->bufferSize(); }
|
||||
//@}
|
||||
|
||||
/// @brief Returns the size in bytes of the memory pool shared with this instance.
|
||||
uint64_t poolSize() const;
|
||||
|
||||
/// @brief Return true if memory is managed (using std::malloc and std:free) by the
|
||||
/// shared pool in this buffer. Else memory is assumed to be managed externally.
|
||||
bool isManaged() const;
|
||||
|
||||
//@{
|
||||
/// @brief Returns true if this buffer has no memory associated with it
|
||||
bool isEmpty() const { return !mPool || mSize == 0 || mData == nullptr; }
|
||||
bool empty() const { return this->isEmpty(); }
|
||||
//@}
|
||||
|
||||
/// @brief Return true if this is a pool, i.e. an empty buffer with a nonempty
|
||||
/// internal pool, i.e. this->size() == 0 and this->poolSize() != 0
|
||||
bool isPool() const { return mSize == 0 && this->poolSize() > 0; }
|
||||
|
||||
/// @brief Return true if the pool exists, is nonempty but has no more available memory
|
||||
bool isFull() const;
|
||||
|
||||
/// @brief Clear this buffer so it is empty.
|
||||
void clear();
|
||||
|
||||
/// @brief Clears all existing buffers that are registered against the memory pool
|
||||
/// and resets the pool so it can be reused to create new buffers.
|
||||
///
|
||||
/// @throw If this instance is not empty or contains no pool.
|
||||
///
|
||||
/// @warning This method is not thread-safe!
|
||||
void reset();
|
||||
|
||||
/// @brief Total number of bytes from the pool currently in use by buffers
|
||||
uint64_t poolUsage() const;
|
||||
|
||||
/// @brief resize the pool size. It will attempt to resize the existing
|
||||
/// memory block, but if that fails a deep copy is performed.
|
||||
/// If @c data is not NULL it will be used as new externally
|
||||
/// managed memory for the pool. All registered buffers are
|
||||
/// updated so GridHandle::grid might return a new address (if
|
||||
/// deep copy was performed).
|
||||
///
|
||||
/// @note This method can be use to resize the memory pool and even
|
||||
/// change it from internally to externally managed memory or vice versa.
|
||||
///
|
||||
/// @throw if @c poolSize is less than this->poolUsage() the used memory
|
||||
/// or allocations fail.
|
||||
void resizePool(uint64_t poolSize, void *data = nullptr);
|
||||
|
||||
}; // HostBuffer class
|
||||
|
||||
// --------------------------> Implementation of HostBuffer::Pool <------------------------------------
|
||||
|
||||
// This is private struct of HostBuffer so you can safely ignore the API
|
||||
struct HostBuffer::Pool
|
||||
{
|
||||
using HashTableT = std::unordered_set<HostBuffer*>;
|
||||
std::mutex mMutex; // mutex for updating mRegister and mFree
|
||||
HashTableT mRegister;
|
||||
void *mData, *mFree;
|
||||
uint64_t mSize, mPadding;
|
||||
bool mManaged;
|
||||
|
||||
/// @brief External memory ctor
|
||||
Pool(uint64_t size = 0, void* data = nullptr)
|
||||
: mData(data)
|
||||
, mFree(mData)
|
||||
, mSize(size)
|
||||
, mPadding(0)
|
||||
, mManaged(data == nullptr)
|
||||
{
|
||||
if (mManaged) {
|
||||
mData = Pool::alloc(mSize);
|
||||
if (mData == nullptr) throw std::runtime_error("Pool::Pool malloc failed");
|
||||
}
|
||||
mPadding = alignmentPadding(mData);
|
||||
if (!mManaged && mPadding != 0) {
|
||||
throw std::runtime_error("Pool::Pool: external memory buffer is not aligned to " +
|
||||
std::to_string(NANOVDB_DATA_ALIGNMENT) +
|
||||
" bytes.\nHint: use nanovdb::alignPtr or std::aligned_alloc (C++17 only)");
|
||||
}
|
||||
mFree = util::PtrAdd(mData, mPadding);
|
||||
}
|
||||
|
||||
/// @brief Custom destructor
|
||||
~Pool()
|
||||
{
|
||||
assert(mRegister.empty());
|
||||
if (mManaged) std::free(mData);
|
||||
}
|
||||
|
||||
/// @brief Disallow copy-construction
|
||||
Pool(const Pool&) = delete;
|
||||
|
||||
/// @brief Disallow move-construction
|
||||
Pool(const Pool&&) = delete;
|
||||
|
||||
/// @brief Disallow copy assignment operation
|
||||
Pool& operator=(const Pool&) = delete;
|
||||
|
||||
/// @brief Disallow move assignment operation
|
||||
Pool& operator=(const Pool&&) = delete;
|
||||
|
||||
/// @brief Return the total number of bytes used from this Pool by buffers
|
||||
uint64_t usage() const { return util::PtrDiff(mFree, mData) - mPadding; }
|
||||
|
||||
/// @brief Allocate a buffer of the specified size and add it to the register
|
||||
void add(HostBuffer* buffer, uint64_t size)
|
||||
{
|
||||
void *alignedFree = util::PtrAdd(mFree, alignmentPadding(mFree));
|
||||
|
||||
if (util::PtrAdd(alignedFree, size) > util::PtrAdd(mData, mPadding + mSize)) {
|
||||
std::stringstream ss;
|
||||
ss << "HostBuffer::Pool: insufficient memory\n"
|
||||
<< "\tA buffer requested " << size << " bytes with " << NANOVDB_DATA_ALIGNMENT
|
||||
<< "-bytes alignment from a pool with "
|
||||
<< 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;
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
mRegister.insert(buffer);
|
||||
buffer->mData = alignedFree;
|
||||
mFree = util::PtrAdd(alignedFree, size);
|
||||
}
|
||||
|
||||
/// @brief Remove the specified buffer from the register
|
||||
void remove(HostBuffer *buffer)
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
mRegister.erase(buffer);
|
||||
}
|
||||
|
||||
/// @brief Replaces buffer1 with buffer2 in the register
|
||||
void replace(HostBuffer *buffer1, HostBuffer *buffer2)
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
mRegister.erase( buffer1);
|
||||
mRegister.insert(buffer2);
|
||||
}
|
||||
|
||||
/// @brief Reset the register and all its buffers
|
||||
void reset()
|
||||
{
|
||||
for (HostBuffer *buffer : mRegister) {
|
||||
buffer->mPool.reset();
|
||||
buffer->mSize = 0;
|
||||
buffer->mData = nullptr;
|
||||
}
|
||||
mRegister.clear();
|
||||
mFree = util::PtrAdd(mData, mPadding);
|
||||
}
|
||||
|
||||
/// @brief Resize this Pool and update registered buffers as needed. If data is no NULL
|
||||
/// it is used as externally managed memory.
|
||||
void resize(uint64_t size, void *data = nullptr)
|
||||
{
|
||||
const uint64_t memUsage = this->usage();
|
||||
|
||||
const bool managed = (data == nullptr);
|
||||
|
||||
if (!managed && alignmentPadding(data) != 0) {
|
||||
throw std::runtime_error("Pool::resize: external memory buffer is not aligned to " +
|
||||
std::to_string(NANOVDB_DATA_ALIGNMENT) + " bytes");
|
||||
}
|
||||
|
||||
if (memUsage > size) {
|
||||
throw std::runtime_error("Pool::resize: insufficient memory");
|
||||
}
|
||||
|
||||
uint64_t padding = 0;
|
||||
if (mManaged && managed && size != mSize) { // managed -> managed
|
||||
padding = mPadding;
|
||||
data = Pool::realloc(mData, memUsage, size, padding); // performs both copy and free of mData
|
||||
} else if (!mManaged && managed) { // un-managed -> managed
|
||||
data = Pool::alloc(size);
|
||||
padding = alignmentPadding(data);
|
||||
}
|
||||
|
||||
if (data == nullptr) {
|
||||
throw std::runtime_error("Pool::resize: allocation failed");
|
||||
} else if (data != mData) {
|
||||
void* paddedData = util::PtrAdd(data, padding);
|
||||
|
||||
if (!(mManaged && managed)) { // no need to copy if managed -> managed
|
||||
memcpy(paddedData, util::PtrAdd(mData, mPadding), memUsage);
|
||||
}
|
||||
|
||||
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
|
||||
if (mManaged && !managed) {// only free if managed -> un-managed
|
||||
std::free(mData);
|
||||
}
|
||||
|
||||
mData = data;
|
||||
mPadding = padding;
|
||||
}
|
||||
mSize = size;
|
||||
mManaged = managed;
|
||||
}
|
||||
/// @brief Return true is all the memory in this pool is in use.
|
||||
bool isFull() const
|
||||
{
|
||||
assert(mFree <= util::PtrAdd(mData, mPadding + mSize));
|
||||
return mSize > 0 ? mFree == util::PtrAdd(mData, mPadding + mSize) : false;
|
||||
}
|
||||
|
||||
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,
|
||||
uint64_t origSize,
|
||||
uint64_t desiredSize,
|
||||
uint64_t& padding)
|
||||
{
|
||||
// make sure we alloc enough space to align the result
|
||||
void* data = std::realloc(origData, desiredSize + NANOVDB_DATA_ALIGNMENT);
|
||||
|
||||
if (data != nullptr && data != origData) {
|
||||
uint64_t newPadding = alignmentPadding(data);
|
||||
// Number of padding bytes may have changed -- move data if that's the case
|
||||
if (newPadding != padding) {
|
||||
// Realloc should not happen when shrinking down buffer, but let's be safe
|
||||
std::memmove(util::PtrAdd(data, newPadding),
|
||||
util::PtrAdd(data, padding),
|
||||
math::Min(origSize, desiredSize));
|
||||
padding = newPadding;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
};// struct HostBuffer::Pool
|
||||
|
||||
// --------------------------> Implementation of HostBuffer <------------------------------------
|
||||
|
||||
inline HostBuffer::HostBuffer(uint64_t size) : mPool(nullptr), mSize(size), mData(nullptr)
|
||||
{
|
||||
if (size>0) {
|
||||
mPool = std::make_shared<Pool>(size);
|
||||
mData = mPool->mFree;
|
||||
mPool->mRegister.insert(this);
|
||||
mPool->mFree = util::PtrAdd(mPool->mFree, size);
|
||||
}
|
||||
}
|
||||
|
||||
inline HostBuffer::HostBuffer(HostBuffer&& other) : mPool(other.mPool), mSize(other.mSize), mData(other.mData)
|
||||
{
|
||||
if (mPool && mSize != 0) {
|
||||
mPool->replace(&other, this);
|
||||
}
|
||||
other.mPool.reset();
|
||||
other.mSize = 0;
|
||||
other.mData = nullptr;
|
||||
}
|
||||
|
||||
inline void HostBuffer::init(uint64_t bufferSize, void *data)
|
||||
{
|
||||
if (bufferSize == 0) {
|
||||
throw std::runtime_error("HostBuffer: invalid buffer size");
|
||||
}
|
||||
if (mPool) {
|
||||
mPool.reset();
|
||||
}
|
||||
if (!mPool || mPool->mSize != bufferSize) {
|
||||
mPool = std::make_shared<Pool>(bufferSize, data);
|
||||
}
|
||||
mPool->add(this, bufferSize);
|
||||
}
|
||||
|
||||
inline HostBuffer& HostBuffer::operator=(HostBuffer&& other)
|
||||
{
|
||||
if (mPool) {
|
||||
mPool->remove(this);
|
||||
}
|
||||
mPool = other.mPool;
|
||||
mSize = other.mSize;
|
||||
mData = other.mData;
|
||||
if (mPool && mSize != 0) {
|
||||
mPool->replace(&other, this);
|
||||
}
|
||||
other.mPool.reset();
|
||||
other.mSize = 0;
|
||||
other.mData = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline uint64_t HostBuffer::poolSize() const
|
||||
{
|
||||
return mPool ? mPool->mSize : 0u;
|
||||
}
|
||||
|
||||
inline uint64_t HostBuffer::poolUsage() const
|
||||
{
|
||||
return mPool ? mPool->usage(): 0u;
|
||||
}
|
||||
|
||||
inline bool HostBuffer::isManaged() const
|
||||
{
|
||||
return mPool ? mPool->mManaged : false;
|
||||
}
|
||||
|
||||
inline bool HostBuffer::isFull() const
|
||||
{
|
||||
return mPool ? mPool->isFull() : false;
|
||||
}
|
||||
|
||||
inline HostBuffer HostBuffer::createPool(uint64_t poolSize, void *data)
|
||||
{
|
||||
if (poolSize == 0) {
|
||||
throw std::runtime_error("HostBuffer: invalid pool size");
|
||||
}
|
||||
HostBuffer buffer;
|
||||
buffer.mPool = std::make_shared<Pool>(poolSize, data);
|
||||
// note the buffer is NOT registered by its pool since it is not using its memory
|
||||
buffer.mSize = 0;
|
||||
buffer.mData = nullptr;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline HostBuffer HostBuffer::createFull(uint64_t bufferSize, void *data)
|
||||
{
|
||||
if (bufferSize == 0) {
|
||||
throw std::runtime_error("HostBuffer: invalid buffer size");
|
||||
}
|
||||
HostBuffer buffer;
|
||||
buffer.mPool = std::make_shared<Pool>(bufferSize, data);
|
||||
buffer.mPool->add(&buffer, bufferSize);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline HostBuffer HostBuffer::create(uint64_t bufferSize, const HostBuffer* pool)
|
||||
{
|
||||
HostBuffer buffer;
|
||||
if (pool == nullptr || !pool->mPool) {
|
||||
buffer.mPool = std::make_shared<Pool>(bufferSize);
|
||||
} else {
|
||||
buffer.mPool = pool->mPool;
|
||||
}
|
||||
buffer.mPool->add(&buffer, bufferSize);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline void HostBuffer::clear()
|
||||
{
|
||||
if (mPool) {// remove self from the buffer register in the pool
|
||||
mPool->remove(this);
|
||||
}
|
||||
mPool.reset();
|
||||
mSize = 0;
|
||||
mData = nullptr;
|
||||
}
|
||||
|
||||
inline void HostBuffer::reset()
|
||||
{
|
||||
if (this->size()>0) {
|
||||
throw std::runtime_error("HostBuffer: only empty buffers can call reset");
|
||||
}
|
||||
if (!mPool) {
|
||||
throw std::runtime_error("HostBuffer: this buffer contains no pool to reset");
|
||||
}
|
||||
mPool->reset();
|
||||
}
|
||||
|
||||
inline void HostBuffer::resizePool(uint64_t size, void *data)
|
||||
{
|
||||
if (!mPool) {
|
||||
throw std::runtime_error("HostBuffer: this buffer contains no pool to resize");
|
||||
}
|
||||
mPool->resize(size, data);
|
||||
}
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // end of NANOVDB_HOSTBUFFER_H_HAS_BEEN_INCLUDED
|
||||
+2244
-656
File diff suppressed because it is too large
Load Diff
@@ -1,327 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/NodeManager.h
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date February 12, 2021
|
||||
|
||||
\brief This class allows for sequential access to nodes
|
||||
in a NanoVDB tree on both the host and device.
|
||||
|
||||
\details The ordering of the sequential access to nodes is always breadth-first!
|
||||
*/
|
||||
|
||||
#include <nanovdb/NanoVDB.h>// for NanoGrid etc
|
||||
#include <nanovdb/HostBuffer.h>// for HostBuffer
|
||||
|
||||
#ifndef NANOVDB_NODEMANAGER_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_NODEMANAGER_H_HAS_BEEN_INCLUDED
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
/// @brief NodeManager allows for sequential access to nodes
|
||||
template <typename BuildT>
|
||||
class NodeManager;
|
||||
|
||||
/// @brief NodeManagerHandle manages the memory of a NodeManager
|
||||
template<typename BufferT = HostBuffer>
|
||||
class NodeManagerHandle;
|
||||
|
||||
/// @brief brief Construct a NodeManager and return its handle
|
||||
///
|
||||
/// @param grid grid whose nodes will be accessed sequentially
|
||||
/// @param buffer buffer from which to allocate the output handle
|
||||
///
|
||||
/// @note This is the only way to create a NodeManager since it's using
|
||||
/// managed memory pointed to by a NodeManagerHandle.
|
||||
template <typename BuildT, typename BufferT = HostBuffer>
|
||||
NodeManagerHandle<BufferT> createNodeManager(const NanoGrid<BuildT> &grid,
|
||||
const BufferT& buffer = BufferT());
|
||||
|
||||
struct NodeManagerData
|
||||
{// 48B = 6*8B
|
||||
uint64_t mMagic;// 8B
|
||||
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
|
||||
};
|
||||
|
||||
/// @brief This class serves to manage a raw memory buffer of a NanoVDB NodeManager or LeafManager.
|
||||
template<typename BufferT>
|
||||
class NodeManagerHandle
|
||||
{
|
||||
GridType mGridType{GridType::Unknown};
|
||||
BufferT mBuffer;
|
||||
|
||||
template<typename BuildT>
|
||||
const NodeManager<BuildT>* getMgr() const {
|
||||
return mGridType == toGridType<BuildT>() ? (const NodeManager<BuildT>*)mBuffer.data() : nullptr;
|
||||
}
|
||||
|
||||
template<typename BuildT, typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, const NodeManager<BuildT>*>::type
|
||||
getDeviceMgr() const {
|
||||
return mGridType == toGridType<BuildT>() ? (const NodeManager<BuildT>*)mBuffer.deviceData() : nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T* no_const(const T* ptr) { return const_cast<T*>(ptr); }
|
||||
|
||||
public:
|
||||
/// @brief Move constructor from a buffer
|
||||
NodeManagerHandle(GridType gridType, BufferT&& buffer) : mGridType(gridType) { mBuffer = std::move(buffer); }
|
||||
/// @brief Empty ctor
|
||||
NodeManagerHandle() = default;
|
||||
/// @brief Disallow copy-construction
|
||||
NodeManagerHandle(const NodeManagerHandle&) = delete;
|
||||
/// @brief Disallow copy assignment operation
|
||||
NodeManagerHandle& operator=(const NodeManagerHandle&) = delete;
|
||||
/// @brief Move copy assignment operation
|
||||
NodeManagerHandle& operator=(NodeManagerHandle&& other) noexcept {
|
||||
mGridType = other.mGridType;
|
||||
mBuffer = std::move(other.mBuffer);
|
||||
other.mGridType = GridType::Unknown;
|
||||
return *this;
|
||||
}
|
||||
/// @brief Move copy-constructor
|
||||
NodeManagerHandle(NodeManagerHandle&& other) noexcept {
|
||||
mGridType = other.mGridType;
|
||||
mBuffer = std::move(other.mBuffer);
|
||||
other.mGridType = GridType::Unknown;
|
||||
}
|
||||
/// @brief Default destructor
|
||||
~NodeManagerHandle() { this->reset(); }
|
||||
/// @brief clear the buffer
|
||||
void reset() { mBuffer.clear(); }
|
||||
|
||||
/// @brief Return a reference to the buffer
|
||||
BufferT& buffer() { return mBuffer; }
|
||||
|
||||
/// @brief Return a const reference to the buffer
|
||||
const BufferT& buffer() const { return mBuffer; }
|
||||
|
||||
/// @brief Returns a non-const pointer to the data.
|
||||
///
|
||||
/// @warning Note that the return pointer can be NULL if the NodeManagerHandle was not initialized
|
||||
void* data() { return mBuffer.data(); }
|
||||
|
||||
/// @brief Returns a const pointer to the data.
|
||||
///
|
||||
/// @warning Note that the return pointer can be NULL if the NodeManagerHandle was not initialized
|
||||
const void* data() const { return mBuffer.data(); }
|
||||
|
||||
/// @brief Returns the size in bytes of the raw memory buffer managed by this NodeManagerHandle's allocator.
|
||||
uint64_t size() const { return mBuffer.size(); }
|
||||
|
||||
/// @brief Returns a const pointer to the NodeManager encoded in this NodeManagerHandle.
|
||||
///
|
||||
/// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
|
||||
template<typename BuildT>
|
||||
const NodeManager<BuildT>* mgr() const { return this->template getMgr<BuildT>(); }
|
||||
|
||||
/// @brief Returns a pointer to the NodeManager encoded in this NodeManagerHandle.
|
||||
///
|
||||
/// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
|
||||
template<typename BuildT>
|
||||
NodeManager<BuildT>* mgr() { return no_const(this->template getMgr<BuildT>()); }
|
||||
|
||||
/// @brief Return a const pointer to the NodeManager encoded in this NodeManagerHandle on the device, e.g. GPU
|
||||
///
|
||||
/// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
|
||||
template<typename BuildT, typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, const NodeManager<BuildT>*>::type
|
||||
deviceMgr() const { return this->template getDeviceMgr<BuildT>(); }
|
||||
|
||||
/// @brief Return a const pointer to the NodeManager encoded in this NodeManagerHandle on the device, e.g. GPU
|
||||
///
|
||||
/// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
|
||||
template<typename BuildT, typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, NodeManager<BuildT>*>::type
|
||||
deviceMgr() { return no_const(this->template getDeviceMgr<BuildT>()); }
|
||||
|
||||
/// @brief Upload the NodeManager to the device, e.g. from CPU to GPU
|
||||
///
|
||||
/// @note This method is only available if the buffer supports devices
|
||||
template<typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, void>::type
|
||||
deviceUpload(void* deviceGrid, void* stream = nullptr, bool sync = true)
|
||||
{
|
||||
assert(deviceGrid);
|
||||
auto *data = reinterpret_cast<NodeManagerData*>(mBuffer.data());
|
||||
void *tmp = data->mGrid;
|
||||
data->mGrid = deviceGrid;
|
||||
mBuffer.deviceUpload(stream, sync);
|
||||
data->mGrid = tmp;
|
||||
}
|
||||
|
||||
/// @brief Download the NodeManager to from the device, e.g. from GPU to CPU
|
||||
///
|
||||
/// @note This method is only available if the buffer supports devices
|
||||
template<typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, void>::type
|
||||
deviceDownload(void* stream = nullptr, bool sync = true)
|
||||
{
|
||||
auto *data = reinterpret_cast<NodeManagerData*>(mBuffer.data());
|
||||
void *tmp = data->mGrid;
|
||||
mBuffer.deviceDownload(stream, sync);
|
||||
data->mGrid = tmp;
|
||||
}
|
||||
};// NodeManagerHandle
|
||||
|
||||
/// @brief This class allows for sequential access to nodes in a NanoVDB tree
|
||||
///
|
||||
/// @details Nodes are always arranged breadth first during sequential access of nodes
|
||||
/// at a particular level.
|
||||
template<typename BuildT>
|
||||
class NodeManager : private NodeManagerData
|
||||
{
|
||||
using DataT = NodeManagerData;
|
||||
using GridT = NanoGrid<BuildT>;
|
||||
using TreeT = typename GridTree<GridT>::type;
|
||||
template<int LEVEL>
|
||||
using NodeT = typename NodeTrait<TreeT, LEVEL>::type;
|
||||
using RootT = NodeT<3>;// root node
|
||||
using Node2 = NodeT<2>;// upper internal node
|
||||
using Node1 = NodeT<1>;// lower internal node
|
||||
using Node0 = NodeT<0>;// leaf node
|
||||
|
||||
public:
|
||||
static constexpr bool FIXED_SIZE = Node0::FIXED_SIZE && Node1::FIXED_SIZE && Node2::FIXED_SIZE;
|
||||
|
||||
NodeManager(const NodeManager&) = delete;
|
||||
NodeManager(NodeManager&&) = delete;
|
||||
NodeManager& operator=(const NodeManager&) = delete;
|
||||
NodeManager& operator=(NodeManager&&) = delete;
|
||||
~NodeManager() = delete;
|
||||
|
||||
/// @brief return true if the nodes have both fixed size and are arranged breadth-first in memory.
|
||||
/// This allows for direct and memory-efficient linear access to nodes.
|
||||
__hostdev__ static bool isLinear(const GridT &grid) {return FIXED_SIZE && grid.isBreadthFirst();}
|
||||
|
||||
/// @brief return true if the nodes have both fixed size and are arranged breadth-first in memory.
|
||||
/// This allows for direct and memory-efficient linear access to nodes.
|
||||
__hostdev__ bool isLinear() const {return DataT::mLinear!=0u;}
|
||||
|
||||
/// @brief Return the memory footprint in bytes of the NodeManager derived from the specified grid
|
||||
__hostdev__ static uint64_t memUsage(const GridT &grid) {
|
||||
uint64_t size = sizeof(NodeManagerData);
|
||||
if (!NodeManager::isLinear(grid)) {
|
||||
const uint32_t *p = grid.tree().mNodeCount;
|
||||
size += sizeof(int64_t)*(p[0]+p[1]+p[2]);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/// @brief Return the memory footprint in bytes of this instance
|
||||
__hostdev__ uint64_t memUsage() const {return NodeManager::memUsage(this->grid());}
|
||||
|
||||
/// @brief Return a reference to the grid
|
||||
__hostdev__ GridT& grid() { return *reinterpret_cast<GridT*>(DataT::mGrid); }
|
||||
__hostdev__ const GridT& grid() const { return *reinterpret_cast<const GridT*>(DataT::mGrid); }
|
||||
|
||||
/// @brief Return a reference to the tree
|
||||
__hostdev__ TreeT& tree() { return this->grid().tree(); }
|
||||
__hostdev__ const TreeT& tree() const { return this->grid().tree(); }
|
||||
|
||||
/// @brief Return a reference to the root
|
||||
__hostdev__ RootT& root() { return this->tree().root(); }
|
||||
__hostdev__ const RootT& root() const { return this->tree().root(); }
|
||||
|
||||
/// @brief Return the number of tree nodes at the specified level
|
||||
/// @details 0 is leaf, 1 is lower internal, and 2 is upper internal level
|
||||
__hostdev__ uint64_t nodeCount(int level) const { return this->tree().nodeCount(level); }
|
||||
|
||||
__hostdev__ uint64_t leafCount() const { return this->tree().nodeCount(0); }
|
||||
__hostdev__ uint64_t lowerCount() const { return this->tree().nodeCount(1); }
|
||||
__hostdev__ uint64_t upperCount() const { return this->tree().nodeCount(2); }
|
||||
|
||||
/// @brief Return the i'th leaf node with respect to breadth-first ordering
|
||||
template <int LEVEL>
|
||||
__hostdev__ const NodeT<LEVEL>& node(uint32_t i) const {
|
||||
NANOVDB_ASSERT(i < this->nodeCount(LEVEL));
|
||||
const NodeT<LEVEL>* ptr = nullptr;
|
||||
if (DataT::mLinear) {
|
||||
ptr = util::PtrAdd<const NodeT<LEVEL>>(DataT::mGrid, DataT::mOff[LEVEL]) + i;
|
||||
} else {
|
||||
ptr = util::PtrAdd<const NodeT<LEVEL>>(DataT::mGrid, DataT::mPtr[LEVEL][i]);
|
||||
}
|
||||
NANOVDB_ASSERT(ptr && isAligned(ptr));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
/// @brief Return the i'th node with respect to breadth-first ordering
|
||||
template <int LEVEL>
|
||||
__hostdev__ NodeT<LEVEL>& node(uint32_t i) {
|
||||
NANOVDB_ASSERT(i < this->nodeCount(LEVEL));
|
||||
NodeT<LEVEL>* ptr = nullptr;
|
||||
if (DataT::mLinear) {
|
||||
ptr = util::PtrAdd<NodeT<LEVEL>>(DataT::mGrid, DataT::mOff[LEVEL]) + i;
|
||||
} else {
|
||||
ptr = util::PtrAdd<NodeT<LEVEL>>(DataT::mGrid, DataT::mPtr[LEVEL][i]);
|
||||
}
|
||||
NANOVDB_ASSERT(ptr && isAligned(ptr));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
/// @brief Return the i'th leaf node with respect to breadth-first ordering
|
||||
__hostdev__ const Node0& leaf(uint32_t i) const { return this->node<0>(i); }
|
||||
__hostdev__ Node0& leaf(uint32_t i) { return this->node<0>(i); }
|
||||
|
||||
/// @brief Return the i'th lower internal node with respect to breadth-first ordering
|
||||
__hostdev__ const Node1& lower(uint32_t i) const { return this->node<1>(i); }
|
||||
__hostdev__ Node1& lower(uint32_t i) { return this->node<1>(i); }
|
||||
|
||||
/// @brief Return the i'th upper internal node with respect to breadth-first ordering
|
||||
__hostdev__ const Node2& upper(uint32_t i) const { return this->node<2>(i); }
|
||||
__hostdev__ Node2& upper(uint32_t i) { return this->node<2>(i); }
|
||||
|
||||
}; // NodeManager<BuildT> class
|
||||
|
||||
template <typename BuildT, typename BufferT>
|
||||
NodeManagerHandle<BufferT> createNodeManager(const NanoGrid<BuildT> &grid,
|
||||
const BufferT& buffer)
|
||||
{
|
||||
NodeManagerHandle<BufferT> handle(toGridType<BuildT>(), BufferT::create(NodeManager<BuildT>::memUsage(grid), &buffer));
|
||||
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
|
||||
|
||||
if (NodeManager<BuildT>::isLinear(grid)) {
|
||||
data->mLinear = uint8_t(1u);
|
||||
data->mOff[0] = util::PtrDiff(grid.tree().template getFirstNode<0>(), &grid);
|
||||
data->mOff[1] = util::PtrDiff(grid.tree().template getFirstNode<1>(), &grid);
|
||||
data->mOff[2] = util::PtrDiff(grid.tree().template getFirstNode<2>(), &grid);
|
||||
} else {
|
||||
int64_t *ptr0 = data->mPtr[0] = reinterpret_cast<int64_t*>(data + 1);
|
||||
int64_t *ptr1 = data->mPtr[1] = data->mPtr[0] + grid.tree().nodeCount(0);
|
||||
int64_t *ptr2 = data->mPtr[2] = data->mPtr[1] + grid.tree().nodeCount(1);
|
||||
// Performs depth first traversal but breadth first insertion
|
||||
for (auto it2 = grid.tree().root().cbeginChild(); it2; ++it2) {
|
||||
*ptr2++ = util::PtrDiff(&*it2, &grid);
|
||||
for (auto it1 = it2->cbeginChild(); it1; ++it1) {
|
||||
*ptr1++ = util::PtrDiff(&*it1, &grid);
|
||||
for (auto it0 = it1->cbeginChild(); it0; ++it0) {
|
||||
*ptr0++ = util::PtrDiff(&*it0, &grid);
|
||||
}// loop over child nodes of the lower internal node
|
||||
}// loop over child nodes of the upper internal node
|
||||
}// loop over child nodes of the root node
|
||||
}
|
||||
|
||||
return handle;// // is converted to r-value so return value is move constructed!
|
||||
}
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#if defined(__CUDACC__)
|
||||
#include <nanovdb/cuda/NodeManager.cuh>
|
||||
#endif// defined(__CUDACC__)
|
||||
|
||||
#endif // NANOVDB_NODEMANAGER_H_HAS_BEEN_INCLUDED
|
||||
+22
-28
@@ -3,7 +3,7 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/PNanoVDB.h
|
||||
\file PNanoVDB.h
|
||||
|
||||
\author Andrew Reidmeyer
|
||||
|
||||
@@ -291,11 +291,7 @@ void pnanovdb_buf_write_uint64(pnanovdb_buf_t buf, uint byte_offset, uvec2 value
|
||||
// struct typedef, static const, inout
|
||||
#if defined(PNANOVDB_C)
|
||||
#define PNANOVDB_STRUCT_TYPEDEF(X) typedef struct X X;
|
||||
#if defined(__CUDA_ARCH__)
|
||||
#define PNANOVDB_STATIC_CONST constexpr __constant__
|
||||
#else
|
||||
#define PNANOVDB_STATIC_CONST static const
|
||||
#endif
|
||||
#define PNANOVDB_INOUT(X) X*
|
||||
#define PNANOVDB_IN(X) const X*
|
||||
#define PNANOVDB_DEREF(X) (*X)
|
||||
@@ -933,7 +929,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 6// 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
|
||||
@@ -962,8 +958,7 @@ PNANOVDB_FORCE_INLINE void pnanovdb_write_vec3(pnanovdb_buf_t buf, pnanovdb_addr
|
||||
#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_END 26
|
||||
|
||||
#define PNANOVDB_GRID_CLASS_UNKNOWN 0
|
||||
#define PNANOVDB_GRID_CLASS_LEVEL_SET 1 // narrow band level set, e.g. SDF
|
||||
@@ -994,17 +989,17 @@ PNANOVDB_FORCE_INLINE void pnanovdb_write_vec3(pnanovdb_buf_t buf, pnanovdb_addr
|
||||
|
||||
// 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_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 };
|
||||
// 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_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 };
|
||||
// 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_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 };
|
||||
// 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_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 };
|
||||
// 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_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 };
|
||||
// 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_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 };
|
||||
|
||||
struct pnanovdb_map_t
|
||||
{
|
||||
@@ -1234,9 +1229,9 @@ PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_version_get_patch(pnanovdb_uint
|
||||
|
||||
struct pnanovdb_gridblindmetadata_t
|
||||
{
|
||||
pnanovdb_int64_t data_offset; // 8 bytes, 0
|
||||
pnanovdb_uint64_t value_count; // 8 bytes, 8
|
||||
pnanovdb_uint32_t value_size; // 4 bytes, 16
|
||||
pnanovdb_int64_t byte_offset; // 8 bytes, 0
|
||||
pnanovdb_uint64_t element_count; // 8 bytes, 8
|
||||
pnanovdb_uint32_t flags; // 4 bytes, 16
|
||||
pnanovdb_uint32_t semantic; // 4 bytes, 20
|
||||
pnanovdb_uint32_t data_class; // 4 bytes, 24
|
||||
pnanovdb_uint32_t data_type; // 4 bytes, 28
|
||||
@@ -1248,22 +1243,22 @@ PNANOVDB_STRUCT_TYPEDEF(pnanovdb_gridblindmetadata_handle_t)
|
||||
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SIZE 288
|
||||
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_OFFSET 0
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_OFF_VALUE_COUNT 8
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_OFF_VALUE_SIZE 16
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_OFF_BYTE_OFFSET 0
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_OFF_ELEMENT_COUNT 8
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_OFF_FLAGS 16
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_OFF_SEMANTIC 20
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_CLASS 24
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_TYPE 28
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_OFF_NAME 32
|
||||
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_gridblindmetadata_get_data_offset(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
|
||||
return pnanovdb_read_int64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_OFFSET));
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_gridblindmetadata_get_byte_offset(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
|
||||
return pnanovdb_read_int64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_BYTE_OFFSET));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_gridblindmetadata_get_value_count(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
|
||||
return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_VALUE_COUNT));
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_gridblindmetadata_get_element_count(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
|
||||
return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_ELEMENT_COUNT));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_value_size(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
|
||||
return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_VALUE_SIZE));
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_flags(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
|
||||
return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_FLAGS));
|
||||
}
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_semantic(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
|
||||
return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_SEMANTIC));
|
||||
@@ -1667,7 +1662,6 @@ PNANOVDB_STATIC_CONST pnanovdb_grid_type_constants_t pnanovdb_grid_type_constant
|
||||
{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},
|
||||
};
|
||||
|
||||
// ------------------------------------------------ Basic Lookup -----------------------------------------------------------
|
||||
@@ -1684,7 +1678,7 @@ PNANOVDB_FORCE_INLINE pnanovdb_gridblindmetadata_handle_t pnanovdb_grid_get_grid
|
||||
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_grid_get_gridblindmetadata_value_address(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, pnanovdb_uint32_t index)
|
||||
{
|
||||
pnanovdb_gridblindmetadata_handle_t meta = pnanovdb_grid_get_gridblindmetadata(buf, grid, index);
|
||||
pnanovdb_int64_t byte_offset = pnanovdb_gridblindmetadata_get_data_offset(buf, meta);
|
||||
pnanovdb_int64_t byte_offset = pnanovdb_gridblindmetadata_get_byte_offset(buf, meta);
|
||||
pnanovdb_address_t address = pnanovdb_address_offset64(meta.address, pnanovdb_int64_as_uint64(byte_offset));
|
||||
return address;
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
#include <nanovdb/io/IO.h> // this is required to read (and write) NanoVDB files on the host
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/tools/NanoToOpenVDB.h>
|
||||
#include <nanovdb/util/IO.h> // this is required to read (and write) NanoVDB files on the host
|
||||
#include <nanovdb/util/CreateNanoGrid.h>
|
||||
#include <nanovdb/util/NanoToOpenVDB.h>
|
||||
|
||||
void usage [[noreturn]] (const std::string& progName, int exitStatus = EXIT_FAILURE)
|
||||
{
|
||||
@@ -47,9 +47,7 @@ void usage [[noreturn]] (const std::string& progName, int exitStatus = EXIT_FAIL
|
||||
|
||||
void version [[noreturn]] (const char* progName, int exitStatus = EXIT_SUCCESS)
|
||||
{
|
||||
char str[8];
|
||||
nanovdb::toStr(str, nanovdb::Version());
|
||||
printf("\n%s was build against NanoVDB version %s\n", progName, str);
|
||||
printf("\n%s was build against NanoVDB version %s\n", progName, nanovdb::Version().c_str());
|
||||
exit(exitStatus);
|
||||
}
|
||||
|
||||
@@ -58,8 +56,8 @@ 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::StatsMode sMode = nanovdb::StatsMode::Default;
|
||||
nanovdb::ChecksumMode cMode = nanovdb::ChecksumMode::Default;
|
||||
nanovdb::GridType qMode = nanovdb::GridType::Unknown;//specify the quantization mode
|
||||
bool verbose = false, overwrite = false, dither = false, absolute = true;
|
||||
float tolerance = -1.0f;
|
||||
@@ -101,11 +99,11 @@ int main(int argc, char* argv[])
|
||||
std::string str(argv[++i]);
|
||||
toLowerCase(str);
|
||||
if (str == "none") {
|
||||
cMode = nanovdb::CheckMode::Disable;
|
||||
cMode = nanovdb::ChecksumMode::Disable;
|
||||
} else if (str == "partial") {
|
||||
cMode = nanovdb::CheckMode::Partial;
|
||||
cMode = nanovdb::ChecksumMode::Partial;
|
||||
} else if (str == "full") {
|
||||
cMode = nanovdb::CheckMode::Full;
|
||||
cMode = nanovdb::ChecksumMode::Full;
|
||||
} else {
|
||||
std::cerr << "Expected one of the following checksum modes: {none, partial, full}\n" << std::endl;
|
||||
usage(argv[0]);
|
||||
@@ -119,13 +117,13 @@ int main(int argc, char* argv[])
|
||||
std::string str(argv[++i]);
|
||||
toLowerCase(str);
|
||||
if (str == "none") {
|
||||
sMode = nanovdb::tools::StatsMode::Disable;
|
||||
sMode = nanovdb::StatsMode::Disable;
|
||||
} else if (str == "bbox") {
|
||||
sMode = nanovdb::tools::StatsMode::BBox;
|
||||
sMode = nanovdb::StatsMode::BBox;
|
||||
} else if (str == "extrema") {
|
||||
sMode = nanovdb::tools::StatsMode::MinMax;
|
||||
sMode = nanovdb::StatsMode::MinMax;
|
||||
} else if (str == "all") {
|
||||
sMode = nanovdb::tools::StatsMode::All;
|
||||
sMode = nanovdb::StatsMode::All;
|
||||
} else {
|
||||
std::cerr << "Expected one of the following stats modes: {none, bbox, extrema, all}\n" << std::endl;
|
||||
usage(argv[0]);
|
||||
@@ -138,7 +136,7 @@ int main(int argc, char* argv[])
|
||||
} else {
|
||||
qMode = nanovdb::GridType::FpN;
|
||||
absolute = true;
|
||||
tolerance = static_cast<float>(atof(argv[++i]));
|
||||
tolerance = atof(argv[++i]);
|
||||
}
|
||||
} else if (arg == "-r" || arg == "--rel-error") {
|
||||
if (i + 1 == argc) {
|
||||
@@ -147,7 +145,7 @@ int main(int argc, char* argv[])
|
||||
} else {
|
||||
qMode = nanovdb::GridType::FpN;
|
||||
absolute = false;
|
||||
tolerance = static_cast<float>(atof(argv[++i]));
|
||||
tolerance = atof(argv[++i]);
|
||||
}
|
||||
} else if (arg == "-g" || arg == "--grid") {
|
||||
if (i + 1 == argc) {
|
||||
@@ -205,7 +203,7 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
using SrcGridT = openvdb::FloatGrid;
|
||||
if (auto floatGrid = openvdb::GridBase::grid<SrcGridT>(base)) {
|
||||
nanovdb::tools::CreateNanoGrid<SrcGridT> s(*floatGrid);
|
||||
nanovdb::CreateNanoGrid<SrcGridT> s(*floatGrid);
|
||||
s.setStats(sMode);
|
||||
s.setChecksum(cMode);
|
||||
s.enableDithering(dither);
|
||||
@@ -219,15 +217,15 @@ int main(int argc, char* argv[])
|
||||
return s.getHandle<nanovdb::Fp16>();
|
||||
case nanovdb::GridType::FpN:
|
||||
if (absolute) {
|
||||
return s.getHandle<nanovdb::FpN>(nanovdb::tools::AbsDiff(tolerance));
|
||||
return s.getHandle<nanovdb::FpN>(nanovdb::AbsDiff(tolerance));
|
||||
} else {
|
||||
return s.getHandle<nanovdb::FpN>(nanovdb::tools::RelDiff(tolerance));
|
||||
return s.getHandle<nanovdb::FpN>(nanovdb::RelDiff(tolerance));
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}// end of switch
|
||||
}
|
||||
return nanovdb::tools::openToNanoVDB(base, sMode, cMode, verbose ? 1 : 0);
|
||||
return nanovdb::openToNanoVDB(base, sMode, cMode, verbose ? 1 : 0);
|
||||
};
|
||||
try {
|
||||
if (toNanoVDB) { // OpenVDB -> NanoVDB
|
||||
@@ -277,7 +275,7 @@ int main(int argc, char* argv[])
|
||||
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));
|
||||
grids->push_back(nanoToOpenVDB(h, 0, i));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -288,7 +286,7 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
if (verbose)
|
||||
std::cout << "Converting NanoVDB grid named \"" << handle.gridMetaData()->shortGridName() << "\" to OpenVDB" << std::endl;
|
||||
grids->push_back(nanovdb::tools::nanoToOpenVDB(handle));
|
||||
grids->push_back(nanoToOpenVDB(handle));
|
||||
}
|
||||
} // loop over input files
|
||||
file.write(*grids);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
\brief Command-line tool that prints information about grids in a nanovdb file
|
||||
*/
|
||||
|
||||
#include <nanovdb/io/IO.h> // this is required to read (and write) NanoVDB files on the host
|
||||
#include <nanovdb/util/IO.h> // this is required to read (and write) NanoVDB files on the host
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
@@ -31,9 +31,7 @@ void usage [[noreturn]] (const std::string& progName, int exitStatus = EXIT_FAIL
|
||||
|
||||
void version [[noreturn]] (const char* progName, int exitStatus = EXIT_SUCCESS)
|
||||
{
|
||||
char str[8];
|
||||
nanovdb::toStr(str, nanovdb::Version());
|
||||
printf("\n%s was build against NanoVDB version %s\n", progName, str);
|
||||
printf("\n%s was build against NanoVDB version %s\n", progName, nanovdb::Version().c_str());
|
||||
exit(exitStatus);
|
||||
}
|
||||
|
||||
@@ -44,7 +42,6 @@ int main(int argc, char* argv[])
|
||||
enum Mode : int { Short = 0,
|
||||
Default = 1,
|
||||
Long = 2 } mode = Default;
|
||||
char str[32];
|
||||
bool verbose = false;
|
||||
std::string gridName;
|
||||
std::vector<std::string> fileNames;
|
||||
@@ -112,7 +109,7 @@ int main(int argc, char* argv[])
|
||||
ss << "(" << v[0] << "," << v[1] << "," << v[2] << ")";
|
||||
return ss.str();
|
||||
};
|
||||
auto wbboxToStr = [](const nanovdb::math::BBox<nanovdb::Vec3d>& bbox) {
|
||||
auto wbboxToStr = [](const nanovdb::BBox<nanovdb::Vec3d>& bbox) {
|
||||
std::stringstream ss;
|
||||
if (bbox.empty()) {
|
||||
ss << "empty grid";
|
||||
@@ -177,15 +174,15 @@ int main(int argc, char* argv[])
|
||||
auto resWidth = std::string("Resolution").length() + padding;
|
||||
for (auto& m : list) {
|
||||
width(nameWidth, m.gridName);
|
||||
width(typeWidth, nanovdb::toStr(str, m.gridType));
|
||||
width(classWidth, nanovdb::toStr(str, m.gridClass));
|
||||
width(codecWidth, nanovdb::io::toStr(str, m.codec));
|
||||
width(typeWidth, nanovdb::toStr(m.gridType));
|
||||
width(classWidth, nanovdb::toStr(m.gridClass));
|
||||
width(codecWidth, nanovdb::io::toStr(m.codec));
|
||||
width(wbboxWidth, wbboxToStr(m.worldBBox));
|
||||
width(ibboxWidth, ibboxToStr(m.indexBBox));
|
||||
width(resWidth, resToStr(m.indexBBox));
|
||||
width(sizeWidth, format(m.gridSize));
|
||||
width(fileWidth, format(m.fileSize));
|
||||
width(versionWidth, nanovdb::toStr(str, m.version));
|
||||
width(versionWidth, std::string(m.version.c_str()));
|
||||
width(configWidth, nodesToStr(m.nodeCount));
|
||||
width(tileWidth, nodesToStr(m.tileCount));
|
||||
width(voxelsWidth, std::to_string(m.voxelCount));
|
||||
@@ -223,11 +220,11 @@ int main(int argc, char* argv[])
|
||||
continue;
|
||||
std::cout << std::left << std::setw(numberWidth) << ++n
|
||||
<< std::left << std::setw(nameWidth) << m.gridName
|
||||
<< std::left << std::setw(typeWidth) << nanovdb::toStr(str, m.gridType);
|
||||
<< std::left << std::setw(typeWidth) << nanovdb::toStr(m.gridType);
|
||||
if (mode != Short) {
|
||||
std::cout << std::left << std::setw(classWidth) << nanovdb::toStr(str, m.gridClass)
|
||||
<< std::left << std::setw(versionWidth) << nanovdb::toStr(str+10, m.version)
|
||||
<< std::left << std::setw(codecWidth) << nanovdb::io::toStr(str + 20, m.codec)
|
||||
std::cout << std::left << std::setw(classWidth) << nanovdb::toStr(m.gridClass)
|
||||
<< std::left << std::setw(versionWidth) << std::string(m.version.c_str())
|
||||
<< std::left << std::setw(codecWidth) << nanovdb::io::toStr(m.codec)
|
||||
<< std::left << std::setw(sizeWidth) << format(m.gridSize)
|
||||
<< std::left << std::setw(fileWidth) << format(m.fileSize)
|
||||
<< std::left << std::setw(voxelSizeWidth) << Vec3dToStr(m.voxelSize);
|
||||
@@ -324,4 +321,4 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
return exitStatus;
|
||||
}// main
|
||||
}
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def open_file(file_path):
|
||||
"""
|
||||
Opens a file. If utf-8 decoding fails, try windows-1252.
|
||||
|
||||
Args:
|
||||
file_path: Path of the file to open.
|
||||
|
||||
Returns:
|
||||
The content of the file in an arbitrary format.
|
||||
"""
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8", errors="replace") as file:
|
||||
return file.read()
|
||||
except UnicodeDecodeError:
|
||||
with open(file_path, "r", encoding="windows-1252", errors="replace") as file:
|
||||
return file.read()
|
||||
|
||||
|
||||
def write_file(file_path, content):
|
||||
"""
|
||||
Writes a file. If utf-8 decoding fails, try windows-1252.
|
||||
|
||||
Args:
|
||||
file_path: Path of the file to open.
|
||||
|
||||
Returns:
|
||||
None.
|
||||
"""
|
||||
try:
|
||||
with open(file_path, "w", encoding="utf-8", errors="replace") as file:
|
||||
file.write(content)
|
||||
except UnicodeDecodeError:
|
||||
with open(file_path, "w", encoding="windows-1252", errors="replace") as file:
|
||||
file.write(content)
|
||||
|
||||
|
||||
def update_files(dir_path):
|
||||
"""
|
||||
Updates the content of files ending in .h, .cuh, .cc, .cu, and .cpp
|
||||
to call the appropriate API as we update NanoVDB from version 32.6 to
|
||||
version 32.7. This includes changes in namespaces, function names, and
|
||||
include directories.
|
||||
|
||||
Args:
|
||||
Directory path: will include files in downstream directories.
|
||||
|
||||
Returns:
|
||||
None. Writes the contents of the file.
|
||||
"""
|
||||
|
||||
# List of file extensions to search for
|
||||
file_extensions = [".h", ".cuh", ".cc", ".cu", ".cpp"]
|
||||
|
||||
nspace_dic = {
|
||||
"math": [
|
||||
"Ray",
|
||||
"DDA<",
|
||||
"HDDA",
|
||||
"Vec3<",
|
||||
"Vec4<",
|
||||
"BBox<",
|
||||
"ZeroCrossing",
|
||||
"TreeMarcher",
|
||||
"PointTreeMarcher",
|
||||
"BoxStencil<",
|
||||
"CurvatureStencil<",
|
||||
"GradStencil<",
|
||||
"WenoStencil<",
|
||||
"AlignUp",
|
||||
"Min",
|
||||
"Max",
|
||||
"Abs",
|
||||
"Clamp",
|
||||
"Sqrt",
|
||||
"Sign",
|
||||
"Maximum<",
|
||||
"Delta<",
|
||||
"RoundDown<",
|
||||
"pi<",
|
||||
"isApproxZero<",
|
||||
"Round<",
|
||||
"createSampler",
|
||||
"SampleFromVoxels<",
|
||||
],
|
||||
"tools": [
|
||||
"createNanoGrid",
|
||||
"StatsMode",
|
||||
"createLevelSetSphere",
|
||||
"createFogVolumeSphere",
|
||||
"createFogVolumeSphere createFogVolumeSphere",
|
||||
"createFogVolumeTorus",
|
||||
"createLevelSetBox",
|
||||
"CreateNanoGrid",
|
||||
"updateGridStats",
|
||||
"evalChecksum",
|
||||
"validateChecksum",
|
||||
"checkGrid",
|
||||
"Extrema",
|
||||
],
|
||||
"util": [
|
||||
"is_floating_point",
|
||||
"findLowestOn",
|
||||
"findHighestOn",
|
||||
"Range",
|
||||
"streq",
|
||||
"strcpy",
|
||||
"strcat",
|
||||
"empty(",
|
||||
"Split",
|
||||
"invoke",
|
||||
"forEach",
|
||||
"reduce",
|
||||
"prefixSum",
|
||||
"is_same",
|
||||
"is_specialization",
|
||||
"PtrAdd",
|
||||
"PtrDiff",
|
||||
],
|
||||
}
|
||||
|
||||
rename_dic = {
|
||||
# list from func4 in updateFiles.sh
|
||||
"nanovdb::build::": "nanovdb::tools::build::",
|
||||
"nanovdb::BBoxR": "nanovdb::Vec3dBBox",
|
||||
"nanovdb::BBox<nanovdb::Vec3d>": "nanovdb::Vec3dBbox",
|
||||
# scope and rename, i.e. list from func2 in updateFiles.sh
|
||||
"nanovdb::cudaCreateNodeManager": "nanovdb::cuda::createNodeManager",
|
||||
"nanovdb::cudaVoxelsToGrid": "nanovdb::cuda::voxelsToGrid",
|
||||
"nanovdb::cudaPointsToGrid": "nanovdb::cuda::pointsToGrid",
|
||||
"nanovdb::DitherLUT": "nanovdb::math::DitherLUT",
|
||||
"nanovdb::PackedRGBA8": "nanovdb::math::Rgba8",
|
||||
"nanovdb::Rgba8": "nanovdb::math::Rgba8",
|
||||
"nanovdb::CpuTimer": "nanovdb::util::Timer",
|
||||
"nanovdb::GpuTimer": "nanovdb::util::cuda::Timer",
|
||||
"nanovdb::CountOn": "nanovdb::util::countOn",
|
||||
}
|
||||
|
||||
movdir_dic = {
|
||||
# list comes from func3 calls on updateFiles.sh
|
||||
"util/GridHandle.h": "GridHandle.h",
|
||||
"util/BuildGrid.h": "tools/GridBuilder.h",
|
||||
"util/GridBuilder.h": "tools/GridBuilder.h",
|
||||
"util/IO.h": "io/IO.h",
|
||||
"util/CSampleFromVoxels.h": "math/CSampleFromVoxels.h",
|
||||
"util/DitherLUT.h": "math/DitherLUT.h",
|
||||
"util/HDDA.h": "math/HDDA.h",
|
||||
"util/Ray.h": "math/Ray.h",
|
||||
"util/SampleFromVoxels.h": "math/SampleFromVoxels.h",
|
||||
"util/Stencils.h": "nanovdb/math/Stencils.h",
|
||||
"util/CreateNanoGrid.h": "tools/CreateNanoGrid.h",
|
||||
"util/Primitives.h": "tools/CreatePrimitives.h",
|
||||
"util/GridChecksum.h": "tools/GridChecksum.h",
|
||||
"util/GridStats.h": "tools/GridStats.h",
|
||||
"util/GridChecksum.h": "tools/GridChecksum.h",
|
||||
"util/GridValidator.h": "tools/GridValidator.h",
|
||||
"util/NanoToOpenVDB.h": "tools/NanoToOpenVDB.h",
|
||||
"util/cuda/CudaGridChecksum.cuh": "tools/cuda/CudaGridChecksum.cuh",
|
||||
"util/cuda/CudaGridStats.cuh": "tools/cuda/CudaGridStats.cuh",
|
||||
"util/cuda/CudaGridValidator.cuh": "tools/cuda/CudaGridValidator.cuh",
|
||||
"util/cuda/CudaIndexToGrid.cuh": "tools/cuda/CudaIndexToGrid.cuh",
|
||||
"util/cuda/CudaPointsToGrid.cuh": "tools/cuda/PointsToGrid.cuh",
|
||||
"util/cuda/CudaSignedFloodFill.cuh": "tools/cuda/CudaSignedFloodFill.cuh",
|
||||
"util/cuda/CudaDeviceBuffer.h": "cuda/DeviceBuffer.h",
|
||||
"util/cuda/CudaGridHandle.cuh": "cuda/GridHandle.cuh",
|
||||
"util/cuda/CudaUtils.h": "util/cuda/Util.h",
|
||||
"util/cuda/GpuTimer.h": "util/cuda/Timer.h",
|
||||
}
|
||||
|
||||
# Iterate over files in the directory and its subdirectories
|
||||
for root, dirs, files in os.walk(dir_path):
|
||||
for file in files:
|
||||
if any(file.endswith(ext) for ext in file_extensions):
|
||||
file_path = os.path.join(root, file)
|
||||
print(f"Processing file: {file_path}")
|
||||
|
||||
content = open_file(file_path)
|
||||
|
||||
# Correspond to func1 $file in updateFiles.sh
|
||||
for key, vals in nspace_dic.items():
|
||||
for val in vals:
|
||||
old_word = "nanovdb::" + val
|
||||
new_word = "nanovdb::" + key + "::" + val
|
||||
content = content.replace(old_word, new_word)
|
||||
|
||||
# Correspond to func4 and func2 in updateFiles.sh
|
||||
for key, val in rename_dic.items():
|
||||
content = content.replace(key, val)
|
||||
|
||||
# Correspond to func3 in updateFiles.sh
|
||||
for key, val in movdir_dic.items():
|
||||
old_path = "<nanovdb/" + key + ">"
|
||||
new_path = "<nanovdb/" + val + ">"
|
||||
content = content.replace(old_path, new_path)
|
||||
|
||||
write_file(file_path, content)
|
||||
|
||||
# Example use:
|
||||
# To update all the files using NanoVDB in the current directory (and directories downstream):
|
||||
# python ./nanovdb/nanovdb/cmd/updateFiles.py
|
||||
# To update all the files using NanoVDB in a directory called foo (and directories downstream):
|
||||
# python ./nanovdb/nanovdb/cmd/updateFiles.py -d /path/to/foo
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Synthetic Data Generation for USD")
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--directory",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Path to directory containing .h, .cc, and .cu files using NanoVDB.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
dir_path = os.getcwd() if args.directory is None else Path(args.directory).resolve()
|
||||
|
||||
update_files(dir_path)
|
||||
@@ -1,99 +0,0 @@
|
||||
#!/bin/bash
|
||||
#Usage process all files in this directory or optionally specify a target directory
|
||||
|
||||
# Define directory in which to find files
|
||||
dir="."
|
||||
if [ "$1" ]; then
|
||||
dir="$1"
|
||||
fi
|
||||
|
||||
# Check if dir is not a directory
|
||||
if [ ! -d "$dir" ]; then
|
||||
echo -e "\nUsage: '$0 <directory>'\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# E.g.: func1 $file "math" "Coord" "Vec3" "Vec4"
|
||||
func1 () {
|
||||
for ((i=3; i<=$#; i++)); do
|
||||
arg="s/nanovdb::${!i}/nanovdb::$2::${!i}/g"
|
||||
#echo "sed -i $arg $1"
|
||||
sed -i $arg $1
|
||||
done
|
||||
}
|
||||
|
||||
# E.G.: func2 file namespace old new : nanovdb::old -> nanovdb::namespace::new in file
|
||||
func2 () {
|
||||
arg="s/nanovdb::$3/nanovdb::$2::$4/g"
|
||||
#echo "sed -i $arg $1"
|
||||
sed -i $arg $1
|
||||
}
|
||||
|
||||
# E.G.: func3 file path1/old.h path2/new.h <nanovdb/path1/old.h> -> <nanovdb/path2/new.h> in file
|
||||
func3 () {
|
||||
arg="s;<nanovdb/$2>;<nanovdb/$3>;g"
|
||||
#echo "sed -i $arg $1"
|
||||
sed -i $arg $1
|
||||
}
|
||||
|
||||
# E.g.: func4 file old new new -> old
|
||||
func4 () {
|
||||
arg="s;$2;$3;g"
|
||||
#echo "sed -i $arg $1"
|
||||
sed -i $arg $1
|
||||
}
|
||||
|
||||
# Loop through files in the target directory
|
||||
for file in $(find "$dir" -name '*.h' -or -name '*.cuh' -or -name '*.cc' -or -name '*.cu' -or -name '*.cpp'); do
|
||||
if [ -f "$file" ]; then
|
||||
echo "Processing file: $file"
|
||||
func1 $file math Ray "DDA<" HDDA "Vec3<" "Vec4<" "BBox<" ZeroCrossing TreeMarcher PointTreeMarcher\
|
||||
"BoxStencil<" "CurvatureStencil<" "GradStencil<" "WenoStencil<" AlignUp Min Max Abs Clamp\
|
||||
Sqrt Sign "Maximum<" "Delta<" "RoundDown<" "pi<" "isApproxZero<" "Round<" createSampler "SampleFromVoxels<"
|
||||
func1 $file tools createNanoGrid StatsMode createLevelSetSphere\
|
||||
createFogVolumeSphere createFogVolumeSphere createFogVolumeSphere\
|
||||
createFogVolumeTorus createLevelSetBox CreateNanoGrid updateGridStats\
|
||||
evalChecksum validateChecksum checkGrid Extrema
|
||||
func1 $file util is_floating_point findLowestOn findHighestOn Range streq strcpy strcat "empty("\
|
||||
Split invoke forEach reduce prefixSum is_same is_specialization PtrAdd PtrDiff
|
||||
func4 $file "nanovdb::build::" "nanovdb::tools::build::"
|
||||
func4 $file "nanovdb::BBoxR" "nanovdb::Vec3dBBox"
|
||||
func4 $file "nanovdb::BBox<nanovdb::Vec3d>" "nanovdb::Vec3dBbox"
|
||||
func2 $file cuda cudaCreateNodeManager createNodeManager
|
||||
func2 $file cuda cudaVoxelsToGrid voxelsToGrid
|
||||
func2 $file cuda cudaPointsToGrid pointsToGrid
|
||||
func2 $file math DitherLUT DitherLUT
|
||||
func2 $file math PackedRGBA8 Rgba8
|
||||
func2 $file math Rgba8 Rgba8
|
||||
func2 $file util CpuTimer Timer
|
||||
func2 $file util GpuTimer "cuda::Timer"
|
||||
func2 $file util CountOn countOn
|
||||
func3 $file "util/GridHandle.h" "GridHandle.h"
|
||||
func3 $file "util/BuildGrid.h" "tools/GridBuilder.h"
|
||||
func3 $file "util/GridBuilder.h" "tools/GridBuilder.h"
|
||||
func3 $file "util/IO.h" "io/IO.h"
|
||||
func3 $file "util/CSampleFromVoxels.h" "math/CSampleFromVoxels.h"
|
||||
func3 $file "util/DitherLUT.h" "math/DitherLUT.h"
|
||||
func3 $file "util/HDDA.h" "math/HDDA.h"
|
||||
func3 $file "util/Ray.h" "math/Ray.h"
|
||||
func3 $file "util/SampleFromVoxels.h" "math/SampleFromVoxels.h"
|
||||
func3 $file "util/Stencils.h" "nanovdb/math/Stencils.h"
|
||||
func3 $file "util/CreateNanoGrid.h" "tools/CreateNanoGrid.h"
|
||||
func3 $file "util/Primitives.h" "tools/CreatePrimitives.h"
|
||||
func3 $file "util/GridChecksum.h" "tools/GridChecksum.h"
|
||||
func3 $file "util/GridStats.h" "tools/GridStats.h"
|
||||
func3 $file "util/GridChecksum.h" "tools/GridChecksum.h"
|
||||
func3 $file "util/GridValidator.h" "tools/GridValidator.h"
|
||||
func3 $file "util/NanoToOpenVDB.h" "tools/NanoToOpenVDB.h"
|
||||
func3 $file "util/cuda/CudaGridChecksum.cuh" "tools/cuda/CudaGridChecksum.cuh"
|
||||
func3 $file "util/cuda/CudaGridStats.cuh" "tools/cuda/CudaGridStats.cuh"
|
||||
func3 $file "util/cuda/CudaGridValidator.cuh" "tools/cuda/CudaGridValidator.cuh"
|
||||
func3 $file "util/cuda/CudaIndexToGrid.cuh" "tools/cuda/CudaIndexToGrid.cuh"
|
||||
func3 $file "util/cuda/CudaPointsToGrid.cuh" "tools/cuda/PointsToGrid.cuh"
|
||||
func3 $file "util/cuda/CudaSignedFloodFill.cuh" "tools/cuda/CudaSignedFloodFill.cuh"
|
||||
func3 $file "util/cuda/CudaDeviceBuffer.h" "cuda/DeviceBuffer.h"
|
||||
func3 $file "util/cuda/CudaGridHandle.cuh" "cuda/GridHandle.cuh"
|
||||
func3 $file "util/cuda/CudaUtils.h" "util/cuda/Util.h"
|
||||
func3 $file "util/cuda/GpuTimer.h" "util/cuda/Timer.h"
|
||||
fi
|
||||
done
|
||||
@@ -11,8 +11,8 @@
|
||||
\brief Command-line tool that validates Grids in nanovdb files
|
||||
*/
|
||||
|
||||
#include <nanovdb/io/IO.h> // this is required to read (and write) NanoVDB files on the host
|
||||
#include <nanovdb/tools/GridValidator.h>
|
||||
#include <nanovdb/util/IO.h> // this is required to read (and write) NanoVDB files on the host
|
||||
#include <nanovdb/util/GridValidator.h>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
@@ -23,7 +23,6 @@ void usage [[noreturn]] (const std::string& progName, int exitStatus = EXIT_FAIL
|
||||
<< "Options:\n"
|
||||
<< "-g,--grid name\tOnly validate grids matching the specified string name\n"
|
||||
<< "-h,--help\tPrints this message\n"
|
||||
<< "-p,--partial\tPerform partial (i.e. fast) validation tests\n"
|
||||
<< "-v,--verbose\tPrint verbose information information useful for debugging\n"
|
||||
<< "--version\tPrint version information to the terminal\n";
|
||||
exit(exitStatus);
|
||||
@@ -31,18 +30,17 @@ void usage [[noreturn]] (const std::string& progName, int exitStatus = EXIT_FAIL
|
||||
|
||||
void version [[noreturn]] (const char* progName, int exitStatus = EXIT_SUCCESS)
|
||||
{
|
||||
char str[8];
|
||||
nanovdb::toStr(str, nanovdb::Version());
|
||||
printf("\n%s was build against NanoVDB version %s\n", progName, str);
|
||||
printf("\n%s was build against NanoVDB version %s\n", progName, nanovdb::Version().c_str());
|
||||
exit(exitStatus);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int exitStatus = EXIT_SUCCESS;
|
||||
bool verbose = false;
|
||||
nanovdb::CheckMode mode = nanovdb::CheckMode::Full;
|
||||
std::string gridName;
|
||||
int exitStatus = EXIT_SUCCESS;
|
||||
|
||||
bool verbose = false;
|
||||
bool detailed = true;
|
||||
std::string gridName;
|
||||
std::vector<std::string> fileNames;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg = argv[i];
|
||||
@@ -53,8 +51,6 @@ int main(int argc, char* argv[])
|
||||
version(argv[0]);
|
||||
} else if (arg == "-v" || arg == "--verbose") {
|
||||
verbose = true;
|
||||
} else if (arg == "-p" || arg == "--partial") {
|
||||
mode = nanovdb::CheckMode::Partial;
|
||||
} else if (arg == "-g" || arg == "--grid") {
|
||||
if (i + 1 == argc) {
|
||||
std::cerr << "\nExpected a grid name to follow the -g,--grid option\n";
|
||||
@@ -83,17 +79,62 @@ int main(int argc, char* argv[])
|
||||
if (!gridName.empty()) {
|
||||
std::vector<nanovdb::io::FileGridMetaData> tmp;
|
||||
for (auto& m : list) {
|
||||
if (nameKey == m.nameKey && gridName == m.gridName) tmp.emplace_back(m);
|
||||
if (nameKey == m.nameKey && gridName == m.gridName)
|
||||
tmp.emplace_back(m);
|
||||
}
|
||||
list = std::move(tmp);
|
||||
list = tmp;
|
||||
}
|
||||
if (list.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (list.size() == 0) continue;
|
||||
|
||||
if (verbose) std::cout << "\nThe file \"" << file << "\" contains the following matching " << list.size() << " grid(s):\n";
|
||||
if (verbose) {
|
||||
std::cout << "\nThe file \"" << file << "\" contains the following matching " << list.size() << " grid(s):\n";
|
||||
}
|
||||
|
||||
for (auto& m : list) {
|
||||
auto handle = nanovdb::io::readGrid(file, m.gridName);
|
||||
const bool test = nanovdb::tools::validateGrids(handle, mode, verbose);
|
||||
auto gridType = handle.gridType();
|
||||
bool test = false;
|
||||
if (gridType == nanovdb::GridType::End) {
|
||||
std::cerr << "GridHandle was empty\n" << std::endl;
|
||||
usage(argv[0]);
|
||||
} else if (auto* grid = handle.grid<float>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<nanovdb::Vec3f>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<uint32_t>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<int32_t>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<int16_t>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<int64_t>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<int16_t>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<double>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<nanovdb::Vec3d>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<nanovdb::ValueMask>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<nanovdb::Rgba8>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<nanovdb::Fp4>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<nanovdb::Fp8>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<nanovdb::Fp16>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<nanovdb::FpN>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else if (auto* grid = handle.grid<bool>()) {
|
||||
test = isValid(*grid, detailed, verbose);
|
||||
} else {
|
||||
std::cerr << "Unsupported GridType: \"" << nanovdb::toStr(gridType) << "\"\n" << std::endl;
|
||||
usage(argv[0]);
|
||||
}
|
||||
if (verbose) {
|
||||
std::cout << "Grid named \"" << m.gridName << "\": " << (test ? "passed" : "failed") << std::endl;
|
||||
} else if (!test) {
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file DeviceBuffer.h
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date January 8, 2020
|
||||
|
||||
\brief Implements a simple dual (host/device) CUDA buffer.
|
||||
|
||||
\note This file has no device-only kernel functions,
|
||||
which explains why it's a .h and not .cuh file.
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_CUDA_DEVICEBUFFER_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_CUDA_DEVICEBUFFER_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/HostBuffer.h>// for BufferTraits
|
||||
#include <nanovdb/util/cuda/Util.h>// for cudaMalloc/cudaMallocManaged/cudaFree
|
||||
|
||||
namespace nanovdb {// ================================================================
|
||||
|
||||
namespace cuda {// ===================================================================
|
||||
|
||||
// ----------------------------> DeviceBuffer <--------------------------------------
|
||||
|
||||
/// @brief Simple memory buffer using un-managed pinned host memory when compiled with NVCC.
|
||||
/// Obviously this class is making explicit used of CUDA so replace it with your own memory
|
||||
/// allocator if you are not using CUDA.
|
||||
/// @note While CUDA's pinned host memory allows for asynchronous memory copy between host and device
|
||||
/// it is significantly slower then cached (un-pinned) memory on the host.
|
||||
class DeviceBuffer
|
||||
{
|
||||
uint64_t mSize; // total number of bytes managed by this buffer (assumed to be identical for host and device)
|
||||
void *mCpuData, *mGpuData; // raw pointers to the host and device buffers
|
||||
bool mManaged;
|
||||
|
||||
public:
|
||||
/// @brief Static factory method that return 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 host If true buffer is initialized only on the host/CPU, else on the device/GPU
|
||||
/// @param stream optional stream argument (defaults to stream NULL)
|
||||
/// @return An instance of this class using move semantics
|
||||
static DeviceBuffer create(uint64_t size, const DeviceBuffer* dummy = nullptr, bool host = true, void* stream = nullptr);
|
||||
|
||||
/// @brief Static factory method that return an instance of this buffer that wraps externally managed memory
|
||||
/// @param size byte size of buffer specified by external memory
|
||||
/// @param cpuData pointer to externally managed host memory
|
||||
/// @param gpuData pointer to externally managed device memory
|
||||
/// @return An instance of this class using move semantics
|
||||
static DeviceBuffer create(uint64_t size, void* cpuData, void* gpuData);
|
||||
|
||||
/// @brief Constructor
|
||||
/// @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 stream optional stream argument (defaults to stream NULL)
|
||||
DeviceBuffer(uint64_t size = 0, bool host = true, void* stream = nullptr)
|
||||
: mSize(0)
|
||||
, mCpuData(nullptr)
|
||||
, mGpuData(nullptr)
|
||||
, mManaged(false)
|
||||
{
|
||||
if (size > 0) this->init(size, host, stream);
|
||||
}
|
||||
|
||||
DeviceBuffer(uint64_t size, void* cpuData, void* gpuData)
|
||||
: mSize(size)
|
||||
, mCpuData(cpuData)
|
||||
, mGpuData(gpuData)
|
||||
, mManaged(false)
|
||||
{
|
||||
}
|
||||
|
||||
/// @brief Disallow copy-construction
|
||||
DeviceBuffer(const DeviceBuffer&) = delete;
|
||||
|
||||
/// @brief Move copy-constructor
|
||||
DeviceBuffer(DeviceBuffer&& other) noexcept
|
||||
: mSize(other.mSize)
|
||||
, mCpuData(other.mCpuData)
|
||||
, mGpuData(other.mGpuData)
|
||||
, mManaged(other.mManaged)
|
||||
{
|
||||
other.mSize = 0;
|
||||
other.mCpuData = nullptr;
|
||||
other.mGpuData = nullptr;
|
||||
other.mManaged = false;
|
||||
}
|
||||
|
||||
/// @brief Disallow copy assignment operation
|
||||
DeviceBuffer& operator=(const DeviceBuffer&) = delete;
|
||||
|
||||
/// @brief Move copy assignment operation
|
||||
DeviceBuffer& operator=(DeviceBuffer&& other) noexcept
|
||||
{
|
||||
this->clear();
|
||||
mSize = other.mSize;
|
||||
mCpuData = other.mCpuData;
|
||||
mGpuData = other.mGpuData;
|
||||
mManaged = other.mManaged;
|
||||
other.mSize = 0;
|
||||
other.mCpuData = nullptr;
|
||||
other.mGpuData = nullptr;
|
||||
other.mManaged = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief Destructor frees memory on both the host and device
|
||||
~DeviceBuffer() { this->clear(); };
|
||||
|
||||
/// @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
|
||||
/// @note All existing buffers are first cleared
|
||||
/// @warning size is expected to be non-zero. Use clear() clear buffer!
|
||||
void init(uint64_t size, bool host = true, void* stream = nullptr);
|
||||
|
||||
/// @brief Retuns a raw pointer to the host/CPU buffer managed by this allocator.
|
||||
/// @warning Note that the pointer can be NULL!
|
||||
void* data() const { return mCpuData; }
|
||||
|
||||
/// @brief Retuns a raw pointer to the device/GPU buffer managed by this allocator.
|
||||
/// @warning Note that the pointer can be NULL!
|
||||
void* deviceData() const { return mGpuData; }
|
||||
|
||||
/// @brief Upload this buffer from the host to the device, i.e. CPU -> GPU.
|
||||
/// @param stream optional CUDA stream (defaults to CUDA stream 0)
|
||||
/// @param sync if false the memory copy is asynchronous
|
||||
/// @note If the device/GPU buffer does not exist it is first allocated
|
||||
/// @warning Assumes that the host/CPU buffer already exists
|
||||
void deviceUpload(void* stream = nullptr, bool sync = true) const;
|
||||
|
||||
/// @brief Upload this buffer from the device to the host, i.e. GPU -> CPU.
|
||||
/// @param stream optional CUDA stream (defaults to CUDA stream 0)
|
||||
/// @param sync if false the memory copy is asynchronous
|
||||
/// @note If the host/CPU buffer does not exist it is first allocated
|
||||
/// @warning Assumes that the device/GPU buffer already exists
|
||||
void deviceDownload(void* stream = nullptr, bool sync = true) const;
|
||||
|
||||
/// @brief Returns the size in bytes of the raw memory buffer managed by this allocator.
|
||||
uint64_t size() const { return mSize; }
|
||||
|
||||
//@{
|
||||
/// @brief Returns true if this allocator is empty, i.e. has no allocated memory
|
||||
bool empty() const { return mSize == 0; }
|
||||
bool isEmpty() const { return mSize == 0; }
|
||||
//@}
|
||||
|
||||
/// @brief De-allocate all memory managed by this allocator and set all pointers to NULL
|
||||
void clear(void* stream = nullptr);
|
||||
|
||||
}; // DeviceBuffer class
|
||||
|
||||
// --------------------------> Implementations below <------------------------------------
|
||||
|
||||
inline DeviceBuffer DeviceBuffer::create(uint64_t size, const DeviceBuffer*, bool host, void* stream)
|
||||
{
|
||||
return DeviceBuffer(size, host, stream);
|
||||
}
|
||||
|
||||
inline DeviceBuffer DeviceBuffer::create(uint64_t size, void* cpuData, void* gpuData)
|
||||
{
|
||||
return DeviceBuffer(size, cpuData, gpuData);
|
||||
}
|
||||
|
||||
inline void DeviceBuffer::init(uint64_t size, bool host, void* stream)
|
||||
{
|
||||
if (mSize>0) this->clear(stream);
|
||||
NANOVDB_ASSERT(size > 0);
|
||||
if (host) {
|
||||
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(util::cuda::mallocAsync((void**)&mGpuData, size, reinterpret_cast<cudaStream_t>(stream))); // un-managed memory on the device, always 32B aligned!
|
||||
checkPtr(mGpuData, "cuda::DeviceBuffer::init: failed to allocate device buffer");
|
||||
}
|
||||
mSize = size;
|
||||
mManaged = true;
|
||||
} // DeviceBuffer::init
|
||||
|
||||
inline void DeviceBuffer::deviceUpload(void* stream, bool sync) const
|
||||
{
|
||||
if (!mManaged) throw std::runtime_error("DeviceBuffer::deviceUpload called on externally managed memory. Replace deviceUpload call with the appropriate external copy operation.");
|
||||
|
||||
checkPtr(mCpuData, "uninitialized cpu data");
|
||||
if (mGpuData == nullptr) {
|
||||
cudaCheck(util::cuda::mallocAsync((void**)&mGpuData, mSize, reinterpret_cast<cudaStream_t>(stream))); // un-managed memory on the device, always 32B aligned!
|
||||
}
|
||||
checkPtr(mGpuData, "uninitialized gpu data");
|
||||
cudaCheck(cudaMemcpyAsync(mGpuData, mCpuData, mSize, cudaMemcpyHostToDevice, reinterpret_cast<cudaStream_t>(stream)));
|
||||
if (sync) cudaCheck(cudaStreamSynchronize(reinterpret_cast<cudaStream_t>(stream)));
|
||||
} // DeviceBuffer::gpuUpload
|
||||
|
||||
inline void DeviceBuffer::deviceDownload(void* stream, bool sync) const
|
||||
{
|
||||
if (!mManaged) throw std::runtime_error("DeviceBuffer::deviceDownload called on externally managed memory. Replace deviceDownload call with the appropriate external copy operation.");
|
||||
|
||||
checkPtr(mGpuData, "uninitialized gpu data");
|
||||
if (mCpuData == nullptr) {
|
||||
cudaCheck(cudaMallocHost((void**)&mCpuData, mSize)); // un-managed pinned memory on the host (can be slow to access!). Always 32B aligned
|
||||
}
|
||||
checkPtr(mCpuData, "uninitialized cpu data");
|
||||
cudaCheck(cudaMemcpyAsync(mCpuData, mGpuData, mSize, cudaMemcpyDeviceToHost, reinterpret_cast<cudaStream_t>(stream)));
|
||||
if (sync) cudaCheck(cudaStreamSynchronize(reinterpret_cast<cudaStream_t>(stream)));
|
||||
} // DeviceBuffer::gpuDownload
|
||||
|
||||
inline void DeviceBuffer::clear(void *stream)
|
||||
{
|
||||
if (mManaged && mGpuData) cudaCheck(util::cuda::freeAsync(mGpuData, reinterpret_cast<cudaStream_t>(stream)));
|
||||
if (mManaged && mCpuData) cudaCheck(cudaFreeHost(mCpuData));
|
||||
mCpuData = mGpuData = nullptr;
|
||||
mSize = 0;
|
||||
mManaged = false;
|
||||
} // DeviceBuffer::clear
|
||||
|
||||
}// namespace cuda
|
||||
|
||||
using CudaDeviceBuffer [[deprecated("Use nanovdb::cuda::DeviceBuffer instead")]] = cuda::DeviceBuffer;
|
||||
|
||||
template<>
|
||||
struct BufferTraits<cuda::DeviceBuffer>
|
||||
{
|
||||
static constexpr bool hasDeviceDual = true;
|
||||
};
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // end of NANOVDB_CUDA_DEVICEBUFFER_H_HAS_BEEN_INCLUDED
|
||||
@@ -1,145 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/cuda/GridHandle.cuh
|
||||
|
||||
\author Ken Museth, Doyub Kim
|
||||
|
||||
\date August 3, 2023
|
||||
|
||||
\brief Contains cuda kernels for GridHandle
|
||||
|
||||
\warning The header file contains cuda device code so be sure
|
||||
to only include it in .cu files (or other .cuh files)
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_CUDA_GRIDHANDLE_CUH_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_CUDA_GRIDHANDLE_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>// required for instantiation of move c-tor of GridHandle
|
||||
#include <nanovdb/tools/cuda/GridChecksum.cuh>// for cuda::updateChecksum
|
||||
#include <nanovdb/GridHandle.h>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace cuda {
|
||||
|
||||
namespace {// anonymous namespace
|
||||
__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)
|
||||
{
|
||||
NANOVDB_ASSERT(gridIndex < gridCount);
|
||||
if (*d_dirty = d_data->mGridIndex != gridIndex || d_data->mGridCount != gridCount) {
|
||||
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
|
||||
|
||||
template<typename BufferT, template <class, class...> class VectorT = std::vector>
|
||||
inline typename util::enable_if<BufferTraits<BufferT>::hasDeviceDual, VectorT<GridHandle<BufferT>>>::type
|
||||
splitGridHandles(const GridHandle<BufferT> &handle, const BufferT* other = nullptr, cudaStream_t stream = 0)
|
||||
{
|
||||
const void *ptr = handle.deviceData();
|
||||
if (ptr == nullptr) return VectorT<GridHandle<BufferT>>();
|
||||
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));
|
||||
for (uint32_t n=0; n<handle.gridCount(); ++n) {
|
||||
auto buffer = BufferT::create(handle.gridSize(n), other, false, 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);
|
||||
cudaCheckError();
|
||||
cudaCheck(cudaMemcpyAsync(&dirty, d_dirty, sizeof(bool), cudaMemcpyDeviceToHost, 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);
|
||||
}// cuda::splitGridHandles
|
||||
|
||||
template<typename BufferT, template <class, class...> class VectorT>
|
||||
inline typename util::enable_if<BufferTraits<BufferT>::hasDeviceDual, GridHandle<BufferT>>::type
|
||||
mergeGridHandles(const VectorT<GridHandle<BufferT>> &handles, const BufferT* other = nullptr, cudaStream_t stream = 0)
|
||||
{
|
||||
uint64_t size = 0u;
|
||||
uint32_t counter = 0u, gridCount = 0u;
|
||||
for (auto &h : handles) {
|
||||
gridCount += h.gridCount();
|
||||
for (uint32_t n=0; n<h.gridCount(); ++n) size += h.gridSize(n);
|
||||
}
|
||||
auto buffer = BufferT::create(size, other, false, stream);
|
||||
void *dst = buffer.deviceData();
|
||||
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));
|
||||
for (auto &h : handles) {
|
||||
const void *src = h.deviceData();
|
||||
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);
|
||||
cudaCheckError();
|
||||
cudaCheck(cudaMemcpyAsync(&dirty, d_dirty, sizeof(bool), cudaMemcpyDeviceToHost, stream));
|
||||
if (dirty) tools::cuda::updateChecksum(data, CheckMode::Partial, stream);
|
||||
dst = util::PtrAdd(dst, h.gridSize(n));
|
||||
src = util::PtrAdd(src, h.gridSize(n));
|
||||
}
|
||||
}
|
||||
cudaCheck(util::cuda::freeAsync(d_dirty, stream));
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
}// cuda::mergeGridHandles
|
||||
|
||||
}// namespace cuda
|
||||
|
||||
template<typename BufferT, template <class, class...> class VectorT = std::vector>
|
||||
[[deprecated("Use nanovdb::cuda::splitGridHandles instead")]]
|
||||
inline typename util::enable_if<BufferTraits<BufferT>::hasDeviceDual, VectorT<GridHandle<BufferT>>>::type
|
||||
splitDeviceGrids(const GridHandle<BufferT> &handle, const BufferT* other = nullptr, cudaStream_t stream = 0)
|
||||
{ return cuda::splitGridHandles(handle, other, stream); }
|
||||
|
||||
template<typename BufferT, template <class, class...> class VectorT>
|
||||
[[deprecated("Use nanovdb::cuda::mergeGridHandles instead")]]
|
||||
inline typename util::enable_if<BufferTraits<BufferT>::hasDeviceDual, GridHandle<BufferT>>::type
|
||||
mergeDeviceGrids(const VectorT<GridHandle<BufferT>> &handles, const BufferT* other = nullptr, cudaStream_t stream = 0)
|
||||
{ return cuda::mergeGridHandles<BufferT, VectorT>(handles, other, stream); }
|
||||
|
||||
template<typename BufferT>
|
||||
template<typename T, typename util::enable_if<BufferTraits<T>::hasDeviceDual, int>::type>
|
||||
GridHandle<BufferT>::GridHandle(T&& buffer)
|
||||
{
|
||||
static_assert(util::is_same<T,BufferT>::value, "Expected U==BufferT");
|
||||
mBuffer = std::move(buffer);
|
||||
if (auto *data = reinterpret_cast<const GridData*>(mBuffer.data())) {
|
||||
if (!data->isValid()) throw std::runtime_error("GridHandle was constructed with an invalid host buffer");
|
||||
mMetaData.resize(data->mGridCount);
|
||||
cpyGridHandleMeta(data, mMetaData.data());
|
||||
} else {
|
||||
if (auto *d_data = reinterpret_cast<const GridData*>(mBuffer.deviceData())) {
|
||||
GridData tmp;
|
||||
cudaCheck(cudaMemcpy(&tmp, d_data, sizeof(GridData), cudaMemcpyDeviceToHost));
|
||||
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);
|
||||
mMetaData.resize(tmp.mGridCount);
|
||||
cudaCheck(cudaMemcpy(mMetaData.data(), d_metaData,tmp.mGridCount*sizeof(GridHandleMetaData), cudaMemcpyDeviceToHost));
|
||||
cudaCheck(cudaFree(d_metaData));
|
||||
}
|
||||
}
|
||||
}// GridHandle(T&& buffer)
|
||||
|
||||
// Dummy function that ensures instantiation of the move-constructor above when BufferT=cuda::DeviceBuffer
|
||||
namespace {auto __dummy(){return GridHandle<cuda::DeviceBuffer>(std::move(cuda::DeviceBuffer()));}}
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_CUDA_GRIDHANDLE_CUH_HAS_BEEN_INCLUDED
|
||||
@@ -1,104 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/cuda/NodeManager.cuh
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date October 3, 2023
|
||||
|
||||
\brief Contains cuda kernels for NodeManager
|
||||
|
||||
\warning The header file contains cuda device code so be sure
|
||||
to only include it in .cu files (or other .cuh files)
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_CUDA_NODE_MANAGER_CUH_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_CUDA_NODE_MANAGER_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/util/cuda/Util.h>// for cuda::lambdaKernel
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/NodeManager.h>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace cuda {
|
||||
|
||||
/// @brief Construct a NodeManager from a device grid pointer
|
||||
///
|
||||
/// @param d_grid device grid pointer whose nodes will be accessed sequentially
|
||||
/// @param buffer buffer from which to allocate the output handle
|
||||
/// @param stream cuda stream
|
||||
/// @return Handle that contains a device NodeManager
|
||||
template <typename BuildT, typename BufferT = DeviceBuffer>
|
||||
inline typename util::enable_if<BufferTraits<BufferT>::hasDeviceDual, NodeManagerHandle<BufferT>>::type
|
||||
createNodeManager(const NanoGrid<BuildT> *d_grid,
|
||||
const BufferT& pool = BufferT(),
|
||||
cudaStream_t stream = 0)
|
||||
{
|
||||
auto buffer = BufferT::create(sizeof(NodeManagerData), &pool, false, stream);
|
||||
auto *d_data = (NodeManagerData*)buffer.deviceData();
|
||||
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_size = sizeof(NodeManagerData);
|
||||
auto &tree = d_grid->tree();
|
||||
if (NodeManager<BuildT>::FIXED_SIZE && d_grid->isBreadthFirst()) {
|
||||
d_data->mLinear = uint8_t(1u);
|
||||
d_data->mOff[0] = util::PtrDiff(tree.template getFirstNode<0>(), d_grid);
|
||||
d_data->mOff[1] = util::PtrDiff(tree.template getFirstNode<1>(), d_grid);
|
||||
d_data->mOff[2] = util::PtrDiff(tree.template getFirstNode<2>(), d_grid);
|
||||
} else {
|
||||
*d_size += sizeof(uint64_t)*tree.totalNodeCount();
|
||||
}
|
||||
});
|
||||
cudaCheckError();
|
||||
cudaCheck(cudaMemcpyAsync(&size, d_size, sizeof(size_t), cudaMemcpyDeviceToHost, stream));
|
||||
cudaCheck(util::cuda::freeAsync(d_size, stream));
|
||||
if (size > sizeof(NodeManagerData)) {
|
||||
auto tmp = BufferT::create(size, &pool, false, stream);// only allocate buffer on the device
|
||||
cudaCheck(cudaMemcpyAsync(tmp.deviceData(), buffer.deviceData(), sizeof(NodeManagerData), cudaMemcpyDeviceToDevice, stream));
|
||||
buffer = std::move(tmp);
|
||||
d_data = reinterpret_cast<NodeManagerData*>(buffer.deviceData());
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, stream>>>(1, [=] __device__ (size_t) {
|
||||
auto &tree = d_grid->tree();
|
||||
int64_t *ptr0 = d_data->mPtr[0] = reinterpret_cast<int64_t*>(d_data + 1);
|
||||
int64_t *ptr1 = d_data->mPtr[1] = d_data->mPtr[0] + tree.nodeCount(0);
|
||||
int64_t *ptr2 = d_data->mPtr[2] = d_data->mPtr[1] + tree.nodeCount(1);
|
||||
// Performs depth first traversal but breadth first insertion
|
||||
for (auto it2 = tree.root().cbeginChild(); it2; ++it2) {
|
||||
*ptr2++ = util::PtrDiff(&*it2, d_grid);
|
||||
for (auto it1 = it2->cbeginChild(); it1; ++it1) {
|
||||
*ptr1++ = util::PtrDiff(&*it1, d_grid);
|
||||
for (auto it0 = it1->cbeginChild(); it0; ++it0) {
|
||||
*ptr0++ = util::PtrDiff(&*it0, d_grid);
|
||||
}// loop over child nodes of the lower internal node
|
||||
}// loop over child nodes of the upper internal node
|
||||
}// loop over child nodes of the root node
|
||||
});
|
||||
}
|
||||
|
||||
return NodeManagerHandle<BufferT>(toGridType<BuildT>(), std::move(buffer));
|
||||
}// cuda::createNodeManager
|
||||
|
||||
}// namespace cuda
|
||||
|
||||
template <typename BuildT, typename BufferT = cuda::DeviceBuffer>
|
||||
[[deprecated("Use cuda::createNodeManager instead")]]
|
||||
inline typename util::enable_if<BufferTraits<BufferT>::hasDeviceDual, NodeManagerHandle<BufferT>>::type
|
||||
cudaCreateNodeManager(const NanoGrid<BuildT> *d_grid,
|
||||
const BufferT& pool = BufferT(),
|
||||
cudaStream_t stream = 0)
|
||||
{
|
||||
return cuda::createNodeManager<BuildT, BufferT>(d_grid, pool, stream);
|
||||
}
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_CUDA_NODE_MANAGER_CUH_HAS_BEEN_INCLUDED
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/HostBuffer.h>
|
||||
#include <nanovdb/util/Primitives.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
#include <nanovdb/util/HostBuffer.h>
|
||||
#include <algorithm>
|
||||
|
||||
//////////////////////////////////////////////
|
||||
@@ -83,11 +83,11 @@ public:
|
||||
|
||||
// Mandatory.
|
||||
// Return non-const pointer to the buffer data.
|
||||
void* data() { return nanovdb::util::PtrAdd(mState->mPoolSlab.data(), mOffset); }
|
||||
uint8_t* data() { return mState->mPoolSlab.data() + mOffset; }
|
||||
|
||||
// Mandatory.
|
||||
// Return const pointer to the buffer data.
|
||||
const void* data() const { return nanovdb::util::PtrAdd(mState->mPoolSlab.data(), mOffset); }
|
||||
const uint8_t* data() const { return mState->mPoolSlab.data() + mOffset; }
|
||||
};
|
||||
|
||||
// we specify this trait to avoid declaring the "device...(...)" convenience methods.
|
||||
@@ -110,8 +110,8 @@ int main()
|
||||
std::vector<nanovdb::GridHandle<PoolBuffer>> gridHdls;
|
||||
|
||||
// create two grids...
|
||||
gridHdls.push_back(nanovdb::tools::createLevelSetSphere<float >(100.0, nanovdb::Vec3d(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "spheref", nanovdb::tools::StatsMode::BBox, nanovdb::CheckMode::Partial, bufferContext));
|
||||
gridHdls.push_back(nanovdb::tools::createLevelSetSphere<double>(100.0, nanovdb::Vec3d( 20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphered", nanovdb::tools::StatsMode::BBox, nanovdb::CheckMode::Partial, bufferContext));
|
||||
gridHdls.push_back(nanovdb::createLevelSetSphere<float >(100.0, nanovdb::Vec3d(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "spheref", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, bufferContext));
|
||||
gridHdls.push_back(nanovdb::createLevelSetSphere<double>(100.0, nanovdb::Vec3d( 20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphered", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, bufferContext));
|
||||
|
||||
// Get a (raw) pointer to the NanoVDB grid form the GridManager.
|
||||
auto* dstGrid = gridHdls[0].grid<float>();
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
#include <nanovdb/util/Primitives.h>
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
|
||||
#if defined(NANOVDB_USE_CUDA)
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
using BufferT = nanovdb::CudaDeviceBuffer;
|
||||
#else
|
||||
using BufferT = nanovdb::HostBuffer;
|
||||
#endif
|
||||
@@ -26,7 +26,7 @@ int main(int ac, char** av)
|
||||
handle = nanovdb::io::readGrid<BufferT>(av[1]);
|
||||
std::cout << "Loaded NanoVDB grid[" << handle.gridMetaData()->shortGridName() << "]...\n";
|
||||
} else {
|
||||
handle = nanovdb::tools::createLevelSetSphere<float, BufferT>(100.0f, nanovdb::Vec3d(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphere");
|
||||
handle = nanovdb::createLevelSetSphere<float, BufferT>(100.0f, nanovdb::Vec3d(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphere");
|
||||
}
|
||||
|
||||
if (handle.gridMetaData()->isLevelSet() == false) {
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
#include <cmath>
|
||||
#include <chrono>
|
||||
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/math/Ray.h>
|
||||
#include <nanovdb/math/HDDA.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
#include <nanovdb/util/Ray.h>
|
||||
#include <nanovdb/util/HDDA.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(NANOVDB_USE_CUDA)
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
using BufferT = nanovdb::CudaDeviceBuffer;
|
||||
#else
|
||||
using BufferT = nanovdb::HostBuffer;
|
||||
#endif
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
#include <openvdb/openvdb.h>
|
||||
#include <openvdb/math/Ray.h>
|
||||
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/tools/NanoToOpenVDB.h>
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
#include <nanovdb/util/NanoToOpenVDB.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(NANOVDB_USE_CUDA)
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
using BufferT = nanovdb::CudaDeviceBuffer;
|
||||
#else
|
||||
using BufferT = nanovdb::HostBuffer;
|
||||
#endif
|
||||
@@ -29,9 +29,9 @@ void runOpenVDB(nanovdb::GridHandle<BufferT>& handle, int numIterations, int num
|
||||
using CoordT = openvdb::Coord;
|
||||
using RealT = float;
|
||||
using Vec3T = openvdb::math::Vec3<RealT>;
|
||||
using RayT = openvdb::math::Ray<RealT>;
|
||||
using RayT = openvdb::math::Ray<RealT>;
|
||||
|
||||
auto srcGrid = nanovdb::tools::nanoToOpenVDB(handle);
|
||||
auto srcGrid = nanovdb::nanoToOpenVDB(handle);
|
||||
std::cout << "Exporting to OpenVDB grid[" << srcGrid->getName() << "]...\n";
|
||||
|
||||
auto h_grid = (GridT*)srcGrid.get();
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/tools/CreatePrimitives.h>// for nanovdb::tools::createLevelSetSphere
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>// for nanovdb::cuda::DeviceBuffer
|
||||
#include <nanovdb/util/CreateNanoGrid.h>
|
||||
#include <nanovdb/util/Primitives.h> // for nanovdb::createLevelSetSphere
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>// for nanovdb::CudaDeviceBuffer
|
||||
|
||||
extern "C" void launch_kernels(const nanovdb::NanoGrid<nanovdb::ValueOnIndex>*,// device grid
|
||||
const nanovdb::NanoGrid<nanovdb::ValueOnIndex>*,// host grid
|
||||
cudaStream_t stream);
|
||||
|
||||
/// @brief This examples depends on NanoVDB and CUDA.
|
||||
/// @brief This examples depends on NanoVDB and CUDA.
|
||||
int main(int, char**)
|
||||
{
|
||||
using SrcGridT = nanovdb::FloatGrid;
|
||||
using DstBuildT = nanovdb::ValueOnIndex;
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
using BufferT = nanovdb::CudaDeviceBuffer;
|
||||
try {
|
||||
// Create an NanoVDB grid of a sphere at the origin with radius 100 and voxel size 1.
|
||||
auto srcHandle = nanovdb::tools::createLevelSetSphere<float>();
|
||||
auto srcHandle = nanovdb::createLevelSetSphere<float>();
|
||||
auto *srcGrid = srcHandle.grid<float>();
|
||||
|
||||
// Converts the FloatGrid to an IndexGrid using CUDA for memory management.
|
||||
auto idxHandle = nanovdb::tools::createNanoGrid<SrcGridT, DstBuildT, BufferT>(*srcGrid, 1u, false , false);// 1 channel, no tiles or stats
|
||||
auto idxHandle = nanovdb::createNanoGrid<SrcGridT, DstBuildT, BufferT>(*srcGrid, 1u, false , false);// 1 channel, no tiles or stats
|
||||
|
||||
cudaStream_t stream; // Create a CUDA stream to allow for asynchronous copy of pinned CUDA memory.
|
||||
cudaStreamCreate(&stream);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#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 <nanovdb/util/cuda/CudaGridHandle.cuh>// required since GridHandle<CudaDeviceBuffer> has device code
|
||||
#include <stdio.h> // for printf
|
||||
|
||||
// This is called by the host only
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/tools/GridBuilder.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/util/GridBuilder.h>
|
||||
#include <nanovdb/util/CreateNanoGrid.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/// @brief Creates a NanoVDB grid with custom values and access them.
|
||||
/// @brief Creates a NanoVDB grids with custom values and access them.
|
||||
///
|
||||
/// @note This example only depends on NanoVDB.
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
nanovdb::tools::build::Grid<float> grid(0.0f);
|
||||
nanovdb::build::Grid<float> grid(0.0f);
|
||||
auto acc = grid.getAccessor();
|
||||
acc.setValue(nanovdb::Coord(1, 2, 3), 1.0f);
|
||||
|
||||
printf("build::Grid: (%i,%i,%i)=%4.2f\t", 1, 2, 3, acc.getValue(nanovdb::Coord(1, 2, 3)));
|
||||
printf("build::Grid: (%i,%i,%i)=%4.2f\n", 1, 2,-3, acc.getValue(nanovdb::Coord(1, 2,-3)));
|
||||
|
||||
auto handle = nanovdb::tools::createNanoGrid(grid);
|
||||
auto handle = nanovdb::createNanoGrid(grid);
|
||||
auto* dstGrid = handle.grid<float>(); // Get a (raw) pointer to the NanoVDB grid form the GridManager.
|
||||
if (!dstGrid)
|
||||
throw std::runtime_error("GridHandle does not contain a grid with value type float");
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
#undef NANOVDB_USE_OPENVDB // Prevents include/openvdb/points/AttributeArray.h:1841:25: error: ‘stride’ cannot be used as a function
|
||||
|
||||
#include <nanovdb/tools/GridBuilder.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/util/GridBuilder.h>
|
||||
#include <nanovdb/util/CreateNanoGrid.h>
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@@ -13,13 +13,13 @@ extern "C" void launch_kernels(const nanovdb::NanoGrid<float>*,// GPU grid
|
||||
const nanovdb::NanoGrid<float>*,// CPU grid
|
||||
cudaStream_t stream);
|
||||
|
||||
/// @brief Creates a NanoVDB grid with custom values and access them.
|
||||
/// @brief Creates a NanoVDB grids with custom values and access them.
|
||||
///
|
||||
/// @note This example only depends on NanoVDB.
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
using GridT = nanovdb::tools::build::Grid<float>;
|
||||
using GridT = nanovdb::build::Grid<float>;
|
||||
GridT grid(0.0f);// empty grid with a background value of zero
|
||||
auto acc = grid.getAccessor();
|
||||
acc.setValue(nanovdb::Coord(1, 2, 3), 1.0f);
|
||||
@@ -27,7 +27,7 @@ int main()
|
||||
printf("build::Grid: (%i,%i,%i)=%4.2f\n", 1, 2, 3, acc.getValue(nanovdb::Coord(1, 2, 3)));
|
||||
|
||||
// convert build::grid to a nanovdb::GridHandle using a Cuda buffer
|
||||
auto handle = nanovdb::tools::createNanoGrid<GridT, float, nanovdb::cuda::DeviceBuffer>(grid);
|
||||
auto handle = nanovdb::createNanoGrid<GridT, float, nanovdb::CudaDeviceBuffer>(grid);
|
||||
|
||||
auto* cpuGrid = handle.grid<float>(); //get a (raw) pointer to a NanoVDB grid of value type float on the CPU
|
||||
if (!cpuGrid) throw std::runtime_error("GridHandle does not contain a grid with value type float");
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#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 <nanovdb/util/cuda/CudaGridHandle.cuh>// required since GridHandle<CudaDeviceBuffer> has device code
|
||||
#include <stdio.h> // for printf
|
||||
|
||||
// This is called by the host only
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/tools/GridBuilder.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/util/GridBuilder.h>
|
||||
#include <nanovdb/util/CreateNanoGrid.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/// @brief Creates a NanoVDB grid with custom values and access them.
|
||||
/// @brief Creates a NanoVDB grids with custom values and access them.
|
||||
///
|
||||
/// @note This example only depends on NanoVDB.
|
||||
int main()
|
||||
@@ -20,12 +20,12 @@ int main()
|
||||
float v = 40.0f + 50.0f*(cos(ijk[0]*0.1f)*sin(ijk[1]*0.1f) +
|
||||
cos(ijk[1]*0.1f)*sin(ijk[2]*0.1f) +
|
||||
cos(ijk[2]*0.1f)*sin(ijk[0]*0.1f));
|
||||
v = math::Max(v, Vec3f(ijk).length() - size);// CSG intersection with a sphere
|
||||
v = Max(v, Vec3f(ijk).length() - size);// CSG intersection with a sphere
|
||||
return v > background ? background : v < -background ? -background : v;// clamp value
|
||||
};
|
||||
tools::build::Grid<float> grid(background, "funny", GridClass::LevelSet);
|
||||
build::Grid<float> grid(background, "funny", GridClass::LevelSet);
|
||||
grid(func, CoordBBox(Coord(-size), Coord(size)));
|
||||
io::writeGrid("data/funny.nvdb", tools::createNanoGrid(grid), io::Codec::BLOSC);
|
||||
io::writeGrid("data/funny.nvdb", createNanoGrid(grid), io::Codec::BLOSC);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "An exception occurred: \"" << e.what() << "\"" << std::endl;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/util/Primitives.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/// @brief Creates a NanoVDB grid of a level set sphere and access a value.
|
||||
/// @brief Creates a NanoVDB grids of a level set sphere and accesses a value.
|
||||
///
|
||||
/// @note This example only depends on NanoVDB.
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
auto handle = nanovdb::tools::createLevelSetSphere(100.0f);
|
||||
auto handle = nanovdb::createLevelSetSphere(100.0f);
|
||||
|
||||
auto* dstGrid = handle.grid<float>(); // Get a (raw) pointer to the NanoVDB grid form the GridManager.
|
||||
if (!dstGrid)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/tools/GridBuilder.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/util/GridBuilder.h>
|
||||
#include <nanovdb/util/CreateNanoGrid.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
|
||||
// Helper struct to create a default value for the type.
|
||||
// We use a helper struct so we can specialize it for different types.
|
||||
@@ -37,7 +37,7 @@ void buildGridForType(std::vector<nanovdb::GridHandle<>>& gridHandles, T const&
|
||||
|
||||
try {
|
||||
|
||||
nanovdb::tools::build::Grid<ValueT> grid(bgValue, typeNameStr);
|
||||
nanovdb::build::Grid<ValueT> grid(bgValue, typeNameStr);
|
||||
auto acc = grid.getAccessor();
|
||||
const int radius = 16;
|
||||
for (int z = -radius; z <= radius; ++z) {
|
||||
@@ -49,7 +49,7 @@ void buildGridForType(std::vector<nanovdb::GridHandle<>>& gridHandles, T const&
|
||||
}
|
||||
}
|
||||
}
|
||||
gridHandles.push_back(nanovdb::tools::createNanoGrid(grid));
|
||||
gridHandles.push_back(nanovdb::createNanoGrid(grid));
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "An exception occurred: \"" << e.what() << "\"" << std::endl;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/HostBuffer.h>
|
||||
#include <nanovdb/util/Primitives.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
#include <nanovdb/util/HostBuffer.h>
|
||||
#include <algorithm>
|
||||
|
||||
//////////////////////////////////////////////
|
||||
@@ -95,8 +95,8 @@ public:
|
||||
}
|
||||
|
||||
uint64_t size() const { return getBuffer(mId).size(); }
|
||||
void* data() { return getBuffer(mId).data(); }
|
||||
const void* data() const { return getBuffer(mId).data(); }
|
||||
uint8_t* data() { return getBuffer(mId).data(); }
|
||||
const uint8_t* data() const { return getBuffer(mId).data(); }
|
||||
|
||||
std::vector<uint64_t> getGridKeys() const
|
||||
{
|
||||
@@ -148,8 +148,8 @@ int main()
|
||||
std::vector<nanovdb::GridHandle<MapPoolBuffer>> gridHdls;
|
||||
|
||||
// create two grids...
|
||||
gridHdls.push_back(nanovdb::tools::createLevelSetSphere<float >(100.0, nanovdb::Vec3d(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "spheref", nanovdb::tools::StatsMode::BBox, nanovdb::CheckMode::Partial, bufferContext));
|
||||
gridHdls.push_back(nanovdb::tools::createLevelSetSphere<double>(100.0, nanovdb::Vec3d( 20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphered", nanovdb::tools::StatsMode::BBox, nanovdb::CheckMode::Partial, bufferContext));
|
||||
gridHdls.push_back(nanovdb::createLevelSetSphere<float >(100.0, nanovdb::Vec3d(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "spheref", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, bufferContext));
|
||||
gridHdls.push_back(nanovdb::createLevelSetSphere<double>(100.0, nanovdb::Vec3d( 20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphered", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, bufferContext));
|
||||
|
||||
// share grid[0]'s buffer into a parent-scope handle to prevent deletion.
|
||||
anotherHdl = nanovdb::GridHandle<MapPoolBuffer>(bufferContext.copy(gridHdls[0].buffer().mId));
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
/// @brief This examples demonstrates how values in a NanpVDB grid can be
|
||||
/// modified on the device. It depends on NanoVDB and CUDA thrust.
|
||||
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/util/Primitives.h>
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
|
||||
extern "C" void scaleActiveVoxels(nanovdb::FloatGrid *grid_d, uint64_t leafCount, float scale);
|
||||
|
||||
@@ -13,7 +13,7 @@ int main()
|
||||
{
|
||||
try {
|
||||
// Create an NanoVDB grid of a sphere at the origin with radius 100 and voxel size 1.
|
||||
auto handle = nanovdb::tools::createLevelSetSphere<float, nanovdb::cuda::DeviceBuffer>(100.0f);
|
||||
auto handle = nanovdb::createLevelSetSphere<float, nanovdb::CudaDeviceBuffer>(100.0f);
|
||||
using GridT = nanovdb::FloatGrid;
|
||||
|
||||
handle.deviceUpload(0, false); // Copy the NanoVDB grid to the GPU asynchronously
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
#include <thrust/iterator/counting_iterator.h>
|
||||
#include <thrust/for_each.h>
|
||||
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/cuda/GridHandle.cuh>
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
#include <nanovdb/util/cuda/CudaGridHandle.cuh>
|
||||
|
||||
extern "C" void scaleActiveVoxels(nanovdb::FloatGrid *grid_d, uint64_t leafCount, float scale)
|
||||
{
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <openvdb/tools/LevelSetSphere.h> // replace with your own dependencies for generating the OpenVDB grid
|
||||
#include <nanovdb/tools/CreateNanoGrid.h> // converter from OpenVDB to NanoVDB (includes NanoVDB.h and GridManager.h)
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/NodeManager.h>
|
||||
#include <nanovdb/util/CreateNanoGrid.h> // converter from OpenVDB to NanoVDB (includes NanoVDB.h and GridManager.h)
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
#include <nanovdb/util/NodeManager.h>
|
||||
|
||||
extern "C" void launch_kernels(const nanovdb::NodeManager<float>*,// device NaodeManager
|
||||
const nanovdb::NodeManager<float>*,// host NodeManager
|
||||
extern "C" void launch_kernels(const nanovdb::NodeManager<float>*,
|
||||
const nanovdb::NodeManager<float>*,
|
||||
cudaStream_t stream);
|
||||
|
||||
extern "C" void cudaCreateNodeManager(const nanovdb::NanoGrid<float>*,// device grid
|
||||
nanovdb::NodeManagerHandle<nanovdb::cuda::DeviceBuffer>*);// Handle to device NodeManager
|
||||
extern "C" void cudaCreateNodeManager(const nanovdb::NanoGrid<float>*,
|
||||
nanovdb::NodeManagerHandle<nanovdb::CudaDeviceBuffer>*);
|
||||
|
||||
/// @brief This examples depends on OpenVDB, NanoVDB and CUDA.
|
||||
int main()
|
||||
{
|
||||
using SrcGridT = openvdb::FloatGrid;
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
using BufferT = nanovdb::CudaDeviceBuffer;
|
||||
try {
|
||||
cudaStream_t stream; // Create a CUDA stream to allow for asynchronous copy of pinned CUDA memory.
|
||||
cudaStreamCreate(&stream);
|
||||
@@ -26,7 +26,7 @@ int main()
|
||||
auto srcGrid = openvdb::tools::createLevelSetSphere<SrcGridT>(100.0f, openvdb::Vec3f(0.0f), 1.0f);
|
||||
|
||||
// Converts the OpenVDB to NanoVDB and returns a GridHandle that uses CUDA for memory management.
|
||||
auto gridHandle = nanovdb::tools::createNanoGrid<SrcGridT, float, BufferT>(*srcGrid);
|
||||
auto gridHandle = nanovdb::createNanoGrid<SrcGridT, float, BufferT>(*srcGrid);
|
||||
gridHandle.deviceUpload(stream, false); // Copy the NanoVDB grid to the GPU asynchronously
|
||||
auto* grid = gridHandle.grid<float>(); // get a (raw) pointer to a NanoVDB grid of value type float on the CPU
|
||||
auto* deviceGrid = gridHandle.deviceGrid<float>(); // get a (raw) pointer to a NanoVDB grid of value type float on the GPU
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/NanoVDB.h> // this defined the core tree data structure of NanoVDB accessable on both the host and device
|
||||
#include <nanovdb/NodeManager.h>
|
||||
#include <nanovdb/cuda/GridHandle.cuh>// required since GridHandle<CudaDeviceBuffer> has device code
|
||||
#include <nanovdb/cuda/NodeManager.cuh>
|
||||
#include <nanovdb/util/NodeManager.h>
|
||||
#include <nanovdb/util/cuda/CudaGridHandle.cuh>// required since GridHandle<CudaDeviceBuffer> has device code
|
||||
#include <nanovdb/util/cuda/CudaNodeManager.cuh>
|
||||
#include <stdio.h> // for printf
|
||||
|
||||
// This is called by the host only
|
||||
@@ -29,9 +29,9 @@ extern "C" void launch_kernels(const nanovdb::NodeManager<float>* deviceMgr,
|
||||
cpu_kernel(cpuMgr); // Launch the host "kernel" (synchronously)
|
||||
}
|
||||
|
||||
// Simple wrapper that makes sure nanovdb::cuda::createNodeManager is initiated
|
||||
// Simple wrapper that makes sure nanovdb::cudaCreateNodeManager is initiated
|
||||
extern "C" void cudaCreateNodeManager(const nanovdb::NanoGrid<float> *d_grid,
|
||||
nanovdb::NodeManagerHandle<nanovdb::CudaDeviceBuffer> *handle)
|
||||
{
|
||||
*handle = std::move(nanovdb::cuda::createNodeManager<float>(d_grid));
|
||||
*handle = std::move(nanovdb::cudaCreateNodeManager<float>(d_grid));
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <openvdb/tools/LevelSetSphere.h> // replace with your own dependencies for generating the OpenVDB grid
|
||||
#include <nanovdb/tools/CreateNanoGrid.h> // converter from OpenVDB to NanoVDB (includes NanoVDB.h and GridManager.h)
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/util/CreateNanoGrid.h> // converter from OpenVDB to NanoVDB (includes NanoVDB.h and GridManager.h)
|
||||
#include <nanovdb/util/IO.h>
|
||||
|
||||
/// @brief Convert an openvdb level set sphere into a nanovdb, access a single value in both grids, and save NanoVDB to file.
|
||||
///
|
||||
@@ -13,7 +13,7 @@ int main()
|
||||
try {
|
||||
// Create an OpenVDB grid of a sphere at the origin with radius 100 and voxel size 1.
|
||||
auto srcGrid = openvdb::tools::createLevelSetSphere<openvdb::FloatGrid>(100.0f, openvdb::Vec3f(0.0f), 1.0f);
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid); // Convert from OpenVDB to NanoVDB and return a shared pointer to a GridHandle.
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid); // Convert from OpenVDB to NanoVDB and return a shared pointer to a GridHandle.
|
||||
auto* dstGrid = handle.grid<float>(); // Get a (raw) pointer to the NanoVDB grid form the GridManager.
|
||||
if (!dstGrid)
|
||||
throw std::runtime_error("GridHandle does not contain a grid with value type float");
|
||||
|
||||
+3
-3
@@ -2,8 +2,8 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <openvdb/tools/LevelSetSphere.h> // replace with your own dependencies for generating the OpenVDB grid
|
||||
#include <nanovdb/tools/CreateNanoGrid.h> // converter from OpenVDB to NanoVDB (includes NanoVDB.h and GridManager.h)
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/util/CreateNanoGrid.h> // converter from OpenVDB to NanoVDB (includes NanoVDB.h and GridManager.h)
|
||||
#include <nanovdb/util/IO.h>
|
||||
|
||||
// Convert an openvdb level set sphere into a nanovdb, use accessors to print out multiple values from both
|
||||
// grids and save the NanoVDB grid to file.
|
||||
@@ -15,7 +15,7 @@ int main()
|
||||
auto srcGrid = openvdb::tools::createLevelSetSphere<openvdb::FloatGrid>(100.0f, openvdb::Vec3f(0.0f), 1.0f);
|
||||
|
||||
// Convert the OpenVDB grid, srcGrid, into a NanoVDB grid handle.
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
|
||||
// Define a (raw) pointer to the NanoVDB grid on the host. Note we match the value type of the srcGrid!
|
||||
auto* dstGrid = handle.grid<float>();
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <openvdb/tools/LevelSetSphere.h> // replace with your own dependencies for generating the OpenVDB grid
|
||||
#include <nanovdb/tools/CreateNanoGrid.h> // converter from OpenVDB to NanoVDB (includes NanoVDB.h and GridManager.h)
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/util/CreateNanoGrid.h> // converter from OpenVDB to NanoVDB (includes NanoVDB.h and GridManager.h)
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
|
||||
extern "C" void launch_kernels(const nanovdb::NanoGrid<float>*,
|
||||
const nanovdb::NanoGrid<float>*,
|
||||
@@ -18,7 +18,7 @@ int main(int, char**)
|
||||
auto srcGrid = openvdb::tools::createLevelSetSphere<SrcGridT>(100.0f, openvdb::Vec3f(0.0f), 1.0f);
|
||||
|
||||
// Converts the OpenVDB to NanoVDB and returns a GridHandle that uses CUDA for memory management.
|
||||
auto handle = nanovdb::tools::createNanoGrid<SrcGridT, float, nanovdb::cuda::DeviceBuffer>(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid<SrcGridT, float, nanovdb::CudaDeviceBuffer>(*srcGrid);
|
||||
|
||||
cudaStream_t stream; // Create a CUDA stream to allow for asynchronous copy of pinned CUDA memory.
|
||||
cudaStreamCreate(&stream);
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#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 <nanovdb/util/cuda/CudaGridHandle.cuh>// required since GridHandle<CudaDeviceBuffer> has device code
|
||||
#include <stdio.h> // for printf
|
||||
|
||||
// This is called by the host only
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
#include <nanovdb/util/Primitives.h>
|
||||
|
||||
#if defined(NANOVDB_USE_CUDA)
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
using BufferT = nanovdb::CudaDeviceBuffer;
|
||||
#else
|
||||
using BufferT = nanovdb::HostBuffer;
|
||||
#endif
|
||||
@@ -26,7 +26,7 @@ int main(int ac, char** av)
|
||||
handle = nanovdb::io::readGrid<BufferT>(av[1]);
|
||||
std::cout << "Loaded NanoVDB grid[" << handle.gridMetaData()->shortGridName() << "]...\n";
|
||||
} else {
|
||||
handle = nanovdb::tools::createFogVolumeSphere<float, BufferT>(100.0f, nanovdb::Vec3d(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphere");
|
||||
handle = nanovdb::createFogVolumeSphere<float, BufferT>(100.0f, nanovdb::Vec3d(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphere");
|
||||
}
|
||||
|
||||
if (handle.gridMetaData()->isFogVolume() == false) {
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
#include <chrono>
|
||||
|
||||
#if defined(NANOVDB_USE_CUDA)
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
using BufferT = nanovdb::CudaDeviceBuffer;
|
||||
#else
|
||||
using BufferT = nanovdb::HostBuffer;
|
||||
#endif
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/math/Ray.h>
|
||||
#include <nanovdb/util/GridHandle.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
#include <nanovdb/util/Ray.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@@ -22,10 +22,10 @@ void runNanoVDB(nanovdb::GridHandle<BufferT>& handle, int numIterations, int wid
|
||||
using GridT = nanovdb::FloatGrid;
|
||||
using CoordT = nanovdb::Coord;
|
||||
using RealT = float;
|
||||
using Vec3T = nanovdb::math::Vec3<RealT>;
|
||||
using RayT = nanovdb::math::Ray<RealT>;
|
||||
using Vec3T = nanovdb::Vec3<RealT>;
|
||||
using RayT = nanovdb::Ray<RealT>;
|
||||
|
||||
auto *h_grid = handle.grid<float>();
|
||||
auto* h_grid = handle.grid<float>();
|
||||
if (!h_grid)
|
||||
throw std::runtime_error("GridHandle does not contain a valid host grid");
|
||||
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
#include <openvdb/math/Ray.h>
|
||||
#include <openvdb/tools/LevelSetSphere.h>
|
||||
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/tools/NanoToOpenVDB.h>
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
#include <nanovdb/util/NanoToOpenVDB.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(NANOVDB_USE_CUDA)
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
using BufferT = nanovdb::CudaDeviceBuffer;
|
||||
#else
|
||||
using BufferT = nanovdb::HostBuffer;
|
||||
#endif
|
||||
@@ -30,7 +30,7 @@ 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);
|
||||
auto srcGrid = nanovdb::nanoToOpenVDB(handle);
|
||||
std::cout << "Exporting to OpenVDB grid[" << srcGrid->getName() << "]...\n";
|
||||
|
||||
auto h_grid = (GridT*)srcGrid.get();
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
#include <nanovdb/util/Primitives.h>
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
|
||||
#if defined(NANOVDB_USE_CUDA)
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
using BufferT = nanovdb::CudaDeviceBuffer;
|
||||
#else
|
||||
using BufferT = nanovdb::HostBuffer;
|
||||
#endif
|
||||
@@ -26,7 +26,7 @@ int main(int ac, char** av)
|
||||
handle = nanovdb::io::readGrid<BufferT>(av[1]);
|
||||
std::cout << "Loaded NanoVDB grid[" << handle.gridMetaData()->shortGridName() << "]...\n";
|
||||
} else {
|
||||
handle = nanovdb::tools::createLevelSetSphere<float, BufferT>(100.0f, nanovdb::Vec3d(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphere");
|
||||
handle = nanovdb::createLevelSetSphere<float, BufferT>(100.0f, nanovdb::Vec3d(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphere");
|
||||
}
|
||||
|
||||
if (handle.gridMetaData()->isLevelSet() == false) {
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
#include <chrono>
|
||||
|
||||
#if defined(NANOVDB_USE_CUDA)
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
using BufferT = nanovdb::CudaDeviceBuffer;
|
||||
#else
|
||||
using BufferT = nanovdb::HostBuffer;
|
||||
#endif
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/math/Ray.h>
|
||||
#include <nanovdb/math/HDDA.h>
|
||||
#include <nanovdb/util/GridHandle.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
#include <nanovdb/util/Ray.h>
|
||||
#include <nanovdb/util/HDDA.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@@ -23,10 +23,10 @@ void runNanoVDB(nanovdb::GridHandle<BufferT>& handle, int numIterations, int wid
|
||||
using GridT = nanovdb::FloatGrid;
|
||||
using CoordT = nanovdb::Coord;
|
||||
using RealT = float;
|
||||
using Vec3T = nanovdb::math::Vec3<RealT>;
|
||||
using RayT = nanovdb::math::Ray<RealT>;
|
||||
using Vec3T = nanovdb::Vec3<RealT>;
|
||||
using RayT = nanovdb::Ray<RealT>;
|
||||
|
||||
auto *h_grid = handle.grid<float>();
|
||||
auto* h_grid = handle.grid<float>();
|
||||
if (!h_grid)
|
||||
throw std::runtime_error("GridHandle does not contain a valid host grid");
|
||||
|
||||
@@ -58,7 +58,7 @@ void runNanoVDB(nanovdb::GridHandle<BufferT>& handle, int numIterations, int wid
|
||||
float t0;
|
||||
CoordT ijk;
|
||||
float v;
|
||||
if (nanovdb::math::ZeroCrossing(iRay, acc, ijk, v, t0)) {
|
||||
if (nanovdb::ZeroCrossing(iRay, acc, ijk, v, t0)) {
|
||||
// write distance to surface. (we assume it is a uniform voxel)
|
||||
float wT0 = t0 * float(grid->voxelSize()[0]);
|
||||
compositeOp(image, i, width, height, wT0 / (wBBoxDimZ * 2), 1.0f);
|
||||
|
||||
@@ -11,18 +11,18 @@
|
||||
#include <openvdb/math/Ray.h>
|
||||
#include <openvdb/tools/RayIntersector.h>
|
||||
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/tools/NanoToOpenVDB.h>
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h>
|
||||
#include <nanovdb/util/NanoToOpenVDB.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(NANOVDB_USE_CUDA)
|
||||
using BufferT = nanovdb::cuda::DeviceBuffer;
|
||||
using BufferT = nanovdb::CudaDeviceBuffer;
|
||||
#else
|
||||
using BufferT = nanovdb::HostBuffer;
|
||||
#endif
|
||||
|
||||
void runOpenVDB(nanovdb::GridHandle<nanovdb::cuda::DeviceBuffer>& handle, int numIterations, int width, int height, BufferT& imageBuffer)
|
||||
void runOpenVDB(nanovdb::GridHandle<nanovdb::CudaDeviceBuffer>& handle, int numIterations, int width, int height, BufferT& imageBuffer)
|
||||
{
|
||||
using GridT = openvdb::FloatGrid;
|
||||
using CoordT = openvdb::Coord;
|
||||
@@ -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);
|
||||
auto srcGrid = nanovdb::nanoToOpenVDB(handle);
|
||||
std::cout << "Exporting to OpenVDB grid[" << srcGrid->getName() << "]...\n";
|
||||
#else
|
||||
openvdb::initialize();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
|
||||
/// @brief Read a NanoVDB grid form file, check pointer and access a single value
|
||||
///
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#include <nanovdb/io/IO.h> // this is required to read (and write) NanoVDB files on the host
|
||||
#include <nanovdb/util/IO.h> // this is required to read (and write) NanoVDB files on the host
|
||||
|
||||
/// @brief Read a NanoVDB grid from a file and print out multiple values.
|
||||
///
|
||||
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/io/IO.h> // this is required to read (and write) NanoVDB files on the host
|
||||
#include <nanovdb/cuda/DeviceBuffer.h> // required for CUDA memory management
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/util/IO.h> // this is required to read (and write) NanoVDB files on the host
|
||||
#include <nanovdb/util/cuda/CudaDeviceBuffer.h> // required for CUDA memory management
|
||||
#include <nanovdb/util/GridHandle.h>
|
||||
|
||||
extern "C" void launch_kernels(const nanovdb::NanoGrid<float>*,
|
||||
const nanovdb::NanoGrid<float>*,
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nanovdb/tools/GridBuilder.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/util/GridBuilder.h>
|
||||
#include <nanovdb/util/CreateNanoGrid.h>
|
||||
|
||||
#define OGT_VOX_IMPLEMENTATION
|
||||
#include "ogt_vox.h"
|
||||
@@ -132,7 +132,7 @@ nanovdb::GridHandle<BufferT> convertVoxToNanoVDB(const std::string& inFilename,
|
||||
try {
|
||||
if (const auto* scene = detail::load_vox_scene(inFilename.c_str())) {
|
||||
// we just merge into one grid...
|
||||
nanovdb::tools::build::Grid<nanovdb::math::Rgba8> grid(nanovdb::math::Rgba8(),modelName,nanovdb::GridClass::VoxelVolume);
|
||||
nanovdb::build::Grid<nanovdb::Rgba8> grid(nanovdb::Rgba8(),modelName,nanovdb::GridClass::VoxelVolume);
|
||||
auto acc = grid.getAccessor();
|
||||
|
||||
auto processModelFn = [&](int modelIndex, const ogt_vox_transform& xform) {
|
||||
@@ -145,7 +145,7 @@ nanovdb::GridHandle<BufferT> convertVoxToNanoVDB(const std::string& inFilename,
|
||||
if (uint8_t color_index = model->voxel_data[voxel_index]) {
|
||||
ogt_vox_rgba rgba = scene->palette.color[color_index];
|
||||
auto ijk = nanovdb::Coord::Floor(detail::matMult4x4((float*)&xform, nanovdb::Vec4f(x, y, z, 1)));
|
||||
acc.setValue(nanovdb::Coord(ijk[0], ijk[2], -ijk[1]), *reinterpret_cast<nanovdb::math::Rgba8*>(&rgba));
|
||||
acc.setValue(nanovdb::Coord(ijk[0], ijk[2], -ijk[1]), *reinterpret_cast<nanovdb::PackedRGBA8*>(&rgba));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,7 +185,7 @@ nanovdb::GridHandle<BufferT> convertVoxToNanoVDB(const std::string& inFilename,
|
||||
|
||||
printf("scene processing end.\n");
|
||||
ogt_vox_destroy_scene(scene);
|
||||
return nanovdb::tools::createNanoGrid(grid);
|
||||
return nanovdb::createNanoGrid(grid);
|
||||
} else {
|
||||
std::ostringstream ss;
|
||||
ss << "Invalid file \"" << inFilename << "\"";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
#include "VoxToNanoVDB.h"
|
||||
|
||||
/// @brief Convert an .vox file into a .nvdb file.
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
#include <nanovdb/tools/cuda/PointsToGrid.cuh>
|
||||
#include <nanovdb/util/cuda/CudaPointsToGrid.cuh>
|
||||
|
||||
/// @brief Demonstrates how to create a NanoVDB grid from voxel coordinates on the GPU
|
||||
int main()
|
||||
{
|
||||
using namespace nanovdb;
|
||||
|
||||
try {
|
||||
// Define list of voxel coordinates and copy them to the device
|
||||
const size_t numVoxels = 3;
|
||||
nanovdb::Coord coords[numVoxels] = {nanovdb::Coord(1, 2, 3), nanovdb::Coord(-1,3,6), nanovdb::Coord(-90,100,5678)}, *d_coords = nullptr;
|
||||
cudaCheck(cudaMalloc(&d_coords, numVoxels * sizeof(nanovdb::Coord)));
|
||||
cudaCheck(cudaMemcpy(d_coords, coords, numVoxels * sizeof(nanovdb::Coord), cudaMemcpyHostToDevice));// coords CPU -> GPU
|
||||
Coord coords[numVoxels] = {Coord(1, 2, 3), Coord(-1,3,6), Coord(-90,100,5678)}, *d_coords = nullptr;
|
||||
cudaCheck(cudaMalloc(&d_coords, numVoxels * sizeof(Coord)));
|
||||
cudaCheck(cudaMemcpy(d_coords, coords, numVoxels * sizeof(Coord), cudaMemcpyHostToDevice));// coords CPU -> GPU
|
||||
|
||||
// Generate a NanoVDB grid that contains the list of voxels on the device
|
||||
auto handle = nanovdb::tools::cuda::voxelsToGrid<float>(d_coords, numVoxels);
|
||||
auto handle = cudaVoxelsToGrid<float>(d_coords, numVoxels);
|
||||
auto *d_grid = handle.deviceGrid<float>();
|
||||
|
||||
// Define a list of values and copy them to the device
|
||||
@@ -24,10 +25,10 @@ int main()
|
||||
cudaCheck(cudaMemcpy(d_values, values, numVoxels * sizeof(float), cudaMemcpyHostToDevice));// values CPU -> GPU
|
||||
|
||||
// Launch a device kernel that sets the values of voxels define above and prints them
|
||||
const unsigned int numThreads = 128, numBlocks = nanovdb::util::cuda::blocksPerGrid(numVoxels, numThreads);
|
||||
nanovdb::util::cuda::lambdaKernel<<<numBlocks, numThreads>>>(numVoxels, [=] __device__(size_t tid) {
|
||||
using OpT = nanovdb::SetVoxel<float>;// defines type of random-access operation (set value)
|
||||
const nanovdb::Coord &ijk = d_coords[tid];
|
||||
const unsigned int numThreads = 128, numBlocks = (numVoxels + numThreads - 1) / numThreads;
|
||||
cudaLambdaKernel<<<numBlocks, numThreads>>>(numVoxels, [=] __device__(size_t tid) {
|
||||
using OpT = SetVoxel<float>;// defines type of random-access operation (set value)
|
||||
const 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));
|
||||
}); cudaCheckError();
|
||||
@@ -36,7 +37,7 @@ int main()
|
||||
handle.deviceDownload();// creates a copy on the CPU
|
||||
auto *grid = handle.grid<float>();
|
||||
for (size_t i=0; i<numVoxels; ++i) {
|
||||
const nanovdb::Coord &ijk = coords[i];
|
||||
const 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));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/util/Primitives.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
|
||||
/// @brief Creates multiple NanoVDB grids, accesses a value in one, and saves all grids to file.
|
||||
///
|
||||
@@ -12,11 +12,11 @@ int main()
|
||||
try {
|
||||
std::vector<nanovdb::GridHandle<>> handles;
|
||||
// Create multiple NanoVDB grids of various types
|
||||
handles.push_back(nanovdb::tools::createLevelSetSphere<float>(100.0f));
|
||||
handles.push_back(nanovdb::tools::createLevelSetTorus<float>(100.0f, 50.0f));
|
||||
handles.push_back(nanovdb::tools::createLevelSetBox<float>(400.0f, 600.0f, 800.0f));
|
||||
handles.push_back(nanovdb::tools::createLevelSetBBox<float>(400.0f, 600.0f, 800.0f, 10.0f));
|
||||
handles.push_back(nanovdb::tools::createPointSphere<float>(1, 100.0f));
|
||||
handles.push_back(nanovdb::createLevelSetSphere<float>(100.0f));
|
||||
handles.push_back(nanovdb::createLevelSetTorus<float>(100.0f, 50.0f));
|
||||
handles.push_back(nanovdb::createLevelSetBox<float>(400.0f, 600.0f, 800.0f));
|
||||
handles.push_back(nanovdb::createLevelSetBBox<float>(400.0f, 600.0f, 800.0f, 10.0f));
|
||||
handles.push_back(nanovdb::createPointSphere<float>(1, 100.0f));
|
||||
|
||||
auto* dstGrid = handles[0].grid<float>(); // Get a (raw) pointer to the NanoVDB grid form the GridManager.
|
||||
if (!dstGrid)
|
||||
|
||||
@@ -1,767 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file IO.h
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date May 1, 2020
|
||||
|
||||
\brief Implements I/O for NanoVDB grids. Features optional BLOSC and ZIP
|
||||
file compression, support for multiple grids per file as well as
|
||||
multiple grid types.
|
||||
|
||||
\note This file does NOT depend on OpenVDB, but optionally on ZIP and BLOSC
|
||||
|
||||
\details NanoVDB files take on of two 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).
|
||||
|
||||
// 1: Segment: FileHeader, MetaData0, gridName0...MetaDataN, gridNameN, compressed Grid0, ... compressed GridN
|
||||
// 2: Raw: Grid0, ... GridN
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_IO_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_IO_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/tools/GridChecksum.h>// for updateGridCount
|
||||
|
||||
#include <fstream> // for std::ifstream
|
||||
#include <iostream> // for std::cerr/cout
|
||||
#include <string> // for std::string
|
||||
#include <sstream> // for std::stringstream
|
||||
#include <cstring> // for std::strcmp
|
||||
#include <memory> // for std::unique_ptr
|
||||
#include <vector> // for std::vector
|
||||
#ifdef NANOVDB_USE_ZIP
|
||||
#include <zlib.h> // for ZIP compression
|
||||
#endif
|
||||
#ifdef NANOVDB_USE_BLOSC
|
||||
#include <blosc.h> // for BLOSC compression
|
||||
#endif
|
||||
|
||||
// Due to a bug in older versions of gcc, including fstream might
|
||||
// define "major" and "minor" which are used as member data below.
|
||||
// See https://bugzilla.redhat.com/show_bug.cgi?id=130601
|
||||
#if defined(major) || defined(minor)
|
||||
#undef major
|
||||
#undef minor
|
||||
#endif
|
||||
|
||||
namespace nanovdb {// ==========================================================
|
||||
|
||||
namespace io {// ===============================================================
|
||||
|
||||
// --------------------------> writeGrid(s) <------------------------------------
|
||||
|
||||
/// @brief Write a single grid to file (over-writing existing content of the file)
|
||||
///
|
||||
/// @note The single grid is written into a single segment, i.e. header with metadata about its type and size.
|
||||
template<typename BufferT>
|
||||
void writeGrid(const std::string& fileName, const GridHandle<BufferT>& handle, io::Codec codec = io::Codec::NONE, int verbose = 0);
|
||||
|
||||
/// @brief Write multiple grids to file (over-writing existing content of the file)
|
||||
///
|
||||
/// @note The multiple grids are written into the same segment, i.e. header with metadata about all grids
|
||||
template<typename BufferT = HostBuffer, template<typename...> class VecT = std::vector>
|
||||
void writeGrids(const std::string& fileName, const VecT<GridHandle<BufferT>>& handles, Codec codec = Codec::NONE, int verbose = 0);
|
||||
|
||||
// --------------------------> readGrid(s) <------------------------------------
|
||||
|
||||
/// @brief Read and return one or all grids from a file into a single GridHandle
|
||||
/// @tparam BufferT Type of buffer used memory allocation
|
||||
/// @param fileName string name of file to be read from
|
||||
/// @param n zero-based signed index of the grid to be read.
|
||||
/// The default value of 0 means read only first grid.
|
||||
/// A negative value of n means read all grids in the file.
|
||||
/// @param verbose specify verbosity level. Default value of zero means quiet.
|
||||
/// @param buffer optional buffer used for memory allocation
|
||||
/// @return return a single GridHandle with one or all grids found in the file
|
||||
/// @throw will throw a std::runtime_error if the file does not contain a grid with index n
|
||||
template<typename BufferT = HostBuffer>
|
||||
GridHandle<BufferT> readGrid(const std::string& fileName, int n = 0, int verbose = 0, const BufferT& buffer = BufferT());
|
||||
|
||||
/// @brief Read and return the first grid with a specific name from a file
|
||||
/// @tparam BufferT Type of buffer used memory allocation
|
||||
/// @param fileName string name of file to be read from
|
||||
/// @param gridName string name of the grid to be read
|
||||
/// @param verbose specify verbosity level. Default value of zero means quiet.
|
||||
/// @param buffer optional buffer used for memory allocation
|
||||
/// @return return a single GridHandle containing the grid with the specific name
|
||||
/// @throw will throw a std::runtime_error if the file does not contain a grid with the specific name
|
||||
template<typename BufferT = HostBuffer>
|
||||
GridHandle<BufferT> readGrid(const std::string& fileName, const std::string& gridName, int verbose = 0, const BufferT& buffer = BufferT());
|
||||
|
||||
/// @brief Read all the grids in the file and return them as a vector of multiple GridHandles, each containing
|
||||
/// all grids encoded in the same segment of the file (i.e. they where written together). This method also
|
||||
/// works if the file contains a raw grid buffer in which case a single GridHandle is returned.
|
||||
/// @tparam BufferT Type of buffer used memory allocation
|
||||
/// @param fileName string name of file to be read from
|
||||
/// @param verbose specify verbosity level. Default value of zero means quiet.
|
||||
/// @param buffer optional buffer used for memory allocation
|
||||
/// @return Return a vector of GridHandles each containing all grids encoded
|
||||
/// in the same segment of the file (i.e. they where written together).
|
||||
template<typename BufferT = HostBuffer, template<typename...> class VecT = std::vector>
|
||||
VecT<GridHandle<BufferT>> readGrids(const std::string& fileName, int verbose = 0, const BufferT& buffer = BufferT());
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// We fix a specific size for counting bytes in files so that they
|
||||
/// are saved the same regardless of machine precision. (Note there are
|
||||
/// still little/bigendian issues, however)
|
||||
using fileSize_t = uint64_t;
|
||||
|
||||
/// @brief Internal functions for compressed read/write of a NanoVDB GridHandle into a stream
|
||||
///
|
||||
/// @warning These functions should never be called directly by client code
|
||||
namespace Internal {
|
||||
static constexpr fileSize_t MAX_SIZE = 1UL << 30; // size is 1 GB
|
||||
|
||||
template<typename BufferT>
|
||||
static fileSize_t write(std::ostream& os, const GridHandle<BufferT>& handle, Codec codec, uint32_t n);
|
||||
|
||||
template<typename BufferT>
|
||||
static void read(std::istream& is, BufferT& buffer, Codec codec);
|
||||
|
||||
static void read(std::istream& is, char* data, fileSize_t size, Codec codec);
|
||||
} // namespace Internal
|
||||
|
||||
/// @brief Standard hash function to use on strings; std::hash may vary by
|
||||
/// platform/implementation and is know to produce frequent collisions.
|
||||
uint64_t stringHash(const char* cstr);
|
||||
|
||||
/// @brief Return a uint64_t hash key of a std::string
|
||||
inline uint64_t stringHash(const std::string& str){return stringHash(str.c_str());}
|
||||
|
||||
/// @brief Return a uint64_t with its bytes reversed so we can check for endianness
|
||||
inline uint64_t reverseEndianness(uint64_t val)
|
||||
{
|
||||
return (((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) |
|
||||
(((val) >> 24) & 0x0000000000FF0000) | (((val) >> 8) & 0x00000000FF000000) |
|
||||
(((val) << 8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) |
|
||||
(((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000);
|
||||
}
|
||||
|
||||
/// @brief This class defines the meta data stored for each grid in a segment
|
||||
///
|
||||
/// @details A segment consists of a FileHeader followed by a list of FileGridMetaData
|
||||
/// each followed by grid names and then finally the grids themselves.
|
||||
///
|
||||
/// @note This class should not be confused with nanovdb::GridMetaData defined in NanoVDB.h
|
||||
/// Also, io::FileMetaData is defined in NanoVDB.h.
|
||||
struct FileGridMetaData : public FileMetaData
|
||||
{
|
||||
static_assert(sizeof(FileMetaData) == 176, "Unexpected sizeof(FileMetaData)");
|
||||
std::string gridName;
|
||||
void read(std::istream& is);
|
||||
void write(std::ostream& os) const;
|
||||
FileGridMetaData() {}
|
||||
FileGridMetaData(uint64_t size, Codec c, const GridData &gridData);
|
||||
uint64_t memUsage() const { return sizeof(FileMetaData) + nameSize; }
|
||||
}; // FileGridMetaData
|
||||
|
||||
/// @brief This class defines all the data stored in segment of a file
|
||||
///
|
||||
/// @details A segment consists of a FileHeader followed by a list of FileGridMetaData
|
||||
/// each followed by grid names and then finally the grids themselves.
|
||||
struct Segment
|
||||
{
|
||||
// Check assumptions made during read and write of FileHeader and FileMetaData
|
||||
static_assert(sizeof(FileHeader) == 16u, "Unexpected sizeof(FileHeader)");
|
||||
FileHeader header;// defined in NanoVDB.h
|
||||
std::vector<FileGridMetaData> meta;// defined in NanoVDB.h
|
||||
Segment(Codec c = Codec::NONE)
|
||||
#ifdef NANOVDB_USE_NEW_MAGIC_NUMBERS
|
||||
: header{NANOVDB_MAGIC_FILE, Version(), 0u, c}
|
||||
#else
|
||||
: header{NANOVDB_MAGIC_NUMB, Version(), 0u, c}
|
||||
#endif
|
||||
, meta()
|
||||
{
|
||||
}
|
||||
template<typename BufferT>
|
||||
void add(const GridHandle<BufferT>& h);
|
||||
bool read(std::istream& is);
|
||||
void write(std::ostream& os) const;
|
||||
uint64_t memUsage() const;
|
||||
}; // Segment
|
||||
|
||||
/// @brief Return true if the file contains a grid with the specified name
|
||||
bool hasGrid(const std::string& fileName, const std::string& gridName);
|
||||
|
||||
/// @brief Return true if the stream contains a grid with the specified name
|
||||
bool hasGrid(std::istream& is, const std::string& gridName);
|
||||
|
||||
/// @brief Reads and returns a vector of meta data for all the grids found in the specified file
|
||||
std::vector<FileGridMetaData> readGridMetaData(const std::string& fileName);
|
||||
|
||||
/// @brief Reads and returns a vector of meta data for all the grids found in the specified stream
|
||||
std::vector<FileGridMetaData> readGridMetaData(std::istream& is);
|
||||
|
||||
// --------------------------> Implementations for Internal <------------------------------------
|
||||
|
||||
template<typename BufferT>
|
||||
fileSize_t Internal::write(std::ostream& os, const GridHandle<BufferT>& handle, Codec codec, unsigned int n)
|
||||
{
|
||||
const char* data = reinterpret_cast<const char*>(handle.gridData(n));
|
||||
fileSize_t total = 0, residual = handle.gridSize(n);
|
||||
|
||||
switch (codec) {
|
||||
case Codec::ZIP: {
|
||||
#ifdef NANOVDB_USE_ZIP
|
||||
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 (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));
|
||||
os.write(reinterpret_cast<const char*>(tmp.get()), outBytes);
|
||||
total += sizeof(fileSize_t) + outBytes;
|
||||
#else
|
||||
throw std::runtime_error("ZIP compression codec was disabled during build");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case Codec::BLOSC: {
|
||||
#ifdef NANOVDB_USE_BLOSC
|
||||
do {
|
||||
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");
|
||||
const fileSize_t outBytes = count;
|
||||
os.write(reinterpret_cast<const char*>(&outBytes), sizeof(fileSize_t));
|
||||
os.write(reinterpret_cast<const char*>(tmp.get()), outBytes);
|
||||
total += sizeof(fileSize_t) + outBytes;
|
||||
data += chunk;
|
||||
residual -= chunk;
|
||||
} while (residual > 0);
|
||||
#else
|
||||
throw std::runtime_error("BLOSC compression codec was disabled during build");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
os.write(data, residual);
|
||||
total += residual;
|
||||
}
|
||||
if (!os) throw std::runtime_error("Failed to write Tree to file");
|
||||
return total;
|
||||
} // Internal::write
|
||||
|
||||
template<typename BufferT>
|
||||
void Internal::read(std::istream& is, BufferT& buffer, Codec codec)
|
||||
{
|
||||
Internal::read(is, reinterpret_cast<char*>(buffer.data()), buffer.size(), codec);
|
||||
} // Internal::read
|
||||
|
||||
/// @brief read compressed grid from stream
|
||||
/// @param is input stream to read from
|
||||
/// @param data data buffer to write into. Must be of size @c residual or larger.
|
||||
/// @param residual expected byte size of uncompressed data.
|
||||
/// @param codec mode of compression
|
||||
void Internal::read(std::istream& is, char* data, fileSize_t residual, Codec codec)
|
||||
{
|
||||
// read tree using optional compression
|
||||
switch (codec) {
|
||||
case Codec::ZIP: {
|
||||
#ifdef NANOVDB_USE_ZIP
|
||||
fileSize_t size;
|
||||
is.read(reinterpret_cast<char*>(&size), sizeof(fileSize_t));
|
||||
std::unique_ptr<Bytef[]> tmp(new Bytef[size]);// temp buffer for compressed data
|
||||
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 (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");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case Codec::BLOSC: {
|
||||
#ifdef NANOVDB_USE_BLOSC
|
||||
do {
|
||||
fileSize_t size;
|
||||
is.read(reinterpret_cast<char*>(&size), sizeof(fileSize_t));
|
||||
std::unique_ptr<char[]> tmp(new char[size]);// temp buffer for compressed data
|
||||
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 != int(chunk)) throw std::runtime_error("BLOSC failed on byte size");
|
||||
data += size_t(chunk);
|
||||
residual -= chunk;
|
||||
} while (residual > 0);
|
||||
#else
|
||||
throw std::runtime_error("BLOSC compression codec was disabled during build");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
is.read(data, residual);// read uncompressed data
|
||||
}
|
||||
if (!is) throw std::runtime_error("Failed to read Tree from file");
|
||||
} // Internal::read
|
||||
|
||||
// --------------------------> Implementations for FileGridMetaData <------------------------------------
|
||||
|
||||
inline FileGridMetaData::FileGridMetaData(uint64_t size, Codec c, const GridData &gridData)
|
||||
: FileMetaData{size, // gridSize
|
||||
size, // fileSize (will typically be redefined)
|
||||
0u, // nameKey
|
||||
0u, // voxelCount
|
||||
gridData.mGridType, // gridType
|
||||
gridData.mGridClass, // gridClass
|
||||
gridData.mWorldBBox, // worldBBox
|
||||
gridData.indexBBox(), // indexBBox
|
||||
gridData.mVoxelSize, // voxelSize
|
||||
0, // nameSize
|
||||
{0, 0, 0, 1}, // nodeCount[4]
|
||||
{0, 0, 0}, // tileCount[3]
|
||||
c, // codec
|
||||
0, // padding
|
||||
Version()}// version
|
||||
, gridName(gridData.gridName())
|
||||
{
|
||||
auto &treeData = *reinterpret_cast<const TreeData*>(gridData.treePtr());
|
||||
nameKey = stringHash(gridName);
|
||||
voxelCount = treeData.mVoxelCount;
|
||||
nameSize = static_cast<uint32_t>(gridName.size() + 1); // include '\0'
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
FileMetaData::nodeCount[i] = treeData.mNodeCount[i];
|
||||
FileMetaData::tileCount[i] = treeData.mTileCount[i];
|
||||
}
|
||||
}// FileGridMetaData::FileGridMetaData
|
||||
|
||||
inline void FileGridMetaData::write(std::ostream& os) const
|
||||
{
|
||||
os.write(reinterpret_cast<const char*>(this), sizeof(FileMetaData));
|
||||
os.write(gridName.c_str(), nameSize);
|
||||
if (!os) throw std::runtime_error("Failed writing FileGridMetaData");
|
||||
}// FileGridMetaData::write
|
||||
|
||||
inline void FileGridMetaData::read(std::istream& is)
|
||||
{
|
||||
is.read(reinterpret_cast<char*>(this), sizeof(FileMetaData));
|
||||
std::unique_ptr<char[]> tmp(new char[nameSize]);
|
||||
is.read(reinterpret_cast<char*>(tmp.get()), nameSize);
|
||||
gridName.assign(tmp.get());
|
||||
if (!is) throw std::runtime_error("Failed reading FileGridMetaData");
|
||||
}// FileGridMetaData::read
|
||||
|
||||
// --------------------------> Implementations for Segment <------------------------------------
|
||||
|
||||
inline uint64_t Segment::memUsage() const
|
||||
{
|
||||
uint64_t sum = sizeof(FileHeader);
|
||||
for (auto& m : meta) sum += m.memUsage();// includes FileMetaData + grid name
|
||||
return sum;
|
||||
}// Segment::memUsage
|
||||
|
||||
template<typename BufferT>
|
||||
inline void Segment::add(const GridHandle<BufferT>& h)
|
||||
{
|
||||
for (uint32_t i = 0; i < h.gridCount(); ++i) {
|
||||
const GridData *gridData = h.gridData(i);
|
||||
if (!gridData) throw std::runtime_error("Segment::add: GridHandle does not contain grid #" + std::to_string(i));
|
||||
meta.emplace_back(h.gridSize(i), header.codec, *gridData);
|
||||
}
|
||||
header.gridCount += h.gridCount();
|
||||
}// Segment::add
|
||||
|
||||
inline void Segment::write(std::ostream& os) const
|
||||
{
|
||||
if (header.gridCount == 0) {
|
||||
throw std::runtime_error("Segment contains no grids");
|
||||
} else if (!os.write(reinterpret_cast<const char*>(&header), sizeof(FileHeader))) {
|
||||
throw std::runtime_error("Failed to write FileHeader of Segment");
|
||||
}
|
||||
for (auto& m : meta) m.write(os);
|
||||
}// Segment::write
|
||||
|
||||
inline bool Segment::read(std::istream& is)
|
||||
{
|
||||
is.read(reinterpret_cast<char*>(&header), sizeof(FileHeader));
|
||||
if (is.eof()) {// The EOF flag is only set once a read tries to read past the end of the file
|
||||
is.clear(std::ios_base::eofbit);// clear eof flag so we can rewind and read again
|
||||
return false;
|
||||
}
|
||||
const MagicType magic = toMagic(header.magic);
|
||||
if (magic != MagicType::NanoVDB && magic != MagicType::NanoFile) {
|
||||
// first check for byte-swapped header magic.
|
||||
if (header.magic == reverseEndianness(NANOVDB_MAGIC_NUMB) ||
|
||||
header.magic == reverseEndianness(NANOVDB_MAGIC_FILE)) {
|
||||
throw std::runtime_error("This nvdb file has reversed endianness");
|
||||
} else {
|
||||
if (magic == MagicType::OpenVDB) {
|
||||
throw std::runtime_error("Expected a NanoVDB file, but read an OpenVDB file!");
|
||||
} else if (magic == MagicType::NanoGrid) {
|
||||
throw std::runtime_error("Expected a NanoVDB file, but read a raw NanoVDB grid!");
|
||||
} else {
|
||||
throw std::runtime_error("Expected a NanoVDB file, but read a file of unknown type!");
|
||||
}
|
||||
}
|
||||
} else if ( !header.version.isCompatible()) {
|
||||
std::stringstream ss;
|
||||
Version v;
|
||||
is.read(reinterpret_cast<char*>(&v), sizeof(Version));// read GridData::mVersion located at byte 16=sizeof(FileHeader) is stream
|
||||
if ( v.getMajor() == NANOVDB_MAJOR_VERSION_NUMBER) {
|
||||
ss << "This file looks like it contains a raw grid buffer and not a standard file with meta data";
|
||||
} else if ( header.version.getMajor() < NANOVDB_MAJOR_VERSION_NUMBER) {
|
||||
char str[30];
|
||||
ss << "The file contains an older version of NanoVDB: " << std::string(toStr(str, header.version)) << "!\n\t"
|
||||
<< "Recommendation: Re-generate this NanoVDB file with this version: " << NANOVDB_MAJOR_VERSION_NUMBER << ".X of NanoVDB";
|
||||
} else {
|
||||
ss << "This tool was compiled against an older version of NanoVDB: " << NANOVDB_MAJOR_VERSION_NUMBER << ".X!\n\t"
|
||||
<< "Recommendation: Re-compile this tool against the newer version: " << header.version.getMajor() << ".X of NanoVDB";
|
||||
}
|
||||
throw std::runtime_error("An unrecoverable error in nanovdb::Segment::read:\n\tIncompatible file format: " + ss.str());
|
||||
}
|
||||
meta.resize(header.gridCount);
|
||||
for (auto& m : meta) {
|
||||
m.read(is);
|
||||
m.version = header.version;
|
||||
}
|
||||
return true;
|
||||
}// Segment::read
|
||||
|
||||
// --------------------------> writeGrid <------------------------------------
|
||||
|
||||
template<typename BufferT>
|
||||
void writeGrid(std::ostream& os, const GridHandle<BufferT>& handle, Codec codec)
|
||||
{
|
||||
Segment seg(codec);
|
||||
seg.add(handle);
|
||||
const auto start = os.tellp();
|
||||
seg.write(os); // write header without the correct fileSize (so it's allocated)
|
||||
for (uint32_t i = 0; i < handle.gridCount(); ++i) {
|
||||
seg.meta[i].fileSize = Internal::write(os, handle, codec, i);
|
||||
}
|
||||
os.seekp(start);
|
||||
seg.write(os);// re-write header with the correct fileSize
|
||||
os.seekp(0, std::ios_base::end);// skip to end
|
||||
}// writeGrid
|
||||
|
||||
template<typename BufferT>
|
||||
void writeGrid(const std::string& fileName, const GridHandle<BufferT>& handle, Codec codec, int verbose)
|
||||
{
|
||||
std::ofstream os(fileName, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
if (!os.is_open()) {
|
||||
throw std::ios_base::failure("Unable to open file named \"" + fileName + "\" for output");
|
||||
}
|
||||
writeGrid<BufferT>(os, handle, codec);
|
||||
if (verbose) {
|
||||
std::cout << "Wrote nanovdb::Grid to file named \"" << fileName << "\"" << std::endl;
|
||||
}
|
||||
}// writeGrid
|
||||
|
||||
// --------------------------> writeGrids <------------------------------------
|
||||
|
||||
template<typename BufferT = HostBuffer, template<typename...> class VecT = std::vector>
|
||||
void writeGrids(std::ostream& os, const VecT<GridHandle<BufferT>>& handles, Codec codec = Codec::NONE)
|
||||
{
|
||||
for (auto& h : handles) writeGrid(os, h, codec);
|
||||
}// writeGrids
|
||||
|
||||
template<typename BufferT, template<typename...> class VecT>
|
||||
void writeGrids(const std::string& fileName, const VecT<GridHandle<BufferT>>& handles, Codec codec, int verbose)
|
||||
{
|
||||
std::ofstream os(fileName, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
if (!os.is_open()) throw std::ios_base::failure("Unable to open file named \"" + fileName + "\" for output");
|
||||
writeGrids<BufferT, VecT>(os, handles, codec);
|
||||
if (verbose) std::cout << "Wrote " << handles.size() << " nanovdb::Grid(s) to file named \"" << fileName << "\"" << std::endl;
|
||||
}// writeGrids
|
||||
|
||||
// --------------------------> readGrid <------------------------------------
|
||||
|
||||
template<typename BufferT>
|
||||
GridHandle<BufferT> readGrid(std::istream& is, int n, const BufferT& pool)
|
||||
{
|
||||
GridHandle<BufferT> handle;
|
||||
if (n<0) {// read all grids into the same buffer
|
||||
try {//first try to read a raw grid buffer
|
||||
handle.read(is, pool);
|
||||
} catch(const std::logic_error&) {
|
||||
Segment seg;
|
||||
uint64_t bufferSize = 0u;
|
||||
uint32_t gridCount = 0u, gridIndex = 0u;
|
||||
const auto start = is.tellg();
|
||||
while (seg.read(is)) {
|
||||
std::streamoff skipSize = 0;
|
||||
for (auto& m : seg.meta) {
|
||||
++gridCount;
|
||||
bufferSize += m.gridSize;
|
||||
skipSize += m.fileSize;
|
||||
}// loop over grids in segment
|
||||
is.seekg(skipSize, std::ios_base::cur); // skip forward from the current position
|
||||
}// loop over segments
|
||||
auto buffer = BufferT::create(bufferSize, &pool);
|
||||
char *ptr = (char*)buffer.data();
|
||||
is.seekg(start);// rewind
|
||||
while (seg.read(is)) {
|
||||
for (auto& m : seg.meta) {
|
||||
Internal::read(is, ptr, m.gridSize, seg.header.codec);
|
||||
tools::updateGridCount((GridData*)ptr, gridIndex++, gridCount);
|
||||
ptr += m.gridSize;
|
||||
}// loop over grids in segment
|
||||
}// loop over segments
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
}
|
||||
} else {// read a specific grid
|
||||
try {//first try to read a raw grid buffer
|
||||
handle.read(is, uint32_t(n), pool);
|
||||
tools::updateGridCount((GridData*)handle.data(), 0u, 1u);
|
||||
} catch(const std::logic_error&) {
|
||||
Segment seg;
|
||||
int counter = -1;
|
||||
while (seg.read(is)) {
|
||||
std::streamoff seek = 0;
|
||||
for (auto& m : seg.meta) {
|
||||
if (++counter == n) {
|
||||
auto buffer = BufferT::create(m.gridSize, &pool);
|
||||
Internal::read(is, buffer, seg.header.codec);
|
||||
tools::updateGridCount((GridData*)buffer.data(), 0u, 1u);
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
} else {
|
||||
seek += m.fileSize;
|
||||
}
|
||||
}// loop over grids in segment
|
||||
is.seekg(seek, std::ios_base::cur); // skip forward from the current position
|
||||
}// loop over segments
|
||||
if (n != counter) throw std::runtime_error("stream does not contain a #" + std::to_string(n) + " grid");
|
||||
}
|
||||
}
|
||||
return handle;
|
||||
}// readGrid
|
||||
|
||||
/// @brief Read the n'th grid
|
||||
template<typename BufferT>
|
||||
GridHandle<BufferT> readGrid(const std::string& fileName, int n, int verbose, const BufferT& buffer)
|
||||
{
|
||||
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");
|
||||
auto handle = readGrid<BufferT>(is, n, buffer);
|
||||
if (verbose) {
|
||||
if (n<0) {
|
||||
std::cout << "Read all NanoGrids from the file named \"" << fileName << "\"" << std::endl;
|
||||
} else {
|
||||
std::cout << "Read NanoGrid # " << n << " from the file named \"" << fileName << "\"" << std::endl;
|
||||
}
|
||||
}
|
||||
return handle; // is converted to r-value and return value is move constructed.
|
||||
}// readGrid
|
||||
|
||||
/// @brief Read a specific grid from an input stream given the name of the grid
|
||||
/// @tparam BufferT Buffer type used for allocation
|
||||
/// @param is input stream from which to read the grid
|
||||
/// @param gridName string name of the (first) grid to be returned
|
||||
/// @param pool optional memory pool from which to allocate the grid buffer
|
||||
/// @return Return the first grid in the input stream with a specific name
|
||||
/// @throw std::runtime_error with no grid exists with the specified name
|
||||
template<typename BufferT>
|
||||
GridHandle<BufferT> readGrid(std::istream& is, const std::string& gridName, const BufferT& pool)
|
||||
{
|
||||
try {
|
||||
GridHandle<BufferT> handle;
|
||||
handle.read(is, gridName, pool);
|
||||
return handle;
|
||||
} catch(const std::logic_error&) {
|
||||
const auto key = stringHash(gridName);
|
||||
Segment seg;
|
||||
while (seg.read(is)) {// loop over all segments in stream
|
||||
std::streamoff seek = 0;
|
||||
for (auto& m : seg.meta) {// loop over all grids in segment
|
||||
if ((m.nameKey == 0u || m.nameKey == key) && m.gridName == gridName) { // check for hash key collision
|
||||
auto buffer = BufferT::create(m.gridSize, &pool);
|
||||
is.seekg(seek, std::ios_base::cur); // rewind
|
||||
Internal::read(is, buffer, seg.header.codec);
|
||||
tools::updateGridCount((GridData*)buffer.data(), 0u, 1u);
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
} else {
|
||||
seek += m.fileSize;
|
||||
}
|
||||
}
|
||||
is.seekg(seek, std::ios_base::cur); // skip forward from the current position
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Grid name '" + gridName + "' not found in file");
|
||||
}// readGrid
|
||||
|
||||
/// @brief Read the first grid with a specific name
|
||||
template<typename BufferT>
|
||||
GridHandle<BufferT> readGrid(const std::string& fileName, const std::string& gridName, int verbose, const BufferT& buffer)
|
||||
{
|
||||
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");
|
||||
auto handle = readGrid<BufferT>(is, gridName, buffer);
|
||||
if (verbose) {
|
||||
if (handle) {
|
||||
std::cout << "Read NanoGrid named \"" << gridName << "\" from the file named \"" << fileName << "\"" << std::endl;
|
||||
} else {
|
||||
std::cout << "File named \"" << fileName << "\" does not contain a grid named \"" + gridName + "\"" << std::endl;
|
||||
}
|
||||
}
|
||||
return handle; // is converted to r-value and return value is move constructed.
|
||||
}// readGrid
|
||||
|
||||
// --------------------------> readGrids <------------------------------------
|
||||
|
||||
template<typename BufferT = HostBuffer, template<typename...> class VecT = std::vector>
|
||||
VecT<GridHandle<BufferT>> readGrids(std::istream& is, const BufferT& pool = BufferT())
|
||||
{
|
||||
VecT<GridHandle<BufferT>> handles;
|
||||
try {//first try to read a raw grid buffer
|
||||
GridHandle<BufferT> handle;
|
||||
handle.read(is, pool);// will throw if stream does not contain a raw grid buffer
|
||||
handles.push_back(std::move(handle)); // force move copy assignment
|
||||
} catch(const std::logic_error&) {
|
||||
Segment seg;
|
||||
while (seg.read(is)) {
|
||||
uint64_t bufferSize = 0;
|
||||
for (auto& m : seg.meta) bufferSize += m.gridSize;
|
||||
auto buffer = BufferT::create(bufferSize, &pool);
|
||||
uint64_t bufferOffset = 0;
|
||||
for (uint16_t i = 0; i < seg.header.gridCount; ++i) {
|
||||
auto *data = util::PtrAdd<GridData>(buffer.data(), bufferOffset);
|
||||
Internal::read(is, (char*)data, seg.meta[i].gridSize, seg.header.codec);
|
||||
tools::updateGridCount(data, uint32_t(i), uint32_t(seg.header.gridCount));
|
||||
bufferOffset += seg.meta[i].gridSize;
|
||||
}// loop over grids in segment
|
||||
handles.emplace_back(std::move(buffer)); // force move copy assignment
|
||||
}// loop over segments
|
||||
}
|
||||
return handles; // is converted to r-value and return value is move constructed.
|
||||
}// readGrids
|
||||
|
||||
/// @brief Read all the grids
|
||||
template<typename BufferT, template<typename...> class VecT>
|
||||
VecT<GridHandle<BufferT>> readGrids(const std::string& fileName, int verbose, const BufferT& buffer)
|
||||
{
|
||||
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");
|
||||
auto handles = readGrids<BufferT, VecT>(is, buffer);
|
||||
if (verbose) std::cout << "Read " << handles.size() << " NanoGrid(s) from the file named \"" << fileName << "\"" << std::endl;
|
||||
return handles; // is converted to r-value and return value is move constructed.
|
||||
}// readGrids
|
||||
|
||||
// --------------------------> readGridMetaData <------------------------------------
|
||||
|
||||
inline std::vector<FileGridMetaData> readGridMetaData(const std::string& fileName)
|
||||
{
|
||||
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");
|
||||
return readGridMetaData(is); // is converted to r-value and return value is move constructed.
|
||||
}// readGridMetaData
|
||||
|
||||
inline std::vector<FileGridMetaData> readGridMetaData(std::istream& is)
|
||||
{
|
||||
Segment seg;
|
||||
std::vector<FileGridMetaData> meta;
|
||||
try {
|
||||
GridHandle<> handle;// if stream contains a raw grid buffer we unfortunately have to load everything
|
||||
handle.read(is);
|
||||
seg.add(handle);
|
||||
meta = std::move(seg.meta);
|
||||
} catch(const std::logic_error&) {
|
||||
while (seg.read(is)) {
|
||||
std::streamoff skip = 0;
|
||||
for (auto& m : seg.meta) {
|
||||
meta.push_back(m);
|
||||
skip += m.fileSize;
|
||||
}// loop over grid meta data in segment
|
||||
is.seekg(skip, std::ios_base::cur);
|
||||
}// loop over segments
|
||||
}
|
||||
return meta; // is converted to r-value and return value is move constructed.
|
||||
}// readGridMetaData
|
||||
|
||||
// --------------------------> hasGrid <------------------------------------
|
||||
|
||||
inline bool hasGrid(const std::string& fileName, const std::string& gridName)
|
||||
{
|
||||
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");
|
||||
return hasGrid(is, gridName);
|
||||
}// hasGrid
|
||||
|
||||
inline bool hasGrid(std::istream& is, const std::string& gridName)
|
||||
{
|
||||
const auto key = stringHash(gridName);
|
||||
Segment seg;
|
||||
while (seg.read(is)) {
|
||||
std::streamoff seek = 0;
|
||||
for (auto& m : seg.meta) {
|
||||
if (m.nameKey == key && m.gridName == gridName) return true; // check for hash key collision
|
||||
seek += m.fileSize;
|
||||
}// loop over grid meta data in segment
|
||||
is.seekg(seek, std::ios_base::cur);
|
||||
}// loop over segments
|
||||
return false;
|
||||
}// hasGrid
|
||||
|
||||
// --------------------------> stringHash <------------------------------------
|
||||
|
||||
inline uint64_t stringHash(const char* c_str)
|
||||
{
|
||||
uint64_t hash = 0;// zero is returned when cstr = nullptr or "\0"
|
||||
if (c_str) {
|
||||
for (auto* str = reinterpret_cast<const unsigned char*>(c_str); *str; ++str) {
|
||||
uint64_t overflow = hash >> (64 - 8);
|
||||
hash *= 67; // Next-ish prime after 26 + 26 + 10
|
||||
hash += *str + overflow;
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}// stringHash
|
||||
|
||||
} // namespace io ======================================================================
|
||||
|
||||
template<typename T>
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& os, const math::BBox<math::Vec3<T>>& b)
|
||||
{
|
||||
os << "(" << b[0][0] << "," << b[0][1] << "," << b[0][2] << ") -> "
|
||||
<< "(" << b[1][0] << "," << b[1][1] << "," << b[1][2] << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& os, const CoordBBox& b)
|
||||
{
|
||||
os << "(" << b[0][0] << "," << b[0][1] << "," << b[0][2] << ") -> "
|
||||
<< "(" << b[1][0] << "," << b[1][1] << "," << b[1][2] << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& os, const Coord& ijk)
|
||||
{
|
||||
os << "(" << ijk[0] << "," << ijk[1] << "," << ijk[2] << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& os, const math::Vec3<T>& v)
|
||||
{
|
||||
os << "(" << v[0] << "," << v[1] << "," << v[2] << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& os, const math::Vec4<T>& v)
|
||||
{
|
||||
os << "(" << v[0] << "," << v[1] << "," << v[2] << "," << v[3] << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace nanovdb ===================================================================
|
||||
|
||||
#endif // NANOVDB_IO_H_HAS_BEEN_INCLUDED
|
||||
@@ -1,189 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
//
|
||||
/// @author Jeff Lait
|
||||
///
|
||||
/// @date May 13, 2021
|
||||
///
|
||||
/// @file DitherLUT.h
|
||||
///
|
||||
/// @brief Defines look up table to do dithering of 8^3 leaf nodes.
|
||||
|
||||
#ifndef NANOVDB_DITHERLUT_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_DITHERLUT_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>// for __hostdev__, Vec3, Min, Max, Pow2, Pow3, Pow4
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace math {
|
||||
|
||||
class DitherLUT
|
||||
{
|
||||
const bool mEnable;
|
||||
public:
|
||||
/// @brief Constructor with an optional scaling factor for the dithering
|
||||
__hostdev__ DitherLUT(bool enable = true) : mEnable(enable) {}
|
||||
|
||||
/// @brief Retrieves dither threshold for an offset within an 8^3 leaf nodes.
|
||||
///
|
||||
/// @param offset into the lookup table of size 512
|
||||
__hostdev__ float operator()(const int offset)
|
||||
{
|
||||
|
||||
// This table was generated with
|
||||
/**************
|
||||
|
||||
static constexpr inline uint32
|
||||
SYSwang_inthash(uint32 key)
|
||||
{
|
||||
// From http://www.concentric.net/~Ttwang/tech/inthash.htm
|
||||
key += ~(key << 16);
|
||||
key ^= (key >> 5);
|
||||
key += (key << 3);
|
||||
key ^= (key >> 13);
|
||||
key += ~(key << 9);
|
||||
key ^= (key >> 17);
|
||||
return key;
|
||||
}
|
||||
|
||||
static void
|
||||
ut_initDitherR(float *pattern, float offset,
|
||||
int x, int y, int z, int res, int goalres)
|
||||
{
|
||||
// These offsets are designed to maximize the difference between
|
||||
// dither values in nearby voxels within a given 2x2x2 cell, without
|
||||
// producing axis-aligned artifacts. The are organized in row-major
|
||||
// order.
|
||||
static const float theDitherOffset[] = {0,4,6,2,5,1,3,7};
|
||||
static const float theScale = 0.125F;
|
||||
int key = (((z << res) + y) << res) + x;
|
||||
|
||||
if (res == goalres)
|
||||
{
|
||||
pattern[key] = offset;
|
||||
return;
|
||||
}
|
||||
|
||||
// Randomly flip (on each axis) the dithering patterns used by the
|
||||
// subcells. This key is xor'd with the subcell index below before
|
||||
// looking up in the dither offset list.
|
||||
key = SYSwang_inthash(key) & 7;
|
||||
|
||||
x <<= 1;
|
||||
y <<= 1;
|
||||
z <<= 1;
|
||||
|
||||
offset *= theScale;
|
||||
for (int i = 0; i < 8; i++)
|
||||
ut_initDitherR(pattern, offset+theDitherOffset[i ^ key]*theScale,
|
||||
x+(i&1), y+((i&2)>>1), z+((i&4)>>2), res+1, goalres);
|
||||
}
|
||||
|
||||
// This is a compact algorithm that accomplishes essentially the same thing
|
||||
// as ut_initDither() above. We should eventually switch to use this and
|
||||
// clean the dead code.
|
||||
static fpreal32 *
|
||||
ut_initDitherRecursive(int goalres)
|
||||
{
|
||||
const int nfloat = 1 << (goalres*3);
|
||||
float *pattern = new float[nfloat];
|
||||
ut_initDitherR(pattern, 1.0F, 0, 0, 0, 0, goalres);
|
||||
|
||||
// This has built an even spacing from 1/nfloat to 1.0.
|
||||
// however, our dither pattern should be 1/(nfloat+1) to nfloat/(nfloat+1)
|
||||
// So we do a correction here. Note that the earlier calculations are
|
||||
// done with powers of 2 so are exact, so it does make sense to delay
|
||||
// the renormalization to this pass.
|
||||
float correctionterm = nfloat / (nfloat+1.0F);
|
||||
for (int i = 0; i < nfloat; i++)
|
||||
pattern[i] *= correctionterm;
|
||||
return pattern;
|
||||
}
|
||||
|
||||
theDitherMatrix = ut_initDitherRecursive(3);
|
||||
|
||||
for (int i = 0; i < 512/8; i ++)
|
||||
{
|
||||
for (int j = 0; j < 8; j ++)
|
||||
std::cout << theDitherMatrix[i*8+j] << "f, ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
**************/
|
||||
static const float LUT[512] =
|
||||
{
|
||||
0.14425f, 0.643275f, 0.830409f, 0.331384f, 0.105263f, 0.604289f, 0.167641f, 0.666667f,
|
||||
0.892788f, 0.393762f, 0.0818713f, 0.580897f, 0.853801f, 0.354776f, 0.916179f, 0.417154f,
|
||||
0.612086f, 0.11306f, 0.79922f, 0.300195f, 0.510721f, 0.0116959f, 0.947368f, 0.448343f,
|
||||
0.362573f, 0.861598f, 0.0506823f, 0.549708f, 0.261209f, 0.760234f, 0.19883f, 0.697856f,
|
||||
0.140351f, 0.639376f, 0.576998f, 0.0779727f, 0.522417f, 0.0233918f, 0.460039f, 0.959064f,
|
||||
0.888889f, 0.389864f, 0.327485f, 0.826511f, 0.272904f, 0.77193f, 0.709552f, 0.210526f,
|
||||
0.483431f, 0.982456f, 0.296296f, 0.795322f, 0.116959f, 0.615984f, 0.0545809f, 0.553606f,
|
||||
0.732943f, 0.233918f, 0.545809f, 0.0467836f, 0.865497f, 0.366472f, 0.803119f, 0.304094f,
|
||||
0.518519f, 0.0194932f, 0.45614f, 0.955166f, 0.729045f, 0.230019f, 0.54191f, 0.042885f,
|
||||
0.269006f, 0.768031f, 0.705653f, 0.206628f, 0.479532f, 0.978558f, 0.292398f, 0.791423f,
|
||||
0.237817f, 0.736842f, 0.424951f, 0.923977f, 0.136452f, 0.635478f, 0.323587f, 0.822612f,
|
||||
0.986355f, 0.487329f, 0.674464f, 0.175439f, 0.88499f, 0.385965f, 0.573099f, 0.0740741f,
|
||||
0.51462f, 0.0155945f, 0.202729f, 0.701754f, 0.148148f, 0.647174f, 0.834308f, 0.335283f,
|
||||
0.265107f, 0.764133f, 0.951267f, 0.452242f, 0.896686f, 0.397661f, 0.08577f, 0.584795f,
|
||||
0.8577f, 0.358674f, 0.920078f, 0.421053f, 0.740741f, 0.241715f, 0.678363f, 0.179337f,
|
||||
0.109162f, 0.608187f, 0.17154f, 0.670565f, 0.491228f, 0.990253f, 0.42885f, 0.927875f,
|
||||
0.0662768f, 0.565302f, 0.62768f, 0.128655f, 0.183236f, 0.682261f, 0.744639f, 0.245614f,
|
||||
0.814815f, 0.315789f, 0.378168f, 0.877193f, 0.931774f, 0.432749f, 0.495127f, 0.994152f,
|
||||
0.0350877f, 0.534113f, 0.97076f, 0.471735f, 0.214425f, 0.71345f, 0.526316f, 0.0272904f,
|
||||
0.783626f, 0.2846f, 0.222222f, 0.721248f, 0.962963f, 0.463938f, 0.276803f, 0.775828f,
|
||||
0.966862f, 0.467836f, 0.405458f, 0.904483f, 0.0701754f, 0.569201f, 0.881092f, 0.382066f,
|
||||
0.218324f, 0.717349f, 0.654971f, 0.155945f, 0.818713f, 0.319688f, 0.132554f, 0.631579f,
|
||||
0.0623782f, 0.561404f, 0.748538f, 0.249513f, 0.912281f, 0.413255f, 0.974659f, 0.475634f,
|
||||
0.810916f, 0.311891f, 0.499025f, 0.998051f, 0.163743f, 0.662768f, 0.226121f, 0.725146f,
|
||||
0.690058f, 0.191033f, 0.00389864f, 0.502924f, 0.557505f, 0.0584795f, 0.120858f, 0.619883f,
|
||||
0.440546f, 0.939571f, 0.752437f, 0.253411f, 0.307992f, 0.807018f, 0.869396f, 0.37037f,
|
||||
0.658869f, 0.159844f, 0.346979f, 0.846004f, 0.588694f, 0.0896686f, 0.152047f, 0.651072f,
|
||||
0.409357f, 0.908382f, 0.596491f, 0.0974659f, 0.339181f, 0.838207f, 0.900585f, 0.401559f,
|
||||
0.34308f, 0.842105f, 0.779727f, 0.280702f, 0.693957f, 0.194932f, 0.25731f, 0.756335f,
|
||||
0.592593f, 0.0935673f, 0.0311891f, 0.530214f, 0.444444f, 0.94347f, 0.506823f, 0.00779727f,
|
||||
0.68616f, 0.187135f, 0.124756f, 0.623782f, 0.288499f, 0.787524f, 0.350877f, 0.849903f,
|
||||
0.436647f, 0.935673f, 0.873294f, 0.374269f, 0.538012f, 0.0389864f, 0.60039f, 0.101365f,
|
||||
0.57115f, 0.0721248f, 0.758285f, 0.259259f, 0.719298f, 0.220273f, 0.532164f, 0.0331384f,
|
||||
0.321637f, 0.820663f, 0.00974659f, 0.508772f, 0.469786f, 0.968811f, 0.282651f, 0.781676f,
|
||||
0.539961f, 0.0409357f, 0.727096f, 0.22807f, 0.500975f, 0.00194932f, 0.563353f, 0.0643275f,
|
||||
0.290448f, 0.789474f, 0.477583f, 0.976608f, 0.251462f, 0.750487f, 0.31384f, 0.812865f,
|
||||
0.94152f, 0.442495f, 0.879142f, 0.380117f, 0.37232f, 0.871345f, 0.309942f, 0.808967f,
|
||||
0.192982f, 0.692008f, 0.130604f, 0.62963f, 0.621832f, 0.122807f, 0.559454f, 0.0604289f,
|
||||
0.660819f, 0.161793f, 0.723197f, 0.224172f, 0.403509f, 0.902534f, 0.840156f, 0.341131f,
|
||||
0.411306f, 0.910331f, 0.473684f, 0.97271f, 0.653021f, 0.153996f, 0.0916179f, 0.590643f,
|
||||
0.196881f, 0.695906f, 0.384016f, 0.883041f, 0.0955166f, 0.594542f, 0.157895f, 0.65692f,
|
||||
0.945419f, 0.446394f, 0.633528f, 0.134503f, 0.844055f, 0.345029f, 0.906433f, 0.407407f,
|
||||
0.165692f, 0.664717f, 0.103314f, 0.602339f, 0.126706f, 0.625731f, 0.189084f, 0.688109f,
|
||||
0.91423f, 0.415205f, 0.851852f, 0.352827f, 0.875244f, 0.376218f, 0.937622f, 0.438596f,
|
||||
0.317739f, 0.816764f, 0.255361f, 0.754386f, 0.996101f, 0.497076f, 0.933723f, 0.434698f,
|
||||
0.567251f, 0.0682261f, 0.504873f, 0.00584795f, 0.247563f, 0.746589f, 0.185185f, 0.684211f,
|
||||
0.037037f, 0.536062f, 0.0994152f, 0.598441f, 0.777778f, 0.278752f, 0.465887f, 0.964912f,
|
||||
0.785575f, 0.28655f, 0.847953f, 0.348928f, 0.0292398f, 0.528265f, 0.7154f, 0.216374f,
|
||||
0.39961f, 0.898636f, 0.961014f, 0.461988f, 0.0487329f, 0.547758f, 0.111111f, 0.610136f,
|
||||
0.649123f, 0.150097f, 0.212476f, 0.711501f, 0.797271f, 0.298246f, 0.859649f, 0.360624f,
|
||||
0.118908f, 0.617934f, 0.0565302f, 0.555556f, 0.329435f, 0.82846f, 0.516569f, 0.0175439f,
|
||||
0.867446f, 0.368421f, 0.805068f, 0.306043f, 0.578947f, 0.079922f, 0.267057f, 0.766082f,
|
||||
0.270955f, 0.76998f, 0.707602f, 0.208577f, 0.668616f, 0.169591f, 0.606238f, 0.107212f,
|
||||
0.520468f, 0.0214425f, 0.45809f, 0.957115f, 0.419103f, 0.918129f, 0.356725f, 0.855751f,
|
||||
0.988304f, 0.489279f, 0.426901f, 0.925926f, 0.450292f, 0.949318f, 0.512671f, 0.0136452f,
|
||||
0.239766f, 0.738791f, 0.676413f, 0.177388f, 0.699805f, 0.20078f, 0.263158f, 0.762183f,
|
||||
0.773879f, 0.274854f, 0.337232f, 0.836257f, 0.672515f, 0.173489f, 0.734893f, 0.235867f,
|
||||
0.0253411f, 0.524366f, 0.586745f, 0.0877193f, 0.423002f, 0.922027f, 0.48538f, 0.984405f,
|
||||
0.74269f, 0.243665f, 0.680312f, 0.181287f, 0.953216f, 0.454191f, 0.1423f, 0.641326f,
|
||||
0.493177f, 0.992203f, 0.430799f, 0.929825f, 0.204678f, 0.703704f, 0.890838f, 0.391813f,
|
||||
0.894737f, 0.395712f, 0.0838207f, 0.582846f, 0.0448343f, 0.54386f, 0.231969f, 0.730994f,
|
||||
0.146199f, 0.645224f, 0.832359f, 0.333333f, 0.793372f, 0.294347f, 0.980507f, 0.481481f,
|
||||
0.364522f, 0.863548f, 0.80117f, 0.302144f, 0.824561f, 0.325536f, 0.138402f, 0.637427f,
|
||||
0.614035f, 0.11501f, 0.0526316f, 0.551657f, 0.0760234f, 0.575049f, 0.88694f, 0.387914f,
|
||||
};
|
||||
return mEnable ? LUT[offset & 511] : 0.5f;// branch prediction should optimize this!
|
||||
}
|
||||
}; // DitherLUT class
|
||||
|
||||
}// namspace math
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_DITHERLUT_HAS_BEEN_INCLUDED
|
||||
@@ -1,510 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/// @file HDDA.h
|
||||
///
|
||||
/// @author Ken Museth
|
||||
///
|
||||
/// @brief Hierarchical Digital Differential Analyzers specialized for VDB.
|
||||
|
||||
#ifndef NANOVDB_HDDA_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_HDDA_H_HAS_BEEN_INCLUDED
|
||||
|
||||
// Comment out to disable this explicit round-off check
|
||||
#define ENFORCE_FORWARD_STEPPING
|
||||
|
||||
#include <nanovdb/NanoVDB.h> // only dependency
|
||||
|
||||
namespace nanovdb::math {
|
||||
|
||||
/// @brief A Digital Differential Analyzer specialized for OpenVDB grids
|
||||
/// @note Conceptually similar to Bresenham's line algorithm applied
|
||||
/// to a 3D Ray intersecting OpenVDB nodes or voxels. Log2Dim = 0
|
||||
/// corresponds to a voxel and Log2Dim a tree node of size 2^Log2Dim.
|
||||
///
|
||||
/// @note The Ray template class is expected to have the following
|
||||
/// methods: test(time), t0(), t1(), invDir(), and operator()(time).
|
||||
/// See the example Ray class above for their definition.
|
||||
template<typename RayT, typename CoordT = Coord>
|
||||
class HDDA
|
||||
{
|
||||
public:
|
||||
using RealType = typename RayT::RealType;
|
||||
using RealT = RealType;
|
||||
using Vec3Type = typename RayT::Vec3Type;
|
||||
using Vec3T = Vec3Type;
|
||||
using CoordType = CoordT;
|
||||
|
||||
/// @brief Default ctor
|
||||
HDDA() = default;
|
||||
|
||||
/// @brief ctor from ray and dimension at which the DDA marches
|
||||
__hostdev__ HDDA(const RayT& ray, int dim) { this->init(ray, dim); }
|
||||
|
||||
/// @brief Re-initializes the HDDA
|
||||
__hostdev__ void init(const RayT& ray, RealT startTime, RealT maxTime, int dim)
|
||||
{
|
||||
assert(startTime <= maxTime);
|
||||
mDim = dim;
|
||||
mT0 = startTime;
|
||||
mT1 = maxTime;
|
||||
const Vec3T &pos = ray(mT0), &dir = ray.dir(), &inv = ray.invDir();
|
||||
mVoxel = RoundDown<CoordT>(pos) & (~(dim - 1));
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
if (dir[axis] == RealT(0)) { //handles dir = +/- 0
|
||||
mNext[axis] = Maximum<RealT>::value(); //i.e. disabled!
|
||||
mStep[axis] = 0;
|
||||
} else if (inv[axis] > 0) {
|
||||
mStep[axis] = 1;
|
||||
mNext[axis] = mT0 + (mVoxel[axis] + dim - pos[axis]) * inv[axis];
|
||||
mDelta[axis] = inv[axis];
|
||||
} else {
|
||||
mStep[axis] = -1;
|
||||
mNext[axis] = mT0 + (mVoxel[axis] - pos[axis]) * inv[axis];
|
||||
mDelta[axis] = -inv[axis];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Simular to init above except it uses the bounds of the input ray
|
||||
__hostdev__ void init(const RayT& ray, int dim) { this->init(ray, ray.t0(), ray.t1(), dim); }
|
||||
|
||||
/// @brief Updates the HDDA to march with the specified dimension
|
||||
__hostdev__ bool update(const RayT& ray, int dim)
|
||||
{
|
||||
if (mDim == dim)
|
||||
return false;
|
||||
mDim = dim;
|
||||
const Vec3T &pos = ray(mT0), &inv = ray.invDir();
|
||||
mVoxel = RoundDown<CoordT>(pos) & (~(dim - 1));
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
if (mStep[axis] == 0)
|
||||
continue;
|
||||
mNext[axis] = mT0 + (mVoxel[axis] - pos[axis]) * inv[axis];
|
||||
if (mStep[axis] > 0)
|
||||
mNext[axis] += dim * inv[axis];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
__hostdev__ int dim() const { return mDim; }
|
||||
|
||||
/// @brief Increment the voxel index to next intersected voxel or node
|
||||
/// and returns true if the step in time does not exceed maxTime.
|
||||
__hostdev__ bool step()
|
||||
{
|
||||
const int axis = MinIndex(mNext);
|
||||
#if 1
|
||||
switch (axis) {
|
||||
case 0:
|
||||
return step<0>();
|
||||
case 1:
|
||||
return step<1>();
|
||||
default:
|
||||
return step<2>();
|
||||
}
|
||||
#else
|
||||
mT0 = mNext[axis];
|
||||
mNext[axis] += mDim * mDelta[axis];
|
||||
mVoxel[axis] += mDim * mStep[axis];
|
||||
return mT0 <= mT1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Return the index coordinates of the next node or voxel
|
||||
/// intersected by the ray. If Log2Dim = 0 the return value is the
|
||||
/// actual signed coordinate of the voxel, else it is the origin
|
||||
/// of the corresponding VDB tree node or tile.
|
||||
/// @note Incurs no computational overhead.
|
||||
__hostdev__ const CoordT& voxel() const { return mVoxel; }
|
||||
|
||||
/// @brief Return the time (parameterized along the Ray) of the
|
||||
/// first hit of a tree node of size 2^Log2Dim.
|
||||
/// @details This value is initialized to startTime or ray.t0()
|
||||
/// depending on the constructor used.
|
||||
/// @note Incurs no computational overhead.
|
||||
__hostdev__ RealType time() const { return mT0; }
|
||||
|
||||
/// @brief Return the maximum time (parameterized along the Ray).
|
||||
__hostdev__ RealType maxTime() const { return mT1; }
|
||||
|
||||
/// @brief Return the time (parameterized along the Ray) of the
|
||||
/// second (i.e. next) hit of a tree node of size 2^Log2Dim.
|
||||
/// @note Incurs a (small) computational overhead.
|
||||
__hostdev__ RealType next() const
|
||||
{
|
||||
#if 1 //def __CUDA_ARCH__
|
||||
return fminf(mT1, fminf(mNext[0], fminf(mNext[1], mNext[2])));
|
||||
#else
|
||||
return std::min(mT1, std::min(mNext[0], std::min(mNext[1], mNext[2])));
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
// helper to implement the general form
|
||||
template<int axis>
|
||||
__hostdev__ bool step()
|
||||
{
|
||||
#ifdef ENFORCE_FORWARD_STEPPING
|
||||
//if (mNext[axis] <= mT0) mNext[axis] += mT0 - mNext[axis] + fmaxf(mNext[axis]*1.0e-6f, 1.0e-6f);
|
||||
//if (mNext[axis] <= mT0) mNext[axis] += mT0 - mNext[axis] + (mNext[axis] + 1.0f)*1.0e-6f;
|
||||
if (mNext[axis] <= mT0) {
|
||||
mNext[axis] += mT0 - 0.999999f * mNext[axis] + 1.0e-6f;
|
||||
}
|
||||
#endif
|
||||
mT0 = mNext[axis];
|
||||
mNext[ axis] += mDim * mDelta[axis];
|
||||
mVoxel[axis] += mDim * mStep[ axis];
|
||||
return mT0 <= mT1;
|
||||
}
|
||||
|
||||
int32_t mDim;
|
||||
RealT mT0, mT1; // min and max allowed times
|
||||
CoordT mVoxel, mStep; // current voxel location and step to next voxel location
|
||||
Vec3T mDelta, mNext; // delta time and next time
|
||||
}; // class HDDA
|
||||
|
||||
/////////////////////////////////////////// ZeroCrossing ////////////////////////////////////////////
|
||||
|
||||
/// @brief returns true if the ray intersects a zero-crossing at the voxel level of the grid in the accessor
|
||||
/// The empty-space ray-marching is performed at all levels of the tree using an
|
||||
/// HDDA. If an intersection is detected, then ijk is updated with the index coordinate of the closest
|
||||
/// voxel after the intersection point, v contains the grid values at ijk, and t is set to the time of
|
||||
/// the intersection along the ray.
|
||||
template<typename RayT, typename AccT>
|
||||
inline __hostdev__ bool ZeroCrossing(RayT& ray, AccT& acc, Coord& ijk, typename AccT::ValueType& v, float& t)
|
||||
{
|
||||
if (!ray.clip(acc.root().bbox()) || ray.t1() > 1e20)
|
||||
return false; // clip ray to bbox
|
||||
static const float Delta = 1.0001f;
|
||||
ijk = RoundDown<Coord>(ray.start()); // first hit of bbox
|
||||
HDDA<RayT, Coord> hdda(ray, acc.getDim(ijk, ray));
|
||||
const auto v0 = acc.getValue(ijk);
|
||||
while (hdda.step()) {
|
||||
ijk = RoundDown<Coord>(ray(hdda.time() + Delta));
|
||||
hdda.update(ray, acc.getDim(ijk, ray));
|
||||
if (hdda.dim() > 1 || !acc.isActive(ijk))
|
||||
continue; // either a tile value or an inactive voxel
|
||||
while (hdda.step() && acc.isActive(hdda.voxel())) { // in the narrow band
|
||||
v = acc.getValue(hdda.voxel());
|
||||
if (v * v0 < 0) { // zero crossing
|
||||
ijk = hdda.voxel();
|
||||
t = hdda.time();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////// DDA ////////////////////////////////////////////
|
||||
|
||||
/// @brief A Digital Differential Analyzer. Unlike HDDA (defined above) this DDA
|
||||
/// uses a fixed step-size defined by the template parameter Dim!
|
||||
///
|
||||
/// @note The Ray template class is expected to have the following
|
||||
/// methods: test(time), t0(), t1(), invDir(), and operator()(time).
|
||||
/// See the example Ray class above for their definition.
|
||||
template<typename RayT, typename CoordT = Coord, int Dim = 1>
|
||||
class DDA
|
||||
{
|
||||
static_assert(Dim >= 1, "Dim must be >= 1");
|
||||
|
||||
public:
|
||||
using RealType = typename RayT::RealType;
|
||||
using RealT = RealType;
|
||||
using Vec3Type = typename RayT::Vec3Type;
|
||||
using Vec3T = Vec3Type;
|
||||
using CoordType = CoordT;
|
||||
|
||||
/// @brief Default ctor
|
||||
DDA() = default;
|
||||
|
||||
/// @brief ctor from ray and dimension at which the DDA marches
|
||||
__hostdev__ DDA(const RayT& ray) { this->init(ray); }
|
||||
|
||||
/// @brief Re-initializes the DDA
|
||||
__hostdev__ void init(const RayT& ray, RealT startTime, RealT maxTime)
|
||||
{
|
||||
assert(startTime <= maxTime);
|
||||
mT0 = startTime;
|
||||
mT1 = maxTime;
|
||||
const Vec3T &pos = ray(mT0), &dir = ray.dir(), &inv = ray.invDir();
|
||||
mVoxel = RoundDown<CoordT>(pos) & (~(Dim - 1));
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
if (dir[axis] == RealT(0)) { //handles dir = +/- 0
|
||||
mNext[axis] = Maximum<RealT>::value(); //i.e. disabled!
|
||||
mStep[axis] = 0;
|
||||
} else if (inv[axis] > 0) {
|
||||
mStep[axis] = Dim;
|
||||
mNext[axis] = (mT0 + (mVoxel[axis] + Dim - pos[axis]) * inv[axis]);
|
||||
mDelta[axis] = inv[axis];
|
||||
} else {
|
||||
mStep[axis] = -Dim;
|
||||
mNext[axis] = mT0 + (mVoxel[axis] - pos[axis]) * inv[axis];
|
||||
mDelta[axis] = -inv[axis];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Simular to init above except it uses the bounds of the input ray
|
||||
__hostdev__ void init(const RayT& ray) { this->init(ray, ray.t0(), ray.t1()); }
|
||||
|
||||
/// @brief Increment the voxel index to next intersected voxel or node
|
||||
/// and returns true if the step in time does not exceed maxTime.
|
||||
__hostdev__ bool step()
|
||||
{
|
||||
const int axis = MinIndex(mNext);
|
||||
#if 1
|
||||
switch (axis) {
|
||||
case 0:
|
||||
return step<0>();
|
||||
case 1:
|
||||
return step<1>();
|
||||
default:
|
||||
return step<2>();
|
||||
}
|
||||
#else
|
||||
#ifdef ENFORCE_FORWARD_STEPPING
|
||||
if (mNext[axis] <= mT0) {
|
||||
mNext[axis] += mT0 - 0.999999f * mNext[axis] + 1.0e-6f;
|
||||
}
|
||||
#endif
|
||||
mT0 = mNext[axis];
|
||||
mNext[axis] += mDelta[axis];
|
||||
mVoxel[axis] += mStep[axis];
|
||||
return mT0 <= mT1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Return the index coordinates of the next node or voxel
|
||||
/// intersected by the ray. If Log2Dim = 0 the return value is the
|
||||
/// actual signed coordinate of the voxel, else it is the origin
|
||||
/// of the corresponding VDB tree node or tile.
|
||||
/// @note Incurs no computational overhead.
|
||||
__hostdev__ const CoordT& voxel() const { return mVoxel; }
|
||||
|
||||
/// @brief Return the time (parameterized along the Ray) of the
|
||||
/// first hit of a tree node of size 2^Log2Dim.
|
||||
/// @details This value is initialized to startTime or ray.t0()
|
||||
/// depending on the constructor used.
|
||||
/// @note Incurs no computational overhead.
|
||||
__hostdev__ RealType time() const { return mT0; }
|
||||
|
||||
/// @brief Return the maximum time (parameterized along the Ray).
|
||||
__hostdev__ RealType maxTime() const { return mT1; }
|
||||
|
||||
/// @brief Return the time (parameterized along the Ray) of the
|
||||
/// second (i.e. next) hit of a tree node of size 2^Log2Dim.
|
||||
/// @note Incurs a (small) computational overhead.
|
||||
__hostdev__ RealType next() const
|
||||
{
|
||||
return Min(mT1, Min(mNext[0], Min(mNext[1], mNext[2])));
|
||||
}
|
||||
|
||||
__hostdev__ int nextAxis() const
|
||||
{
|
||||
return nanovdb::math::MinIndex(mNext);
|
||||
}
|
||||
|
||||
private:
|
||||
// helper to implement the general form
|
||||
template<int axis>
|
||||
__hostdev__ bool step()
|
||||
{
|
||||
#ifdef ENFORCE_FORWARD_STEPPING
|
||||
if (mNext[axis] <= mT0) {
|
||||
mNext[axis] += mT0 - 0.999999f * mNext[axis] + 1.0e-6f;
|
||||
}
|
||||
#endif
|
||||
mT0 = mNext[axis];
|
||||
mNext[axis] += mDelta[axis];
|
||||
mVoxel[axis] += mStep[axis];
|
||||
return mT0 <= mT1;
|
||||
}
|
||||
|
||||
RealT mT0, mT1; // min and max allowed times
|
||||
CoordT mVoxel, mStep; // current voxel location and step to next voxel location
|
||||
Vec3T mDelta, mNext; // delta time and next time
|
||||
}; // class DDA
|
||||
|
||||
/////////////////////////////////////////// ZeroCrossingNode ////////////////////////////////////////////
|
||||
|
||||
template<typename RayT, typename NodeT>
|
||||
inline __hostdev__ bool ZeroCrossingNode(RayT& ray, const NodeT& node, float v0, nanovdb::math::Coord& ijk, float& v, float& t)
|
||||
{
|
||||
math::BBox<Coord> bbox(node.origin(), node.origin() + Coord(node.dim() - 1));
|
||||
|
||||
if (!ray.clip(node.bbox())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float t0 = ray.t0();
|
||||
|
||||
static const float Delta = 1.0001f;
|
||||
ijk = Coord::Floor(ray(ray.t0() + Delta));
|
||||
|
||||
t = t0;
|
||||
v = 0;
|
||||
|
||||
DDA<RayT, Coord, 1 << NodeT::LOG2DIM> dda(ray);
|
||||
while (dda.step()) {
|
||||
ijk = dda.voxel();
|
||||
|
||||
if (bbox.isInside(ijk) == false)
|
||||
return false;
|
||||
|
||||
v = node.getValue(ijk);
|
||||
if (v * v0 < 0) {
|
||||
t = dda.time();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////// TreeMarcher ////////////////////////////////////////////
|
||||
|
||||
/// @brief returns true if the ray intersects an active value at any level of the grid in the accessor.
|
||||
/// The empty-space ray-marching is performed at all levels of the tree using an
|
||||
/// HDDA. If an intersection is detected, then ijk is updated with the index coordinate of the first
|
||||
/// active voxel or tile, and t is set to the time of its intersection along the ray.
|
||||
template<typename RayT, typename AccT>
|
||||
inline __hostdev__ bool firstActive(RayT& ray, AccT& acc, Coord &ijk, float& t)
|
||||
{
|
||||
if (!ray.clip(acc.root().bbox()) || ray.t1() > 1e20) {// clip ray to bbox
|
||||
return false;// missed or undefined bbox
|
||||
}
|
||||
static const float Delta = 1.0001f;// forward step-size along the ray to avoid getting stuck
|
||||
t = ray.t0();// initiate time
|
||||
ijk = RoundDown<Coord>(ray.start()); // first voxel inside bbox
|
||||
for (HDDA<RayT, Coord> hdda(ray, acc.getDim(ijk, ray)); !acc.isActive(ijk); hdda.update(ray, acc.getDim(ijk, ray))) {
|
||||
if (!hdda.step()) return false;// leap-frog HDDA and exit if ray bound is exceeded
|
||||
t = hdda.time() + Delta;// update time
|
||||
ijk = RoundDown<Coord>( ray(t) );// update ijk
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////// TreeMarcher ////////////////////////////////////////////
|
||||
|
||||
/// @brief A Tree Marcher for Generic Grids
|
||||
|
||||
template<typename NodeT, typename RayT, typename AccT, typename CoordT = Coord>
|
||||
class TreeMarcher
|
||||
{
|
||||
public:
|
||||
using ChildT = typename NodeT::ChildNodeType;
|
||||
using RealType = typename RayT::RealType;
|
||||
using RealT = RealType;
|
||||
using CoordType = CoordT;
|
||||
|
||||
inline __hostdev__ TreeMarcher(AccT& acc)
|
||||
: mAcc(acc)
|
||||
{
|
||||
}
|
||||
|
||||
/// @brief Initialize the TreeMarcher with an index-space ray.
|
||||
inline __hostdev__ bool init(const RayT& indexRay)
|
||||
{
|
||||
mRay = indexRay;
|
||||
if (!mRay.clip(mAcc.root().bbox()))
|
||||
return false; // clip ray to bbox
|
||||
|
||||
// tweak the intersection span into the bbox.
|
||||
// CAVEAT: this will potentially clip some tiny corner intersections.
|
||||
static const float Eps = 0.000001f;
|
||||
const float t0 = mRay.t0() + Eps;
|
||||
const float t1 = mRay.t1() - Eps;
|
||||
if (t0 > t1)
|
||||
return false;
|
||||
|
||||
const CoordT ijk = RoundDown<Coord>(mRay(t0));
|
||||
const uint32_t dim = mAcc.getDim(ijk, mRay);
|
||||
mHdda.init(mRay, t0, t1, nanovdb::math::Max(dim, NodeT::dim()));
|
||||
|
||||
mT0 = (dim <= ChildT::dim()) ? mHdda.time() : -1; // potentially begin a span.
|
||||
mTmax = t1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief step the ray through the tree. If the ray hits a node then
|
||||
/// populate t0 & t1, and the node.
|
||||
/// @return true when a node of type NodeT is intersected, false otherwise.
|
||||
inline __hostdev__ bool step(const NodeT** node, float& t0, float& t1)
|
||||
{
|
||||
// CAVEAT: if Delta is too large then it will clip corners of nodes in a visible way.
|
||||
// but it has to be quite large when very far from the grid (due to fp32 rounding)
|
||||
static const float Delta = 0.01f;
|
||||
bool hddaIsValid;
|
||||
|
||||
do {
|
||||
t0 = mT0;
|
||||
|
||||
auto currentNode = mAcc.template getNode<NodeT>();
|
||||
|
||||
// get next node intersection...
|
||||
hddaIsValid = mHdda.step();
|
||||
const CoordT nextIjk = RoundDown<Coord>(mRay(mHdda.time() + Delta));
|
||||
const auto nextDim = mAcc.getDim(nextIjk, mRay);
|
||||
mHdda.update(mRay, (int)Max(nextDim, NodeT::dim()));
|
||||
mT0 = (nextDim <= ChildT::dim()) ? mHdda.time() : -1; // potentially begin a span.
|
||||
|
||||
if (t0 >= 0) { // we are in a span.
|
||||
t1 = Min(mTmax, mHdda.time());
|
||||
|
||||
// TODO: clean this up!
|
||||
if (t0 >= t1 || currentNode == nullptr)
|
||||
continue;
|
||||
|
||||
*node = currentNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
} while (hddaIsValid);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline __hostdev__ const RayT& ray() const { return mRay; }
|
||||
|
||||
inline __hostdev__ RayT& ray() { return mRay; }
|
||||
|
||||
private:
|
||||
AccT& mAcc;
|
||||
RayT mRay;
|
||||
HDDA<RayT, Coord> mHdda;
|
||||
float mT0;
|
||||
float mTmax;
|
||||
};// TreeMarcher
|
||||
|
||||
/////////////////////////////////////////// PointTreeMarcher ////////////////////////////////////////////
|
||||
|
||||
/// @brief A Tree Marcher for Point Grids
|
||||
///
|
||||
/// @note This class will handle correctly offseting the ray by 0.5 to ensure that
|
||||
/// the underlying HDDA will intersect with the grid-cells. See details below.
|
||||
|
||||
template<typename AccT, typename RayT, typename CoordT = Coord>
|
||||
class PointTreeMarcher : public TreeMarcher<LeafNode<typename AccT::ValueType>, RayT, AccT, CoordT>
|
||||
{
|
||||
using BaseT = TreeMarcher<LeafNode<typename AccT::ValueType>, RayT, AccT, CoordT>;
|
||||
public:
|
||||
__hostdev__ PointTreeMarcher(AccT& acc) : BaseT(acc) {}
|
||||
|
||||
/// @brief Initiates this instance with a ray in index space.
|
||||
///
|
||||
/// @details An offset by 0.5 is applied to the ray to account for the fact that points in vdb
|
||||
/// grids are bucketed into so-called grid cell, which are centered round grid voxels,
|
||||
/// whereas the DDA is based on so-called grid nodes, which are coincident with grid
|
||||
/// voxels. So, rather than offsettting the points by 0.5 to bring them into a grid
|
||||
/// node representation this method offsets the eye of the ray by 0.5, which effectively
|
||||
/// ensures that the DDA operates on grid cells as oppose to grid nodes. This subtle
|
||||
/// but important offset by 0.5 is explined in more details in our online documentation.
|
||||
__hostdev__ bool init(RayT ray) { return BaseT::init(ray.offsetEye(0.5)); }
|
||||
};// PointTreeMarcher
|
||||
|
||||
} // namespace nanovdb::math
|
||||
|
||||
#endif // NANOVDB_HDDA_HAS_BEEN_INCLUDED
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,557 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/// @file Ray.h
|
||||
///
|
||||
/// @author Ken Museth
|
||||
///
|
||||
/// @brief A Ray class.
|
||||
|
||||
#ifndef NANOVDB_MATH_RAY_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_MATH_RAY_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h> // for Vec3
|
||||
namespace nanovdb {// ===================================================
|
||||
|
||||
namespace math {// ======================================================
|
||||
|
||||
template<typename RealT>
|
||||
class Ray
|
||||
{
|
||||
public:
|
||||
using RealType = RealT;
|
||||
using Vec3Type = Vec3<RealT>;
|
||||
using Vec3T = Vec3Type;
|
||||
|
||||
struct TimeSpan
|
||||
{
|
||||
RealT t0, t1;
|
||||
/// @brief Default constructor
|
||||
__hostdev__ TimeSpan() {}
|
||||
/// @brief Constructor
|
||||
__hostdev__ TimeSpan(RealT _t0, RealT _t1)
|
||||
: t0(_t0)
|
||||
, t1(_t1)
|
||||
{
|
||||
}
|
||||
/// @brief Set both times
|
||||
__hostdev__ void set(RealT _t0, RealT _t1)
|
||||
{
|
||||
t0 = _t0;
|
||||
t1 = _t1;
|
||||
}
|
||||
/// @brief Get both times
|
||||
__hostdev__ void get(RealT& _t0, RealT& _t1) const
|
||||
{
|
||||
_t0 = t0;
|
||||
_t1 = t1;
|
||||
}
|
||||
/// @brief Return @c true if t1 is larger than t0 by at least eps.
|
||||
__hostdev__ bool valid(RealT eps = Delta<RealT>::value()) const { return (t1 - t0) > eps; }
|
||||
/// @brief Return the midpoint of the ray.
|
||||
__hostdev__ RealT mid() const { return 0.5 * (t0 + t1); }
|
||||
/// @brief Multiplies both times
|
||||
__hostdev__ void scale(RealT s)
|
||||
{
|
||||
assert(s > 0);
|
||||
t0 *= s;
|
||||
t1 *= s;
|
||||
}
|
||||
/// @brief Return @c true if time is inclusive
|
||||
__hostdev__ bool test(RealT t) const { return (t >= t0 && t <= t1); }
|
||||
};
|
||||
|
||||
__hostdev__ Ray(const Vec3Type& eye = Vec3Type(0, 0, 0),
|
||||
const Vec3Type& direction = Vec3Type(1, 0, 0),
|
||||
RealT t0 = Delta<RealT>::value(),
|
||||
RealT t1 = Maximum<RealT>::value())
|
||||
: mEye(eye)
|
||||
, mDir(direction)
|
||||
, mInvDir(1 / mDir[0], 1 / mDir[1], 1 / mDir[2])
|
||||
, mTimeSpan(t0, t1)
|
||||
, mSign{mInvDir[0] < 0, mInvDir[1] < 0, mInvDir[2] < 0}
|
||||
{
|
||||
}
|
||||
|
||||
__hostdev__ Ray& offsetEye(RealT offset)
|
||||
{
|
||||
mEye[0] += offset;
|
||||
mEye[1] += offset;
|
||||
mEye[2] += offset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__hostdev__ Ray& setEye(const Vec3Type& eye)
|
||||
{
|
||||
mEye = eye;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__hostdev__ Ray& setDir(const Vec3Type& dir)
|
||||
{
|
||||
mDir = dir;
|
||||
mInvDir[0] = 1.0 / mDir[0];
|
||||
mInvDir[1] = 1.0 / mDir[1];
|
||||
mInvDir[2] = 1.0 / mDir[2];
|
||||
mSign[0] = mInvDir[0] < 0;
|
||||
mSign[1] = mInvDir[1] < 0;
|
||||
mSign[2] = mInvDir[2] < 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__hostdev__ Ray& setMinTime(RealT t0)
|
||||
{
|
||||
mTimeSpan.t0 = t0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__hostdev__ Ray& setMaxTime(RealT t1)
|
||||
{
|
||||
mTimeSpan.t1 = t1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__hostdev__ Ray& setTimes(
|
||||
RealT t0 = Delta<RealT>::value(),
|
||||
RealT t1 = Maximum<RealT>::value())
|
||||
{
|
||||
assert(t0 > 0 && t1 > 0);
|
||||
mTimeSpan.set(t0, t1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
__hostdev__ Ray& scaleTimes(RealT scale)
|
||||
{
|
||||
mTimeSpan.scale(scale);
|
||||
return *this;
|
||||
}
|
||||
|
||||
__hostdev__ Ray& reset(
|
||||
const Vec3Type& eye,
|
||||
const Vec3Type& direction,
|
||||
RealT t0 = Delta<RealT>::value(),
|
||||
RealT t1 = Maximum<RealT>::value())
|
||||
{
|
||||
this->setEye(eye);
|
||||
this->setDir(direction);
|
||||
this->setTimes(t0, t1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
__hostdev__ const Vec3T& eye() const { return mEye; }
|
||||
|
||||
__hostdev__ const Vec3T& dir() const { return mDir; }
|
||||
|
||||
__hostdev__ const Vec3T& invDir() const { return mInvDir; }
|
||||
|
||||
__hostdev__ RealT t0() const { return mTimeSpan.t0; }
|
||||
|
||||
__hostdev__ RealT t1() const { return mTimeSpan.t1; }
|
||||
|
||||
__hostdev__ int sign(int i) const { return mSign[i]; }
|
||||
|
||||
/// @brief Return the position along the ray at the specified time.
|
||||
__hostdev__ Vec3T operator()(RealT time) const
|
||||
{
|
||||
#if 1
|
||||
return Vec3T(fmaf(time, mDir[0], mEye[0]),
|
||||
fmaf(time, mDir[1], mEye[1]),
|
||||
fmaf(time, mDir[2], mEye[2]));
|
||||
#else
|
||||
return mEye + mDir * time;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Return the starting point of the ray.
|
||||
__hostdev__ Vec3T start() const { return (*this)(mTimeSpan.t0); }
|
||||
|
||||
/// @brief Return the endpoint of the ray.
|
||||
__hostdev__ Vec3T end() const { return (*this)(mTimeSpan.t1); }
|
||||
|
||||
/// @brief Return the midpoint of the ray.
|
||||
__hostdev__ Vec3T mid() const { return (*this)(mTimeSpan.mid()); }
|
||||
|
||||
/// @brief Return @c true if t1 is larger than t0 by at least eps.
|
||||
__hostdev__ bool valid(RealT eps = Delta<float>::value()) const { return mTimeSpan.valid(eps); }
|
||||
|
||||
/// @brief Return @c true if @a time is within t0 and t1, both inclusive.
|
||||
__hostdev__ bool test(RealT time) const { return mTimeSpan.test(time); }
|
||||
|
||||
/// @brief Return a new Ray that is transformed with the specified map.
|
||||
///
|
||||
/// @param map the map from which to construct the new Ray.
|
||||
///
|
||||
/// @warning Assumes a linear map and a normalized direction.
|
||||
///
|
||||
/// @details The requirement that the direction is normalized
|
||||
/// follows from the transformation of t0 and t1 - and that fact that
|
||||
/// we want applyMap and applyInverseMap to be inverse operations.
|
||||
template<typename MapType>
|
||||
__hostdev__ Ray applyMap(const MapType& map) const
|
||||
{
|
||||
const Vec3T eye = map.applyMap(mEye);
|
||||
const Vec3T dir = map.applyJacobian(mDir);
|
||||
const RealT length = dir.length(), invLength = RealT(1) / length;
|
||||
RealT t1 = mTimeSpan.t1;
|
||||
if (mTimeSpan.t1 < Maximum<RealT>::value()) {
|
||||
t1 *= length;
|
||||
}
|
||||
return Ray(eye, dir * invLength, length * mTimeSpan.t0, t1);
|
||||
}
|
||||
template<typename MapType>
|
||||
__hostdev__ Ray applyMapF(const MapType& map) const
|
||||
{
|
||||
const Vec3T eye = map.applyMapF(mEye);
|
||||
const Vec3T dir = map.applyJacobianF(mDir);
|
||||
const RealT length = dir.length(), invLength = RealT(1) / length;
|
||||
RealT t1 = mTimeSpan.t1;
|
||||
if (mTimeSpan.t1 < Maximum<RealT>::value()) {
|
||||
t1 *= length;
|
||||
}
|
||||
return Ray(eye, dir * invLength, length * mTimeSpan.t0, t1);
|
||||
}
|
||||
|
||||
/// @brief Return a new Ray that is transformed with the inverse of the specified map.
|
||||
///
|
||||
/// @param map the map from which to construct the new Ray by inverse mapping.
|
||||
///
|
||||
/// @warning Assumes a linear map and a normalized direction.
|
||||
///
|
||||
/// @details The requirement that the direction is normalized
|
||||
/// follows from the transformation of t0 and t1 - and that fact that
|
||||
/// we want applyMap and applyInverseMap to be inverse operations.
|
||||
template<typename MapType>
|
||||
__hostdev__ Ray applyInverseMap(const MapType& map) const
|
||||
{
|
||||
const Vec3T eye = map.applyInverseMap(mEye);
|
||||
const Vec3T dir = map.applyInverseJacobian(mDir);
|
||||
const RealT length = dir.length(), invLength = RealT(1) / length;
|
||||
return Ray(eye, dir * invLength, length * mTimeSpan.t0, length * mTimeSpan.t1);
|
||||
}
|
||||
template<typename MapType>
|
||||
__hostdev__ Ray applyInverseMapF(const MapType& map) const
|
||||
{
|
||||
const Vec3T eye = map.applyInverseMapF(mEye);
|
||||
const Vec3T dir = map.applyInverseJacobianF(mDir);
|
||||
const RealT length = dir.length(), invLength = RealT(1) / length;
|
||||
return Ray(eye, dir * invLength, length * mTimeSpan.t0, length * mTimeSpan.t1);
|
||||
}
|
||||
|
||||
/// @brief Return a new ray in world space, assuming the existing
|
||||
/// ray is represented in the index space of the specified grid.
|
||||
template<typename GridType>
|
||||
__hostdev__ Ray indexToWorldF(const GridType& grid) const
|
||||
{
|
||||
const Vec3T eye = grid.indexToWorldF(mEye);
|
||||
const Vec3T dir = grid.indexToWorldDirF(mDir);
|
||||
const RealT length = dir.length(), invLength = RealT(1) / length;
|
||||
RealT t1 = mTimeSpan.t1;
|
||||
if (mTimeSpan.t1 < Maximum<RealT>::value()) {
|
||||
t1 *= length;
|
||||
}
|
||||
return Ray(eye, dir * invLength, length * mTimeSpan.t0, t1);
|
||||
}
|
||||
|
||||
/// @brief Return a new ray in index space, assuming the existing
|
||||
/// ray is represented in the world space of the specified grid.
|
||||
template<typename GridType>
|
||||
__hostdev__ Ray worldToIndexF(const GridType& grid) const
|
||||
{
|
||||
const Vec3T eye = grid.worldToIndexF(mEye);
|
||||
const Vec3T dir = grid.worldToIndexDirF(mDir);
|
||||
const RealT length = dir.length(), invLength = RealT(1) / length;
|
||||
RealT t1 = mTimeSpan.t1;
|
||||
if (mTimeSpan.t1 < Maximum<RealT>::value()) {
|
||||
t1 *= length;
|
||||
}
|
||||
return Ray(eye, dir * invLength, length * mTimeSpan.t0, t1);
|
||||
}
|
||||
|
||||
/// @brief Return true if this ray intersects the specified sphere.
|
||||
///
|
||||
/// @param center The center of the sphere in the same space as this ray.
|
||||
/// @param radius The radius of the sphere in the same units as this ray.
|
||||
/// @param t0 The first intersection point if an intersection exists.
|
||||
/// @param t1 The second intersection point if an intersection exists.
|
||||
///
|
||||
/// @note If the return value is true, i.e. a hit, and t0 =
|
||||
/// this->t0() or t1 == this->t1() only one true intersection exist.
|
||||
__hostdev__ bool intersects(const Vec3T& center, RealT radius, RealT& t0, RealT& t1) const
|
||||
{
|
||||
const Vec3T origin = mEye - center;
|
||||
const RealT A = mDir.lengthSqr();
|
||||
const RealT B = 2 * mDir.dot(origin);
|
||||
const RealT C = origin.lengthSqr() - radius * radius;
|
||||
const RealT D = B * B - 4 * A * C;
|
||||
|
||||
if (D < 0) {
|
||||
return false;
|
||||
}
|
||||
const RealT Q = RealT(-0.5) * (B < 0 ? (B + Sqrt(D)) : (B - Sqrt(D)));
|
||||
|
||||
t0 = Q / A;
|
||||
t1 = C / Q;
|
||||
|
||||
if (t0 > t1) {
|
||||
RealT tmp = t0;
|
||||
t0 = t1;
|
||||
t1 = tmp;
|
||||
}
|
||||
if (t0 < mTimeSpan.t0) {
|
||||
t0 = mTimeSpan.t0;
|
||||
}
|
||||
if (t1 > mTimeSpan.t1) {
|
||||
t1 = mTimeSpan.t1;
|
||||
}
|
||||
return t0 <= t1;
|
||||
}
|
||||
|
||||
/// @brief Return true if this ray intersects the specified sphere.
|
||||
///
|
||||
/// @param center The center of the sphere in the same space as this ray.
|
||||
/// @param radius The radius of the sphere in the same units as this ray.
|
||||
__hostdev__ bool intersects(const Vec3T& center, RealT radius) const
|
||||
{
|
||||
RealT t0, t1;
|
||||
return this->intersects(center, radius, t0, t1) > 0;
|
||||
}
|
||||
|
||||
/// @brief Return true if this ray intersects the specified sphere.
|
||||
///
|
||||
/// @note For intersection this ray is clipped to the two intersection points.
|
||||
///
|
||||
/// @param center The center of the sphere in the same space as this ray.
|
||||
/// @param radius The radius of the sphere in the same units as this ray.
|
||||
__hostdev__ bool clip(const Vec3T& center, RealT radius)
|
||||
{
|
||||
RealT t0, t1;
|
||||
const bool hit = this->intersects(center, radius, t0, t1);
|
||||
if (hit) {
|
||||
mTimeSpan.set(t0, t1);
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
#if 0
|
||||
/// @brief Return true if the Ray intersects the specified
|
||||
/// axisaligned bounding box.
|
||||
///
|
||||
/// @param bbox Axis-aligned bounding box in the same space as the Ray.
|
||||
/// @param t0 If an intersection is detected this is assigned
|
||||
/// the time for the first intersection point.
|
||||
/// @param t1 If an intersection is detected this is assigned
|
||||
/// the time for the second intersection point.
|
||||
template<typename BBoxT>
|
||||
__hostdev__ bool intersects(const BBoxT& bbox, RealT& t0, RealT& t1) const
|
||||
{
|
||||
t0 = (bbox[ mSign[0]][0] - mEye[0]) * mInvDir[0];
|
||||
RealT t2 = (bbox[1-mSign[1]][1] - mEye[1]) * mInvDir[1];
|
||||
if (t0 > t2) return false;
|
||||
t1 = (bbox[1-mSign[0]][0] - mEye[0]) * mInvDir[0];
|
||||
RealT t3 = (bbox[ mSign[1]][1] - mEye[1]) * mInvDir[1];
|
||||
if (t3 > t1) return false;
|
||||
if (t3 > t0) t0 = t3;
|
||||
if (t2 < t1) t1 = t2;
|
||||
t3 = (bbox[ mSign[2]][2] - mEye[2]) * mInvDir[2];
|
||||
if (t3 > t1) return false;
|
||||
t2 = (bbox[1-mSign[2]][2] - mEye[2]) * mInvDir[2];
|
||||
if (t0 > t2) return false;
|
||||
if (t3 > t0) t0 = t3;
|
||||
if (mTimeSpan.t1 < t0) return false;
|
||||
if (t2 < t1) t1 = t2;
|
||||
if (mTimeSpan.t0 > t1) return false;
|
||||
if (mTimeSpan.t0 > t0) t0 = mTimeSpan.t0;
|
||||
if (mTimeSpan.t1 < t1) t1 = mTimeSpan.t1;
|
||||
return true;
|
||||
/*
|
||||
mTimeSpan.get(_t0, _t1);
|
||||
double t0 = _t0, t1 = _t1;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
//if (abs(mDir[i])<1e-3) continue;
|
||||
double a = (double(bbox.min()[i]) - mEye[i]) * mInvDir[i];
|
||||
double b = (double(bbox.max()[i]) - mEye[i]) * mInvDir[i];
|
||||
if (a > b) {
|
||||
double tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
if (a > t0) t0 = a;
|
||||
if (b < t1) t1 = b;
|
||||
if (t0 > t1) {
|
||||
//if (gVerbose) printf("Missed BBOX: (%i,%i,%i) -> (%i,%i,%i) t0=%f t1=%f\n",
|
||||
// bbox.min()[0], bbox.min()[1], bbox.min()[2],
|
||||
// bbox.max()[0], bbox.max()[1], bbox.max()[2], t0, t1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_t0 = t0; _t1 = t1;
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
#else
|
||||
/// @brief Returns true if this ray intersects an index bounding box.
|
||||
/// If the return value is true t0 and t1 are set to the intersection
|
||||
/// times along the ray.
|
||||
///
|
||||
/// @warning Intersection with a CoordBBox internally converts to a floating-point bbox
|
||||
/// which imples that the max is padded with one voxel, i.e. bbox.max += 1! This
|
||||
/// avoids gaps between neighboring CoordBBox'es, say from neighboring tree nodes.
|
||||
__hostdev__ bool intersects(const CoordBBox& bbox, RealT& t0, RealT& t1) const
|
||||
{
|
||||
mTimeSpan.get(t0, t1);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
RealT a = RealT(bbox.min()[i]), b = RealT(bbox.max()[i] + 1);
|
||||
if (a >= b) { // empty bounding box
|
||||
return false;
|
||||
}
|
||||
a = (a - mEye[i]) * mInvDir[i];
|
||||
b = (b - mEye[i]) * mInvDir[i];
|
||||
if (a > b) {
|
||||
RealT tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
if (a > t0) {
|
||||
t0 = a;
|
||||
}
|
||||
if (b < t1) {
|
||||
t1 = b;
|
||||
}
|
||||
if (t0 > t1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/// @brief Returns true if this ray intersects a floating-point bounding box.
|
||||
/// If the return value is true t0 and t1 are set to the intersection
|
||||
/// times along the ray.
|
||||
template<typename OtherVec3T>
|
||||
__hostdev__ bool intersects(const BBox<OtherVec3T>& bbox, RealT& t0, RealT& t1) const
|
||||
{
|
||||
static_assert(util::is_floating_point<typename OtherVec3T::ValueType>::value, "Ray::intersects: Expected a floating point coordinate");
|
||||
mTimeSpan.get(t0, t1);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
RealT a = RealT(bbox.min()[i]), b = RealT(bbox.max()[i]);
|
||||
if (a >= b) { // empty bounding box
|
||||
return false;
|
||||
}
|
||||
a = (a - mEye[i]) * mInvDir[i];
|
||||
b = (b - mEye[i]) * mInvDir[i];
|
||||
if (a > b) {
|
||||
RealT tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
if (a > t0) {
|
||||
t0 = a;
|
||||
}
|
||||
if (b < t1) {
|
||||
t1 = b;
|
||||
}
|
||||
if (t0 > t1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief Return true if this ray intersects the specified bounding box.
|
||||
///
|
||||
/// @param bbox Axis-aligned bounding box in the same space as this ray.
|
||||
///
|
||||
/// @warning If @a bbox is of the type CoordBBox it is converted to a floating-point
|
||||
/// bounding box, which imples that the max is padded with one voxel, i.e.
|
||||
/// bbox.max += 1! This avoids gaps between neighboring CoordBBox'es, say
|
||||
/// from neighboring tree nodes.
|
||||
template<typename BBoxT>
|
||||
__hostdev__ bool intersects(const BBoxT& bbox) const
|
||||
{
|
||||
#if 1
|
||||
RealT t0, t1;
|
||||
return this->intersects(bbox, t0, t1);
|
||||
#else
|
||||
//BBox<Vec3T> bbox(Vec3T(_bbox[0][0]-1e-4,_bbox[0][1]-1e-4,_bbox[0][2]-1e-4),
|
||||
// Vec3T(_bbox[1][0]+1e-4,_bbox[1][1]+1e-4,_bbox[1][2]+1e-4));
|
||||
RealT t0 = (bbox[mSign[0]][0] - mEye[0]) * mInvDir[0];
|
||||
RealT t2 = (bbox[1 - mSign[1]][1] - mEye[1]) * mInvDir[1];
|
||||
if (t0 > t2) return false;
|
||||
RealT t1 = (bbox[1 - mSign[0]][0] - mEye[0]) * mInvDir[0];
|
||||
RealT t3 = (bbox[mSign[1]][1] - mEye[1]) * mInvDir[1];
|
||||
if (t3 > t1) return false;
|
||||
if (t3 > t0) t0 = t3;
|
||||
if (t2 < t1) t1 = t2;
|
||||
t3 = (bbox[mSign[2]][2] - mEye[2]) * mInvDir[2];
|
||||
if (t3 > t1) return false;
|
||||
t2 = (bbox[1 - mSign[2]][2] - mEye[2]) * mInvDir[2];
|
||||
if (t0 > t2) return false;
|
||||
//if (t3 > t0) t0 = t3;
|
||||
//if (mTimeSpan.t1 < t0) return false;
|
||||
//if (t2 < t1) t1 = t2;
|
||||
//return mTimeSpan.t0 < t1;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Return true if this ray intersects the specified bounding box.
|
||||
///
|
||||
/// @param bbox Axis-aligned bounding box in the same space as this ray.
|
||||
///
|
||||
/// @warning If @a bbox is of the type CoordBBox it is converted to a floating-point
|
||||
/// bounding box, which imples that the max is padded with one voxel, i.e.
|
||||
/// bbox.max += 1! This avoids gaps between neighboring CoordBBox'es, say
|
||||
/// from neighboring tree nodes.
|
||||
///
|
||||
/// @note For intersection this ray is clipped to the two intersection points.
|
||||
template<typename BBoxT>
|
||||
__hostdev__ bool clip(const BBoxT& bbox)
|
||||
{
|
||||
RealT t0, t1;
|
||||
const bool hit = this->intersects(bbox, t0, t1);
|
||||
if (hit) {
|
||||
mTimeSpan.set(t0, t1);
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
/// @brief Return true if the Ray intersects the plane specified
|
||||
/// by a normal and distance from the origin.
|
||||
///
|
||||
/// @param normal Normal of the plane.
|
||||
/// @param distance Distance of the plane to the origin.
|
||||
/// @param t Time of intersection, if one exists.
|
||||
__hostdev__ bool intersects(const Vec3T& normal, RealT distance, RealT& t) const
|
||||
{
|
||||
const RealT cosAngle = mDir.dot(normal);
|
||||
if (isApproxZero(cosAngle)) {
|
||||
return false; // ray is parallel to plane
|
||||
}
|
||||
t = (distance - mEye.dot(normal)) / cosAngle;
|
||||
return this->test(t);
|
||||
}
|
||||
|
||||
/// @brief Return true if the Ray intersects the plane specified
|
||||
/// by a normal and point.
|
||||
///
|
||||
/// @param normal Normal of the plane.
|
||||
/// @param point Point in the plane.
|
||||
/// @param t Time of intersection, if one exists.
|
||||
__hostdev__ bool intersects(const Vec3T& normal, const Vec3T& point, RealT& t) const
|
||||
{
|
||||
return this->intersects(normal, point.dot(normal), t);
|
||||
}
|
||||
|
||||
private:
|
||||
Vec3T mEye, mDir, mInvDir;
|
||||
TimeSpan mTimeSpan;
|
||||
int mSign[3];
|
||||
}; // end of Ray class
|
||||
|
||||
} // namespace math =========================================================
|
||||
|
||||
template<typename RealT>
|
||||
using Ray [[deprecated("Use nanovdb::math::Ray instead")]] = math::Ray<RealT>;
|
||||
|
||||
} // namespace nanovdb =======================================================
|
||||
|
||||
#endif // NANOVDB_MATH_RAY_HAS_BEEN_INCLUDED
|
||||
@@ -1,996 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// @file SampleFromVoxels.h
|
||||
///
|
||||
/// @brief NearestNeighborSampler, TrilinearSampler, TriquadraticSampler and TricubicSampler
|
||||
///
|
||||
/// @note These interpolators employ internal caching for better performance when used repeatedly
|
||||
/// in the same voxel location, so try to reuse an instance of these classes more than once.
|
||||
///
|
||||
/// @warning While all the interpolators defined below work with both scalars and vectors
|
||||
/// values (e.g. float and Vec3<float>) TrilinarSampler::zeroCrossing and
|
||||
/// Trilinear::gradient will only compile with floating point value types.
|
||||
///
|
||||
/// @author Ken Museth
|
||||
///
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef NANOVDB_SAMPLE_FROM_VOXELS_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_SAMPLE_FROM_VOXELS_H_HAS_BEEN_INCLUDED
|
||||
|
||||
// Only define __hostdev__ when compiling as NVIDIA CUDA
|
||||
#if defined(__CUDACC__) || defined(__HIP__)
|
||||
#define __hostdev__ __host__ __device__
|
||||
#else
|
||||
#include <cmath> // for floor
|
||||
#define __hostdev__
|
||||
#endif
|
||||
|
||||
#include <nanovdb/math/Math.h>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace math {
|
||||
|
||||
// Forward declaration of sampler with specific polynomial orders
|
||||
template<typename TreeT, int Order, bool UseCache = true>
|
||||
class SampleFromVoxels;
|
||||
|
||||
/// @brief Factory free-function for a sampler of specific polynomial orders
|
||||
///
|
||||
/// @details This allows for the compact syntax:
|
||||
/// @code
|
||||
/// auto acc = grid.getAccessor();
|
||||
/// auto smp = nanovdb::math::createSampler<1>( acc );
|
||||
/// @endcode
|
||||
template<int Order, typename TreeOrAccT, bool UseCache = true>
|
||||
__hostdev__ SampleFromVoxels<TreeOrAccT, Order, UseCache> createSampler(const TreeOrAccT& acc)
|
||||
{
|
||||
return SampleFromVoxels<TreeOrAccT, Order, UseCache>(acc);
|
||||
}
|
||||
|
||||
/// @brief Utility function that returns the Coord of the round-down of @a xyz
|
||||
/// and redefined @xyz as the fractional part, ie xyz-in = return-value + xyz-out
|
||||
template<typename CoordT, typename RealT, template<typename> class Vec3T>
|
||||
__hostdev__ inline CoordT Floor(Vec3T<RealT>& xyz);
|
||||
|
||||
/// @brief Template specialization of Floor for Vec3<float>
|
||||
template<typename CoordT, template<typename> class Vec3T>
|
||||
__hostdev__ inline CoordT Floor(Vec3T<float>& xyz)
|
||||
{
|
||||
const float ijk[3] = {floorf(xyz[0]), floorf(xyz[1]), floorf(xyz[2])};
|
||||
xyz[0] -= ijk[0];
|
||||
xyz[1] -= ijk[1];
|
||||
xyz[2] -= ijk[2];
|
||||
return CoordT(int32_t(ijk[0]), int32_t(ijk[1]), int32_t(ijk[2]));
|
||||
}
|
||||
|
||||
/// @brief Template specialization of Floor for Vec3<float>
|
||||
template<typename CoordT, template<typename> class Vec3T>
|
||||
__hostdev__ inline CoordT Floor(Vec3T<double>& xyz)
|
||||
{
|
||||
const double ijk[3] = {floor(xyz[0]), floor(xyz[1]), floor(xyz[2])};
|
||||
xyz[0] -= ijk[0];
|
||||
xyz[1] -= ijk[1];
|
||||
xyz[2] -= ijk[2];
|
||||
return CoordT(int32_t(ijk[0]), int32_t(ijk[1]), int32_t(ijk[2]));
|
||||
}
|
||||
|
||||
// ------------------------------> NearestNeighborSampler <--------------------------------------
|
||||
|
||||
/// @brief Nearest neighbor, i.e. zero order, interpolator with caching
|
||||
template<typename TreeOrAccT>
|
||||
class SampleFromVoxels<TreeOrAccT, 0, true>
|
||||
{
|
||||
public:
|
||||
using ValueT = typename TreeOrAccT::ValueType;
|
||||
using CoordT = typename TreeOrAccT::CoordType;
|
||||
|
||||
static const int ORDER = 0;
|
||||
/// @brief Construction from a Tree or ReadAccessor
|
||||
__hostdev__ SampleFromVoxels(const TreeOrAccT& acc)
|
||||
: mAcc(acc)
|
||||
, mPos(CoordT::max())
|
||||
{
|
||||
}
|
||||
|
||||
__hostdev__ const TreeOrAccT& accessor() const { return mAcc; }
|
||||
|
||||
/// @note xyz is in index space space
|
||||
template<typename Vec3T>
|
||||
inline __hostdev__ ValueT operator()(const Vec3T& xyz) const;
|
||||
|
||||
inline __hostdev__ ValueT operator()(const CoordT& ijk) const;
|
||||
|
||||
private:
|
||||
const TreeOrAccT& mAcc;
|
||||
mutable CoordT mPos;
|
||||
mutable ValueT mVal; // private cache
|
||||
}; // SampleFromVoxels<TreeOrAccT, 0, true>
|
||||
|
||||
/// @brief Nearest neighbor, i.e. zero order, interpolator without caching
|
||||
template<typename TreeOrAccT>
|
||||
class SampleFromVoxels<TreeOrAccT, 0, false>
|
||||
{
|
||||
public:
|
||||
using ValueT = typename TreeOrAccT::ValueType;
|
||||
using CoordT = typename TreeOrAccT::CoordType;
|
||||
static const int ORDER = 0;
|
||||
|
||||
/// @brief Construction from a Tree or ReadAccessor
|
||||
__hostdev__ SampleFromVoxels(const TreeOrAccT& acc)
|
||||
: mAcc(acc)
|
||||
{
|
||||
}
|
||||
|
||||
__hostdev__ const TreeOrAccT& accessor() const { return mAcc; }
|
||||
|
||||
/// @note xyz is in index space space
|
||||
template<typename Vec3T>
|
||||
inline __hostdev__ ValueT operator()(const Vec3T& xyz) const;
|
||||
|
||||
inline __hostdev__ ValueT operator()(const CoordT& ijk) const { return mAcc.getValue(ijk);}
|
||||
|
||||
private:
|
||||
const TreeOrAccT& mAcc;
|
||||
}; // SampleFromVoxels<TreeOrAccT, 0, false>
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 0, true>::operator()(const Vec3T& xyz) const
|
||||
{
|
||||
const CoordT ijk = math::Round<CoordT>(xyz);
|
||||
if (ijk != mPos) {
|
||||
mPos = ijk;
|
||||
mVal = mAcc.getValue(mPos);
|
||||
}
|
||||
return mVal;
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 0, true>::operator()(const CoordT& ijk) const
|
||||
{
|
||||
if (ijk != mPos) {
|
||||
mPos = ijk;
|
||||
mVal = mAcc.getValue(mPos);
|
||||
}
|
||||
return mVal;
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 0, false>::operator()(const Vec3T& xyz) const
|
||||
{
|
||||
return mAcc.getValue(math::Round<CoordT>(xyz));
|
||||
}
|
||||
|
||||
// ------------------------------> TrilinearSampler <--------------------------------------
|
||||
|
||||
/// @brief Tri-linear sampler, i.e. first order, interpolator
|
||||
template<typename TreeOrAccT>
|
||||
class TrilinearSampler
|
||||
{
|
||||
protected:
|
||||
const TreeOrAccT& mAcc;
|
||||
|
||||
public:
|
||||
using ValueT = typename TreeOrAccT::ValueType;
|
||||
using CoordT = typename TreeOrAccT::CoordType;
|
||||
static const int ORDER = 1;
|
||||
|
||||
/// @brief Protected constructor from a Tree or ReadAccessor
|
||||
__hostdev__ TrilinearSampler(const TreeOrAccT& acc) : mAcc(acc) {}
|
||||
|
||||
__hostdev__ const TreeOrAccT& accessor() const { return mAcc; }
|
||||
|
||||
/// @brief Extract the stencil of 8 values
|
||||
inline __hostdev__ void stencil(CoordT& ijk, ValueT (&v)[2][2][2]) const;
|
||||
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
static inline __hostdev__ ValueT sample(const Vec3T<RealT> &uvw, const ValueT (&v)[2][2][2]);
|
||||
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
static inline __hostdev__ Vec3T<ValueT> gradient(const Vec3T<RealT> &uvw, const ValueT (&v)[2][2][2]);
|
||||
|
||||
static inline __hostdev__ bool zeroCrossing(const ValueT (&v)[2][2][2]);
|
||||
}; // TrilinearSamplerBase
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
__hostdev__ void TrilinearSampler<TreeOrAccT>::stencil(CoordT& ijk, ValueT (&v)[2][2][2]) const
|
||||
{
|
||||
v[0][0][0] = mAcc.getValue(ijk); // i, j, k
|
||||
|
||||
ijk[2] += 1;
|
||||
v[0][0][1] = mAcc.getValue(ijk); // i, j, k + 1
|
||||
|
||||
ijk[1] += 1;
|
||||
v[0][1][1] = mAcc.getValue(ijk); // i, j+1, k + 1
|
||||
|
||||
ijk[2] -= 1;
|
||||
v[0][1][0] = mAcc.getValue(ijk); // i, j+1, k
|
||||
|
||||
ijk[0] += 1;
|
||||
ijk[1] -= 1;
|
||||
v[1][0][0] = mAcc.getValue(ijk); // i+1, j, k
|
||||
|
||||
ijk[2] += 1;
|
||||
v[1][0][1] = mAcc.getValue(ijk); // i+1, j, k + 1
|
||||
|
||||
ijk[1] += 1;
|
||||
v[1][1][1] = mAcc.getValue(ijk); // i+1, j+1, k + 1
|
||||
|
||||
ijk[2] -= 1;
|
||||
v[1][1][0] = mAcc.getValue(ijk); // i+1, j+1, k
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType TrilinearSampler<TreeOrAccT>::sample(const Vec3T<RealT> &uvw, const ValueT (&v)[2][2][2])
|
||||
{
|
||||
#if 0
|
||||
auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b-a, a); };// = w*(b-a) + a
|
||||
//auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b, fma(-w, a, a));};// = (1-w)*a + w*b
|
||||
#else
|
||||
auto lerp = [](ValueT a, ValueT b, RealT w) { return a + ValueT(w) * (b - a); };
|
||||
#endif
|
||||
return lerp(lerp(lerp(v[0][0][0], v[0][0][1], uvw[2]), lerp(v[0][1][0], v[0][1][1], uvw[2]), uvw[1]),
|
||||
lerp(lerp(v[1][0][0], v[1][0][1], uvw[2]), lerp(v[1][1][0], v[1][1][1], uvw[2]), uvw[1]),
|
||||
uvw[0]);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ Vec3T<typename TreeOrAccT::ValueType> TrilinearSampler<TreeOrAccT>::gradient(const Vec3T<RealT> &uvw, const ValueT (&v)[2][2][2])
|
||||
{
|
||||
static_assert(util::is_floating_point<ValueT>::value, "TrilinearSampler::gradient requires a floating-point type");
|
||||
#if 0
|
||||
auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b-a, a); };// = w*(b-a) + a
|
||||
//auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b, fma(-w, a, a));};// = (1-w)*a + w*b
|
||||
#else
|
||||
auto lerp = [](ValueT a, ValueT b, RealT w) { return a + ValueT(w) * (b - a); };
|
||||
#endif
|
||||
|
||||
ValueT D[4] = {v[0][0][1] - v[0][0][0], v[0][1][1] - v[0][1][0], v[1][0][1] - v[1][0][0], v[1][1][1] - v[1][1][0]};
|
||||
|
||||
// Z component
|
||||
Vec3T<ValueT> grad(0, 0, lerp(lerp(D[0], D[1], uvw[1]), lerp(D[2], D[3], uvw[1]), uvw[0]));
|
||||
|
||||
const ValueT w = ValueT(uvw[2]);
|
||||
D[0] = v[0][0][0] + D[0] * w;
|
||||
D[1] = v[0][1][0] + D[1] * w;
|
||||
D[2] = v[1][0][0] + D[2] * w;
|
||||
D[3] = v[1][1][0] + D[3] * w;
|
||||
|
||||
// X component
|
||||
grad[0] = lerp(D[2], D[3], uvw[1]) - lerp(D[0], D[1], uvw[1]);
|
||||
|
||||
// Y component
|
||||
grad[1] = lerp(D[1] - D[0], D[3] - D[2], uvw[0]);
|
||||
|
||||
return grad;
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
__hostdev__ bool TrilinearSampler<TreeOrAccT>::zeroCrossing(const ValueT (&v)[2][2][2])
|
||||
{
|
||||
static_assert(util::is_floating_point<ValueT>::value, "TrilinearSampler::zeroCrossing requires a floating-point type");
|
||||
const bool less = v[0][0][0] < ValueT(0);
|
||||
return (less ^ (v[0][0][1] < ValueT(0))) ||
|
||||
(less ^ (v[0][1][1] < ValueT(0))) ||
|
||||
(less ^ (v[0][1][0] < ValueT(0))) ||
|
||||
(less ^ (v[1][0][0] < ValueT(0))) ||
|
||||
(less ^ (v[1][0][1] < ValueT(0))) ||
|
||||
(less ^ (v[1][1][1] < ValueT(0))) ||
|
||||
(less ^ (v[1][1][0] < ValueT(0)));
|
||||
}
|
||||
|
||||
/// @brief Template specialization that does not use caching of stencil points
|
||||
template<typename TreeOrAccT>
|
||||
class SampleFromVoxels<TreeOrAccT, 1, false> : public TrilinearSampler<TreeOrAccT>
|
||||
{
|
||||
using BaseT = TrilinearSampler<TreeOrAccT>;
|
||||
using ValueT = typename TreeOrAccT::ValueType;
|
||||
using CoordT = typename TreeOrAccT::CoordType;
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Construction from a Tree or ReadAccessor
|
||||
__hostdev__ SampleFromVoxels(const TreeOrAccT& acc) : BaseT(acc) {}
|
||||
|
||||
/// @note xyz is in index space space
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ ValueT operator()(Vec3T<RealT> xyz) const;
|
||||
|
||||
/// @note ijk is in index space space
|
||||
__hostdev__ ValueT operator()(const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);}
|
||||
|
||||
/// @brief Return the gradient in index space.
|
||||
///
|
||||
/// @warning Will only compile with floating point value types
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ Vec3T<ValueT> gradient(Vec3T<RealT> xyz) const;
|
||||
|
||||
/// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position.
|
||||
///
|
||||
/// @warning Will only compile with floating point value types
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ bool zeroCrossing(Vec3T<RealT> xyz) const;
|
||||
|
||||
}; // SampleFromVoxels<TreeOrAccT, 1, false>
|
||||
|
||||
/// @brief Template specialization with caching of stencil values
|
||||
template<typename TreeOrAccT>
|
||||
class SampleFromVoxels<TreeOrAccT, 1, true> : public TrilinearSampler<TreeOrAccT>
|
||||
{
|
||||
using BaseT = TrilinearSampler<TreeOrAccT>;
|
||||
using ValueT = typename TreeOrAccT::ValueType;
|
||||
using CoordT = typename TreeOrAccT::CoordType;
|
||||
|
||||
mutable CoordT mPos;
|
||||
mutable ValueT mVal[2][2][2];
|
||||
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ void cache(Vec3T<RealT>& xyz) const;
|
||||
public:
|
||||
|
||||
/// @brief Construction from a Tree or ReadAccessor
|
||||
__hostdev__ SampleFromVoxels(const TreeOrAccT& acc) : BaseT(acc), mPos(CoordT::max()){}
|
||||
|
||||
/// @note xyz is in index space space
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ ValueT operator()(Vec3T<RealT> xyz) const;
|
||||
|
||||
// @note ijk is in index space space
|
||||
__hostdev__ ValueT operator()(const CoordT &ijk) const;
|
||||
|
||||
/// @brief Return the gradient in index space.
|
||||
///
|
||||
/// @warning Will only compile with floating point value types
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ Vec3T<ValueT> gradient(Vec3T<RealT> xyz) const;
|
||||
|
||||
/// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position.
|
||||
///
|
||||
/// @warning Will only compile with floating point value types
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ bool zeroCrossing(Vec3T<RealT> xyz) const;
|
||||
|
||||
/// @brief Return true if the cached tri-linear stencil has a zero crossing.
|
||||
///
|
||||
/// @warning Will only compile with floating point value types
|
||||
__hostdev__ bool zeroCrossing() const { return BaseT::zeroCrossing(mVal); }
|
||||
|
||||
}; // SampleFromVoxels<TreeOrAccT, 1, true>
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 1, true>::operator()(Vec3T<RealT> xyz) const
|
||||
{
|
||||
this->cache(xyz);
|
||||
return BaseT::sample(xyz, mVal);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 1, true>::operator()(const CoordT &ijk) const
|
||||
{
|
||||
return ijk == mPos ? mVal[0][0][0] : BaseT::mAcc.getValue(ijk);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ Vec3T<typename TreeOrAccT::ValueType> SampleFromVoxels<TreeOrAccT, 1, true>::gradient(Vec3T<RealT> xyz) const
|
||||
{
|
||||
this->cache(xyz);
|
||||
return BaseT::gradient(xyz, mVal);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ bool SampleFromVoxels<TreeOrAccT, 1, true>::zeroCrossing(Vec3T<RealT> xyz) const
|
||||
{
|
||||
this->cache(xyz);
|
||||
return BaseT::zeroCrossing(mVal);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ void SampleFromVoxels<TreeOrAccT, 1, true>::cache(Vec3T<RealT>& xyz) const
|
||||
{
|
||||
CoordT ijk = Floor<CoordT>(xyz);
|
||||
if (ijk != mPos) {
|
||||
mPos = ijk;
|
||||
BaseT::stencil(ijk, mVal);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 1, false>::operator()(Vec3T<RealT> xyz) const
|
||||
{
|
||||
ValueT val[2][2][2];
|
||||
CoordT ijk = Floor<CoordT>(xyz);
|
||||
BaseT::stencil(ijk, val);
|
||||
return BaseT::sample(xyz, val);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 1, false>::operator()(Vec3T<RealT> xyz) const
|
||||
{
|
||||
auto lerp = [](ValueT a, ValueT b, RealT w) { return a + ValueT(w) * (b - a); };
|
||||
|
||||
CoordT coord = Floor<CoordT>(xyz);
|
||||
|
||||
ValueT vx, vx1, vy, vy1, vz, vz1;
|
||||
|
||||
vz = BaseT::mAcc.getValue(coord);
|
||||
coord[2] += 1;
|
||||
vz1 = BaseT::mAcc.getValue(coord);
|
||||
vy = lerp(vz, vz1, xyz[2]);
|
||||
|
||||
coord[1] += 1;
|
||||
|
||||
vz1 = BaseT::mAcc.getValue(coord);
|
||||
coord[2] -= 1;
|
||||
vz = BaseT::mAcc.getValue(coord);
|
||||
vy1 = lerp(vz, vz1, xyz[2]);
|
||||
|
||||
vx = lerp(vy, vy1, xyz[1]);
|
||||
|
||||
coord[0] += 1;
|
||||
|
||||
vz = BaseT::mAcc.getValue(coord);
|
||||
coord[2] += 1;
|
||||
vz1 = BaseT::mAcc.getValue(coord);
|
||||
vy1 = lerp(vz, vz1, xyz[2]);
|
||||
|
||||
coord[1] -= 1;
|
||||
|
||||
vz1 = BaseT::mAcc.getValue(coord);
|
||||
coord[2] -= 1;
|
||||
vz = BaseT::mAcc.getValue(coord);
|
||||
vy = lerp(vz, vz1, xyz[2]);
|
||||
|
||||
vx1 = lerp(vy, vy1, xyz[1]);
|
||||
|
||||
return lerp(vx, vx1, xyz[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ inline Vec3T<typename TreeOrAccT::ValueType> SampleFromVoxels<TreeOrAccT, 1, false>::gradient(Vec3T<RealT> xyz) const
|
||||
{
|
||||
ValueT val[2][2][2];
|
||||
CoordT ijk = Floor<CoordT>(xyz);
|
||||
BaseT::stencil(ijk, val);
|
||||
return BaseT::gradient(xyz, val);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ bool SampleFromVoxels<TreeOrAccT, 1, false>::zeroCrossing(Vec3T<RealT> xyz) const
|
||||
{
|
||||
ValueT val[2][2][2];
|
||||
CoordT ijk = Floor<CoordT>(xyz);
|
||||
BaseT::stencil(ijk, val);
|
||||
return BaseT::zeroCrossing(val);
|
||||
}
|
||||
|
||||
// ------------------------------> TriquadraticSampler <--------------------------------------
|
||||
|
||||
/// @brief Tri-quadratic sampler, i.e. second order, interpolator
|
||||
template<typename TreeOrAccT>
|
||||
class TriquadraticSampler
|
||||
{
|
||||
protected:
|
||||
const TreeOrAccT& mAcc;
|
||||
|
||||
public:
|
||||
using ValueT = typename TreeOrAccT::ValueType;
|
||||
using CoordT = typename TreeOrAccT::CoordType;
|
||||
static const int ORDER = 1;
|
||||
|
||||
/// @brief Protected constructor from a Tree or ReadAccessor
|
||||
__hostdev__ TriquadraticSampler(const TreeOrAccT& acc) : mAcc(acc) {}
|
||||
|
||||
__hostdev__ const TreeOrAccT& accessor() const { return mAcc; }
|
||||
|
||||
/// @brief Extract the stencil of 27 values
|
||||
inline __hostdev__ void stencil(const CoordT &ijk, ValueT (&v)[3][3][3]) const;
|
||||
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
static inline __hostdev__ ValueT sample(const Vec3T<RealT> &uvw, const ValueT (&v)[3][3][3]);
|
||||
|
||||
static inline __hostdev__ bool zeroCrossing(const ValueT (&v)[3][3][3]);
|
||||
}; // TriquadraticSamplerBase
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
__hostdev__ void TriquadraticSampler<TreeOrAccT>::stencil(const CoordT &ijk, ValueT (&v)[3][3][3]) const
|
||||
{
|
||||
CoordT p(ijk[0] - 1, 0, 0);
|
||||
for (int dx = 0; dx < 3; ++dx, ++p[0]) {
|
||||
p[1] = ijk[1] - 1;
|
||||
for (int dy = 0; dy < 3; ++dy, ++p[1]) {
|
||||
p[2] = ijk[2] - 1;
|
||||
for (int dz = 0; dz < 3; ++dz, ++p[2]) {
|
||||
v[dx][dy][dz] = mAcc.getValue(p);// extract the stencil of 27 values
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType TriquadraticSampler<TreeOrAccT>::sample(const Vec3T<RealT> &uvw, const ValueT (&v)[3][3][3])
|
||||
{
|
||||
auto kernel = [](const ValueT* value, double weight)->ValueT {
|
||||
return weight * (weight * (0.5f * (value[0] + value[2]) - value[1]) +
|
||||
0.5f * (value[2] - value[0])) + value[1];
|
||||
};
|
||||
|
||||
ValueT vx[3];
|
||||
for (int dx = 0; dx < 3; ++dx) {
|
||||
ValueT vy[3];
|
||||
for (int dy = 0; dy < 3; ++dy) {
|
||||
vy[dy] = kernel(&v[dx][dy][0], uvw[2]);
|
||||
}//loop over y
|
||||
vx[dx] = kernel(vy, uvw[1]);
|
||||
}//loop over x
|
||||
return kernel(vx, uvw[0]);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
__hostdev__ bool TriquadraticSampler<TreeOrAccT>::zeroCrossing(const ValueT (&v)[3][3][3])
|
||||
{
|
||||
static_assert(util::is_floating_point<ValueT>::value, "TrilinearSampler::zeroCrossing requires a floating-point type");
|
||||
const bool less = v[0][0][0] < ValueT(0);
|
||||
for (int dx = 0; dx < 3; ++dx) {
|
||||
for (int dy = 0; dy < 3; ++dy) {
|
||||
for (int dz = 0; dz < 3; ++dz) {
|
||||
if (less ^ (v[dx][dy][dz] < ValueT(0))) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief Template specialization that does not use caching of stencil points
|
||||
template<typename TreeOrAccT>
|
||||
class SampleFromVoxels<TreeOrAccT, 2, false> : public TriquadraticSampler<TreeOrAccT>
|
||||
{
|
||||
using BaseT = TriquadraticSampler<TreeOrAccT>;
|
||||
using ValueT = typename TreeOrAccT::ValueType;
|
||||
using CoordT = typename TreeOrAccT::CoordType;
|
||||
public:
|
||||
|
||||
/// @brief Construction from a Tree or ReadAccessor
|
||||
__hostdev__ SampleFromVoxels(const TreeOrAccT& acc) : BaseT(acc) {}
|
||||
|
||||
/// @note xyz is in index space space
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ ValueT operator()(Vec3T<RealT> xyz) const;
|
||||
|
||||
__hostdev__ ValueT operator()(const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);}
|
||||
|
||||
/// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position.
|
||||
///
|
||||
/// @warning Will only compile with floating point value types
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ bool zeroCrossing(Vec3T<RealT> xyz) const;
|
||||
|
||||
}; // SampleFromVoxels<TreeOrAccT, 2, false>
|
||||
|
||||
/// @brief Template specialization with caching of stencil values
|
||||
template<typename TreeOrAccT>
|
||||
class SampleFromVoxels<TreeOrAccT, 2, true> : public TriquadraticSampler<TreeOrAccT>
|
||||
{
|
||||
using BaseT = TriquadraticSampler<TreeOrAccT>;
|
||||
using ValueT = typename TreeOrAccT::ValueType;
|
||||
using CoordT = typename TreeOrAccT::CoordType;
|
||||
|
||||
mutable CoordT mPos;
|
||||
mutable ValueT mVal[3][3][3];
|
||||
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ void cache(Vec3T<RealT>& xyz) const;
|
||||
public:
|
||||
|
||||
/// @brief Construction from a Tree or ReadAccessor
|
||||
__hostdev__ SampleFromVoxels(const TreeOrAccT& acc) : BaseT(acc), mPos(CoordT::max()){}
|
||||
|
||||
/// @note xyz is in index space space
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ ValueT operator()(Vec3T<RealT> xyz) const;
|
||||
|
||||
inline __hostdev__ ValueT operator()(const CoordT &ijk) const;
|
||||
|
||||
/// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position.
|
||||
///
|
||||
/// @warning Will only compile with floating point value types
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ bool zeroCrossing(Vec3T<RealT> xyz) const;
|
||||
|
||||
/// @brief Return true if the cached tri-linear stencil has a zero crossing.
|
||||
///
|
||||
/// @warning Will only compile with floating point value types
|
||||
__hostdev__ bool zeroCrossing() const { return BaseT::zeroCrossing(mVal); }
|
||||
|
||||
}; // SampleFromVoxels<TreeOrAccT, 2, true>
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 2, true>::operator()(Vec3T<RealT> xyz) const
|
||||
{
|
||||
this->cache(xyz);
|
||||
return BaseT::sample(xyz, mVal);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 2, true>::operator()(const CoordT &ijk) const
|
||||
{
|
||||
return ijk == mPos ? mVal[1][1][1] : BaseT::mAcc.getValue(ijk);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ bool SampleFromVoxels<TreeOrAccT, 2, true>::zeroCrossing(Vec3T<RealT> xyz) const
|
||||
{
|
||||
this->cache(xyz);
|
||||
return BaseT::zeroCrossing(mVal);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ void SampleFromVoxels<TreeOrAccT, 2, true>::cache(Vec3T<RealT>& xyz) const
|
||||
{
|
||||
CoordT ijk = Floor<CoordT>(xyz);
|
||||
if (ijk != mPos) {
|
||||
mPos = ijk;
|
||||
BaseT::stencil(ijk, mVal);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 2, false>::operator()(Vec3T<RealT> xyz) const
|
||||
{
|
||||
ValueT val[3][3][3];
|
||||
CoordT ijk = Floor<CoordT>(xyz);
|
||||
BaseT::stencil(ijk, val);
|
||||
return BaseT::sample(xyz, val);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ bool SampleFromVoxels<TreeOrAccT, 2, false>::zeroCrossing(Vec3T<RealT> xyz) const
|
||||
{
|
||||
ValueT val[3][3][3];
|
||||
CoordT ijk = Floor<CoordT>(xyz);
|
||||
BaseT::stencil(ijk, val);
|
||||
return BaseT::zeroCrossing(val);
|
||||
}
|
||||
|
||||
// ------------------------------> TricubicSampler <--------------------------------------
|
||||
|
||||
/// @brief Tri-cubic sampler, i.e. third order, interpolator.
|
||||
///
|
||||
/// @details See the following paper for implementation details:
|
||||
/// Lekien, F. and Marsden, J.: Tricubic interpolation in three dimensions.
|
||||
/// In: International Journal for Numerical Methods
|
||||
/// in Engineering (2005), No. 63, p. 455-471
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
class TricubicSampler
|
||||
{
|
||||
protected:
|
||||
using ValueT = typename TreeOrAccT::ValueType;
|
||||
using CoordT = typename TreeOrAccT::CoordType;
|
||||
|
||||
const TreeOrAccT& mAcc;
|
||||
|
||||
public:
|
||||
/// @brief Construction from a Tree or ReadAccessor
|
||||
__hostdev__ TricubicSampler(const TreeOrAccT& acc)
|
||||
: mAcc(acc)
|
||||
{
|
||||
}
|
||||
|
||||
__hostdev__ const TreeOrAccT& accessor() const { return mAcc; }
|
||||
|
||||
/// @brief Extract the stencil of 8 values
|
||||
inline __hostdev__ void stencil(const CoordT& ijk, ValueT (&c)[64]) const;
|
||||
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
static inline __hostdev__ ValueT sample(const Vec3T<RealT> &uvw, const ValueT (&c)[64]);
|
||||
}; // TricubicSampler
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
__hostdev__ void TricubicSampler<TreeOrAccT>::stencil(const CoordT& ijk, ValueT (&C)[64]) const
|
||||
{
|
||||
auto fetch = [&](int i, int j, int k) -> ValueT& { return C[((i + 1) << 4) + ((j + 1) << 2) + k + 1]; };
|
||||
|
||||
// fetch 64 point stencil values
|
||||
for (int i = -1; i < 3; ++i) {
|
||||
for (int j = -1; j < 3; ++j) {
|
||||
fetch(i, j, -1) = mAcc.getValue(ijk + CoordT(i, j, -1));
|
||||
fetch(i, j, 0) = mAcc.getValue(ijk + CoordT(i, j, 0));
|
||||
fetch(i, j, 1) = mAcc.getValue(ijk + CoordT(i, j, 1));
|
||||
fetch(i, j, 2) = mAcc.getValue(ijk + CoordT(i, j, 2));
|
||||
}
|
||||
}
|
||||
const ValueT half(0.5), quarter(0.25), eighth(0.125);
|
||||
const ValueT X[64] = {// values of f(x,y,z) at the 8 corners (each from 1 stencil value).
|
||||
fetch(0, 0, 0),
|
||||
fetch(1, 0, 0),
|
||||
fetch(0, 1, 0),
|
||||
fetch(1, 1, 0),
|
||||
fetch(0, 0, 1),
|
||||
fetch(1, 0, 1),
|
||||
fetch(0, 1, 1),
|
||||
fetch(1, 1, 1),
|
||||
// values of df/dx at the 8 corners (each from 2 stencil values).
|
||||
half * (fetch(1, 0, 0) - fetch(-1, 0, 0)),
|
||||
half * (fetch(2, 0, 0) - fetch(0, 0, 0)),
|
||||
half * (fetch(1, 1, 0) - fetch(-1, 1, 0)),
|
||||
half * (fetch(2, 1, 0) - fetch(0, 1, 0)),
|
||||
half * (fetch(1, 0, 1) - fetch(-1, 0, 1)),
|
||||
half * (fetch(2, 0, 1) - fetch(0, 0, 1)),
|
||||
half * (fetch(1, 1, 1) - fetch(-1, 1, 1)),
|
||||
half * (fetch(2, 1, 1) - fetch(0, 1, 1)),
|
||||
// values of df/dy at the 8 corners (each from 2 stencil values).
|
||||
half * (fetch(0, 1, 0) - fetch(0, -1, 0)),
|
||||
half * (fetch(1, 1, 0) - fetch(1, -1, 0)),
|
||||
half * (fetch(0, 2, 0) - fetch(0, 0, 0)),
|
||||
half * (fetch(1, 2, 0) - fetch(1, 0, 0)),
|
||||
half * (fetch(0, 1, 1) - fetch(0, -1, 1)),
|
||||
half * (fetch(1, 1, 1) - fetch(1, -1, 1)),
|
||||
half * (fetch(0, 2, 1) - fetch(0, 0, 1)),
|
||||
half * (fetch(1, 2, 1) - fetch(1, 0, 1)),
|
||||
// values of df/dz at the 8 corners (each from 2 stencil values).
|
||||
half * (fetch(0, 0, 1) - fetch(0, 0, -1)),
|
||||
half * (fetch(1, 0, 1) - fetch(1, 0, -1)),
|
||||
half * (fetch(0, 1, 1) - fetch(0, 1, -1)),
|
||||
half * (fetch(1, 1, 1) - fetch(1, 1, -1)),
|
||||
half * (fetch(0, 0, 2) - fetch(0, 0, 0)),
|
||||
half * (fetch(1, 0, 2) - fetch(1, 0, 0)),
|
||||
half * (fetch(0, 1, 2) - fetch(0, 1, 0)),
|
||||
half * (fetch(1, 1, 2) - fetch(1, 1, 0)),
|
||||
// values of d2f/dxdy at the 8 corners (each from 4 stencil values).
|
||||
quarter * (fetch(1, 1, 0) - fetch(-1, 1, 0) - fetch(1, -1, 0) + fetch(-1, -1, 0)),
|
||||
quarter * (fetch(2, 1, 0) - fetch(0, 1, 0) - fetch(2, -1, 0) + fetch(0, -1, 0)),
|
||||
quarter * (fetch(1, 2, 0) - fetch(-1, 2, 0) - fetch(1, 0, 0) + fetch(-1, 0, 0)),
|
||||
quarter * (fetch(2, 2, 0) - fetch(0, 2, 0) - fetch(2, 0, 0) + fetch(0, 0, 0)),
|
||||
quarter * (fetch(1, 1, 1) - fetch(-1, 1, 1) - fetch(1, -1, 1) + fetch(-1, -1, 1)),
|
||||
quarter * (fetch(2, 1, 1) - fetch(0, 1, 1) - fetch(2, -1, 1) + fetch(0, -1, 1)),
|
||||
quarter * (fetch(1, 2, 1) - fetch(-1, 2, 1) - fetch(1, 0, 1) + fetch(-1, 0, 1)),
|
||||
quarter * (fetch(2, 2, 1) - fetch(0, 2, 1) - fetch(2, 0, 1) + fetch(0, 0, 1)),
|
||||
// values of d2f/dxdz at the 8 corners (each from 4 stencil values).
|
||||
quarter * (fetch(1, 0, 1) - fetch(-1, 0, 1) - fetch(1, 0, -1) + fetch(-1, 0, -1)),
|
||||
quarter * (fetch(2, 0, 1) - fetch(0, 0, 1) - fetch(2, 0, -1) + fetch(0, 0, -1)),
|
||||
quarter * (fetch(1, 1, 1) - fetch(-1, 1, 1) - fetch(1, 1, -1) + fetch(-1, 1, -1)),
|
||||
quarter * (fetch(2, 1, 1) - fetch(0, 1, 1) - fetch(2, 1, -1) + fetch(0, 1, -1)),
|
||||
quarter * (fetch(1, 0, 2) - fetch(-1, 0, 2) - fetch(1, 0, 0) + fetch(-1, 0, 0)),
|
||||
quarter * (fetch(2, 0, 2) - fetch(0, 0, 2) - fetch(2, 0, 0) + fetch(0, 0, 0)),
|
||||
quarter * (fetch(1, 1, 2) - fetch(-1, 1, 2) - fetch(1, 1, 0) + fetch(-1, 1, 0)),
|
||||
quarter * (fetch(2, 1, 2) - fetch(0, 1, 2) - fetch(2, 1, 0) + fetch(0, 1, 0)),
|
||||
// values of d2f/dydz at the 8 corners (each from 4 stencil values).
|
||||
quarter * (fetch(0, 1, 1) - fetch(0, -1, 1) - fetch(0, 1, -1) + fetch(0, -1, -1)),
|
||||
quarter * (fetch(1, 1, 1) - fetch(1, -1, 1) - fetch(1, 1, -1) + fetch(1, -1, -1)),
|
||||
quarter * (fetch(0, 2, 1) - fetch(0, 0, 1) - fetch(0, 2, -1) + fetch(0, 0, -1)),
|
||||
quarter * (fetch(1, 2, 1) - fetch(1, 0, 1) - fetch(1, 2, -1) + fetch(1, 0, -1)),
|
||||
quarter * (fetch(0, 1, 2) - fetch(0, -1, 2) - fetch(0, 1, 0) + fetch(0, -1, 0)),
|
||||
quarter * (fetch(1, 1, 2) - fetch(1, -1, 2) - fetch(1, 1, 0) + fetch(1, -1, 0)),
|
||||
quarter * (fetch(0, 2, 2) - fetch(0, 0, 2) - fetch(0, 2, 0) + fetch(0, 0, 0)),
|
||||
quarter * (fetch(1, 2, 2) - fetch(1, 0, 2) - fetch(1, 2, 0) + fetch(1, 0, 0)),
|
||||
// values of d3f/dxdydz at the 8 corners (each from 8 stencil values).
|
||||
eighth * (fetch(1, 1, 1) - fetch(-1, 1, 1) - fetch(1, -1, 1) + fetch(-1, -1, 1) - fetch(1, 1, -1) + fetch(-1, 1, -1) + fetch(1, -1, -1) - fetch(-1, -1, -1)),
|
||||
eighth * (fetch(2, 1, 1) - fetch(0, 1, 1) - fetch(2, -1, 1) + fetch(0, -1, 1) - fetch(2, 1, -1) + fetch(0, 1, -1) + fetch(2, -1, -1) - fetch(0, -1, -1)),
|
||||
eighth * (fetch(1, 2, 1) - fetch(-1, 2, 1) - fetch(1, 0, 1) + fetch(-1, 0, 1) - fetch(1, 2, -1) + fetch(-1, 2, -1) + fetch(1, 0, -1) - fetch(-1, 0, -1)),
|
||||
eighth * (fetch(2, 2, 1) - fetch(0, 2, 1) - fetch(2, 0, 1) + fetch(0, 0, 1) - fetch(2, 2, -1) + fetch(0, 2, -1) + fetch(2, 0, -1) - fetch(0, 0, -1)),
|
||||
eighth * (fetch(1, 1, 2) - fetch(-1, 1, 2) - fetch(1, -1, 2) + fetch(-1, -1, 2) - fetch(1, 1, 0) + fetch(-1, 1, 0) + fetch(1, -1, 0) - fetch(-1, -1, 0)),
|
||||
eighth * (fetch(2, 1, 2) - fetch(0, 1, 2) - fetch(2, -1, 2) + fetch(0, -1, 2) - fetch(2, 1, 0) + fetch(0, 1, 0) + fetch(2, -1, 0) - fetch(0, -1, 0)),
|
||||
eighth * (fetch(1, 2, 2) - fetch(-1, 2, 2) - fetch(1, 0, 2) + fetch(-1, 0, 2) - fetch(1, 2, 0) + fetch(-1, 2, 0) + fetch(1, 0, 0) - fetch(-1, 0, 0)),
|
||||
eighth * (fetch(2, 2, 2) - fetch(0, 2, 2) - fetch(2, 0, 2) + fetch(0, 0, 2) - fetch(2, 2, 0) + fetch(0, 2, 0) + fetch(2, 0, 0) - fetch(0, 0, 0))};
|
||||
|
||||
// 4Kb of static table (int8_t has a range of -127 -> 127 which suffices)
|
||||
static const int8_t A[64][64] = {
|
||||
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{-3, 3, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{2, -2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{-3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{9, -9, -9, 9, 0, 0, 0, 0, 6, 3, -6, -3, 0, 0, 0, 0, 6, -6, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{-6, 6, 6, -6, 0, 0, 0, 0, -3, -3, 3, 3, 0, 0, 0, 0, -4, 4, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{-6, 6, 6, -6, 0, 0, 0, 0, -4, -2, 4, 2, 0, 0, 0, 0, -3, 3, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{4, -4, -4, 4, 0, 0, 0, 0, 2, 2, -2, -2, 0, 0, 0, 0, 2, -2, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, -9, -9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, -6, -3, 0, 0, 0, 0, 6, -6, 3, -3, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 3, 3, 0, 0, 0, 0, -4, 4, -2, 2, 0, 0, 0, 0, -2, -2, -1, -1, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -2, 4, 2, 0, 0, 0, 0, -3, 3, -3, 3, 0, 0, 0, 0, -2, -1, -2, -1, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4, -4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, -2, -2, 0, 0, 0, 0, 2, -2, 2, -2, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0},
|
||||
{-3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{9, -9, 0, 0, -9, 9, 0, 0, 6, 3, 0, 0, -6, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{-6, 6, 0, 0, 6, -6, 0, 0, -3, -3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 4, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, -9, 0, 0, -9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, 0, 0, -6, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 3, -3, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 4, 0, 0, -2, 2, 0, 0, -2, -2, 0, 0, -1, -1, 0, 0},
|
||||
{9, 0, -9, 0, -9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, -6, 0, -3, 0, 6, 0, -6, 0, 3, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 9, 0, -9, 0, -9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, -6, 0, -3, 0, 6, 0, -6, 0, 3, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0},
|
||||
{-27, 27, 27, -27, 27, -27, -27, 27, -18, -9, 18, 9, 18, 9, -18, -9, -18, 18, -9, 9, 18, -18, 9, -9, -18, 18, 18, -18, -9, 9, 9, -9, -12, -6, -6, -3, 12, 6, 6, 3, -12, -6, 12, 6, -6, -3, 6, 3, -12, 12, -6, 6, -6, 6, -3, 3, -8, -4, -4, -2, -4, -2, -2, -1},
|
||||
{18, -18, -18, 18, -18, 18, 18, -18, 9, 9, -9, -9, -9, -9, 9, 9, 12, -12, 6, -6, -12, 12, -6, 6, 12, -12, -12, 12, 6, -6, -6, 6, 6, 6, 3, 3, -6, -6, -3, -3, 6, 6, -6, -6, 3, 3, -3, -3, 8, -8, 4, -4, 4, -4, 2, -2, 4, 4, 2, 2, 2, 2, 1, 1},
|
||||
{-6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, -3, 0, 3, 0, 3, 0, -4, 0, 4, 0, -2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -2, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, -3, 0, 3, 0, 3, 0, -4, 0, 4, 0, -2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -2, 0, -1, 0, -1, 0},
|
||||
{18, -18, -18, 18, -18, 18, 18, -18, 12, 6, -12, -6, -12, -6, 12, 6, 9, -9, 9, -9, -9, 9, -9, 9, 12, -12, -12, 12, 6, -6, -6, 6, 6, 3, 6, 3, -6, -3, -6, -3, 8, 4, -8, -4, 4, 2, -4, -2, 6, -6, 6, -6, 3, -3, 3, -3, 4, 2, 4, 2, 2, 1, 2, 1},
|
||||
{-12, 12, 12, -12, 12, -12, -12, 12, -6, -6, 6, 6, 6, 6, -6, -6, -6, 6, -6, 6, 6, -6, 6, -6, -8, 8, 8, -8, -4, 4, 4, -4, -3, -3, -3, -3, 3, 3, 3, 3, -4, -4, 4, 4, -2, -2, 2, 2, -4, 4, -4, 4, -2, 2, -2, 2, -2, -2, -2, -2, -1, -1, -1, -1},
|
||||
{2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{-6, 6, 0, 0, 6, -6, 0, 0, -4, -2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{4, -4, 0, 0, -4, 4, 0, 0, 2, 2, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -3, 3, 0, 0, -2, -1, 0, 0, -2, -1, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, -4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
|
||||
{-6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, -2, 0, 4, 0, 2, 0, -3, 0, 3, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, -2, 0, 4, 0, 2, 0, -3, 0, 3, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, -2, 0, -1, 0},
|
||||
{18, -18, -18, 18, -18, 18, 18, -18, 12, 6, -12, -6, -12, -6, 12, 6, 12, -12, 6, -6, -12, 12, -6, 6, 9, -9, -9, 9, 9, -9, -9, 9, 8, 4, 4, 2, -8, -4, -4, -2, 6, 3, -6, -3, 6, 3, -6, -3, 6, -6, 3, -3, 6, -6, 3, -3, 4, 2, 2, 1, 4, 2, 2, 1},
|
||||
{-12, 12, 12, -12, 12, -12, -12, 12, -6, -6, 6, 6, 6, 6, -6, -6, -8, 8, -4, 4, 8, -8, 4, -4, -6, 6, 6, -6, -6, 6, 6, -6, -4, -4, -2, -2, 4, 4, 2, 2, -3, -3, 3, 3, -3, -3, 3, 3, -4, 4, -2, 2, -4, 4, -2, 2, -2, -2, -1, -1, -2, -2, -1, -1},
|
||||
{4, 0, -4, 0, -4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, -2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 4, 0, -4, 0, -4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, -2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0},
|
||||
{-12, 12, 12, -12, 12, -12, -12, 12, -8, -4, 8, 4, 8, 4, -8, -4, -6, 6, -6, 6, 6, -6, 6, -6, -6, 6, 6, -6, -6, 6, 6, -6, -4, -2, -4, -2, 4, 2, 4, 2, -4, -2, 4, 2, -4, -2, 4, 2, -3, 3, -3, 3, -3, 3, -3, 3, -2, -1, -2, -1, -2, -1, -2, -1},
|
||||
{8, -8, -8, 8, -8, 8, 8, -8, 4, 4, -4, -4, -4, -4, 4, 4, 4, -4, 4, -4, -4, 4, -4, 4, 4, -4, -4, 4, 4, -4, -4, 4, 2, 2, 2, 2, -2, -2, -2, -2, 2, 2, -2, -2, 2, 2, -2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 1, 1, 1, 1, 1, 1, 1, 1}};
|
||||
|
||||
for (int i = 0; i < 64; ++i) { // C = A * X
|
||||
C[i] = ValueT(0);
|
||||
#if 0
|
||||
for (int j = 0; j < 64; j += 4) {
|
||||
C[i] = fma(A[i][j], X[j], fma(A[i][j+1], X[j+1], fma(A[i][j+2], X[j+2], fma(A[i][j+3], X[j+3], C[i]))));
|
||||
}
|
||||
#else
|
||||
for (int j = 0; j < 64; j += 4) {
|
||||
C[i] += A[i][j] * X[j] + A[i][j + 1] * X[j + 1] + A[i][j + 2] * X[j + 2] + A[i][j + 3] * X[j + 3];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType TricubicSampler<TreeOrAccT>::sample(const Vec3T<RealT> &xyz, const ValueT (&C)[64])
|
||||
{
|
||||
ValueT zPow(1), sum(0);
|
||||
for (int k = 0, n = 0; k < 4; ++k) {
|
||||
ValueT yPow(1);
|
||||
for (int j = 0; j < 4; ++j, n += 4) {
|
||||
#if 0
|
||||
sum = fma( yPow, zPow * fma(xyz[0], fma(xyz[0], fma(xyz[0], C[n + 3], C[n + 2]), C[n + 1]), C[n]), sum);
|
||||
#else
|
||||
sum += yPow * zPow * (C[n] + xyz[0] * (C[n + 1] + xyz[0] * (C[n + 2] + xyz[0] * C[n + 3])));
|
||||
#endif
|
||||
yPow *= xyz[1];
|
||||
}
|
||||
zPow *= xyz[2];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
class SampleFromVoxels<TreeOrAccT, 3, true> : public TricubicSampler<TreeOrAccT>
|
||||
{
|
||||
using BaseT = TricubicSampler<TreeOrAccT>;
|
||||
using ValueT = typename TreeOrAccT::ValueType;
|
||||
using CoordT = typename TreeOrAccT::CoordType;
|
||||
|
||||
mutable CoordT mPos;
|
||||
mutable ValueT mC[64];
|
||||
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ void cache(Vec3T<RealT>& xyz) const;
|
||||
|
||||
public:
|
||||
/// @brief Construction from a Tree or ReadAccessor
|
||||
__hostdev__ SampleFromVoxels(const TreeOrAccT& acc)
|
||||
: BaseT(acc)
|
||||
{
|
||||
}
|
||||
|
||||
/// @note xyz is in index space space
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ ValueT operator()(Vec3T<RealT> xyz) const;
|
||||
|
||||
// @brief Return value at the coordinate @a ijk in index space space
|
||||
__hostdev__ ValueT operator()(const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);}
|
||||
|
||||
}; // SampleFromVoxels<TreeOrAccT, 3, true>
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 3, true>::operator()(Vec3T<RealT> xyz) const
|
||||
{
|
||||
this->cache(xyz);
|
||||
return BaseT::sample(xyz, mC);
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ void SampleFromVoxels<TreeOrAccT, 3, true>::cache(Vec3T<RealT>& xyz) const
|
||||
{
|
||||
CoordT ijk = Floor<CoordT>(xyz);
|
||||
if (ijk != mPos) {
|
||||
mPos = ijk;
|
||||
BaseT::stencil(ijk, mC);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
class SampleFromVoxels<TreeOrAccT, 3, false> : public TricubicSampler<TreeOrAccT>
|
||||
{
|
||||
using BaseT = TricubicSampler<TreeOrAccT>;
|
||||
using ValueT = typename TreeOrAccT::ValueType;
|
||||
using CoordT = typename TreeOrAccT::CoordType;
|
||||
|
||||
public:
|
||||
/// @brief Construction from a Tree or ReadAccessor
|
||||
__hostdev__ SampleFromVoxels(const TreeOrAccT& acc)
|
||||
: BaseT(acc)
|
||||
{
|
||||
}
|
||||
|
||||
/// @note xyz is in index space space
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
inline __hostdev__ ValueT operator()(Vec3T<RealT> xyz) const;
|
||||
|
||||
__hostdev__ ValueT operator()(const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);}
|
||||
|
||||
}; // SampleFromVoxels<TreeOrAccT, 3, true>
|
||||
|
||||
template<typename TreeOrAccT>
|
||||
template<typename RealT, template<typename...> class Vec3T>
|
||||
__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels<TreeOrAccT, 3, false>::operator()(Vec3T<RealT> xyz) const
|
||||
{
|
||||
ValueT C[64];
|
||||
CoordT ijk = Floor<CoordT>(xyz);
|
||||
BaseT::stencil(ijk, C);
|
||||
return BaseT::sample(xyz, C);
|
||||
}
|
||||
|
||||
}// namespace math
|
||||
|
||||
template<int Order, typename TreeOrAccT, bool UseCache = true>
|
||||
[[deprecated("Use nanovdb::math::createSampler instead")]]
|
||||
__hostdev__ math::SampleFromVoxels<TreeOrAccT, Order, UseCache> createSampler(const TreeOrAccT& acc)
|
||||
{
|
||||
return math::SampleFromVoxels<TreeOrAccT, Order, UseCache>(acc);
|
||||
}
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_SAMPLE_FROM_VOXELS_H_HAS_BEEN_INCLUDED
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,427 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/GridChecksum.h
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\brief Computes a pair of uint32_t checksums, of a Grid, by means of 32 bit Cyclic Redundancy Check (CRC32)
|
||||
|
||||
\details A CRC32 is the 32 bit remainder, or residue, of binary division of a message, by a polynomial.
|
||||
|
||||
|
||||
\note before v32.6.0: checksum[0] = Grid+Tree+Root, checksum[1] = nodes
|
||||
after v32.6.0: checksum[0] = Grid+Tree, checksum[1] = nodes + blind data in 4K blocks
|
||||
|
||||
When serialized:
|
||||
[Grid,Tree][Root][ROOT TILES...][Node<5>...][Node<4>...][Leaf<3>...][BlindMeta...][BlindData...]
|
||||
checksum[2] before v32.6.0: <------------- [0] ------------><-------------- [1] --------------->
|
||||
checksum[2] after v32.6.0: <---[0]---><----------------------------------------[1]---------------------------------------->
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_TOOLS_GRIDCHECKSUM_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_TOOLS_GRIDCHECKSUM_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <algorithm>// for std::generate
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <cstddef>// offsetof macro
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
#include <memory>// for std::unique_ptr
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/util/ForEach.h>
|
||||
#include <nanovdb/NodeManager.h>
|
||||
|
||||
// Define log of block size for FULL CRC32 computation.
|
||||
// A value of 12 corresponds to a block size of 4KB (2^12 = 4096).
|
||||
#define NANOVDB_CRC32_LOG2_BLOCK_SIZE 12
|
||||
|
||||
namespace nanovdb {// ==================================================================
|
||||
|
||||
namespace tools {// ====================================================================
|
||||
|
||||
/// @brief Compute the (2 x CRC32) checksum of the specified @c gridData
|
||||
/// @param gridData Base pointer to the grid from which the checksum is computed.
|
||||
/// @param mode Defines the mode of computation for the checksum.
|
||||
/// @return Return the (2 x CRC32) checksum of the specified @c gridData
|
||||
Checksum evalChecksum(const GridData *gridData, CheckMode mode = CheckMode::Default);
|
||||
|
||||
/// @brief Extract the checksum of a grid
|
||||
/// @param gridData Base pointer to grid with a checksum
|
||||
/// @return Checksum encoded in the specified grid
|
||||
inline Checksum getChecksum(const GridData *gridData)
|
||||
{
|
||||
NANOVDB_ASSERT(gridData);
|
||||
return gridData->mChecksum;
|
||||
}
|
||||
|
||||
/// @brief Return true if the checksum of @c gridData matches the expected
|
||||
/// value already encoded into the grid's meta data.
|
||||
/// @tparam BuildT Template parameter used to build NanoVDB grid.
|
||||
/// @param grid Grid whose checksum is validated.
|
||||
/// @param mode Defines the mode of computation for the checksum.
|
||||
bool validateChecksum(const GridData *gridData, CheckMode mode = CheckMode::Default);
|
||||
|
||||
/// @brief Updates the checksum of a grid
|
||||
/// @param grid Grid whose checksum will be updated.
|
||||
/// @param mode Defines the mode of computation for the checksum.
|
||||
inline void updateChecksum(GridData *gridData, CheckMode mode)
|
||||
{
|
||||
NANOVDB_ASSERT(gridData);
|
||||
gridData->mChecksum = evalChecksum(gridData, mode);
|
||||
}
|
||||
|
||||
/// @brief Updates the checksum of a grid by preserving its mode
|
||||
/// @param gridData Base pointer to grid
|
||||
inline void updateChecksum(GridData *gridData)
|
||||
{
|
||||
updateChecksum(gridData, gridData->mChecksum.mode());
|
||||
}
|
||||
|
||||
}// namespace tools
|
||||
|
||||
namespace util {
|
||||
|
||||
/// @brief Initiate single entry in look-up-table for CRC32 computations
|
||||
/// @param lut pointer of size 256 for look-up-table
|
||||
/// @param n entry in table (assumed n < 256)
|
||||
inline __hostdev__ void initCrc32Lut(uint32_t lut[256], uint32_t n)
|
||||
{
|
||||
lut[n] = n;
|
||||
uint32_t &cs = lut[n];
|
||||
for (int i = 0; i < 8; ++i) cs = (cs >> 1) ^ ((cs & 1) ? 0xEDB88320 : 0);
|
||||
}
|
||||
|
||||
/// @brief Initiate entire look-up-table for CRC32 computations
|
||||
/// @param lut pointer of size 256 for look-up-table
|
||||
inline __hostdev__ void initCrc32Lut(uint32_t lut[256]){for (uint32_t n = 0u; n < 256u; ++n) initCrc32Lut(lut, n);}
|
||||
|
||||
/// @brief Create and initiate entire look-up-table for CRC32 computations
|
||||
/// @return returns a unique pointer to the lookup table of size 256.
|
||||
inline std::unique_ptr<uint32_t[]> createCrc32Lut()
|
||||
{
|
||||
std::unique_ptr<uint32_t[]> lut(new uint32_t[256]);
|
||||
initCrc32Lut(lut.get());
|
||||
return lut;
|
||||
}
|
||||
|
||||
/// @brief Compute crc32 checksum of @c data of @c size bytes (without a lookup table))
|
||||
/// @param data pointer to beginning of data
|
||||
/// @param size byte size of data
|
||||
/// @param crc initial value of crc32 checksum
|
||||
/// @return return crc32 checksum of @c data
|
||||
inline __hostdev__ uint32_t crc32(const void* data, size_t size, uint32_t crc = 0)
|
||||
{
|
||||
NANOVDB_ASSERT(data);
|
||||
crc = ~crc;
|
||||
for (auto *p = (const uint8_t*)data, *q = p + size; p != q; ++p) {
|
||||
crc ^= *p;
|
||||
for (int j = 0; j < 8; ++j) crc = (crc >> 1) ^ (0xEDB88320 & (-(crc & 1)));
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
/// @brief Compute crc32 checksum of data between @c begin and @c end
|
||||
/// @param begin points to beginning of data
|
||||
/// @param end points to end of @data, (exclusive)
|
||||
/// @param crc initial value of crc32 checksum
|
||||
/// @return return crc32 checksum
|
||||
inline __hostdev__ uint32_t crc32(const void *begin, const void *end, uint32_t crc = 0)
|
||||
{
|
||||
NANOVDB_ASSERT(begin && end);
|
||||
NANOVDB_ASSERT(end >= begin);
|
||||
return crc32(begin, (const char*)end - (const char*)begin, crc);
|
||||
}
|
||||
|
||||
/// @brief Compute crc32 checksum of @c data with @c size bytes using a lookup table
|
||||
/// @param data pointer to begenning of data
|
||||
/// @param size byte size
|
||||
/// @param lut pointer to loopup table for accelerated crc32 computation
|
||||
/// @param crc initial value of the checksum
|
||||
/// @return crc32 checksum of @c data with @c size bytes
|
||||
inline __hostdev__ uint32_t crc32(const void *data, size_t size, const uint32_t lut[256], uint32_t crc = 0)
|
||||
{
|
||||
NANOVDB_ASSERT(data);
|
||||
crc = ~crc;
|
||||
for (auto *p = (const uint8_t*)data, *q = p + size; p != q; ++p) crc = lut[(crc ^ *p) & 0xFF] ^ (crc >> 8);
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
/// @brief Compute crc32 checksum of data between @c begin and @c end using a lookup table
|
||||
/// @param begin points to beginning of data
|
||||
/// @param end points to end of @data, (exclusive)
|
||||
/// @param lut pointer to loopup table for accelerated crc32 computation
|
||||
/// @param crc initial value of crc32 checksum
|
||||
/// @return return crc32 checksum
|
||||
inline __hostdev__ uint32_t crc32(const void *begin, const void *end, const uint32_t lut[256], uint32_t crc = 0)
|
||||
{
|
||||
NANOVDB_ASSERT(begin && end);
|
||||
NANOVDB_ASSERT(end >= begin);
|
||||
return crc32(begin, (const char*)end - (const char*)begin, lut, crc);
|
||||
}// uint32_t util::crc32(const void *begin, const void *end, const uint32_t lut[256], uint32_t crc = 0)
|
||||
|
||||
/// @brief
|
||||
/// @param data
|
||||
/// @param size
|
||||
/// @param lut
|
||||
/// @return
|
||||
inline uint32_t blockedCrc32(const void *data, size_t size, const uint32_t *lut)
|
||||
{
|
||||
if (size == 0 ) return ~uint32_t(0);
|
||||
const uint64_t blockCount = size >> NANOVDB_CRC32_LOG2_BLOCK_SIZE;// number of 4 KB (4096 byte) blocks
|
||||
std::unique_ptr<uint32_t[]> checksums(new uint32_t[blockCount]);
|
||||
forEach(0, blockCount, 64, [&](const Range1D &r) {
|
||||
uint32_t blockSize = 1 << NANOVDB_CRC32_LOG2_BLOCK_SIZE, *p = checksums.get() + r.begin();
|
||||
for (auto i = r.begin(); i != r.end(); ++i) {
|
||||
if (i+1 == blockCount) blockSize += static_cast<uint32_t>(size - (blockCount<<NANOVDB_CRC32_LOG2_BLOCK_SIZE));
|
||||
*p++ = crc32((const uint8_t*)data + (i<<NANOVDB_CRC32_LOG2_BLOCK_SIZE), blockSize, lut);
|
||||
}
|
||||
});
|
||||
return crc32(checksums.get(), sizeof(uint32_t)*blockCount, lut);
|
||||
}// uint32_t util::blockedCrc32(const void *data, size_t size, const uint32_t *lut)
|
||||
|
||||
/// @brief
|
||||
/// @param begin
|
||||
/// @param end
|
||||
/// @param lut
|
||||
/// @return
|
||||
inline uint32_t blockedCrc32(const void *begin, const void *end, const uint32_t *lut)
|
||||
{
|
||||
return blockedCrc32(begin, PtrDiff(end, begin), lut);
|
||||
}
|
||||
|
||||
}// namespace util =======================================================================================
|
||||
|
||||
namespace tools {// ======================================================================================
|
||||
|
||||
// When serialized:
|
||||
// [Grid,Tree][Root][ROOT TILES...][Node<5>...][Node<4>...][Leaf<3>...][BlindMeta...][BlindData...]
|
||||
// checksum[2] before v32.6.0: <------------- [0] ------------><-------------- [1] --------------->
|
||||
// checksum[]2 after v32.6.0: <---[0]---><----------------------------------------[1]---------------------------------------->
|
||||
|
||||
// ----------------------------> crc32Head <--------------------------------------
|
||||
|
||||
/// @brief
|
||||
/// @tparam ValueT
|
||||
/// @param grid
|
||||
/// @param mode
|
||||
/// @return
|
||||
inline __hostdev__ uint32_t crc32Head(const GridData *gridData, const uint32_t *lut)
|
||||
{
|
||||
NANOVDB_ASSERT(gridData);
|
||||
const uint8_t *begin = (const uint8_t*)(gridData), *mid = begin + sizeof(GridData) + sizeof(TreeData);
|
||||
if (gridData->mVersion <= Version(32,6,0)) mid = (const uint8_t*)(gridData->template nodePtr<2>());
|
||||
return util::crc32(begin + 16u, mid, lut);// exclude GridData::mMagic and GridData::mChecksum
|
||||
}// uint32_t crc32Head(const GridData *gridData, const uint32_t *lut)
|
||||
|
||||
/// @brief
|
||||
/// @param gridData
|
||||
/// @return
|
||||
inline __hostdev__ uint32_t crc32Head(const GridData *gridData)
|
||||
{
|
||||
NANOVDB_ASSERT(gridData);
|
||||
const uint8_t *begin = (const uint8_t*)(gridData), *mid = begin + sizeof(GridData) + sizeof(TreeData);
|
||||
if (gridData->mVersion <= Version(32,6,0)) mid = (const uint8_t*)(gridData->template nodePtr<2>());
|
||||
return util::crc32(begin + 16, mid);// exclude GridData::mMagic and GridData::mChecksum
|
||||
}// uint32_t crc32Head(const GridData *gridData)
|
||||
|
||||
// ----------------------------> crc32TailOld <--------------------------------------
|
||||
|
||||
// Old checksum
|
||||
template <typename ValueT>
|
||||
uint32_t crc32TailOld(const NanoGrid<ValueT> *grid, const uint32_t *lut)
|
||||
{
|
||||
NANOVDB_ASSERT(grid->mVersion <= Version(32,6,0));
|
||||
const auto &tree = grid->tree();
|
||||
auto nodeMgrHandle = createNodeManager(*grid);
|
||||
auto *nodeMgr = nodeMgrHandle.template mgr<ValueT>();
|
||||
assert(nodeMgr && isAligned(nodeMgr));
|
||||
const auto nodeCount = tree.nodeCount(0) + tree.nodeCount(1) + tree.nodeCount(2);
|
||||
std::vector<uint32_t> checksums(nodeCount, 0);
|
||||
util::forEach(0, tree.nodeCount(2), 1,[&](const util::Range1D &r) {// process upper internal nodes
|
||||
uint32_t *p = checksums.data() + r.begin();
|
||||
for (auto i = r.begin(); i != r.end(); ++i) {
|
||||
const auto &node = nodeMgr->upper(static_cast<uint32_t>(i));
|
||||
*p++ = util::crc32(&node, node.memUsage(), lut);
|
||||
}
|
||||
});
|
||||
util::forEach(0, tree.nodeCount(1), 1, [&](const util::Range1D &r) { // process lower internal nodes
|
||||
uint32_t *p = checksums.data() + r.begin() + tree.nodeCount(2);
|
||||
for (auto i = r.begin(); i != r.end(); ++i) {
|
||||
const auto &node = nodeMgr->lower(static_cast<uint32_t>(i));
|
||||
*p++ = util::crc32(&node, node.memUsage(), lut);
|
||||
}
|
||||
});
|
||||
util::forEach(0, tree.nodeCount(0), 8, [&](const util::Range1D &r) { // process leaf nodes
|
||||
uint32_t *p = checksums.data() + r.begin() + tree.nodeCount(1) + tree.nodeCount(2);
|
||||
for (auto i = r.begin(); i != r.end(); ++i) {
|
||||
const auto &leaf = nodeMgr->leaf(static_cast<uint32_t>(i));
|
||||
*p++ = util::crc32(&leaf, leaf.memUsage(), lut);
|
||||
}
|
||||
});
|
||||
return util::crc32(checksums.data(), sizeof(uint32_t)*checksums.size(), lut);
|
||||
}// uint32_t crc32TailOld(const NanoGrid<ValueT> *grid, const uint32_t *lut)
|
||||
|
||||
struct Crc32TailOld {
|
||||
template <typename BuildT>
|
||||
static uint32_t known(const GridData *gridData, const uint32_t *lut)
|
||||
{
|
||||
return crc32TailOld((const NanoGrid<BuildT>*)gridData, lut);
|
||||
}
|
||||
static uint32_t unknown(const GridData*, const uint32_t*)
|
||||
{
|
||||
throw std::runtime_error("Cannot call Crc32TailOld with grid of unknown type");
|
||||
return 0u;//dummy
|
||||
}
|
||||
};// struct Crc32TailOld
|
||||
|
||||
inline uint32_t crc32Tail(const GridData *gridData, const uint32_t *lut)
|
||||
{
|
||||
NANOVDB_ASSERT(gridData);
|
||||
if (gridData->mVersion > Version(32,6,0)) {
|
||||
const uint8_t *begin = (const uint8_t*)(gridData);
|
||||
return util::blockedCrc32(begin + sizeof(GridData) + sizeof(TreeData), begin + gridData->mGridSize, lut);
|
||||
} else {
|
||||
return callNanoGrid<Crc32TailOld>(gridData, lut);
|
||||
}
|
||||
}// uint32_t crc32Tail(const GridData *gridData, const uint32_t *lut)
|
||||
|
||||
template <typename ValueT>
|
||||
uint32_t crc32Tail(const NanoGrid<ValueT> *grid, const uint32_t *lut)
|
||||
{
|
||||
NANOVDB_ASSERT(grid);
|
||||
if (grid->mVersion > Version(32,6,0)) {
|
||||
const uint8_t *begin = (const uint8_t*)(grid);
|
||||
return util::blockedCrc32(begin + sizeof(GridData) + sizeof(TreeData), begin + grid->mGridSize, lut);
|
||||
} else {
|
||||
return crc32TailOld(grid, lut);
|
||||
}
|
||||
}// uint32_t crc32Tail(const NanoGrid<ValueT> *gridData, const uint32_t *lut)
|
||||
|
||||
// ----------------------------> evalChecksum <--------------------------------------
|
||||
|
||||
/// @brief
|
||||
/// @tparam ValueT
|
||||
/// @param grid
|
||||
/// @param mode
|
||||
/// @return
|
||||
template <typename ValueT>
|
||||
Checksum evalChecksum(const NanoGrid<ValueT> *grid, CheckMode mode)
|
||||
{
|
||||
NANOVDB_ASSERT(grid);
|
||||
Checksum cs;
|
||||
if (mode != CheckMode::Empty) {
|
||||
auto lut = util::createCrc32Lut();
|
||||
cs.head() = crc32Head(grid, lut.get());
|
||||
if (mode == CheckMode::Full) cs.tail() = crc32Tail(grid, lut.get());
|
||||
}
|
||||
return cs;
|
||||
}// checksum(const NanoGrid*, CheckMode)
|
||||
|
||||
template <typename ValueT>
|
||||
[[deprecated("Use evalChecksum(const NanoGrid<ValueT> *grid, CheckMode mode) instead")]]
|
||||
Checksum checksum(const NanoGrid<ValueT> *grid, CheckMode mode){return evalChecksum(grid, mode);}
|
||||
|
||||
inline Checksum evalChecksum(const GridData *gridData, CheckMode mode)
|
||||
{
|
||||
NANOVDB_ASSERT(gridData);
|
||||
Checksum cs;
|
||||
if (mode != CheckMode::Disable) {
|
||||
auto lut = util::createCrc32Lut();
|
||||
cs.head() = crc32Head(gridData, lut.get());
|
||||
if (mode == CheckMode::Full) cs.tail() = crc32Tail(gridData, lut.get());
|
||||
}
|
||||
return cs;
|
||||
}// evalChecksum(GridData *data, CheckMode mode)
|
||||
|
||||
[[deprecated("Use evalChecksum(const NanoGrid*, CheckMode) instead")]]
|
||||
inline Checksum checksum(const GridData *gridData, CheckMode mode){return evalChecksum(gridData, mode);}
|
||||
|
||||
template <typename ValueT>
|
||||
[[deprecated("Use checksum(const NanoGrid*, CheckMode) instead")]]
|
||||
Checksum checksum(const NanoGrid<ValueT> &grid, CheckMode mode){return checksum(&grid, mode);}
|
||||
|
||||
// ----------------------------> validateChecksum <--------------------------------------
|
||||
|
||||
/// @brief
|
||||
/// @tparam ValueT
|
||||
/// @param grid
|
||||
/// @param mode
|
||||
/// @return
|
||||
template <typename ValueT>
|
||||
bool validateChecksum(const NanoGrid<ValueT> *grid, CheckMode mode)
|
||||
{
|
||||
if (grid->mChecksum.isEmpty() || mode == CheckMode::Empty) return true;
|
||||
auto lut = util::createCrc32Lut();
|
||||
bool checkHead = grid->mChecksum.head() == crc32Head(grid->data(), lut.get());
|
||||
if (grid->mChecksum.isHalf() || mode == CheckMode::Half || !checkHead) {
|
||||
return checkHead;
|
||||
} else {
|
||||
return grid->mChecksum.tail() == crc32Tail(grid, lut.get());
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief
|
||||
/// @tparam ValueT
|
||||
/// @param grid
|
||||
/// @param mode
|
||||
/// @return
|
||||
inline bool validateChecksum(const GridData *gridData, CheckMode mode)
|
||||
{
|
||||
if (gridData->mChecksum.isEmpty()|| mode == CheckMode::Empty) return true;
|
||||
auto lut = util::createCrc32Lut();
|
||||
bool checkHead = gridData->mChecksum.head() == crc32Head(gridData, lut.get());
|
||||
if (gridData->mChecksum.isHalf() || mode == CheckMode::Half || !checkHead) {
|
||||
return checkHead;
|
||||
} else {
|
||||
return gridData->mChecksum.tail() == crc32Tail(gridData, lut.get());
|
||||
}
|
||||
}// bool validateChecksum(const GridData *gridData, CheckMode mode)
|
||||
|
||||
template <typename ValueT>
|
||||
[[deprecated("Use validateChecksum(const NanoGrid*, CheckMode) instead")]]
|
||||
bool validateChecksum(const NanoGrid<ValueT> &grid, CheckMode mode){return validateChecksum(&grid, mode);}
|
||||
|
||||
// ----------------------------> updateChecksum <--------------------------------------
|
||||
|
||||
/// @brief
|
||||
/// @tparam ValueT
|
||||
/// @param grid
|
||||
/// @param mode
|
||||
template <typename ValueT>
|
||||
void updateChecksum(NanoGrid<ValueT> *grid, CheckMode mode){grid->mChecksum = evalChecksum(grid, mode);}
|
||||
|
||||
template <typename ValueT>
|
||||
void updateChecksum(NanoGrid<ValueT> *grid){grid->mChecksum = evalChecksum(grid, grid->mChecksum.mode());}
|
||||
|
||||
// deprecated method that takes a reference vs a pointer
|
||||
template <typename ValueT>
|
||||
[[deprecated("Use updateChecksum(const NanoGrid*, CheckMode) instead")]]
|
||||
void updateChecksum(NanoGrid<ValueT> &grid, CheckMode mode){updateChecksum(&grid, mode);}
|
||||
|
||||
// ----------------------------> updateGridCount <--------------------------------------
|
||||
|
||||
/// @brief Updates the ground index and count, as well as the head checksum if needed
|
||||
/// @param data Pointer to grid data
|
||||
/// @param gridIndex New value of the index
|
||||
/// @param gridCount New value of the grid count
|
||||
inline void updateGridCount(GridData *data, uint32_t gridIndex, uint32_t gridCount)
|
||||
{
|
||||
NANOVDB_ASSERT(data && gridIndex < gridCount);
|
||||
if (data->mGridIndex != gridIndex || data->mGridCount != gridCount) {
|
||||
data->mGridIndex = gridIndex;
|
||||
data->mGridCount = gridCount;
|
||||
if (!data->mChecksum.isEmpty()) data->mChecksum.head() = crc32Head(data);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tools ======================================================================
|
||||
|
||||
|
||||
} // namespace nanovdb ====================================================================
|
||||
|
||||
#endif // NANOVDB_TOOLS_GRIDCHECKSUM_H_HAS_BEEN_INCLUDED
|
||||
@@ -1,877 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/GridStats.h
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date August 29, 2020
|
||||
|
||||
\brief Re-computes min/max/avg/var/bbox information for each node in a
|
||||
pre-existing NanoVDB grid.
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_TOOLS_GRIDSTATS_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_TOOLS_GRIDSTATS_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
|
||||
#ifdef NANOVDB_USE_TBB
|
||||
#include <tbb/blocked_range.h>
|
||||
#include <tbb/parallel_reduce.h>
|
||||
#endif
|
||||
|
||||
#if defined(__CUDACC__)
|
||||
#include <cuda/std/limits>// for cuda::std::numeric_limits
|
||||
#else
|
||||
#include <limits.h>// for std::numeric_limits
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools {//=======================================================================
|
||||
|
||||
/// @brief Grid flags which indicate what extra information is present in the grid buffer
|
||||
enum class StatsMode : uint32_t {
|
||||
Disable = 0,// disable the computation of any type of statistics (obviously the FASTEST!)
|
||||
BBox = 1,// only compute the bbox of active values per node and total activeVoxelCount
|
||||
MinMax = 2,// additionally compute extrema values
|
||||
All = 3,// compute all of the statics, i.e. bbox, min/max, average and standard deviation
|
||||
Default = 3,// default computational mode for statistics
|
||||
End = 4,
|
||||
};
|
||||
|
||||
/// @brief Re-computes the min/max, stats and bbox information for an existing NanoVDB Grid
|
||||
/// @param grid Grid whose stats to update
|
||||
/// @param mode Mode of computation for the statistics.
|
||||
template<typename BuildT>
|
||||
void updateGridStats(NanoGrid<BuildT>* grid, StatsMode mode = StatsMode::Default);
|
||||
|
||||
template<typename ValueT, int Rank = TensorTraits<ValueT>::Rank>
|
||||
class Extrema;
|
||||
|
||||
/// @brief Determine the extrema of all the values in a grid that
|
||||
/// intersects the specified bounding box.
|
||||
/// @tparam BuildT Build type of the input grid
|
||||
/// @param grid typed grid
|
||||
/// @param bbox index bounding box in which min/max are computed
|
||||
/// @return Extream of values insixe @c bbox
|
||||
template<typename BuildT>
|
||||
Extrema<typename NanoGrid<BuildT>::ValueType>
|
||||
getExtrema(const NanoGrid<BuildT>& grid, const CoordBBox &bbox);
|
||||
|
||||
//================================================================================================
|
||||
|
||||
/// @brief Template specialization of Extrema on scalar value types, i.e. rank = 0
|
||||
template<typename ValueT>
|
||||
class Extrema<ValueT, 0>
|
||||
{
|
||||
protected:
|
||||
ValueT mMin, mMax;
|
||||
|
||||
public:
|
||||
using ValueType = ValueT;
|
||||
__hostdev__ Extrema()
|
||||
#if defined(__CUDACC__)
|
||||
// note "::cuda" is needed since we also define a cuda namespace
|
||||
: mMin(::cuda::std::numeric_limits<ValueT>::max())
|
||||
, mMax(::cuda::std::numeric_limits<ValueT>::lowest())
|
||||
#else
|
||||
: mMin(std::numeric_limits<ValueT>::max())
|
||||
, mMax(std::numeric_limits<ValueT>::lowest())
|
||||
#endif
|
||||
{
|
||||
}
|
||||
__hostdev__ Extrema(const ValueT& v)
|
||||
: mMin(v)
|
||||
, mMax(v)
|
||||
{
|
||||
}
|
||||
__hostdev__ Extrema(const ValueT& a, const ValueT& b)
|
||||
: mMin(a)
|
||||
, mMax(b)
|
||||
{
|
||||
}
|
||||
__hostdev__ Extrema& min(const ValueT& v)
|
||||
{
|
||||
if (v < mMin) mMin = v;
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Extrema& max(const ValueT& v)
|
||||
{
|
||||
if (v > mMax) mMax = v;
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Extrema& add(const ValueT& v)
|
||||
{
|
||||
this->min(v);
|
||||
this->max(v);
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Extrema& add(const ValueT& v, uint64_t) { return this->add(v); }
|
||||
__hostdev__ Extrema& add(const Extrema& other)
|
||||
{
|
||||
this->min(other.mMin);
|
||||
this->max(other.mMax);
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ const ValueT& min() const { return mMin; }
|
||||
__hostdev__ const ValueT& max() const { return mMax; }
|
||||
__hostdev__ operator bool() const { return mMin <= mMax; }
|
||||
__hostdev__ static constexpr bool hasMinMax() { return !util::is_same<bool, ValueT>::value; }
|
||||
__hostdev__ static constexpr bool hasAverage() { return false; }
|
||||
__hostdev__ static constexpr bool hasStdDeviation() { return false; }
|
||||
__hostdev__ static constexpr bool hasStats() { return !util::is_same<bool, ValueT>::value; }
|
||||
__hostdev__ static constexpr size_t size() { return 0; }
|
||||
|
||||
template <typename NodeT>
|
||||
__hostdev__ void setStats(NodeT &node) const
|
||||
{
|
||||
node.setMin(this->min());
|
||||
node.setMax(this->max());
|
||||
}
|
||||
}; // Extrema<T, 0>
|
||||
|
||||
/// @brief Template specialization of Extrema on vector value types, i.e. rank = 1
|
||||
template<typename VecT>
|
||||
class Extrema<VecT, 1>
|
||||
{
|
||||
protected:
|
||||
using Real = typename VecT::ValueType; // this works with both nanovdb and openvdb vectors
|
||||
struct Pair
|
||||
{
|
||||
Real scalar;
|
||||
VecT vector;
|
||||
|
||||
__hostdev__ Pair(Real s)// is only used by Extrema() default c-tor
|
||||
: scalar(s)
|
||||
, vector(s)
|
||||
{
|
||||
}
|
||||
__hostdev__ Pair(const VecT& v)
|
||||
: scalar(v.lengthSqr())
|
||||
, vector(v)
|
||||
{
|
||||
}
|
||||
__hostdev__ bool operator<(const Pair& rhs) const { return scalar < rhs.scalar; }
|
||||
} mMin, mMax;
|
||||
__hostdev__ Extrema& add(const Pair& p)
|
||||
{
|
||||
if (p < mMin) mMin = p;
|
||||
if (mMax < p) mMax = p;
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
using ValueType = VecT;
|
||||
__hostdev__ Extrema()
|
||||
#if defined(__CUDACC__)
|
||||
// note "::cuda" is needed since we also define a cuda namespace
|
||||
: mMin(::cuda::std::numeric_limits<Real>::max())
|
||||
, mMax(::cuda::std::numeric_limits<Real>::lowest())
|
||||
#else
|
||||
: mMin(std::numeric_limits<Real>::max())
|
||||
, mMax(std::numeric_limits<Real>::lowest())
|
||||
#endif
|
||||
{
|
||||
}
|
||||
__hostdev__ Extrema(const VecT& v)
|
||||
: mMin(v)
|
||||
, mMax(v)
|
||||
{
|
||||
}
|
||||
__hostdev__ Extrema(const VecT& a, const VecT& b)
|
||||
: mMin(a)
|
||||
, mMax(b)
|
||||
{
|
||||
}
|
||||
__hostdev__ Extrema& min(const VecT& v)
|
||||
{
|
||||
Pair tmp(v);
|
||||
if (tmp < mMin) mMin = tmp;
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Extrema& max(const VecT& v)
|
||||
{
|
||||
Pair tmp(v);
|
||||
if (mMax < tmp) mMax = tmp;
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ Extrema& add(const VecT& v) { return this->add(Pair(v)); }
|
||||
__hostdev__ Extrema& add(const VecT& v, uint64_t) { return this->add(Pair(v)); }
|
||||
__hostdev__ Extrema& add(const Extrema& other)
|
||||
{
|
||||
if (other.mMin < mMin) mMin = other.mMin;
|
||||
if (mMax < other.mMax) mMax = other.mMax;
|
||||
return *this;
|
||||
}
|
||||
__hostdev__ const VecT& min() const { return mMin.vector; }
|
||||
__hostdev__ const VecT& max() const { return mMax.vector; }
|
||||
__hostdev__ operator bool() const { return !(mMax < mMin); }
|
||||
__hostdev__ static constexpr bool hasMinMax() { return !util::is_same<bool, Real>::value; }
|
||||
__hostdev__ static constexpr bool hasAverage() { return false; }
|
||||
__hostdev__ static constexpr bool hasStdDeviation() { return false; }
|
||||
__hostdev__ static constexpr bool hasStats() { return !util::is_same<bool, Real>::value; }
|
||||
__hostdev__ static constexpr size_t size() { return 0; }
|
||||
|
||||
template <typename NodeT>
|
||||
__hostdev__ void setStats(NodeT &node) const
|
||||
{
|
||||
node.setMin(this->min());
|
||||
node.setMax(this->max());
|
||||
}
|
||||
}; // Extrema<T, 1>
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename ValueT, int Rank = TensorTraits<ValueT>::Rank>
|
||||
class Stats;
|
||||
|
||||
/// @brief This class computes statistics (minimum value, maximum
|
||||
/// value, mean, variance and standard deviation) of a population
|
||||
/// of floating-point values.
|
||||
///
|
||||
/// @details variance = Mean[ (X-Mean[X])^2 ] = Mean[X^2] - Mean[X]^2,
|
||||
/// standard deviation = sqrt(variance)
|
||||
///
|
||||
/// @note This class employs incremental computation and double precision.
|
||||
template<typename ValueT>
|
||||
class Stats<ValueT, 0> : public Extrema<ValueT, 0>
|
||||
{
|
||||
protected:
|
||||
using BaseT = Extrema<ValueT, 0>;
|
||||
using RealT = double; // for accuracy the internal precission must be 64 bit floats
|
||||
size_t mSize;
|
||||
double mAvg, mAux;
|
||||
|
||||
public:
|
||||
using ValueType = ValueT;
|
||||
__hostdev__ Stats()
|
||||
: BaseT()
|
||||
, mSize(0)
|
||||
, mAvg(0.0)
|
||||
, mAux(0.0)
|
||||
{
|
||||
}
|
||||
__hostdev__ Stats(const ValueT& val)
|
||||
: BaseT(val)
|
||||
, mSize(1)
|
||||
, mAvg(RealT(val))
|
||||
, mAux(0.0)
|
||||
{
|
||||
}
|
||||
/// @brief Add a single sample
|
||||
__hostdev__ Stats& add(const ValueT& val)
|
||||
{
|
||||
BaseT::add(val);
|
||||
mSize += 1;
|
||||
const double delta = double(val) - mAvg;
|
||||
mAvg += delta / double(mSize);
|
||||
mAux += delta * (double(val) - mAvg);
|
||||
return *this;
|
||||
}
|
||||
/// @brief Add @a n samples with constant value @a val.
|
||||
__hostdev__ Stats& add(const ValueT& val, uint64_t n)
|
||||
{
|
||||
const double denom = 1.0 / double(mSize + n);
|
||||
const double delta = double(val) - mAvg;
|
||||
mAvg += denom * delta * double(n);
|
||||
mAux += denom * delta * delta * double(mSize) * double(n);
|
||||
BaseT::add(val);
|
||||
mSize += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Add the samples from the other Stats instance.
|
||||
__hostdev__ Stats& add(const Stats& other)
|
||||
{
|
||||
if (other.mSize > 0) {
|
||||
const double denom = 1.0 / double(mSize + other.mSize);
|
||||
const double delta = other.mAvg - mAvg;
|
||||
mAvg += denom * delta * double(other.mSize);
|
||||
mAux += other.mAux + denom * delta * delta * double(mSize) * double(other.mSize);
|
||||
BaseT::add(other);
|
||||
mSize += other.mSize;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
__hostdev__ static constexpr bool hasMinMax() { return !util::is_same<bool, ValueT>::value; }
|
||||
__hostdev__ static constexpr bool hasAverage() { return !util::is_same<bool, ValueT>::value; }
|
||||
__hostdev__ static constexpr bool hasStdDeviation() { return !util::is_same<bool, ValueT>::value; }
|
||||
__hostdev__ static constexpr bool hasStats() { return !util::is_same<bool, ValueT>::value; }
|
||||
|
||||
__hostdev__ size_t size() const { return mSize; }
|
||||
|
||||
//@{
|
||||
/// Return the arithmetic mean, i.e. average, value.
|
||||
__hostdev__ double avg() const { return mAvg; }
|
||||
__hostdev__ double mean() const { return mAvg; }
|
||||
//@}
|
||||
|
||||
//@{
|
||||
/// @brief Return the population variance.
|
||||
///
|
||||
/// @note The unbiased sample variance = population variance * num/(num-1)
|
||||
__hostdev__ double var() const { return mSize < 2 ? 0.0 : mAux / double(mSize); }
|
||||
__hostdev__ double variance() const { return this->var(); }
|
||||
//@}
|
||||
|
||||
//@{
|
||||
/// @brief Return the standard deviation (=Sqrt(variance)) as
|
||||
/// defined from the (biased) population variance.
|
||||
__hostdev__ double std() const { return sqrt(this->var()); }
|
||||
__hostdev__ double stdDev() const { return this->std(); }
|
||||
//@}
|
||||
|
||||
template <typename NodeT>
|
||||
__hostdev__ void setStats(NodeT &node) const
|
||||
{
|
||||
node.setMin(this->min());
|
||||
node.setMax(this->max());
|
||||
node.setAvg(this->avg());
|
||||
node.setDev(this->std());
|
||||
}
|
||||
}; // end Stats<T, 0>
|
||||
|
||||
/// @brief This class computes statistics (minimum value, maximum
|
||||
/// value, mean, variance and standard deviation) of a population
|
||||
/// of floating-point values.
|
||||
///
|
||||
/// @details variance = Mean[ (X-Mean[X])^2 ] = Mean[X^2] - Mean[X]^2,
|
||||
/// standard deviation = sqrt(variance)
|
||||
///
|
||||
/// @note This class employs incremental computation and double precision.
|
||||
template<typename ValueT>
|
||||
class Stats<ValueT, 1> : public Extrema<ValueT, 1>
|
||||
{
|
||||
protected:
|
||||
using BaseT = Extrema<ValueT, 1>;
|
||||
using RealT = double; // for accuracy the internal precision must be 64 bit floats
|
||||
size_t mSize;
|
||||
double mAvg, mAux;
|
||||
|
||||
public:
|
||||
using ValueType = ValueT;
|
||||
__hostdev__ Stats()
|
||||
: BaseT()
|
||||
, mSize(0)
|
||||
, mAvg(0.0)
|
||||
, mAux(0.0)
|
||||
{
|
||||
}
|
||||
/// @brief Add a single sample
|
||||
__hostdev__ Stats& add(const ValueT& val)
|
||||
{
|
||||
typename BaseT::Pair tmp(val);
|
||||
BaseT::add(tmp);
|
||||
mSize += 1;
|
||||
const double delta = tmp.scalar - mAvg;
|
||||
mAvg += delta / double(mSize);
|
||||
mAux += delta * (tmp.scalar - mAvg);
|
||||
return *this;
|
||||
}
|
||||
/// @brief Add @a n samples with constant value @a val.
|
||||
__hostdev__ Stats& add(const ValueT& val, uint64_t n)
|
||||
{
|
||||
typename BaseT::Pair tmp(val);
|
||||
const double denom = 1.0 / double(mSize + n);
|
||||
const double delta = tmp.scalar - mAvg;
|
||||
mAvg += denom * delta * double(n);
|
||||
mAux += denom * delta * delta * double(mSize) * double(n);
|
||||
BaseT::add(tmp);
|
||||
mSize += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Add the samples from the other Stats instance.
|
||||
__hostdev__ Stats& add(const Stats& other)
|
||||
{
|
||||
if (other.mSize > 0) {
|
||||
const double denom = 1.0 / double(mSize + other.mSize);
|
||||
const double delta = other.mAvg - mAvg;
|
||||
mAvg += denom * delta * double(other.mSize);
|
||||
mAux += other.mAux + denom * delta * delta * double(mSize) * double(other.mSize);
|
||||
BaseT::add(other);
|
||||
mSize += other.mSize;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
__hostdev__ static constexpr bool hasMinMax() { return !util::is_same<bool, ValueT>::value; }
|
||||
__hostdev__ static constexpr bool hasAverage() { return !util::is_same<bool, ValueT>::value; }
|
||||
__hostdev__ static constexpr bool hasStdDeviation() { return !util::is_same<bool, ValueT>::value; }
|
||||
__hostdev__ static constexpr bool hasStats() { return !util::is_same<bool, ValueT>::value; }
|
||||
|
||||
__hostdev__ size_t size() const { return mSize; }
|
||||
|
||||
//@{
|
||||
/// Return the arithmetic mean, i.e. average, value.
|
||||
__hostdev__ double avg() const { return mAvg; }
|
||||
__hostdev__ double mean() const { return mAvg; }
|
||||
//@}
|
||||
|
||||
//@{
|
||||
/// @brief Return the population variance.
|
||||
///
|
||||
/// @note The unbiased sample variance = population variance * num/(num-1)
|
||||
__hostdev__ double var() const { return mSize < 2 ? 0.0 : mAux / double(mSize); }
|
||||
__hostdev__ double variance() const { return this->var(); }
|
||||
//@}
|
||||
|
||||
//@{
|
||||
/// @brief Return the standard deviation (=Sqrt(variance)) as
|
||||
/// defined from the (biased) population variance.
|
||||
__hostdev__ double std() const { return sqrt(this->var()); }
|
||||
__hostdev__ double stdDev() const { return this->std(); }
|
||||
//@}
|
||||
|
||||
template <typename NodeT>
|
||||
__hostdev__ void setStats(NodeT &node) const
|
||||
{
|
||||
node.setMin(this->min());
|
||||
node.setMax(this->max());
|
||||
node.setAvg(this->avg());
|
||||
node.setDev(this->std());
|
||||
}
|
||||
}; // end Stats<T, 1>
|
||||
|
||||
/// @brief No-op Stats class
|
||||
template<typename ValueT>
|
||||
struct NoopStats
|
||||
{
|
||||
using ValueType = ValueT;
|
||||
__hostdev__ NoopStats() {}
|
||||
__hostdev__ NoopStats(const ValueT&) {}
|
||||
__hostdev__ NoopStats& add(const ValueT&) { return *this; }
|
||||
__hostdev__ NoopStats& add(const ValueT&, uint64_t) { return *this; }
|
||||
__hostdev__ NoopStats& add(const NoopStats&) { return *this; }
|
||||
__hostdev__ static constexpr size_t size() { return 0; }
|
||||
__hostdev__ static constexpr bool hasMinMax() { return false; }
|
||||
__hostdev__ static constexpr bool hasAverage() { return false; }
|
||||
__hostdev__ static constexpr bool hasStdDeviation() { return false; }
|
||||
__hostdev__ static constexpr bool hasStats() { return false; }
|
||||
template <typename NodeT>
|
||||
__hostdev__ void setStats(NodeT&) const{}
|
||||
}; // end NoopStats<T>
|
||||
|
||||
//================================================================================================
|
||||
|
||||
/// @brief Allows for the construction of NanoVDB grids without any dependency
|
||||
template<typename GridT, typename StatsT = Stats<typename GridT::ValueType>>
|
||||
class GridStats
|
||||
{
|
||||
struct NodeStats;
|
||||
using TreeT = typename GridT::TreeType;
|
||||
using ValueT = typename TreeT::ValueType;
|
||||
using BuildT = typename TreeT::BuildType;
|
||||
using Node0 = typename TreeT::Node0; // leaf
|
||||
using Node1 = typename TreeT::Node1; // lower
|
||||
using Node2 = typename TreeT::Node2; // upper
|
||||
using RootT = typename TreeT::Node3; // root
|
||||
static_assert(util::is_same<ValueT, typename StatsT::ValueType>::value, "Mismatching type");
|
||||
|
||||
ValueT mDelta; // skip rendering of node if: node.max < -mDelta || node.min > mDelta
|
||||
|
||||
void process( GridT& );// process grid and all tree nodes
|
||||
void process( TreeT& );// process Tree, root node and child nodes
|
||||
void process( RootT& );// process root node and child nodes
|
||||
NodeStats process( Node0& );// process leaf node
|
||||
|
||||
template<typename NodeT>
|
||||
NodeStats process( NodeT& );// process internal node and child nodes
|
||||
|
||||
template<typename DataT, int Rank>
|
||||
void setStats(DataT*, const Extrema<ValueT, Rank>&);
|
||||
template<typename DataT, int Rank>
|
||||
void setStats(DataT*, const Stats<ValueT, Rank>&);
|
||||
template<typename DataT>
|
||||
void setStats(DataT*, const NoopStats<ValueT>&) {}
|
||||
|
||||
template<typename T, typename FlagT>
|
||||
typename std::enable_if<!std::is_floating_point<T>::value>::type
|
||||
setFlag(const T&, const T&, FlagT& flag) const { flag &= ~FlagT(1); } // unset 1st bit to enable rendering
|
||||
|
||||
template<typename T, typename FlagT>
|
||||
typename std::enable_if<std::is_floating_point<T>::value>::type
|
||||
setFlag(const T& min, const T& max, FlagT& flag) const;
|
||||
|
||||
public:
|
||||
GridStats() = default;
|
||||
|
||||
void update(GridT& grid, ValueT delta = ValueT(0));
|
||||
|
||||
}; // GridStats
|
||||
|
||||
template<typename GridT, typename StatsT>
|
||||
struct GridStats<GridT, StatsT>::NodeStats
|
||||
{
|
||||
StatsT stats;
|
||||
CoordBBox bbox;
|
||||
|
||||
NodeStats(): stats(), bbox() {}//activeCount(0), bbox() {};
|
||||
|
||||
NodeStats& add(const NodeStats &other)
|
||||
{
|
||||
stats.add( other.stats );// no-op for NoopStats?!
|
||||
bbox[0].minComponent(other.bbox[0]);
|
||||
bbox[1].maxComponent(other.bbox[1]);
|
||||
return *this;
|
||||
}
|
||||
};// GridStats::NodeStats
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename GridT, typename StatsT>
|
||||
void GridStats<GridT, StatsT>::update(GridT& grid, ValueT delta)
|
||||
{
|
||||
mDelta = delta; // delta = voxel size for level sets, else 0
|
||||
this->process( grid );
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename GridT, typename StatsT>
|
||||
template<typename DataT, int Rank>
|
||||
inline void GridStats<GridT, StatsT>::
|
||||
setStats(DataT* data, const Extrema<ValueT, Rank>& e)
|
||||
{
|
||||
data->setMin(e.min());
|
||||
data->setMax(e.max());
|
||||
}
|
||||
|
||||
template<typename GridT, typename StatsT>
|
||||
template<typename DataT, int Rank>
|
||||
inline void GridStats<GridT, StatsT>::
|
||||
setStats(DataT* data, const Stats<ValueT, Rank>& s)
|
||||
{
|
||||
data->setMin(s.min());
|
||||
data->setMax(s.max());
|
||||
data->setAvg(s.avg());
|
||||
data->setDev(s.std());
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename GridT, typename StatsT>
|
||||
template<typename T, typename FlagT>
|
||||
inline typename std::enable_if<std::is_floating_point<T>::value>::type
|
||||
GridStats<GridT, StatsT>::
|
||||
setFlag(const T& min, const T& max, FlagT& flag) const
|
||||
{
|
||||
if (mDelta > 0 && (min > mDelta || max < -mDelta)) {// LS: min > dx || max < -dx
|
||||
flag |= FlagT(1u);// set 1st bit to disable rendering
|
||||
} else {
|
||||
flag &= ~FlagT(1u);// unset 1st bit to enable rendering
|
||||
}
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename GridT, typename StatsT>
|
||||
void GridStats<GridT, StatsT>::process( GridT &grid )
|
||||
{
|
||||
this->process( grid.tree() );// this processes tree, root and all nodes
|
||||
|
||||
// set world space AABB
|
||||
auto& data = *grid.data();
|
||||
const auto& indexBBox = grid.tree().root().bbox();
|
||||
if (indexBBox.empty()) {
|
||||
data.mWorldBBox = Vec3dBBox();
|
||||
data.setBBoxOn(false);
|
||||
} else {
|
||||
// Note that below max is offset by one since CoordBBox.max is inclusive
|
||||
// while bbox<Vec3d>.max is exclusive. However, min is inclusive in both
|
||||
// CoordBBox and Vec3dBBox. This also guarantees that a grid with a single
|
||||
// active voxel, does not have an empty world bbox! E.g. if a grid with a
|
||||
// unit index-to-world transformation only contains the active voxel (0,0,0)
|
||||
// then indeBBox = (0,0,0) -> (0,0,0) and then worldBBox = (0.0, 0.0, 0.0)
|
||||
// -> (1.0, 1.0, 1.0). This is a consequence of the different definitions
|
||||
// of index and world bounding boxes inherited from OpenVDB!
|
||||
grid.mWorldBBox = CoordBBox(indexBBox[0], indexBBox[1].offsetBy(1)).transform(grid.map());
|
||||
grid.setBBoxOn(true);
|
||||
}
|
||||
|
||||
// set bit flags
|
||||
data.setMinMaxOn(StatsT::hasMinMax());
|
||||
data.setAverageOn(StatsT::hasAverage());
|
||||
data.setStdDeviationOn(StatsT::hasStdDeviation());
|
||||
} // GridStats::process( Grid )
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename GridT, typename StatsT>
|
||||
inline void GridStats<GridT, StatsT>::process( typename GridT::TreeType &tree )
|
||||
{
|
||||
this->process( tree.root() );
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename GridT, typename StatsT>
|
||||
void GridStats<GridT, StatsT>::process(RootT &root)
|
||||
{
|
||||
using ChildT = Node2;
|
||||
auto &data = *root.data();
|
||||
if (data.mTableSize == 0) { // empty root node
|
||||
data.mMinimum = data.mMaximum = data.mBackground;
|
||||
data.mAverage = data.mStdDevi = 0;
|
||||
data.mBBox = CoordBBox();
|
||||
} else {
|
||||
NodeStats total;
|
||||
for (uint32_t i = 0; i < data.mTableSize; ++i) {
|
||||
auto* tile = data.tile(i);
|
||||
if (tile->isChild()) { // process child node
|
||||
total.add( this->process( *data.getChild(tile) ) );
|
||||
} else if (tile->state) { // active tile
|
||||
const Coord ijk = tile->origin();
|
||||
total.bbox[0].minComponent(ijk);
|
||||
total.bbox[1].maxComponent(ijk + Coord(ChildT::DIM - 1));
|
||||
if (StatsT::hasStats()) { // resolved at compile time
|
||||
total.stats.add(tile->value, ChildT::NUM_VALUES);
|
||||
}
|
||||
}
|
||||
}
|
||||
this->setStats(&data, total.stats);
|
||||
if (total.bbox.empty()) {
|
||||
std::cerr << "\nWarning in GridStats: input tree only contained inactive root tiles!"
|
||||
<< "\nWhile not strictly an error it's rather suspicious!\n";
|
||||
}
|
||||
data.mBBox = total.bbox;
|
||||
}
|
||||
} // GridStats::process( RootNode )
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename GridT, typename StatsT>
|
||||
template<typename NodeT>
|
||||
typename GridStats<GridT, StatsT>::NodeStats
|
||||
GridStats<GridT, StatsT>::process(NodeT &node)
|
||||
{
|
||||
static_assert(util::is_same<NodeT,Node1>::value || util::is_same<NodeT,Node2>::value, "Incorrect node type");
|
||||
using ChildT = typename NodeT::ChildNodeType;
|
||||
|
||||
NodeStats total;
|
||||
auto* data = node.data();
|
||||
|
||||
// Serial processing of active tiles
|
||||
if (const auto tileCount = data->mValueMask.countOn()) {
|
||||
//total.activeCount = tileCount * ChildT::NUM_VALUES; // active tiles
|
||||
for (auto it = data->mValueMask.beginOn(); it; ++it) {
|
||||
if (StatsT::hasStats()) { // resolved at compile time
|
||||
total.stats.add( data->mTable[*it].value, ChildT::NUM_VALUES );
|
||||
}
|
||||
const Coord ijk = node.offsetToGlobalCoord(*it);
|
||||
total.bbox[0].minComponent(ijk);
|
||||
total.bbox[1].maxComponent(ijk + Coord(int32_t(ChildT::DIM) - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Serial or parallel processing of child nodes
|
||||
if (const size_t childCount = data->mChildMask.countOn()) {
|
||||
#ifndef NANOVDB_USE_TBB
|
||||
for (auto it = data->mChildMask.beginOn(); it; ++it) {
|
||||
total.add( this->process( *data->getChild(*it) ) );
|
||||
}
|
||||
#else
|
||||
std::unique_ptr<ChildT*[]> childNodes(new ChildT*[childCount]);
|
||||
ChildT **ptr = childNodes.get();
|
||||
for (auto it = data->mChildMask.beginOn(); it; ++it) {
|
||||
*ptr++ = data->getChild( *it );
|
||||
}
|
||||
using RangeT = tbb::blocked_range<size_t>;
|
||||
total.add( tbb::parallel_reduce(RangeT(0, childCount), NodeStats(),
|
||||
[&](const RangeT &r, NodeStats local)->NodeStats {
|
||||
for(size_t i=r.begin(); i!=r.end(); ++i){
|
||||
local.add( this->process( *childNodes[i] ) );
|
||||
}
|
||||
return local;},
|
||||
[](NodeStats a, const NodeStats &b)->NodeStats { return a.add( b ); }
|
||||
));
|
||||
#endif
|
||||
}
|
||||
|
||||
data->mBBox = total.bbox;
|
||||
if (total.bbox.empty()) {
|
||||
data->mFlags |= uint32_t(1); // set 1st bit on to disable rendering of node
|
||||
data->mFlags &= ~uint32_t(2); // set 2nd bit off since node does not contain active values
|
||||
} else {
|
||||
data->mFlags |= uint32_t(2); // set 2nd bit on since node contains active values
|
||||
if (StatsT::hasStats()) { // resolved at compile time
|
||||
this->setStats(data, total.stats);
|
||||
this->setFlag(data->mMinimum, data->mMaximum, data->mFlags);
|
||||
}
|
||||
}
|
||||
return total;
|
||||
} // GridStats::process( InternalNode )
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename GridT, typename StatsT>
|
||||
typename GridStats<GridT, StatsT>::NodeStats
|
||||
GridStats<GridT, StatsT>::process(Node0 &leaf)
|
||||
{
|
||||
NodeStats local;
|
||||
if (leaf.updateBBox()) {// optionally update active bounding box (updates data->mFlags)
|
||||
local.bbox[0] = local.bbox[1] = leaf.mBBoxMin;
|
||||
local.bbox[1] += Coord(leaf.mBBoxDif[0], leaf.mBBoxDif[1], leaf.mBBoxDif[2]);
|
||||
if (StatsT::hasStats()) {// resolved at compile time
|
||||
for (auto it = leaf.cbeginValueOn(); it; ++it) local.stats.add(*it);
|
||||
this->setStats(&leaf, local.stats);
|
||||
this->setFlag(leaf.getMin(), leaf.getMax(), leaf.mFlags);
|
||||
}
|
||||
}
|
||||
return local;
|
||||
} // GridStats::process( LeafNode )
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename BuildT>
|
||||
void updateGridStats(NanoGrid<BuildT>* grid, StatsMode mode)
|
||||
{
|
||||
NANOVDB_ASSERT(grid);
|
||||
using GridT = NanoGrid<BuildT>;
|
||||
using ValueT = typename GridT::ValueType;
|
||||
if (mode == StatsMode::Disable) {
|
||||
return;
|
||||
} else if (mode == StatsMode::BBox || util::is_same<bool, ValueT>::value) {
|
||||
GridStats<GridT, NoopStats<ValueT> > stats;
|
||||
stats.update(*grid);
|
||||
} else if (mode == StatsMode::MinMax) {
|
||||
GridStats<GridT, Extrema<ValueT> > stats;
|
||||
stats.update(*grid);
|
||||
} else if (mode == StatsMode::All) {
|
||||
GridStats<GridT, Stats<ValueT> > stats;
|
||||
stats.update(*grid);
|
||||
} else {
|
||||
throw std::runtime_error("gridStats: Unsupported statistics mode.");
|
||||
}
|
||||
}// updateGridStats
|
||||
|
||||
template<typename BuildT>
|
||||
[[deprecated("Use nanovdb::tools::updateGridStats(NanoGrid*, StatsMode) instead")]]
|
||||
void gridStats(NanoGrid<BuildT>& grid, StatsMode mode = StatsMode::Default)
|
||||
{
|
||||
updateGridStats<BuildT>(&grid, mode);
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
|
||||
namespace {
|
||||
|
||||
// returns a bitmask (of size 32^3 or 16^3) that marks all the entries
|
||||
// in a node table that intersects with the specified bounding box.
|
||||
template<typename NodeT>
|
||||
Mask<NodeT::LOG2DIM> getBBoxMask(const CoordBBox &bbox, const NodeT* node)
|
||||
{
|
||||
Mask<NodeT::LOG2DIM> mask;// typically 32^3 or 16^3 bit mask
|
||||
auto b = CoordBBox::createCube(node->origin(), node->dim());
|
||||
assert( bbox.hasOverlap(b) );
|
||||
if ( bbox.isInside(b) ) {
|
||||
mask.setOn();//node is completely inside the bbox so early out
|
||||
} else {
|
||||
b.intersect(bbox);// trim bounding box
|
||||
// transform bounding box from global to local coordinates
|
||||
b.min() &= NodeT::DIM-1u;
|
||||
b.min() >>= NodeT::ChildNodeType::TOTAL;
|
||||
b.max() &= NodeT::DIM-1u;
|
||||
b.max() >>= NodeT::ChildNodeType::TOTAL;
|
||||
assert( !b.empty() );
|
||||
auto it = b.begin();// iterates over all the child nodes or tiles that intersects bbox
|
||||
for (const Coord& ijk = *it; it; ++it) {
|
||||
mask.setOn(ijk[2] + (ijk[1] << NodeT::LOG2DIM) + (ijk[0] << 2*NodeT::LOG2DIM));
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}// getBBoxMask
|
||||
|
||||
}// end of unnamed namespace
|
||||
|
||||
/// @brief return the extrema of all the values in a grid that
|
||||
/// intersects the specified bounding box.
|
||||
template<typename BuildT>
|
||||
Extrema<typename NanoGrid<BuildT>::ValueType>
|
||||
getExtrema(const NanoGrid<BuildT>& grid, const CoordBBox &bbox)
|
||||
{
|
||||
using GridT = NanoGrid<BuildT>;
|
||||
using ValueT = typename GridT::ValueType;
|
||||
using TreeT = typename GridTree<GridT>::type;
|
||||
using RootT = typename NodeTrait<TreeT, 3>::type;// root node
|
||||
using Node2 = typename NodeTrait<TreeT, 2>::type;// upper internal node
|
||||
using Node1 = typename NodeTrait<TreeT, 1>::type;// lower internal node
|
||||
using Node0 = typename NodeTrait<TreeT, 0>::type;// leaf node
|
||||
|
||||
Extrema<ValueT> extrema;
|
||||
const RootT &root = grid.tree().root();
|
||||
const auto &bbox3 = root.bbox();
|
||||
if (bbox.isInside(bbox3)) {// bbox3 is contained inside bbox
|
||||
extrema.min(root.minimum());
|
||||
extrema.max(root.maximum());
|
||||
extrema.add(root.background());
|
||||
} else if (bbox.hasOverlap(bbox3)) {
|
||||
const auto *data3 = root.data();
|
||||
for (uint32_t i=0; i<data3->mTableSize; ++i) {
|
||||
const auto *tile = data3->tile(i);
|
||||
CoordBBox bbox2 = CoordBBox::createCube(tile->origin(), Node2::dim());
|
||||
if (!bbox.hasOverlap(bbox2)) continue;
|
||||
if (tile->isChild()) {
|
||||
const Node2 *node2 = data3->getChild(tile);
|
||||
if (bbox.isInside(bbox2)) {
|
||||
extrema.min(node2->minimum());
|
||||
extrema.max(node2->maximum());
|
||||
} else {// partial intersections at level 2
|
||||
auto *data2 = node2->data();
|
||||
const auto bboxMask2 = getBBoxMask(bbox, node2);
|
||||
for (auto it2 = bboxMask2.beginOn(); it2; ++it2) {
|
||||
if (data2->mChildMask.isOn(*it2)) {
|
||||
const Node1* node1 = data2->getChild(*it2);
|
||||
CoordBBox bbox1 = CoordBBox::createCube(node1->origin(), Node1::dim());
|
||||
if (bbox.isInside(bbox1)) {
|
||||
extrema.min(node1->minimum());
|
||||
extrema.max(node1->maximum());
|
||||
} else {// partial intersection at level 1
|
||||
auto *data1 = node1->data();
|
||||
const auto bboxMask1 = getBBoxMask(bbox, node1);
|
||||
for (auto it1 = bboxMask1.beginOn(); it1; ++it1) {
|
||||
if (data1->mChildMask.isOn(*it1)) {
|
||||
const Node0* node0 = data1->getChild(*it1);
|
||||
CoordBBox bbox0 = CoordBBox::createCube(node0->origin(), Node0::dim());
|
||||
if (bbox.isInside(bbox0)) {
|
||||
extrema.min(node0->minimum());
|
||||
extrema.max(node0->maximum());
|
||||
} else {// partial intersection at level 0
|
||||
auto *data0 = node0->data();
|
||||
const auto bboxMask0 = getBBoxMask(bbox, node0);
|
||||
for (auto it0 = bboxMask0.beginOn(); it0; ++it0) {
|
||||
extrema.add(data0->getValue(*it0));
|
||||
}
|
||||
}// end partial intersection at level 0
|
||||
} else {// tile at level 1
|
||||
extrema.add(data1->mTable[*it1].value);
|
||||
}
|
||||
}
|
||||
}// end of partial intersection at level 1
|
||||
} else {// tile at level 2
|
||||
extrema.add(data2->mTable[*it2].value);
|
||||
}
|
||||
}// loop over tiles and nodes at level 2
|
||||
}// end of partial intersection at level 1
|
||||
} else {// tile at root level
|
||||
extrema.add(tile->value);
|
||||
}
|
||||
}// loop over root table
|
||||
} else {// bbox does not overlap the grid
|
||||
extrema.add(root.background());
|
||||
}
|
||||
return extrema;
|
||||
}// getExtrema
|
||||
|
||||
}// namespace tools
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_TOOLS_GRIDSTATS_H_HAS_BEEN_INCLUDED
|
||||
@@ -1,244 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/GridValidator.h
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date August 30, 2020
|
||||
|
||||
\brief Checks the validity of an existing NanoVDB grid.
|
||||
|
||||
\note before v32.6.0: checksum[0] = Grid+Tree+Root, checksum[1] = nodes
|
||||
after v32.6.0: checksum[0] = Grid+Tree, checksum[1] = nodes + blind data in 4K blocks
|
||||
|
||||
When serialized:
|
||||
[Grid,Tree][Root][ROOT TILES...][Node<5>...][Node<4>...][Leaf<3>...][BlindMeta...][BlindData...]
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_TOOLS_GRID_VALIDATOR_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_TOOLS_GRID_VALIDATOR_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <iostream> // for std::cerr
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/tools/GridChecksum.h>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools {
|
||||
|
||||
/// @brief Performs several validation tests on a grid pointer.
|
||||
/// @tparam ValueT Build type of the input grid
|
||||
/// @param grid const point to the grid that needs validation
|
||||
/// @param mode Mode of the validation check (defined in GridChecksum.h)
|
||||
/// @param verbose If true information about the first failed test is printed to std::cerr
|
||||
/// @return Return true if the specified grid passes several validation tests.
|
||||
template <typename ValueT>
|
||||
bool isValid(const NanoGrid<ValueT> *grid, CheckMode mode, bool verbose = false);
|
||||
|
||||
/// @brief Return true if the specified grid passes several validation tests.
|
||||
/// @tparam ValueT Build type of the input grid
|
||||
/// @param grid Grid to validate
|
||||
/// @param detailed If true the validation test is detailed and relatively slow.
|
||||
/// @param verbose If true information about the first failed test is printed to std::cerr
|
||||
/// @note This method has been deprecated by the one defined above
|
||||
template <typename ValueT>
|
||||
[[deprecated("Use isValue(const NanoGrid<ValueT>*, CheckMode, bool) instead.")]]
|
||||
bool isValid(const NanoGrid<ValueT> &grid, bool detailed = true, bool verbose = false)
|
||||
{
|
||||
return isValid(&grid, detailed ? CheckMode::Full : CheckMode::Half, verbose);
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
|
||||
/// @brief validate grid
|
||||
template<typename ValueT>
|
||||
__hostdev__ char* checkGrid(const NanoGrid<ValueT> *grid, char *error, CheckMode mode = CheckMode::Full)
|
||||
{
|
||||
*error = '\0';// reset error string
|
||||
char str[32];// temporary buffer for toStr
|
||||
|
||||
// check Grid
|
||||
if (grid == nullptr) {
|
||||
return util::sprint(error, "Invalid pointer: Grid is NULL");
|
||||
} else if (!isAligned(grid)) {
|
||||
return util::sprint(error, "Invalid pointer: Grid is misaligned");
|
||||
} else if (grid->mMagic != NANOVDB_MAGIC_NUMB && grid->mMagic != NANOVDB_MAGIC_GRID) {
|
||||
return util::sprint(error, "Invalid magic number: ", toStr(str, toMagic(grid->mMagic)));
|
||||
} else if (!grid->mVersion.isCompatible()) {
|
||||
return util::sprint(error, "Incompatible version number: ", toStr(str, grid->mVersion));
|
||||
} else if (grid->mGridCount == 0) {
|
||||
return util::sprint(error, "Zero grid count");
|
||||
} else if (grid->mGridIndex >= grid->mGridCount) {
|
||||
return util::sprint(error, "grid index(", int(grid->mGridIndex), ") >= grid count(", int(grid->mGridCount), ")");
|
||||
} else if (grid->mGridClass >= GridClass::End) {
|
||||
return util::sprint(error, "Invalid GridClass(", toStr(str, grid->mGridClass), ")");
|
||||
} else if (grid->mGridType >= GridType::End) {
|
||||
return util::sprint(error, "Invalid GridType(", toStr(str, grid->mGridType), ")");
|
||||
} else if (grid->mGridType != toGridType<ValueT>()) {
|
||||
return util::sprint(error, "Invalid combination of BuildType(", toStr(str, toGridType<ValueT>()), ") and GridType(", toStr(str+16, grid->mGridType), ")");
|
||||
} else if (!isValid(grid->mGridType, grid->mGridClass)) {
|
||||
return util::sprint(error, "Invalid combination of GridType(", toStr(str, grid->mGridType), ") and GridClass(", toStr(str+16,grid->mGridClass), ")");
|
||||
}
|
||||
|
||||
// check Tree
|
||||
auto &tree = grid->tree();
|
||||
if (auto *p = tree.getRoot()) {
|
||||
if (!isAligned(p)) return util::strcpy(error, "Invalid pointer: Root is misaligned");
|
||||
} else {
|
||||
return util::strcpy(error, "Invalid pointer: Root is NULL");
|
||||
}
|
||||
|
||||
// check Root
|
||||
auto &root = tree.root();
|
||||
auto *rootData = root.data();
|
||||
if (rootData == nullptr) {
|
||||
return util::strcpy(error, "Invalid pointer: Root is NULL");
|
||||
} else if (!isAligned((const void*)rootData)) {
|
||||
return util::strcpy(error, "Invalid pointer: Root is misaligned");
|
||||
} else if ( (const uint8_t*)(rootData) < (const uint8_t*)(&tree+1)) {
|
||||
return util::strcpy(error, "Invalid root pointer (should be located after the Grid and Tree)");
|
||||
} else if ( (const void*)(rootData) > util::PtrAdd(rootData, root.memUsage())) {
|
||||
return util::strcpy(error, "Invalid root pointer (appears to be located after the end of the buffer)");
|
||||
} else {// check root tiles
|
||||
const void *bounds[2] = {rootData + 1, util::PtrAdd(rootData, root.memUsage())};
|
||||
for (uint32_t i = 0; i<rootData->mTableSize; ++i) {
|
||||
const void *tile = rootData->tile(i);
|
||||
if ( tile < bounds[0] ) {
|
||||
return util::strcpy(error, "Invalid root tile pointer (below lower bound");
|
||||
} else if (tile >= bounds[1]) {
|
||||
return util::strcpy(error, "Invalid root tile pointer (above higher bound");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mode == CheckMode::Half) return error;
|
||||
|
||||
// check nodes
|
||||
const bool test = grid->isBreadthFirst();
|
||||
auto *n0 = tree.template getFirstNode<0>();
|
||||
auto *n1 = tree.template getFirstNode<1>();
|
||||
auto *n2 = tree.template getFirstNode<2>();
|
||||
const void *bounds[3][2] = {{n0, util::PtrAdd(n0, grid->gridSize())}, {n1, n0}, {n2, n1}};
|
||||
|
||||
auto check = [&](const void *ptr, int level) -> bool {
|
||||
if (ptr==nullptr) {
|
||||
util::strcpy(error, "Invalid node pointer: node is NULL");
|
||||
} else if (!isAligned(ptr)) {
|
||||
util::strcpy(error, "Invalid node pointer: node is misaligned");
|
||||
} else if (test && level == 0 && (const void*)(n0++) != ptr) {
|
||||
util::strcpy(error, "Leaf node is not stored breadth-first");
|
||||
} else if (test && level == 1 && (const void*)(n1++) != ptr) {
|
||||
util::strcpy(error, "Lower node is not stored breadth-first");
|
||||
} else if (test && level == 2 && (const void*)(n2++) != ptr) {
|
||||
util::strcpy(error, "Upper node is not stored breadth-first");
|
||||
} else if ( ptr < bounds[level][0] ) {
|
||||
util::strcpy(error, "Invalid node pointer: below lower bound");
|
||||
} else if ( ptr >= bounds[level][1] ) {
|
||||
util::strcpy(error, "Invalid node pointer: above higher bound");
|
||||
}
|
||||
return !util::empty(error);
|
||||
};
|
||||
|
||||
for (auto it2 = root.cbeginChild(); it2; ++it2) {
|
||||
if (check(&*it2, 2)) return error;
|
||||
for (auto it1 = it2->cbeginChild(); it1; ++it1) {
|
||||
if (check(&*it1, 1)) return error;
|
||||
for (auto it0 = it1->cbeginChild(); it0; ++it0) if (check(&*it0, 0)) return error;
|
||||
}// loop over child nodes of the upper internal node
|
||||
}// loop over child nodes of the root node
|
||||
|
||||
return error;
|
||||
} // checkGrid
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template <typename ValueT>
|
||||
bool isValid(const NanoGrid<ValueT> *grid, CheckMode mode, bool verbose)
|
||||
{
|
||||
std::unique_ptr<char[]> strUP(new char[100]);
|
||||
char *str = strUP.get();
|
||||
|
||||
tools::checkGrid(grid, str, mode);
|
||||
|
||||
if (util::empty(str) && !validateChecksum(grid, mode)) util::strcpy(str, "Mis-matching checksum");
|
||||
if (verbose && !util::empty(str)) std::cerr << "Validation failed: " << str << std::endl;
|
||||
|
||||
return util::empty(str);
|
||||
}// isValid
|
||||
|
||||
//================================================================================================
|
||||
|
||||
struct IsNanoGridValid {
|
||||
template <typename BuildT>
|
||||
static bool known(const GridData *gridData, CheckMode mode, bool verbose)
|
||||
{
|
||||
return tools::isValid((const NanoGrid<BuildT>*)gridData, mode, verbose);
|
||||
}
|
||||
static bool unknown(const GridData *gridData, CheckMode, bool verbose)
|
||||
{
|
||||
if (verbose) {
|
||||
char str[16];
|
||||
std::cerr << "Unsupported GridType: \"" << toStr(str, gridData->mGridType) << "\"\n" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};// IsNanoGridValid
|
||||
|
||||
/// @brief Validate a specific grid in a GridHandle
|
||||
/// @tparam GridHandleT Type of GridHandle
|
||||
/// @param handle GridHandle containing host grids
|
||||
/// @param gridID linear index of the grid to be validated
|
||||
/// @param mode node of validation tests
|
||||
/// @param verbose if true information is printed if the grid fails a validation test
|
||||
/// @return true if grid @c gridID passes all the validation tests
|
||||
template <typename GridHandleT>
|
||||
bool validateGrid(const GridHandleT &handle, uint32_t gridID, CheckMode mode, bool verbose)
|
||||
{
|
||||
if (mode == CheckMode::Disable) {
|
||||
return true;
|
||||
} else if (gridID >= handle.gridCount()) {
|
||||
if (verbose) std::cerr << "grid index " << gridID << " exceeds available grid count " << handle.gridCount() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return callNanoGrid<IsNanoGridValid>(handle.gridData(gridID), mode, verbose);
|
||||
}// validateGrid
|
||||
|
||||
//================================================================================================
|
||||
|
||||
/// @brief Validate all the grids in a GridHandle
|
||||
/// @tparam GridHandleT Type of GridHandle
|
||||
/// @param handle GridHandle containing host grids (0,1...,N)
|
||||
/// @param mode node of validation tests
|
||||
/// @param verbose if true information is printed if a grid fails a validation test
|
||||
/// @return true if all grids pass alle the validation tests
|
||||
template <typename GridHandleT>
|
||||
bool validateGrids(const GridHandleT &handle, CheckMode mode, bool verbose)
|
||||
{
|
||||
if (mode == CheckMode::Disable) return true;
|
||||
for (uint32_t gridID=0; gridID<handle.gridCount(); ++gridID) {
|
||||
if (!validateGrid(handle, gridID, mode, verbose)) return false;
|
||||
}
|
||||
return true;
|
||||
}// validateGrids
|
||||
|
||||
}// namespace tools
|
||||
|
||||
template<typename ValueT>
|
||||
[[deprecated("Use nanovdb:tools::checkGrid instead.")]]
|
||||
__hostdev__ char* checkGrid(const NanoGrid<ValueT> *grid, char *error, CheckMode mode = CheckMode::Full)
|
||||
{
|
||||
return tools::checkGrid<ValueT>(grid, error, mode);
|
||||
}
|
||||
|
||||
template <typename ValueT>
|
||||
[[deprecated("Use nanovdb:tools::isValid instead.")]]
|
||||
bool isValid(const NanoGrid<ValueT> *grid, CheckMode mode, bool verbose = false)
|
||||
{
|
||||
return tools::isValid<ValueT>(grid, mode, verbose);
|
||||
}
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_TOOLS_GRID_VALIDATOR_H_HAS_BEEN_INCLUDED
|
||||
@@ -1,366 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/NanoToOpenVDB.h
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date May 6, 2020
|
||||
|
||||
\brief This class will deserialize an NanoVDB grid into an OpenVDB grid.
|
||||
|
||||
\todo Add support for PointIndexGrid and PointDataGrid
|
||||
*/
|
||||
|
||||
#include <nanovdb/NanoVDB.h> // manages and streams the raw memory buffer of a NanoVDB grid.
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/util/ForEach.h>
|
||||
|
||||
#include <openvdb/openvdb.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);
|
||||
|
||||
/// @brief Forward declaration of free-standing function that de-serializes a NanoVDB GridHandle into an OpenVDB GridBase
|
||||
template<typename BufferT>
|
||||
openvdb::GridBase::Ptr
|
||||
nanoToOpenVDB(const GridHandle<BufferT>& handle, int verbose = 0, uint32_t n = 0);
|
||||
|
||||
/// @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);
|
||||
|
||||
private:
|
||||
|
||||
template<typename NanoNodeT, typename OpenNodeT>
|
||||
OpenNodeT* processNode(const NanoNodeT*);
|
||||
|
||||
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 <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 <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);}
|
||||
|
||||
}; // 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();
|
||||
mat.setMat3(openvdb::math::Mat3<double>(nanoMap.mMatD));
|
||||
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
|
||||
auto &root = dstGrid->tree().root();
|
||||
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)) );
|
||||
} else {
|
||||
root.addTile(tile->origin(), Convert(tile->value), tile->state);
|
||||
}
|
||||
}
|
||||
|
||||
return dstGrid;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename SrcNodeT, typename DstNodeT>
|
||||
DstNodeT*
|
||||
NanoToOpenVDB<T>::processNode(const SrcNodeT *srcNode)
|
||||
{
|
||||
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();
|
||||
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(Convert(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) );
|
||||
}
|
||||
};
|
||||
|
||||
#if 0
|
||||
kernel(Range1D(0, childCount));
|
||||
#else
|
||||
util::forEach(0, childCount, 1, kernel);
|
||||
#endif
|
||||
return dstNode;
|
||||
} // processNode
|
||||
|
||||
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)
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
return dstNode;
|
||||
} // process(NanoNode0)
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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());
|
||||
|
||||
return dstNode;
|
||||
} // process(NanoNode0)
|
||||
|
||||
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)
|
||||
{
|
||||
NanoToOpenVDB<NanoBuildT> tmp;
|
||||
return tmp(grid, verbose);
|
||||
}
|
||||
|
||||
template<typename BufferT>
|
||||
openvdb::GridBase::Ptr
|
||||
nanoToOpenVDB(const GridHandle<BufferT>& handle, int verbose, uint32_t n)
|
||||
{
|
||||
if (auto grid = handle.template grid<float>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<double>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<int32_t>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} else if (auto grid = handle.template grid<int64_t>(n)) {
|
||||
return tools::nanoToOpenVDB(*grid, verbose);
|
||||
} 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);
|
||||
} else {
|
||||
OPENVDB_THROW(openvdb::RuntimeError, "Unsupported NanoVDB grid type!");
|
||||
}
|
||||
}// tools::nanoToOpenVDB
|
||||
|
||||
}// namespace tools
|
||||
|
||||
/// @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)
|
||||
{
|
||||
return tools::nanoToOpenVDB(grid, verbose);
|
||||
}
|
||||
|
||||
/// @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)
|
||||
{
|
||||
return tools::nanoToOpenVDB(handle, verbose, n);
|
||||
}
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_TOOLS_NANOTOOPENVDB_H_HAS_BEEN_INCLUDED
|
||||
@@ -1,146 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/AddBlindData.cuh
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date August 3, 2023
|
||||
|
||||
\brief Defines function that appends blind device data to and existing device NanoGrid
|
||||
|
||||
\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_ADDBLINDDATA_CUH_HAS_BEEN_INCLUDED
|
||||
#define NVIDIA_TOOLS_CUDA_ADDBLINDDATA_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
#include <nanovdb/tools/GridChecksum.h>
|
||||
#include <nanovdb/tools/cuda/GridChecksum.cuh>
|
||||
|
||||
#include <cstring> // for std::strcpy
|
||||
|
||||
namespace nanovdb {// ================================================
|
||||
|
||||
namespace tools::cuda {// ============================================
|
||||
|
||||
/// @brief This function appends blind data to and existing NanoGrid
|
||||
/// @tparam BuildT Build type of the grid
|
||||
/// @tparam BlindDataT Type of the blind data
|
||||
/// @tparam BufferT Type of the buffer used for allocation
|
||||
/// @param d_grid Pointer to device grid
|
||||
/// @param d_blindData Pointer to device blind data
|
||||
/// @param valueCount number of values in the blind data
|
||||
/// @param blindClass class of the blind data
|
||||
/// @param semantics semantics of the blind data
|
||||
/// @param name optional name of the blind data
|
||||
/// @param pool optional pool used for allocation
|
||||
/// @param stream optional CUDA stream (defaults to CUDA stream 0)
|
||||
/// @return GridHandle with blind data appended
|
||||
template<typename BuildT, typename BlindDataT, typename BufferT = nanovdb::cuda::DeviceBuffer>
|
||||
GridHandle<BufferT>
|
||||
addBlindData(const NanoGrid<BuildT> *d_grid,
|
||||
const BlindDataT *d_blindData,
|
||||
uint64_t valueCount,
|
||||
GridBlindDataClass blindClass = GridBlindDataClass::Unknown,
|
||||
GridBlindDataSemantic semantics = GridBlindDataSemantic::Unknown,
|
||||
const char *name = "",
|
||||
const BufferT &pool = BufferT(),
|
||||
cudaStream_t stream = 0)
|
||||
{
|
||||
// In: |-----------|--------- |-----------|
|
||||
// old grid old meta old data
|
||||
// Out: |-----------|----------|----------|-----------|------------|
|
||||
// old grid old meta new meta old data new data
|
||||
|
||||
static_assert(BufferTraits<BufferT>::hasDeviceDual, "Expected BufferT to support device allocation");
|
||||
|
||||
// extract byte sizes of the grid, blind meta data and blind data
|
||||
enum {GRID=0, META=1, DATA=2, CHECKSUM=3};
|
||||
uint64_t tmp[4], *d_tmp;
|
||||
cudaCheck(util::cuda::mallocAsync((void**)&d_tmp, 4*sizeof(uint64_t), stream));
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, stream>>>(1, [=] __device__(size_t) {
|
||||
if (auto count = d_grid->blindDataCount()) {
|
||||
d_tmp[GRID] = util::PtrDiff(&d_grid->blindMetaData(0), d_grid);
|
||||
d_tmp[META] = count*sizeof(GridBlindMetaData);
|
||||
d_tmp[DATA] = d_grid->gridSize() - d_tmp[GRID] - d_tmp[META];
|
||||
} else {
|
||||
d_tmp[GRID] = d_grid->gridSize();
|
||||
d_tmp[META] = d_tmp[DATA] = 0u;
|
||||
}
|
||||
d_tmp[CHECKSUM] = d_grid->checksum().full();
|
||||
}); cudaCheckError();
|
||||
cudaCheck(cudaMemcpyAsync(&tmp, d_tmp, 4*sizeof(uint64_t), cudaMemcpyDeviceToHost, stream));
|
||||
|
||||
GridBlindMetaData metaData{int64_t(sizeof(GridBlindMetaData) + tmp[DATA]), valueCount,
|
||||
sizeof(BlindDataT), semantics, blindClass, toGridType<BlindDataT>()};
|
||||
if (!metaData.isValid()) throw std::runtime_error("cudaAddBlindData: invalid combination of blind meta data");
|
||||
std::strcpy(metaData.mName, name);
|
||||
auto buffer = BufferT::create(tmp[GRID] + tmp[META] + sizeof(GridBlindMetaData) + tmp[DATA] + metaData.blindDataSize(), &pool, false);
|
||||
void *d_data = buffer.deviceData();
|
||||
|
||||
// 1: |-----------|----------|
|
||||
// old grid old meta
|
||||
cudaCheck(cudaMemcpyAsync(d_data, d_grid, tmp[GRID] + tmp[META], cudaMemcpyDeviceToDevice, stream));
|
||||
|
||||
// 2: |-----------|----------|----------|
|
||||
// old grid old meta new meta
|
||||
cudaCheck(cudaMemcpyAsync((char*)d_data + tmp[GRID] + tmp[META], &metaData, sizeof(GridBlindMetaData), cudaMemcpyHostToDevice, stream));
|
||||
|
||||
// 3: |-----------|----------|----------|-----------|
|
||||
// old grid old meta new meta old data
|
||||
cudaCheck(cudaMemcpyAsync((char*)d_data + tmp[GRID] + tmp[META] + sizeof(GridBlindMetaData),
|
||||
(const char*)d_grid + tmp[GRID] + tmp[META], tmp[DATA], cudaMemcpyDeviceToDevice, stream));
|
||||
|
||||
// 4: |-----------|----------|----------|-----------|------------|
|
||||
// old grid old meta new meta old data new data
|
||||
const size_t dataSize = valueCount*sizeof(BlindDataT);// no padding
|
||||
cudaCheck(cudaMemcpyAsync((char*)d_data + tmp[GRID] + tmp[META] + sizeof(GridBlindMetaData) + tmp[DATA],
|
||||
d_blindData, dataSize, cudaMemcpyDeviceToDevice, stream));
|
||||
if (auto padding = metaData.blindDataSize() - dataSize) {// zero out possible padding
|
||||
cudaCheck(cudaMemsetAsync((char*)d_data + tmp[GRID] + tmp[META] + sizeof(GridBlindMetaData) + tmp[DATA] + dataSize, 0, padding, stream));
|
||||
}
|
||||
|
||||
// increment grid size and blind data counter in output grid
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, stream>>>(1, [=] __device__(size_t) {
|
||||
auto &grid = *reinterpret_cast<NanoGrid<BuildT>*>(d_data);
|
||||
grid.mBlindMetadataCount += 1;
|
||||
grid.mBlindMetadataOffset = d_tmp[GRID];
|
||||
auto *meta = util::PtrAdd<GridBlindMetaData>(d_data, grid.mBlindMetadataOffset);// points to first blind meta data
|
||||
for (uint32_t i=0, n=grid.mBlindMetadataCount-1; i<n; ++i, ++meta) meta->mDataOffset += sizeof(GridBlindMetaData);
|
||||
grid.mGridSize += sizeof(GridBlindMetaData) + meta->blindDataSize();// expansion with 32 byte alignment
|
||||
}); cudaCheckError();
|
||||
cudaCheck(util::cuda::freeAsync(d_tmp, stream));
|
||||
|
||||
Checksum cs(tmp[CHECKSUM]);
|
||||
cuda::updateChecksum(reinterpret_cast<GridData*>(d_data), cs.mode(), stream);
|
||||
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
}// cudaAddBlindData
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
template<typename BuildT, typename BlindDataT, typename BufferT = cuda::DeviceBuffer>
|
||||
[[deprecated("Use nanovdb::cuda::addBlindData instead")]]
|
||||
GridHandle<BufferT>
|
||||
cudaAddBlindData(const NanoGrid<BuildT> *d_grid,
|
||||
const BlindDataT *d_blindData,
|
||||
uint64_t valueCount,
|
||||
GridBlindDataClass blindClass = GridBlindDataClass::Unknown,
|
||||
GridBlindDataSemantic semantics = GridBlindDataSemantic::Unknown,
|
||||
const char *name = "",
|
||||
const BufferT &pool = BufferT(),
|
||||
cudaStream_t stream = 0)
|
||||
{
|
||||
return tools::cuda::addBlindData<BuildT, BlindDataT, BufferT>(d_grid, d_blindData, valueCount, blindClass, semantics, name, pool, stream);
|
||||
}
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NVIDIA_TOOLS_CUDA_ADDBLINDDATA_CUH_HAS_BEEN_INCLUDED
|
||||
@@ -1,441 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/GridChecksum.cuh
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date September 28, 2023
|
||||
|
||||
\brief Compute CRC32 checksum of NanoVDB grids
|
||||
|
||||
\note before v32.6.0: checksum[0] = Grid+Tree+Root, checksum[1] = nodes
|
||||
after v32.6.0: checksum[0] = Grid+Tree, checksum[1] = nodes + blind data in 4K blocks
|
||||
|
||||
When serialized:
|
||||
[Grid,Tree][Root][ROOT TILES...][Node<5>...][Node<4>...][Leaf<3>...][BlindMeta...][BlindData...]
|
||||
checksum[2] before v32.6.0: <------------- [0] ------------><-------------- [1] --------------->
|
||||
checksum[]2 after v32.6.0: <---[0]---><----------------------------------------[1]---------------------------------------->
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_TOOLS_CUDA_GRIDCHECKSUM_CUH_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_TOOLS_CUDA_GRIDCHECKSUM_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>// required for instantiation of move c-tor of GridHandle
|
||||
#include <nanovdb/cuda/NodeManager.cuh>
|
||||
#include <nanovdb/tools/GridChecksum.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
|
||||
namespace nanovdb {// =======================================================================
|
||||
|
||||
namespace tools::cuda {// ===================================================================
|
||||
|
||||
/// @brief Compute the (2 x CRC32) checksum of the specified @c d_gridData on the device
|
||||
/// @param d_gridData Device base pointer to the grid from which the checksum is computed.
|
||||
/// @param mode Defines the mode of computation for the checksum.
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
/// @return Return the (2 x CRC32) checksum of the specified @c d_gridData
|
||||
Checksum evalChecksum(const GridData *d_gridData, CheckMode mode = CheckMode::Default, cudaStream_t stream = 0);
|
||||
|
||||
/// @brief Extract the checksum of a device grid
|
||||
/// @param d_gridData Device basepointer to grid with a checksum
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
/// @return Checksum encoded in the specified grid
|
||||
Checksum getChecksum(const GridData *d_gridData, cudaStream_t stream = 0);
|
||||
|
||||
/// @brief Return true if the checksum of @c d_gridData matches the expected
|
||||
/// value already encoded into the grid's meta data.
|
||||
/// @tparam BuildT Template parameter used to build NanoVDB grid.
|
||||
/// @param d_gridData Grid whose checksum is validated.
|
||||
/// @param mode Defines the mode of computation for the checksum.
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
bool validateChecksum(const GridData *d_gridData, CheckMode mode = CheckMode::Default, cudaStream_t stream = 0);
|
||||
|
||||
/// @brief Update the checksum of a device grid
|
||||
/// @param d_gridData device pointer to GridData
|
||||
/// @param mode Mode of computation for the checksum.
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
void updateChecksum(GridData *d_gridData, CheckMode mode, cudaStream_t stream = 0);
|
||||
|
||||
/// @brief Updates the checksum of a device grid by preserving its mode
|
||||
/// @param d_gridData Device base pointer to grid
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
inline void updateChecksum(GridData *d_gridData, cudaStream_t stream = 0)
|
||||
{
|
||||
updateChecksum(d_gridData, getChecksum(d_gridData, stream).mode(), stream);
|
||||
}
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
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_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
|
||||
/// @param d_lut device pointer to CRC32 Lookup Table
|
||||
template <typename T>
|
||||
__global__ void crc32Kernel(const T *d_data, uint32_t* d_blockCRC, uint32_t blockCount, uint32_t blockSize, const uint32_t *d_lut)
|
||||
{
|
||||
const uint32_t tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < blockCount) d_blockCRC[tid] = crc32((const uint8_t*)d_data + tid * blockSize, blockSize, d_lut);
|
||||
}
|
||||
|
||||
/// @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_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
|
||||
template <typename T>
|
||||
__global__ void crc32Kernel(const T *d_data, uint32_t* d_blockCRC, uint32_t blockCount, uint32_t blockSize)
|
||||
{
|
||||
const uint32_t tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < blockCount) d_blockCRC[tid] = crc32((const uint8_t*)d_data + tid * blockSize, blockSize);
|
||||
}
|
||||
|
||||
/// @brief Host function to allocate and initiate a Look-Up-Table of size 256 for subsequent CRC32 computation on the device
|
||||
/// @param extra number of extra elements in the LUT
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
/// @return returns a nanovdb::util::cuda::unique_ptr point to a lookup-table for CRC32 computation
|
||||
inline unique_ptr<uint32_t> createCrc32Lut(size_t extra = 0, cudaStream_t stream = 0)
|
||||
{
|
||||
unique_ptr<uint32_t> lut(256 + extra, stream);
|
||||
uint32_t *d_lut = lut.get();
|
||||
lambdaKernel<<<1, 256, 0, stream>>>(256, [=] __device__(size_t tid) {initCrc32Lut(d_lut, tid);});
|
||||
cudaCheckError();
|
||||
return lut;
|
||||
}
|
||||
|
||||
/// @brief Compute CRC32 checksum of 4K block
|
||||
/// @param d_data device pointer to start of data
|
||||
/// @param size number of bytes
|
||||
/// @param d_lut Look-Up-Table for CRC32 computation
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
inline void blockedCRC32(const void *d_data, size_t size, const uint32_t *d_lut, uint32_t *d_crc, cudaStream_t stream)
|
||||
{
|
||||
NANOVDB_ASSERT(d_data && d_lut && d_crc);
|
||||
static constexpr unsigned int threadsPerBlock = 128;// seems faster than the old value of 256!
|
||||
const uint64_t checksumCount = size >> NANOVDB_CRC32_LOG2_BLOCK_SIZE;// 4 KB (4096 byte)
|
||||
unique_ptr<uint32_t> buffer(checksumCount, stream);// for checksums of 4 KB blocks
|
||||
uint32_t *d_checksums = buffer.get();
|
||||
lambdaKernel<<<blocksPerGrid(checksumCount, threadsPerBlock), threadsPerBlock, 0, stream>>>(checksumCount, [=] __device__(size_t tid) {
|
||||
uint32_t blockSize = 1 << NANOVDB_CRC32_LOG2_BLOCK_SIZE;
|
||||
if (tid+1 == checksumCount) blockSize += size - (checksumCount<<NANOVDB_CRC32_LOG2_BLOCK_SIZE);
|
||||
d_checksums[tid] = crc32((const uint8_t*)d_data + (tid<<NANOVDB_CRC32_LOG2_BLOCK_SIZE), blockSize, d_lut);
|
||||
}); cudaCheckError();
|
||||
lambdaKernel<<<1, 1, 0, stream>>>(1, [=] __device__(size_t) {// Compute CRC32 of all the 4K blocks
|
||||
*d_crc = crc32((const uint8_t*)d_checksums, checksumCount*sizeof(uint32_t), d_lut);
|
||||
}); cudaCheckError();
|
||||
}// void cudaBlockedCRC32(const void *d_data, size_t size, const uint32_t *d_lut, uint32_t *d_crc, cudaStream_t stream)
|
||||
|
||||
/// @brief Compute CRC32 checksum of 4K block
|
||||
/// @param d_begin device pointer to start of data (inclusive)
|
||||
/// @param d_end device pointer to end of data (exclusive)
|
||||
/// @param d_lut pointer to Look-Up-Table for accelerated CRC32 computation
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
inline void blockedCRC32(const void *d_begin, const void *d_end, const uint32_t *d_lut, uint32_t *d_crc, cudaStream_t stream)
|
||||
{
|
||||
blockedCRC32(d_begin, PtrDiff(d_end, d_begin), d_lut, d_crc, stream);
|
||||
}
|
||||
|
||||
}// namespace util::cuda
|
||||
|
||||
namespace tools::cuda {
|
||||
|
||||
/// @brief
|
||||
/// @param d_gridData
|
||||
/// @param d_lut pointer to Look-Up-Table for accelerated CRC32 computation
|
||||
/// @param d_crc
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
inline void crc32Head(const GridData *d_gridData, const uint32_t *d_lut, uint32_t *d_crc, cudaStream_t stream)
|
||||
{
|
||||
NANOVDB_ASSERT(d_gridData && d_lut && d_crc);
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, stream>>>(1, [=] __device__(size_t){*d_crc = tools::crc32Head(d_gridData, d_lut);});
|
||||
}// void cudaCrc32Head(const GridData *d_gridData, const uint32_t *d_lut, uint32_t *d_crc, cudaStream_t stream)
|
||||
|
||||
/// @brief
|
||||
/// @param d_gridData
|
||||
/// @param gridData
|
||||
/// @param d_lut pointer to Look-Up-Table for accelerated CRC32 computation
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
inline void crc32Tail(const GridData *d_gridData, const GridData *gridData, const uint32_t *d_lut, uint32_t *d_crc, cudaStream_t stream)
|
||||
{
|
||||
NANOVDB_ASSERT(d_gridData && gridData && d_lut && d_crc);
|
||||
NANOVDB_ASSERT(gridData->mVersion > Version(32,6,0));
|
||||
const uint8_t *d_begin = (const uint8_t*)d_gridData;
|
||||
util::cuda::blockedCRC32(d_begin + sizeof(GridData) + sizeof(TreeData), d_begin + gridData->mGridSize, d_lut, d_crc, stream);
|
||||
}
|
||||
|
||||
/// @brief
|
||||
/// @tparam ValueT
|
||||
/// @param d_grid
|
||||
/// @param gridData
|
||||
/// @param d_lut pointer to Look-Up-Table for accelerated CRC32 computation
|
||||
/// @param d_crc
|
||||
/// @param stream
|
||||
template <typename ValueT>
|
||||
void crc32TailOld(const NanoGrid<ValueT> *d_grid, const GridData *gridData, const uint32_t *d_lut, uint32_t *d_crc, cudaStream_t stream)
|
||||
{
|
||||
static constexpr unsigned int threadsPerBlock = 128;// seems faster than the old value of 256!
|
||||
auto nodeMgrHandle = nanovdb::cuda::createNodeManager<ValueT, nanovdb::cuda::DeviceBuffer>(d_grid, nanovdb::cuda::DeviceBuffer(), stream);
|
||||
auto *d_nodeMgr = nodeMgrHandle.template deviceMgr<ValueT>();
|
||||
NANOVDB_ASSERT(isAligned(d_nodeMgr));
|
||||
const uint32_t nodeCount[3]={gridData->template nodeCount<0>(), gridData->template nodeCount<1>(), gridData->template nodeCount<2>()};
|
||||
util::cuda::unique_ptr<uint32_t> d_checksumsUP(nodeCount[0]+nodeCount[1]+nodeCount[2]);
|
||||
uint32_t *d_checksums = d_checksumsUP.get(), *d_ptr = d_checksums;
|
||||
|
||||
util::cuda::lambdaKernel<<<util::cuda::blocksPerGrid(nodeCount[2], threadsPerBlock), threadsPerBlock, 0, stream>>>(nodeCount[2], [=] __device__(size_t tid) {
|
||||
auto &node = d_nodeMgr->upper(uint32_t(tid));
|
||||
d_ptr[tid] = util::crc32((const uint8_t*)&node, node.memUsage(), d_lut);
|
||||
}); cudaCheckError();
|
||||
|
||||
d_ptr += nodeCount[2];
|
||||
util::cuda::lambdaKernel<<<util::cuda::blocksPerGrid(nodeCount[1], threadsPerBlock), threadsPerBlock, 0, stream>>>(nodeCount[1], [=] __device__(size_t tid) {
|
||||
auto &node = d_nodeMgr->lower(uint32_t(tid));
|
||||
d_ptr[tid] = util::crc32((const uint8_t*)&node, node.memUsage(), d_lut);
|
||||
}); cudaCheckError();
|
||||
|
||||
d_ptr += nodeCount[1];
|
||||
util::cuda::lambdaKernel<<<util::cuda::blocksPerGrid(nodeCount[0], threadsPerBlock), threadsPerBlock, 0, stream>>>(nodeCount[0], [=] __device__(size_t tid) {
|
||||
auto &node = d_nodeMgr->leaf(uint32_t(tid));
|
||||
d_ptr[tid] = util::crc32((const uint8_t*)&node, node.memUsage(), d_lut);
|
||||
}); cudaCheckError();
|
||||
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, stream>>>(1, [=] __device__(size_t) {
|
||||
*d_crc = util::crc32(d_checksums, d_nodeMgr->tree().totalNodeCount()*sizeof(uint32_t), d_lut);
|
||||
}); cudaCheckError();
|
||||
}// void cudaCrc32TailOld(const NanoGrid<ValueT> *d_grid, const GridData *gridData, uint32_t *d_lut, cudaStream_t stream)
|
||||
|
||||
struct Crc32TailOld {
|
||||
template <typename BuildT>
|
||||
static void known(const GridData *d_gridData, const GridData *gridData, const uint32_t *d_lut, uint32_t *d_crc, cudaStream_t stream)
|
||||
{
|
||||
crc32TailOld((const NanoGrid<BuildT>*)d_gridData, gridData, d_lut, d_crc, stream);
|
||||
}
|
||||
static void unknown(const GridData*, const GridData*, const uint32_t*, uint32_t*, cudaStream_t)
|
||||
{
|
||||
throw std::runtime_error("Cannot call cudaCrc32TailOld with grid of unknown type");
|
||||
}
|
||||
};// Crc32TailOld
|
||||
|
||||
/// @brief
|
||||
/// @param d_gridData
|
||||
/// @param mode
|
||||
/// @param stream
|
||||
/// @return
|
||||
inline Checksum evalChecksum(const GridData *d_gridData, CheckMode mode, cudaStream_t stream)
|
||||
{
|
||||
static const int headSize = sizeof(GridData) + sizeof(TreeData);
|
||||
NANOVDB_ASSERT(d_gridData);
|
||||
Checksum cs;
|
||||
if (mode != CheckMode::Empty) {
|
||||
auto d_lut = util::cuda::createCrc32Lut(1, stream);
|
||||
crc32Head(d_gridData, d_lut.get(), d_lut.get() + 256, stream);
|
||||
cudaCheck(cudaMemcpyAsync(&(cs.head()), d_lut.get() + 256, headSize, cudaMemcpyDeviceToHost, stream));
|
||||
if (mode == CheckMode::Full) {
|
||||
std::unique_ptr<char[]> buffer(new char[headSize]);
|
||||
auto *gridData = (GridData*)(buffer.get());
|
||||
cudaCheck(cudaMemcpyAsync(gridData, d_gridData, headSize, cudaMemcpyDeviceToHost, stream));
|
||||
if (gridData->mVersion > Version(32,6,0)) {
|
||||
crc32Tail(d_gridData, gridData, d_lut.get(), d_lut.get() + 256, stream);
|
||||
} else {
|
||||
callNanoGrid<Crc32TailOld>(d_gridData, gridData, d_lut.get(), d_lut.get() + 256, stream);
|
||||
}
|
||||
cudaCheck(cudaMemcpyAsync(&(cs.tail()), d_lut.get() + 256, headSize, cudaMemcpyDeviceToHost, stream));
|
||||
}
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
/// @brief
|
||||
/// @tparam BuildT
|
||||
/// @param d_grid
|
||||
/// @param mode
|
||||
/// @param stream
|
||||
/// @return
|
||||
template <typename BuildT>
|
||||
Checksum evalChecksum(const NanoGrid<BuildT> *d_grid, CheckMode mode, cudaStream_t stream = 0)
|
||||
{
|
||||
static const int headSize = sizeof(GridData) + sizeof(TreeData);
|
||||
NANOVDB_ASSERT(d_grid);
|
||||
Checksum cs;
|
||||
if (mode != CheckMode::Empty) {
|
||||
auto d_lut = util::cuda::createCrc32Lut(1, stream);
|
||||
crc32Head(d_grid, d_lut.get(), d_lut.get() + 256, stream);
|
||||
cudaCheck(cudaMemcpyAsync(&(cs.head()), d_lut.get() + 256, headSize, cudaMemcpyDeviceToHost, stream));
|
||||
if (mode == CheckMode::Full) {
|
||||
std::unique_ptr<char[]> buffer(new char[headSize]);
|
||||
auto *gridData = (GridData*)(buffer.get());
|
||||
cudaCheck(cudaMemcpyAsync(gridData, d_grid, headSize, cudaMemcpyDeviceToHost, stream));
|
||||
if (gridData->mVersion > Version(32,6,0)) {
|
||||
crc32Tail(d_grid, gridData, d_lut.get(), d_lut.get() + 256, stream);
|
||||
} else {
|
||||
crc32TailOld(d_grid, gridData, d_lut.get(), d_lut.get() + 256, stream);
|
||||
}
|
||||
cudaCheck(cudaMemcpyAsync(&(cs.tail()), d_lut.get() + 256, headSize, cudaMemcpyDeviceToHost, stream));
|
||||
}
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
/// @brief
|
||||
/// @param d_gridData
|
||||
/// @param mode
|
||||
/// @param stream
|
||||
/// @return
|
||||
inline bool validateChecksum(const GridData *d_gridData, CheckMode mode, cudaStream_t stream)
|
||||
{
|
||||
static const int headSize = sizeof(GridData) + sizeof(TreeData);
|
||||
NANOVDB_ASSERT(d_gridData);
|
||||
if (mode == CheckMode::Empty) return true;
|
||||
|
||||
// Copy just the GridData from the device to the host
|
||||
std::unique_ptr<char[]> buffer(new char[headSize]);
|
||||
auto *gridData = (GridData*)(buffer.get());
|
||||
cudaCheck(cudaMemcpyAsync(gridData, d_gridData, headSize, cudaMemcpyDeviceToHost, stream));
|
||||
if (gridData->mChecksum.isEmpty()) return true;// checksum is empty so nothing to check
|
||||
|
||||
// Allocate device LUT for CRC32 computation
|
||||
auto d_lut = util::cuda::createCrc32Lut(1, stream);// unique pointer
|
||||
uint32_t crc = 0, *d_crc = d_lut.get() + 256;
|
||||
|
||||
// Check head checksum
|
||||
crc32Head(d_gridData, d_lut.get(), d_crc, stream);
|
||||
cudaCheck(cudaMemcpyAsync(&crc, d_crc, sizeof(uint32_t), cudaMemcpyDeviceToHost, stream));
|
||||
const bool checkHead = (crc == gridData->mChecksum.head());
|
||||
if (gridData->mChecksum.isHalf() || mode == CheckMode::Half || !checkHead) return checkHead;
|
||||
|
||||
// Check tail checksum
|
||||
if (gridData->mVersion > Version(32,6,0)) {
|
||||
crc32Tail(d_gridData, gridData, d_lut.get(), d_crc, stream);
|
||||
} else {
|
||||
callNanoGrid<Crc32TailOld>(d_gridData, gridData, d_lut.get(), d_crc, stream);
|
||||
}
|
||||
cudaCheck(cudaMemcpyAsync(&crc, d_crc, sizeof(uint32_t), cudaMemcpyDeviceToHost, stream));
|
||||
return crc == gridData->mChecksum.tail();
|
||||
}// bool cudaValidateChecksum(const GridData *d_gridData, CheckMode mode, cudaStream_t stream = 0)
|
||||
|
||||
/// @brief
|
||||
/// @tparam BuildT
|
||||
/// @param d_grid
|
||||
/// @param mode
|
||||
/// @param stream
|
||||
/// @return
|
||||
template <typename BuildT>
|
||||
bool validateChecksum(const NanoGrid<BuildT> *d_grid, CheckMode mode, cudaStream_t stream = 0)
|
||||
{
|
||||
static const int headSize = sizeof(GridData) + sizeof(TreeData);
|
||||
NANOVDB_ASSERT(d_grid);
|
||||
if (mode == CheckMode::Empty) return true;
|
||||
|
||||
// Copy just the GridData from the device to the host
|
||||
std::unique_ptr<char[]> buffer(new char[headSize]);
|
||||
auto *gridData = (GridData*)(buffer.get());
|
||||
cudaCheck(cudaMemcpyAsync(gridData, d_grid, headSize, cudaMemcpyDeviceToHost, stream));
|
||||
if (gridData->mChecksum.isEmpty()) return true;// checksum is empty so nothing to check
|
||||
|
||||
// Allocate device LUT for CRC32 computation
|
||||
auto d_lut = util::cuda::createCrc32Lut(1, stream);// unique pointer
|
||||
uint32_t crc = 0, *d_crc = d_lut.get() + 256;
|
||||
|
||||
// Check head checksum
|
||||
crc32Head(d_grid, d_lut.get(), d_crc, stream);
|
||||
cudaCheck(cudaMemcpyAsync(&crc, d_crc, sizeof(uint32_t), cudaMemcpyDeviceToHost, stream));
|
||||
const bool checkHead = (crc == gridData->mChecksum.head());
|
||||
if (gridData->mChecksum.isHalf() || mode == CheckMode::Half || !checkHead) return checkHead;
|
||||
|
||||
// Check tail checksum
|
||||
if (gridData->mVersion > Version(32,6,0)) {
|
||||
crc32Tail(d_grid, gridData, d_lut.get(), d_crc, stream);
|
||||
} else {
|
||||
crc32TailOld(d_grid, gridData, d_lut.get(), d_crc, stream);
|
||||
}
|
||||
cudaCheck(cudaMemcpyAsync(&crc, d_crc, sizeof(uint32_t), cudaMemcpyDeviceToHost, stream));
|
||||
return crc == gridData->mChecksum.tail();
|
||||
}// bool cudaValidateChecksum(const GridData *d_gridData, CheckMode mode, cudaStream_t stream = 0)
|
||||
|
||||
/// @brief Extract the checksum of a device grid
|
||||
/// @param d_gridData Device pointer to grid with a checksum
|
||||
/// @param stream optional cuda stream (defaults to zero)
|
||||
inline Checksum getChecksum(const GridData *d_gridData, cudaStream_t stream)
|
||||
{
|
||||
NANOVDB_ASSERT(d_gridData);
|
||||
Checksum cs;
|
||||
cudaCheck(cudaMemcpyAsync(&cs, (const uint8_t*)d_gridData + 8, sizeof(cs), cudaMemcpyDeviceToHost, stream));
|
||||
return cs;
|
||||
}
|
||||
|
||||
/// @brief Update the checksum of a device grid
|
||||
/// @param d_gridData device pointer to GridData
|
||||
/// @param mode Mode of computation for the checksum.
|
||||
/// @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).
|
||||
inline void updateChecksum(GridData *d_gridData, CheckMode mode, cudaStream_t stream)
|
||||
{
|
||||
NANOVDB_ASSERT(d_gridData);
|
||||
if (mode == CheckMode::Empty) return;
|
||||
|
||||
// Allocate device LUT for CRC32 computation
|
||||
auto d_lut = util::cuda::createCrc32Lut(0, stream);// unique pointers
|
||||
|
||||
// Update head checksum
|
||||
crc32Head(d_gridData, d_lut.get(), (uint32_t*)d_gridData + 2, stream);
|
||||
|
||||
if (mode == CheckMode::Half) return;
|
||||
|
||||
// Copy just the GridData from the device to the host
|
||||
std::unique_ptr<char[]> buffer(new char[sizeof(GridData) + sizeof(TreeData)]);
|
||||
auto *gridData = (GridData*)(buffer.get());
|
||||
cudaCheck(cudaMemcpyAsync(gridData, d_gridData, sizeof(GridData) + sizeof(TreeData), cudaMemcpyDeviceToHost, stream));
|
||||
|
||||
// Update tail checksum
|
||||
uint32_t *d_tail = (uint32_t*)d_gridData + 3;
|
||||
if (gridData->mVersion > Version(32,6,0)) {
|
||||
crc32Tail(d_gridData, gridData, d_lut.get(), d_tail, stream);
|
||||
} else {
|
||||
callNanoGrid<Crc32TailOld>(d_gridData, gridData, d_lut.get(), d_tail, stream);
|
||||
}
|
||||
}// cudaUpdateChecksum
|
||||
|
||||
/// @brief
|
||||
/// @tparam ValueT
|
||||
/// @param d_grid
|
||||
/// @param mode
|
||||
/// @param stream
|
||||
template <typename ValueT>
|
||||
void updateChecksum(NanoGrid<ValueT> *d_grid, CheckMode mode, cudaStream_t stream = 0)
|
||||
{
|
||||
NANOVDB_ASSERT(d_grid);
|
||||
if (mode == CheckMode::Empty) return;
|
||||
|
||||
// Allocate device LUT for CRC32 computation
|
||||
auto d_lut = util::cuda::createCrc32Lut(0, stream);// unique pointers
|
||||
|
||||
// Update head checksum
|
||||
cuda::crc32Head(d_grid, d_lut.get(), (uint32_t*)d_grid + 2, stream);
|
||||
if (mode == CheckMode::Half) return;
|
||||
|
||||
// Copy just the GridData from the device to the host
|
||||
std::unique_ptr<char[]> buffer(new char[sizeof(GridData) + sizeof(TreeData)]);
|
||||
auto *gridData = (GridData*)(buffer.get());
|
||||
cudaCheck(cudaMemcpyAsync(gridData, d_grid, sizeof(GridData) + sizeof(TreeData), cudaMemcpyDeviceToHost, stream));
|
||||
|
||||
// Update tail checksum
|
||||
uint32_t *d_tail = (uint32_t*)d_grid + 3;
|
||||
if (gridData->mVersion > Version(32,6,0)) {
|
||||
crc32Tail(d_grid->data(), gridData, d_lut.get(), d_tail, stream);
|
||||
} else {
|
||||
crc32TailOld(d_grid, gridData, d_lut.get(), d_tail, stream);
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace tools::cuda // ================================================
|
||||
|
||||
}// namespace nanovdb // ====================================================
|
||||
|
||||
#endif // NANOVDB_TOOLS_CUDA_GRIDCHECKSUM_CUH_HAS_BEEN_INCLUDED
|
||||
@@ -1,249 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/GridStats.cuh
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date October 9, 2023
|
||||
|
||||
\brief Re-computes min/max/avg/var/bbox information for each node in a
|
||||
pre-existing NanoVDB grid on the device.
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_TOOLS_CUDA_GRIDSTATS_CUH_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_TOOLS_CUDA_GRIDSTATS_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/tools/GridStats.h>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools::cuda {
|
||||
|
||||
/// @brief Update, i.e. re-compute, grid statistics like min/max, stats and bbox
|
||||
/// information for an existing NanoVDB Grid.
|
||||
/// @param grid Grid whose stats to update
|
||||
/// @param mode Mode of computation for the statistics.
|
||||
/// @param stream Optional cuda stream (defaults to zero)
|
||||
template<typename BuildT>
|
||||
void updateGridStats(NanoGrid<BuildT> *d_grid, StatsMode mode = StatsMode::Default, cudaStream_t stream = 0);
|
||||
|
||||
//================================================================================================
|
||||
|
||||
/// @brief Allows for the construction of NanoVDB grids without any dependecy
|
||||
template<typename BuildT, typename StatsT = Stats<typename NanoGrid<BuildT>::ValueType>>
|
||||
class GridStats
|
||||
{
|
||||
using GridT = NanoGrid<BuildT>;
|
||||
using TreeT = typename GridT::TreeType;
|
||||
using ValueT = typename TreeT::ValueType;
|
||||
using Node0 = typename TreeT::Node0; // leaf
|
||||
using Node1 = typename TreeT::Node1; // lower
|
||||
using Node2 = typename TreeT::Node2; // upper
|
||||
using RootT = typename TreeT::Node3; // root
|
||||
static_assert(util::is_same<ValueT, typename StatsT::ValueType>::value, "Mismatching type");
|
||||
|
||||
ValueT mDelta; // skip rendering of node if: node.max < -mDelta || node.min > mDelta
|
||||
|
||||
public:
|
||||
GridStats(ValueT delta = ValueT(0)) : mDelta(delta) {}
|
||||
|
||||
void update(GridT *d_grid, cudaStream_t stream = 0);
|
||||
|
||||
}; // cuda::GridStats
|
||||
|
||||
//================================================================================================
|
||||
|
||||
namespace {// define cuda kernels in an unnamed namespace
|
||||
|
||||
template<typename BuildT, typename StatsT>
|
||||
__global__ void processLeaf(NodeManager<BuildT> *d_nodeMgr, StatsT *d_stats)
|
||||
{
|
||||
const uint32_t tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid >= d_nodeMgr->leafCount()) return;
|
||||
auto &d_leaf = d_nodeMgr->leaf(tid);
|
||||
|
||||
if (d_leaf.updateBBox()) {// updates active bounding box (also updates data->mFlags) and return true if non-empty
|
||||
if constexpr(StatsT::hasStats()) {
|
||||
StatsT stats;
|
||||
for (auto it = d_leaf.cbeginValueOn(); it; ++it) stats.add(*it);
|
||||
if constexpr(StatsT::hasAverage()) {
|
||||
d_stats[tid] = stats;
|
||||
*reinterpret_cast<uint32_t*>(&d_leaf.mMinimum) = tid;
|
||||
} else {
|
||||
stats.setStats(d_leaf);
|
||||
}
|
||||
}
|
||||
}
|
||||
d_leaf.mFlags &= ~uint8_t(1u);// enable rendering
|
||||
}// processLeaf
|
||||
|
||||
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);
|
||||
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];
|
||||
s.setStats(child);
|
||||
stats.add(s);
|
||||
} else if constexpr(StatsT::hasMinMax()) {
|
||||
stats.add(child.minimum());
|
||||
stats.add(child.maximum());
|
||||
}
|
||||
}
|
||||
for (auto it = d_node.cbeginValueOn(); it; ++it) {
|
||||
const Coord ijk = it.getCoord();
|
||||
bbox[0].minComponent(ijk);
|
||||
bbox[1].maxComponent(ijk + Coord(ChildT::DIM - 1));
|
||||
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;
|
||||
} else if constexpr(StatsT::hasMinMax()) {
|
||||
stats.setStats(d_node);
|
||||
}
|
||||
d_node.mFlags &= ~uint64_t(1u);// enable rendering
|
||||
}// processInternal
|
||||
|
||||
template<typename BuildT, typename StatsT>
|
||||
__global__ void processRootAndGrid(NodeManager<BuildT> *d_nodeMgr, StatsT *d_stats)
|
||||
{
|
||||
using ChildT = NanoUpper<BuildT>;
|
||||
using ValueT = typename ChildT::ValueType;
|
||||
|
||||
// process root
|
||||
auto &root = d_nodeMgr->root();
|
||||
root.mBBox = CoordBBox();
|
||||
if (root.isEmpty()) {
|
||||
root.mMinimum = root.mMaximum = root.mBackground;
|
||||
root.mAverage = root.mStdDevi = 0;
|
||||
} else {
|
||||
ValueT v;
|
||||
StatsT s;
|
||||
for (auto it = root.beginDense(); it; ++it) {
|
||||
if (auto *child = it.probeChild(v)) {
|
||||
root.mBBox.expand( child->bbox() );
|
||||
if constexpr(StatsT::hasAverage()) {
|
||||
StatsT &stats = d_stats[*reinterpret_cast<uint32_t*>(&child->mMinimum)];
|
||||
stats.setStats(*child);
|
||||
s.add(stats);
|
||||
} else if constexpr(StatsT::hasMinMax()){
|
||||
s.add(child->minimum());
|
||||
s.add(child->maximum());
|
||||
}
|
||||
} else if (it.isValueOn()) {
|
||||
const Coord ijk = it.getCoord();
|
||||
root.mBBox[0].minComponent(ijk);
|
||||
root.mBBox[1].maxComponent(ijk + Coord(ChildT::DIM - 1));
|
||||
if constexpr(StatsT::hasStats()) s.add(v, ChildT::NUM_VALUES);
|
||||
}
|
||||
}
|
||||
s.setStats(root);
|
||||
}
|
||||
|
||||
// process Grid
|
||||
auto& grid = d_nodeMgr->grid();
|
||||
const auto& indexBBox = root.bbox();
|
||||
if (indexBBox.empty()) {
|
||||
grid.mWorldBBox = Vec3dBBox();
|
||||
grid.setBBoxOn(false);
|
||||
} else {
|
||||
// Note that below max is offset by one since CoordBBox.max is inclusive
|
||||
// while bbox<Vec3d>.max is exclusive. However, min is inclusive in both
|
||||
// CoordBBox and Vec3dBBox. This also guarantees that a grid with a single
|
||||
// active voxel, does not have an empty world bbox! E.g. if a grid with a
|
||||
// unit index-to-world transformation only contains the active voxel (0,0,0)
|
||||
// then indeBBox = (0,0,0) -> (0,0,0) and then worldBBox = (0.0, 0.0, 0.0)
|
||||
// -> (1.0, 1.0, 1.0). This is a consequence of the different definitions
|
||||
// of index and world bounding boxes inherited from OpenVDB!
|
||||
grid.mWorldBBox = CoordBBox(indexBBox[0], indexBBox[1].offsetBy(1)).transform(grid.map());
|
||||
grid.setBBoxOn(true);
|
||||
}
|
||||
|
||||
// set bit flags
|
||||
grid.setMinMaxOn(StatsT::hasMinMax());
|
||||
grid.setAverageOn(StatsT::hasAverage());
|
||||
grid.setStdDeviationOn(StatsT::hasStdDeviation());
|
||||
}// processRootAndGrid
|
||||
|
||||
}// cuda kernels are defined in an unnamed namespace
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename BuildT, typename StatsT>
|
||||
void GridStats<BuildT, StatsT>::update(NanoGrid<BuildT> *d_grid, cudaStream_t stream)
|
||||
{
|
||||
static const uint32_t threadsPerBlock = 128;
|
||||
auto blocksPerGrid = [&](uint32_t count)->uint32_t{return (count + (threadsPerBlock - 1)) / threadsPerBlock;};
|
||||
|
||||
auto nodeMgrHandle = nanovdb::cuda::createNodeManager(d_grid, CudaDeviceBuffer(), stream);
|
||||
auto *d_nodeMgr = nodeMgrHandle.template deviceMgr<BuildT>();
|
||||
|
||||
uint32_t nodeCount[3];// {leaf, lower, upper}
|
||||
cudaCheck(cudaMemcpyAsync(nodeCount, (char*)d_grid + sizeof(GridData) + 4*sizeof(uint64_t), 3*sizeof(uint32_t), cudaMemcpyDeviceToHost, stream));
|
||||
//cudaStreamSynchronize(stream);// finish all device tasks in stream
|
||||
|
||||
StatsT *d_stats = nullptr;
|
||||
|
||||
if constexpr(StatsT::hasAverage()) cudaCheck(util::cuda::mallocAsync((void**)&d_stats, nodeCount[0]*sizeof(StatsT), stream));
|
||||
|
||||
processLeaf<BuildT><<<blocksPerGrid(nodeCount[0]), threadsPerBlock, 0, stream>>>(d_nodeMgr, d_stats);
|
||||
|
||||
processInternal<BuildT, StatsT, 1><<<blocksPerGrid(nodeCount[1]), threadsPerBlock, 0, stream>>>(d_nodeMgr, d_stats);
|
||||
|
||||
processInternal<BuildT, StatsT, 2><<<blocksPerGrid(nodeCount[2]), threadsPerBlock, 0, stream>>>(d_nodeMgr, d_stats);
|
||||
|
||||
processRootAndGrid<BuildT><<<1, 1, 0, stream>>>(d_nodeMgr, d_stats);
|
||||
|
||||
if constexpr(StatsT::hasAverage()) cudaCheck(util::cuda::freeAsync(d_stats, stream));
|
||||
|
||||
} // cuda::GridStats::update( Grid )
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename BuildT>
|
||||
void updateGridStats(NanoGrid<BuildT> *d_grid, StatsMode mode, cudaStream_t stream)
|
||||
{
|
||||
if (d_grid == nullptr && mode == StatsMode::Disable) {
|
||||
return;
|
||||
} else if (mode == StatsMode::BBox || util::is_same<bool, BuildT>::value) {
|
||||
GridStats<BuildT, NoopStats<BuildT> > stats;
|
||||
stats.update(d_grid, stream);
|
||||
} else if (mode == StatsMode::MinMax) {
|
||||
GridStats<BuildT, Extrema<BuildT> > stats;
|
||||
stats.update(d_grid, stream);
|
||||
} else if (mode == StatsMode::All) {
|
||||
GridStats<BuildT, Stats<BuildT> > stats;
|
||||
stats.update(d_grid, stream);
|
||||
} else {
|
||||
throw std::runtime_error("GridStats: Unsupported statistics mode.");
|
||||
}
|
||||
}// cuda::updateGridStats
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
template<typename BuildT>
|
||||
[[deprecated("Use nanovdb::cuda::updateGridStats instead")]]
|
||||
void cudaGridStats(NanoGrid<BuildT> *d_grid, tools::StatsMode mode = tools::StatsMode::Default, cudaStream_t stream = 0)
|
||||
{
|
||||
tools::cuda::updateGridStats<BuildT>(d_grid, mode, stream);
|
||||
}
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_TOOLS_CUDA_GRIDSTATS_CUH_HAS_BEEN_INCLUDED
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/GridValidator.cuh
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date November 3, 2023
|
||||
|
||||
\brief Checks the validity of an existing NanoVDB device grid.
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_TOOLS_CUDA_GRIDVALIDATOR_CUH_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_TOOLS_CUDA_GRIDVALIDATOR_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/tools/GridValidator.h>
|
||||
#include <nanovdb/tools/cuda/GridChecksum.cuh>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools::cuda {
|
||||
|
||||
/// @brief Return true if the specified grid passes several validation tests.
|
||||
///
|
||||
/// @param grid Grid to validate
|
||||
/// @param detailed If true the validation test is detailed and relatively slow.
|
||||
/// @param verbose If true information about the first failed test is printed to std::cerr
|
||||
template <typename ValueT>
|
||||
bool isValid(const NanoGrid<ValueT> *d_grid, CheckMode mode, bool verbose = false, cudaStream_t stream = 0)
|
||||
{
|
||||
static const int size = 100;
|
||||
std::unique_ptr<char[]> strUP(new char[size]);
|
||||
util::cuda::unique_ptr<char> d_strUP(size);
|
||||
char *str = strUP.get(), *d_str = d_strUP.get();
|
||||
|
||||
util::cuda::lambdaKernel<<<1, 1, 0, stream>>>(1, [=] __device__(size_t) {nanovdb::tools::checkGrid(d_grid, d_str, mode);});
|
||||
cudaMemcpyAsync(str, d_str, size, cudaMemcpyDeviceToHost, stream);
|
||||
|
||||
if (util::empty(str) && !cuda::validateChecksum(d_grid, mode)) util::strcpy(str, "Mis-matching checksum");
|
||||
if (verbose && !util::empty(str)) std::cerr << "Validation failed: " << str << std::endl;
|
||||
|
||||
return util::empty(str);
|
||||
}// tools::cuda::isValid
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
template <typename ValueT>
|
||||
[[deprecated("Use cuda::isValid() instead.")]]
|
||||
bool cudaIsValid(const NanoGrid<ValueT> *d_grid, CheckMode mode, bool verbose = false, cudaStream_t stream = 0)
|
||||
{
|
||||
return tools::cuda::isValid(d_grid, mode, verbose, stream);
|
||||
}
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_TOOLS_CUDA_GRIDVALIDATOR_CUH_HAS_BEEN_INCLUDED
|
||||
@@ -1,407 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/IndexToGrid.cuh
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date April 17, 2023
|
||||
|
||||
\brief Combines an IndexGrid and values into a regular Grid 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_INDEXTOGRID_CUH_HAS_BEEN_INCLUDED
|
||||
#define NVIDIA_TOOLS_CUDA_INDEXTOGRID_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/util/cuda/Timer.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
namespace nanovdb {// ================================================================
|
||||
|
||||
namespace tools::cuda {// ============================================================
|
||||
|
||||
/// @brief Freestanding function that combines an IndexGrid and values into a regular Grid
|
||||
/// @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_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
|
||||
/// @note If d_srcGrid has stats (min,max,avg,std-div), the d_srcValues is also assumed
|
||||
/// to have the same information, all of which are then copied to the destination/output grid.
|
||||
/// An exception to this rule is if the type of d_srcValues is different from the stats type
|
||||
/// NanoRoot<DstBuildT>::FloatType, e.g. if DstBuildT=Vec3f then NanoRoot<DstBuildT>::FloatType=float,
|
||||
/// in which case average and standard-deviation is undefined in the output grid.
|
||||
/// @return returns handle to grid that combined IndexGrid and values
|
||||
template<typename DstBuildT, typename SrcBuildT, typename BufferT = nanovdb::cuda::DeviceBuffer>
|
||||
typename util::enable_if<BuildTraits<SrcBuildT>::is_index, GridHandle<BufferT>>::type
|
||||
indexToGrid(const NanoGrid<SrcBuildT> *d_srcGrid, const typename BuildToValueMap<DstBuildT>::type *d_srcValues, const BufferT &pool = BufferT(), cudaStream_t stream = 0);
|
||||
|
||||
|
||||
template<typename DstBuildT, typename SrcBuildT, typename BufferT = nanovdb::cuda::DeviceBuffer>
|
||||
typename util::enable_if<BuildTraits<SrcBuildT>::is_index, GridHandle<BufferT>>::type
|
||||
createNanoGrid(const NanoGrid<SrcBuildT> *d_srcGrid, const typename BuildToValueMap<DstBuildT>::type *d_srcValues, const BufferT &pool = BufferT(), cudaStream_t stream = 0)
|
||||
{
|
||||
return indexToGrid<DstBuildT, SrcBuildT, BufferT>(d_srcGrid, d_srcValues, pool, stream);
|
||||
}
|
||||
|
||||
namespace {// anonymous namespace
|
||||
|
||||
template<typename SrcBuildT>
|
||||
class IndexToGrid
|
||||
{
|
||||
using SrcGridT = NanoGrid<SrcBuildT>;
|
||||
public:
|
||||
struct NodeAccessor;
|
||||
|
||||
/// @brief Constructor from a source IndeGrid
|
||||
/// @param srcGrid Device pointer to IndexGrid used as the source
|
||||
IndexToGrid(const SrcGridT *d_srcGrid, cudaStream_t stream = 0);
|
||||
|
||||
~IndexToGrid() {cudaCheck(util::cuda::freeAsync(mDevNodeAcc, mStream));}
|
||||
|
||||
/// @brief Toggle on and off verbose mode
|
||||
/// @param on if true verbose is turned on
|
||||
void setVerbose(bool on = true) {mVerbose = on; }
|
||||
|
||||
/// @brief Set the name of the destination/output grid
|
||||
/// @param name Name used for the destination grid
|
||||
void setGridName(const std::string &name) {mGridName = name;}
|
||||
|
||||
/// @brief Combines the IndexGrid with values to produce a regular Grid
|
||||
/// @tparam DstBuildT Template parameter of the destination grid and value type
|
||||
/// @tparam BufferT Template parameter of the memory allocator
|
||||
/// @param srcValues pointer to values that will be inserted into the output grid
|
||||
/// @param buffer optional buffer used for memory allocation
|
||||
/// @return A new GridHandle with the grid of type @c DstBuildT
|
||||
template<typename DstBuildT, typename BufferT = nanovdb::cuda::DeviceBuffer>
|
||||
GridHandle<BufferT> getHandle(const typename BuildToValueMap<DstBuildT>::type *srcValues, const BufferT &buffer = BufferT());
|
||||
|
||||
private:
|
||||
cudaStream_t mStream{0};
|
||||
util::cuda::Timer mTimer;
|
||||
std::string mGridName;
|
||||
bool mVerbose{false};
|
||||
NodeAccessor mNodeAcc, *mDevNodeAcc;
|
||||
|
||||
template<typename DstBuildT, typename BufferT>
|
||||
BufferT getBuffer(const BufferT &pool);
|
||||
};// IndexToGrid
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename SrcBuildT>
|
||||
struct IndexToGrid<SrcBuildT>::NodeAccessor
|
||||
{
|
||||
uint64_t grid, tree, root, node[3], meta, blind, size;// byte offsets, node: 0=leaf,1=lower, 2=upper
|
||||
const SrcGridT *d_srcGrid;// device point to source IndexGrid
|
||||
void *d_dstPtr;// device pointer to buffer with destination Grid
|
||||
char *d_gridName;
|
||||
uint32_t nodeCount[4];// 0=leaf, 1=lower, 2=upper, 3=root tiles
|
||||
|
||||
__device__ const NanoGrid<SrcBuildT>& srcGrid() const {return *d_srcGrid;}
|
||||
__device__ const NanoTree<SrcBuildT>& srcTree() const {return d_srcGrid->tree();}
|
||||
__device__ const NanoRoot<SrcBuildT>& srcRoot() const {return d_srcGrid->tree().root();}
|
||||
template <int LEVEL>
|
||||
__device__ const typename NanoNode<SrcBuildT, LEVEL>::type& srcNode(int i) const {
|
||||
return *(this->srcTree().template getFirstNode<LEVEL>() + i);
|
||||
}
|
||||
|
||||
template <typename DstBuildT>
|
||||
__device__ NanoGrid<DstBuildT>& dstGrid() const {return *util::PtrAdd<NanoGrid<DstBuildT>>(d_dstPtr, grid);}
|
||||
template <typename DstBuildT>
|
||||
__device__ NanoTree<DstBuildT>& dstTree() const {return *util::PtrAdd<NanoTree<DstBuildT>>(d_dstPtr, tree);}
|
||||
template <typename DstBuildT>
|
||||
__device__ NanoRoot<DstBuildT>& dstRoot() const {return *util::PtrAdd<NanoRoot<DstBuildT>>(d_dstPtr, root);}
|
||||
template <typename DstBuildT, int LEVEL>
|
||||
__device__ typename NanoNode<DstBuildT, LEVEL>::type& dstNode(int i) const {
|
||||
return *(util::PtrAdd<typename NanoNode<DstBuildT,LEVEL>::type>(d_dstPtr, node[LEVEL])+i);
|
||||
}
|
||||
};// IndexToGrid<SrcBuildT>::NodeAccessor
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename SrcBuildT, typename DstBuildT>
|
||||
__global__ void processGridTreeRootKernel(typename IndexToGrid<SrcBuildT>::NodeAccessor *nodeAcc,
|
||||
const typename BuildToValueMap<DstBuildT>::type *srcValues)
|
||||
{
|
||||
using SrcValueT = typename BuildToValueMap<DstBuildT>::type;
|
||||
using DstStatsT = typename NanoRoot<DstBuildT>::FloatType;
|
||||
|
||||
auto &srcGrid = nodeAcc->srcGrid();
|
||||
auto &dstGrid = nodeAcc->template dstGrid<DstBuildT>();
|
||||
auto &srcTree = srcGrid.tree();
|
||||
auto &dstTree = nodeAcc->template dstTree<DstBuildT>();
|
||||
auto &srcRoot = srcTree.root();
|
||||
auto &dstRoot = nodeAcc->template dstRoot<DstBuildT>();
|
||||
|
||||
// process Grid
|
||||
*dstGrid.data() = *srcGrid.data();
|
||||
dstGrid.mGridType = toGridType<DstBuildT>();
|
||||
dstGrid.mData1 = 0u;
|
||||
// we will recompute GridData::mChecksum later
|
||||
|
||||
// process Tree
|
||||
*dstTree.data() = *srcTree.data();
|
||||
dstTree.setRoot(&dstRoot);
|
||||
dstTree.setFirstNode(&nodeAcc->template dstNode<DstBuildT,2>(0));
|
||||
dstTree.setFirstNode(&nodeAcc->template dstNode<DstBuildT,1>(0));
|
||||
dstTree.setFirstNode(&nodeAcc->template dstNode<DstBuildT,0>(0));
|
||||
|
||||
// process Root
|
||||
dstRoot.mBBox = srcRoot.mBBox;
|
||||
dstRoot.mTableSize = srcRoot.mTableSize;
|
||||
dstRoot.mBackground = srcValues[srcRoot.mBackground];
|
||||
if (srcGrid.hasMinMax()) {
|
||||
dstRoot.mMinimum = srcValues[srcRoot.mMinimum];
|
||||
dstRoot.mMaximum = srcValues[srcRoot.mMaximum];
|
||||
}
|
||||
if constexpr(util::is_same<SrcValueT, DstStatsT>::value) {// e.g. {float,float} or {Vec3f,float}
|
||||
if (srcGrid.hasAverage()) dstRoot.mAverage = srcValues[srcRoot.mAverage];
|
||||
if (srcGrid.hasStdDeviation()) dstRoot.mStdDevi = srcValues[srcRoot.mStdDevi];
|
||||
}
|
||||
}// processGridTreeRootKernel
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename SrcBuildT, typename DstBuildT>
|
||||
__global__ void processRootTilesKernel(typename IndexToGrid<SrcBuildT>::NodeAccessor *nodeAcc,
|
||||
const typename BuildToValueMap<DstBuildT>::type *srcValues)
|
||||
{
|
||||
const auto tid = blockIdx.x;
|
||||
|
||||
// Process children and tiles
|
||||
const auto &srcTile = *nodeAcc->srcRoot().tile(tid);
|
||||
auto &dstTile = *nodeAcc->template dstRoot<DstBuildT>().tile(tid);
|
||||
dstTile.key = srcTile.key;
|
||||
if (srcTile.child) {
|
||||
dstTile.child = sizeof(NanoRoot<DstBuildT>) + sizeof(NanoRoot<DstBuildT>::Tile)*((srcTile.child - sizeof(NanoRoot<SrcBuildT>))/sizeof(NanoRoot<SrcBuildT>::Tile));
|
||||
dstTile.value = srcValues[0];// set to background
|
||||
dstTile.state = false;
|
||||
} else {
|
||||
dstTile.child = 0;// i.e. no child node
|
||||
dstTile.value = srcValues[srcTile.value];
|
||||
dstTile.state = srcTile.state;
|
||||
}
|
||||
}// processRootTilesKernel
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename SrcBuildT, typename DstBuildT, int LEVEL>
|
||||
__global__ void processNodesKernel(typename IndexToGrid<SrcBuildT>::NodeAccessor *nodeAcc,
|
||||
const typename BuildToValueMap<DstBuildT>::type *srcValues)
|
||||
{
|
||||
using SrcNodeT = typename NanoNode<SrcBuildT, LEVEL>::type;
|
||||
using DstNodeT = typename NanoNode<DstBuildT, LEVEL>::type;
|
||||
using SrcChildT = typename SrcNodeT::ChildNodeType;
|
||||
using DstChildT = typename DstNodeT::ChildNodeType;
|
||||
using SrcValueT = typename BuildToValueMap<DstBuildT>::type;
|
||||
using DstStatsT = typename NanoRoot<DstBuildT>::FloatType;
|
||||
|
||||
auto &srcNode = nodeAcc->template srcNode<LEVEL>(blockIdx.x);
|
||||
auto &dstNode = nodeAcc->template dstNode<DstBuildT, LEVEL>(blockIdx.x);
|
||||
|
||||
if (threadIdx.x == 0 && threadIdx.y == 0) {
|
||||
dstNode.mBBox = srcNode.mBBox;
|
||||
dstNode.mFlags = srcNode.mFlags;
|
||||
dstNode.mValueMask = srcNode.mValueMask;
|
||||
dstNode.mChildMask = srcNode.mChildMask;
|
||||
auto &srcGrid = nodeAcc->srcGrid();
|
||||
if (srcGrid.hasMinMax()) {
|
||||
dstNode.mMinimum = srcValues[srcNode.mMinimum];
|
||||
dstNode.mMaximum = srcValues[srcNode.mMaximum];
|
||||
}
|
||||
if constexpr(util::is_same<SrcValueT, DstStatsT>::value) {// e.g. {float,float} or {Vec3f,float}
|
||||
if (srcGrid.hasAverage()) dstNode.mAverage = srcValues[srcNode.mAverage];
|
||||
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;
|
||||
if (srcNode.mChildMask.isOn(i)) {
|
||||
if constexpr(sizeof(SrcNodeT)==sizeof(DstNodeT) && sizeof(SrcChildT)==sizeof(DstChildT)) {
|
||||
dstNode.mTable[i].child = srcNode.mTable[i].child;
|
||||
} else {
|
||||
const uint64_t childID = (srcNode.mTable[i].child - srcOff)/sizeof(SrcChildT);
|
||||
dstNode.mTable[i].child = dstOff + childID*sizeof(DstChildT);
|
||||
}
|
||||
} else {
|
||||
dstNode.mTable[i].value = srcValues[srcNode.mTable[i].value];
|
||||
}
|
||||
}
|
||||
}// processNodesKernel
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename SrcBuildT, typename DstBuildT>
|
||||
__global__ void processLeafsKernel(typename IndexToGrid<SrcBuildT>::NodeAccessor *nodeAcc,
|
||||
const typename BuildToValueMap<DstBuildT>::type *srcValues)
|
||||
{
|
||||
using SrcValueT = typename BuildToValueMap<DstBuildT>::type;
|
||||
using DstStatsT = typename NanoRoot<DstBuildT>::FloatType;
|
||||
static_assert(!BuildTraits<DstBuildT>::is_special, "Invalid destination type!");
|
||||
auto &srcLeaf = nodeAcc->template srcNode<0>(blockIdx.x);
|
||||
auto &dstLeaf = nodeAcc->template dstNode<DstBuildT,0>(blockIdx.x);
|
||||
if (threadIdx.x == 0 && threadIdx.y == 0) {
|
||||
dstLeaf.mBBoxMin = srcLeaf.mBBoxMin;
|
||||
for (int i=0; i<3; ++i) dstLeaf.mBBoxDif[i] = srcLeaf.mBBoxDif[i];
|
||||
dstLeaf.mFlags = srcLeaf.mFlags;
|
||||
dstLeaf.mValueMask = srcLeaf.mValueMask;
|
||||
///
|
||||
auto &srcGrid = nodeAcc->srcGrid();
|
||||
if (srcGrid.hasMinMax()) {
|
||||
dstLeaf.mMinimum = srcValues[srcLeaf.getMin()];
|
||||
dstLeaf.mMaximum = srcValues[srcLeaf.getMax()];
|
||||
}
|
||||
if constexpr(util::is_same<SrcValueT, DstStatsT>::value) {// e.g. {float,float} or {Vec3f,float}
|
||||
if (srcGrid.hasAverage()) dstLeaf.mAverage = srcValues[srcLeaf.getAvg()];
|
||||
if (srcGrid.hasStdDeviation()) dstLeaf.mStdDevi = srcValues[srcLeaf.getDev()];
|
||||
}
|
||||
}
|
||||
const int off = blockDim.x*blockDim.y*threadIdx.x + blockDim.x*threadIdx.y;
|
||||
auto *dst = dstLeaf.mValues + off;
|
||||
for (int threadIdx_z=0; threadIdx_z<blockDim.x; ++threadIdx_z) {
|
||||
const int i = off + threadIdx_z;
|
||||
*dst++ = srcValues[srcLeaf.getValue(i)];
|
||||
}
|
||||
}// processLeafsKernel
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template <typename SrcBuildT>
|
||||
__global__ void cpyNodeCountKernel(const NanoGrid<SrcBuildT> *srcGrid,
|
||||
typename IndexToGrid<SrcBuildT>::NodeAccessor *nodeAcc)
|
||||
{
|
||||
assert(srcGrid->isSequential());
|
||||
nodeAcc->d_srcGrid = srcGrid;
|
||||
for (int i=0; i<3; ++i) nodeAcc->nodeCount[i] = srcGrid->tree().nodeCount(i);
|
||||
nodeAcc->nodeCount[3] = srcGrid->tree().root().tileCount();
|
||||
}
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template <typename SrcBuildT>
|
||||
IndexToGrid<SrcBuildT>::IndexToGrid(const SrcGridT *d_srcGrid, cudaStream_t stream)
|
||||
: mStream(stream), mTimer(stream)
|
||||
{
|
||||
NANOVDB_ASSERT(d_srcGrid);
|
||||
cudaCheck(util::cuda::mallocAsync((void**)&mDevNodeAcc, sizeof(NodeAccessor), mStream));
|
||||
cpyNodeCountKernel<SrcBuildT><<<1, 1, 0, mStream>>>(d_srcGrid, mDevNodeAcc);
|
||||
cudaCheckError();
|
||||
cudaCheck(cudaMemcpyAsync(&mNodeAcc, mDevNodeAcc, sizeof(NodeAccessor), cudaMemcpyDeviceToHost, mStream));// mNodeAcc = *mDevNodeAcc
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template <typename SrcBuildT>
|
||||
template <typename DstBuildT, typename BufferT>
|
||||
GridHandle<BufferT> IndexToGrid<SrcBuildT>::getHandle(const typename BuildToValueMap<DstBuildT>::type *srcValues,
|
||||
const BufferT &pool)
|
||||
{
|
||||
if (mVerbose) mTimer.start("Initiate buffer");
|
||||
auto buffer = this->template getBuffer<DstBuildT, BufferT>(pool);
|
||||
|
||||
if (mVerbose) mTimer.restart("Process grid,tree,root");
|
||||
processGridTreeRootKernel<SrcBuildT,DstBuildT><<<1, 1, 0, mStream>>>(mDevNodeAcc, srcValues);
|
||||
cudaCheckError();
|
||||
|
||||
if (mVerbose) mTimer.restart("Process root children and tiles");
|
||||
processRootTilesKernel<SrcBuildT,DstBuildT><<<mNodeAcc.nodeCount[3], 1, 0, mStream>>>(mDevNodeAcc, srcValues);
|
||||
cudaCheckError();
|
||||
|
||||
cudaCheck(util::cuda::freeAsync(mNodeAcc.d_gridName, mStream));
|
||||
|
||||
if (mVerbose) mTimer.restart("Process upper internal nodes");
|
||||
processNodesKernel<SrcBuildT,DstBuildT,2><<<mNodeAcc.nodeCount[2], dim3(32,32), 0, mStream>>>(mDevNodeAcc, srcValues);
|
||||
cudaCheckError();
|
||||
|
||||
if (mVerbose) mTimer.restart("Process lower internal nodes");
|
||||
processNodesKernel<SrcBuildT,DstBuildT,1><<<mNodeAcc.nodeCount[1], dim3(16,16), 0, mStream>>>(mDevNodeAcc, srcValues);
|
||||
cudaCheckError();
|
||||
|
||||
if (mVerbose) mTimer.restart("Process leaf nodes");
|
||||
processLeafsKernel<SrcBuildT,DstBuildT><<<mNodeAcc.nodeCount[0], dim3(8,8), 0, mStream>>>(mDevNodeAcc, srcValues);
|
||||
if (mVerbose) mTimer.stop();
|
||||
cudaCheckError();
|
||||
|
||||
if (mVerbose) mTimer.restart("Compute checksums");
|
||||
updateChecksum((GridData*)mNodeAcc.d_dstPtr, mStream);
|
||||
if (mVerbose) mTimer.stop();
|
||||
|
||||
//cudaStreamSynchronize(mStream);// finish all device tasks in mStream
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
}// IndexToGrid::getHandle
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template <typename SrcBuildT>
|
||||
template <typename DstBuildT, typename BufferT>
|
||||
inline BufferT IndexToGrid<SrcBuildT>::getBuffer(const BufferT &pool)
|
||||
{
|
||||
mNodeAcc.grid = 0;// grid is always stored at the start of the buffer!
|
||||
mNodeAcc.tree = NanoGrid<DstBuildT>::memUsage(); // grid ends and tree begins
|
||||
mNodeAcc.root = mNodeAcc.tree + NanoTree<DstBuildT>::memUsage(); // tree ends and root node begins
|
||||
mNodeAcc.node[2] = mNodeAcc.root + NanoRoot<DstBuildT>::memUsage(mNodeAcc.nodeCount[3]); // root node ends and upper internal nodes begin
|
||||
mNodeAcc.node[1] = mNodeAcc.node[2] + NanoUpper<DstBuildT>::memUsage()*mNodeAcc.nodeCount[2]; // upper internal nodes ends and lower internal nodes begin
|
||||
mNodeAcc.node[0] = mNodeAcc.node[1] + NanoLower<DstBuildT>::memUsage()*mNodeAcc.nodeCount[1]; // lower internal nodes ends and leaf nodes begin
|
||||
mNodeAcc.meta = mNodeAcc.node[0] + NanoLeaf<DstBuildT>::DataType::memUsage()*mNodeAcc.nodeCount[0];// leaf nodes end and blind meta data begins
|
||||
mNodeAcc.blind = mNodeAcc.meta + 0*sizeof(GridBlindMetaData); // meta data ends and blind data begins
|
||||
mNodeAcc.size = mNodeAcc.blind;// end of buffer
|
||||
auto buffer = BufferT::create(mNodeAcc.size, &pool, false, mStream);
|
||||
mNodeAcc.d_dstPtr = buffer.deviceData();
|
||||
if (mNodeAcc.d_dstPtr == nullptr) throw std::runtime_error("Failed memory allocation on the device");
|
||||
|
||||
if (size_t size = mGridName.size()) {
|
||||
cudaCheck(util::cuda::mallocAsync((void**)&mNodeAcc.d_gridName, size, mStream));
|
||||
cudaCheck(cudaMemcpyAsync(mNodeAcc.d_gridName, mGridName.data(), size, cudaMemcpyHostToDevice, mStream));
|
||||
} else {
|
||||
mNodeAcc.d_gridName = nullptr;
|
||||
}
|
||||
cudaCheck(cudaMemcpyAsync(mDevNodeAcc, &mNodeAcc, sizeof(NodeAccessor), cudaMemcpyHostToDevice, mStream));// copy NodeAccessor CPU -> GPU
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename DstBuildT, typename SrcBuildT, typename BufferT>
|
||||
typename util::enable_if<BuildTraits<SrcBuildT>::is_index, GridHandle<BufferT>>::type
|
||||
indexToGrid(const NanoGrid<SrcBuildT> *d_srcGrid, const typename BuildToValueMap<DstBuildT>::type *d_srcValues, const BufferT &pool, cudaStream_t stream)
|
||||
{
|
||||
IndexToGrid<SrcBuildT> converter(d_srcGrid, stream);
|
||||
return converter.template getHandle<DstBuildT>(d_srcValues, pool);
|
||||
}
|
||||
|
||||
}// namespace tools::cuda =============================================================
|
||||
|
||||
template<typename DstBuildT, typename SrcBuildT, typename BufferT = cuda::DeviceBuffer>
|
||||
[[deprecated("Use nanovdb::cuda::indexToGrid instead")]]
|
||||
typename util::enable_if<BuildTraits<SrcBuildT>::is_index, GridHandle<BufferT>>::type
|
||||
cudaIndexToGrid(const NanoGrid<SrcBuildT> *d_srcGrid, const typename BuildToValueMap<DstBuildT>::type *d_srcValues, const BufferT &pool = BufferT(), cudaStream_t stream = 0)
|
||||
{
|
||||
return tools::cuda::indexToGrid<DstBuildT, SrcBuildT, BufferT>(d_srcGrid, d_srcValues, pool, stream);
|
||||
}
|
||||
|
||||
|
||||
template<typename DstBuildT, typename SrcBuildT, typename BufferT = cuda::DeviceBuffer>
|
||||
[[deprecated("Use nanovdb::cuda::indexToGrid instead")]]
|
||||
typename util::enable_if<BuildTraits<SrcBuildT>::is_index, GridHandle<BufferT>>::type
|
||||
cudaCreateNanoGrid(const NanoGrid<SrcBuildT> *d_srcGrid, const typename BuildToValueMap<DstBuildT>::type *d_srcValues, const BufferT &pool = BufferT(), cudaStream_t stream = 0)
|
||||
{
|
||||
return tools::cuda::indexToGrid<DstBuildT, SrcBuildT, BufferT>(d_srcGrid, d_srcValues, pool, stream);
|
||||
}
|
||||
|
||||
}// nanovdb namespace ===================================================================
|
||||
|
||||
#endif // NVIDIA_TOOLS_CUDA_INDEXTOGRID_CUH_HAS_BEEN_INCLUDED
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,213 +0,0 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/tools/cuda/SignedFloodFill.cuh
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
\date May 3, 2023
|
||||
|
||||
\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
|
||||
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.
|
||||
|
||||
\warning The header file contains cuda device code so be sure
|
||||
to only include it in .cu files (or other .cuh files)
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_TOOLS_CUDA_SIGNEDFLOODFILL_CUH_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_TOOLS_CUDA_SIGNEDFLOODFILL_CUH_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/util/cuda/Timer.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
#include <nanovdb/tools/cuda/GridChecksum.cuh>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace tools::cuda {
|
||||
|
||||
/// @brief Performs signed flood-fill operation on the hierarchical tree structure on the device
|
||||
/// @tparam BuildT Build type of the grid to be flood-filled
|
||||
/// @param d_grid Non-const device pointer to the grid that will be flood-filled
|
||||
/// @param verbose If true timing information will be printed to the terminal
|
||||
/// @param stream optional cuda stream
|
||||
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
|
||||
{
|
||||
public:
|
||||
SignedFloodFill(bool verbose = false, cudaStream_t stream = 0)
|
||||
: mStream(stream), mVerbose(verbose) {}
|
||||
|
||||
/// @brief Toggle on and off verbose mode
|
||||
/// @param on if true verbose is turned on
|
||||
void setVerbose(bool on = true) {mVerbose = on;}
|
||||
|
||||
void operator()(NanoGrid<BuildT> *d_grid);
|
||||
|
||||
private:
|
||||
cudaStream_t mStream{0};
|
||||
util::cuda::Timer mTimer;
|
||||
bool mVerbose{false};
|
||||
|
||||
};// SignedFloodFill
|
||||
|
||||
//================================================================================================
|
||||
|
||||
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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
*/
|
||||
//root.setBackground(mOutside, /*updateChildNodes=*/false);
|
||||
}// processRootKernel
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename BuildT, int LEVEL>
|
||||
__global__ void processNodeKernel(NanoTree<BuildT> *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)));
|
||||
const auto &mask = node.childMask();
|
||||
if (mask.isOn(nValue)) return;// ignore if child
|
||||
auto value = 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;
|
||||
} else if ((n = mask.template findPrev<true>(nValue)) < NodeT::SIZE) {
|
||||
if (node.getChild(n)->getLastValue() < 0) value = -value;
|
||||
} else if (node.getValue(0)<0) {
|
||||
value = -value;
|
||||
}
|
||||
node.setValue(nValue, value);
|
||||
}// processNodeKernel
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename BuildT>
|
||||
__global__ void processLeafKernel(NanoTree<BuildT> *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));
|
||||
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
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template <typename BuildT>
|
||||
__global__ void cpyNodeCountKernel(NanoGrid<BuildT> *d_grid, uint64_t *d_count)
|
||||
{
|
||||
NANOVDB_ASSERT(d_grid->isSequential());
|
||||
for (int i=0; i<3; ++i) *d_count++ = d_grid->tree().nodeCount(i);
|
||||
*d_count = d_grid->tree().root().tileCount();
|
||||
}
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template <typename BuildT>
|
||||
void SignedFloodFill<BuildT>::operator()(NanoGrid<BuildT> *d_grid)
|
||||
{
|
||||
static_assert(BuildTraits<BuildT>::is_float, "cuda::SignedFloodFill only works on float grids");
|
||||
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);
|
||||
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);
|
||||
|
||||
if (mVerbose) mTimer.start("\nProcess leaf nodes");
|
||||
processLeafKernel<BuildT><<<blocksPerGrid(count[0]<<9), threadsPerBlock, 0, mStream>>>(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);
|
||||
cudaCheckError();
|
||||
|
||||
if (mVerbose) mTimer.restart("Process upper internal nodes");
|
||||
processNodeKernel<BuildT,2><<<blocksPerGrid(count[2]<<15), threadsPerBlock, 0, mStream>>>(tree, count[2]<<15);
|
||||
cudaCheckError();
|
||||
|
||||
//if (mVerbose) mTimer.restart("Process root node");
|
||||
//processRootKernel<BuildT><<<1, 1, 0, mStream>>>(tree);
|
||||
if (mVerbose) mTimer.stop();
|
||||
cudaCheckError();
|
||||
}// SignedFloodFill::operator()
|
||||
|
||||
//================================================================================================
|
||||
|
||||
template<typename BuildT>
|
||||
typename util::enable_if<BuildTraits<BuildT>::is_float, void>::type
|
||||
signedFloodFill(NanoGrid<BuildT> *d_grid, bool verbose, cudaStream_t stream)
|
||||
{
|
||||
SignedFloodFill<BuildT> sff(verbose, stream);
|
||||
sff(d_grid);
|
||||
auto *d_gridData = d_grid->data();
|
||||
Checksum cs = getChecksum(d_gridData, stream);
|
||||
if (cs.isFull()) {// CheckMode::Partial checksum is unaffected
|
||||
updateChecksum(d_gridData, CheckMode::Full, stream);
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace tools::cuda
|
||||
|
||||
template<typename BuildT>
|
||||
[[deprecated("Use nanovdb::tools::cuda::signedFloodFill instead.")]]
|
||||
typename util::enable_if<BuildTraits<BuildT>::is_float, void>::type
|
||||
cudaSignedFloodFill(NanoGrid<BuildT> *d_grid, bool verbose = false, cudaStream_t stream = 0)
|
||||
{
|
||||
return tools::cuda::signedFloodFill<BuildT>(d_grid, verbose, stream);
|
||||
}
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_TOOLS_CUDA_SIGNEDFLOODFILL_CUH_HAS_BEEN_INCLUDED
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,22 +7,22 @@
|
||||
#include <cstdio>// for FILE
|
||||
#include <cmath>
|
||||
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/tools/NanoToOpenVDB.h>
|
||||
#include <nanovdb/tools/GridValidator.h>
|
||||
#include <nanovdb/math/SampleFromVoxels.h>
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/NodeManager.h>
|
||||
#include <nanovdb/math/Ray.h>
|
||||
#include <nanovdb/tools/GridStats.h>
|
||||
#include <nanovdb/math/HDDA.h>
|
||||
#include <nanovdb/util/Timer.h>
|
||||
#include <nanovdb/tools/GridBuilder.h>
|
||||
#include <nanovdb/util/IO.h>
|
||||
#include <nanovdb/util/CreateNanoGrid.h>
|
||||
#include <nanovdb/util/NanoToOpenVDB.h>
|
||||
#include <nanovdb/util/GridValidator.h>
|
||||
#include <nanovdb/util/SampleFromVoxels.h>
|
||||
#include <nanovdb/util/Primitives.h>
|
||||
#include <nanovdb/util/NodeManager.h>
|
||||
#include <nanovdb/util/Ray.h>
|
||||
#include <nanovdb/util/GridStats.h>
|
||||
#include <nanovdb/util/HDDA.h>
|
||||
#include <nanovdb/util/CpuTimer.h>
|
||||
#include <nanovdb/util/GridBuilder.h>
|
||||
|
||||
#if !defined(_MSC_VER) // does not compile in msvc c++ due to zero-sized arrays.
|
||||
#include <nanovdb/CNanoVDB.h>
|
||||
#include <nanovdb/math/CSampleFromVoxels.h>
|
||||
#include <nanovdb/util/CSampleFromVoxels.h>
|
||||
#endif
|
||||
|
||||
#include <openvdb/openvdb.h>
|
||||
@@ -58,14 +58,12 @@ protected:
|
||||
void SetUp() override
|
||||
{
|
||||
openvdb::initialize();
|
||||
mStr = new char[256];
|
||||
// Code here will be called immediately after the constructor (right
|
||||
// before each test).
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
delete [] mStr;
|
||||
// Code here will be called immediately after each test (right
|
||||
// before the destructor).
|
||||
}
|
||||
@@ -185,7 +183,6 @@ protected:
|
||||
}
|
||||
|
||||
openvdb::util::CpuTimer mTimer;
|
||||
char *mStr;
|
||||
}; // TestOpenVDB
|
||||
|
||||
// make -j && ./unittest/testOpenVDB --gtest_break_on_failure --gtest_filter="*getExtrema"
|
||||
@@ -193,7 +190,7 @@ TEST_F(TestOpenVDB, getExtrema)
|
||||
{
|
||||
using wBBoxT = openvdb::math::BBox<openvdb::Vec3d>;
|
||||
auto srcGrid = this->getSrcGrid(false, 0, 3);// level set of a bunny if available, else an octahedron
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid, nanovdb::tools::StatsMode::All);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid, nanovdb::StatsMode::All);
|
||||
EXPECT_TRUE(handle);
|
||||
auto* dstGrid = handle.grid<float>();
|
||||
EXPECT_TRUE(dstGrid);
|
||||
@@ -208,15 +205,15 @@ TEST_F(TestOpenVDB, getExtrema)
|
||||
const wBBoxT iBBox = wBBox.applyInverseMap(*indexToWorldMap);
|
||||
//std::cerr << "Query bbox: iBBox = " << iBBox << ", wBBox = " << wBBox << std::endl;
|
||||
|
||||
const nanovdb::CoordBBox bbox(nanovdb::math::Round<nanovdb::Coord>(iBBox.min()),
|
||||
nanovdb::math::Round<nanovdb::Coord>(iBBox.max()));
|
||||
const nanovdb::CoordBBox bbox(nanovdb::Round<nanovdb::Coord>(iBBox.min()),
|
||||
nanovdb::Round<nanovdb::Coord>(iBBox.max()));
|
||||
//std::cerr << "Query index bbox = " << bbox << std::endl;
|
||||
|
||||
//nanovdb::NodeManager<nanovdb::FloatGrid> mgr(*dstGrid);
|
||||
//std::cerr << "Root child nodes: " << mgr.nodeCount(2) << std::endl;
|
||||
|
||||
//mTimer.start("getExtrema");
|
||||
nanovdb::tools::Extrema<float> ext1 = nanovdb::tools::getExtrema(*dstGrid, bbox), ext2;
|
||||
nanovdb::Extrema<float> ext1 = nanovdb::getExtrema(*dstGrid, bbox), ext2;
|
||||
//mTimer.restart("naive approach");
|
||||
for (auto it = bbox.begin(); it; ++it) ext2.add(dstAcc.getValue(*it));
|
||||
//mTimer.stop();
|
||||
@@ -245,9 +242,9 @@ TEST_F(TestOpenVDB, MapToNano)
|
||||
EXPECT_EQ(ijk2, nanovdb::Coord(1, 2, -4));
|
||||
}
|
||||
{// Vec3f
|
||||
constexpr bool test1 = nanovdb::util::is_same<nanovdb::Vec3f, nanovdb::tools::MapToNano<openvdb::Vec3f>::type>::value;
|
||||
constexpr bool test1 = nanovdb::is_same<nanovdb::Vec3f, nanovdb::MapToNano<openvdb::Vec3f>::type>::value;
|
||||
EXPECT_TRUE(test1);
|
||||
constexpr bool test2 = nanovdb::util::is_same<nanovdb::Vec3d, nanovdb::tools::MapToNano<openvdb::Vec3f>::type>::value;
|
||||
constexpr bool test2 = nanovdb::is_same<nanovdb::Vec3d, nanovdb::MapToNano<openvdb::Vec3f>::type>::value;
|
||||
EXPECT_FALSE(test2);
|
||||
const openvdb::Vec3f xyz1(1, 2, -4);
|
||||
nanovdb::Vec3f xyz2(-2, 7, 9);
|
||||
@@ -256,9 +253,9 @@ TEST_F(TestOpenVDB, MapToNano)
|
||||
EXPECT_EQ(xyz2, nanovdb::Vec3f(1, 2, -4));
|
||||
}
|
||||
{// Vec4d
|
||||
constexpr bool test1 = nanovdb::util::is_same<nanovdb::Vec4d, nanovdb::tools::MapToNano<openvdb::Vec4d>::type>::value;
|
||||
constexpr bool test1 = nanovdb::is_same<nanovdb::Vec4d, nanovdb::MapToNano<openvdb::Vec4d>::type>::value;
|
||||
EXPECT_TRUE(test1);
|
||||
constexpr bool test2 = nanovdb::util::is_same<nanovdb::Vec4f, nanovdb::tools::MapToNano<openvdb::Vec4d>::type>::value;
|
||||
constexpr bool test2 = nanovdb::is_same<nanovdb::Vec4f, nanovdb::MapToNano<openvdb::Vec4d>::type>::value;
|
||||
EXPECT_FALSE(test2);
|
||||
const openvdb::Vec4d xyz1(1, 2, -4, 7);
|
||||
nanovdb::Vec4d xyz2(-2, 7, 9, -4);
|
||||
@@ -267,9 +264,9 @@ TEST_F(TestOpenVDB, MapToNano)
|
||||
EXPECT_EQ(xyz2, nanovdb::Vec4d(1, 2, -4, 7));
|
||||
}
|
||||
{// MaskValue
|
||||
constexpr bool test1 = nanovdb::util::is_same<nanovdb::ValueMask, nanovdb::tools::MapToNano<openvdb::ValueMask>::type>::value;
|
||||
constexpr bool test1 = nanovdb::is_same<nanovdb::ValueMask, nanovdb::MapToNano<openvdb::ValueMask>::type>::value;
|
||||
EXPECT_TRUE(test1);
|
||||
constexpr bool test2 = nanovdb::util::is_same<nanovdb::Vec3f, nanovdb::tools::MapToNano<openvdb::ValueMask>::type>::value;
|
||||
constexpr bool test2 = nanovdb::is_same<nanovdb::Vec3f, nanovdb::MapToNano<openvdb::ValueMask>::type>::value;
|
||||
EXPECT_FALSE(test2);
|
||||
EXPECT_EQ(sizeof(nanovdb::ValueMask), sizeof(openvdb::ValueMask));
|
||||
}
|
||||
@@ -298,8 +295,8 @@ TEST_F(TestOpenVDB, BasicGrid)
|
||||
|
||||
const std::string name("test name");
|
||||
|
||||
EXPECT_EQ(nanovdb::math::AlignUp<NANOVDB_DATA_ALIGNMENT>(8 + 8 + 2 + 2 + 4 + 8 + nanovdb::GridData::MaxNameSize + 48 + sizeof(nanovdb::Map) + 24 + 4 + 4 + 8 + 4), sizeof(GridT));
|
||||
EXPECT_EQ(nanovdb::math::AlignUp<NANOVDB_DATA_ALIGNMENT>(4*8 + 2 * 4 * 3 + 8), sizeof(TreeT));
|
||||
EXPECT_EQ(nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(8 + 8 + 2 + 2 + 4 + 8 + nanovdb::GridData::MaxNameSize + 48 + sizeof(nanovdb::Map) + 24 + 4 + 4 + 8 + 4), sizeof(GridT));
|
||||
EXPECT_EQ(nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(4*8 + 2 * 4 * 3 + 8), sizeof(TreeT));
|
||||
EXPECT_EQ(size_t(4*8 + 2 * 4 * 3 + 8), sizeof(TreeT));// should already be 32 byte aligned
|
||||
|
||||
size_t bytes[9];
|
||||
@@ -527,7 +524,7 @@ TEST_F(TestOpenVDB, BasicGrid)
|
||||
EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), grid->version().getPatch());
|
||||
EXPECT_TRUE(grid->isValid());
|
||||
EXPECT_EQ(grid->gridType(), nanovdb::GridType::Float);
|
||||
EXPECT_EQ(grid->gridClass(),nanovdb::GridClass::Unknown);
|
||||
EXPECT_EQ(grid->gridClass(), nanovdb::GridClass::Unknown);
|
||||
EXPECT_FALSE(grid->isLevelSet());
|
||||
EXPECT_FALSE(grid->isFogVolume());
|
||||
EXPECT_FALSE(grid->isStaggered());
|
||||
@@ -556,35 +553,12 @@ TEST_F(TestOpenVDB, BasicGrid)
|
||||
}
|
||||
} // BaseGrid
|
||||
|
||||
|
||||
TEST_F(TestOpenVDB, MagicType)
|
||||
{
|
||||
{// toMagic(uint64_t)
|
||||
EXPECT_EQ( nanovdb::toMagic(NANOVDB_MAGIC_NUMB), nanovdb::MagicType::NanoVDB );
|
||||
EXPECT_EQ( nanovdb::toMagic(NANOVDB_MAGIC_GRID), nanovdb::MagicType::NanoGrid );
|
||||
EXPECT_EQ( nanovdb::toMagic(NANOVDB_MAGIC_FILE), nanovdb::MagicType::NanoFile );
|
||||
EXPECT_EQ( nanovdb::toMagic(NANOVDB_MAGIC_NODE), nanovdb::MagicType::NanoNode );
|
||||
EXPECT_EQ( nanovdb::toMagic(NANOVDB_MAGIC_FRAG), nanovdb::MagicType::NanoFrag );
|
||||
EXPECT_EQ( nanovdb::toMagic( 0x56444220UL), nanovdb::MagicType::OpenVDB );
|
||||
}
|
||||
|
||||
{// toStr(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 );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestOpenVDB, OpenToNanoVDB_Empty)
|
||||
{
|
||||
{ // empty grid
|
||||
openvdb::FloatGrid srcGrid(0.0f);
|
||||
auto srcAcc = srcGrid.getAccessor();
|
||||
auto handle = nanovdb::tools::createNanoGrid(srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(srcGrid);
|
||||
EXPECT_TRUE(handle);
|
||||
auto* meta = handle.gridMetaData();
|
||||
EXPECT_TRUE(meta);
|
||||
@@ -619,7 +593,7 @@ TEST_F(TestOpenVDB, OpenToNanoVDB_Basic1)
|
||||
srcAcc.setValue(openvdb::Coord(1, 2, 3), 1.0f);
|
||||
EXPECT_TRUE(srcAcc.isValueOn(openvdb::Coord(1, 2, 3)));
|
||||
EXPECT_EQ(1.0f, srcAcc.getValue(openvdb::Coord(1, 2, 3)));
|
||||
auto handle = nanovdb::tools::createNanoGrid(srcGrid, nanovdb::tools::StatsMode::All);
|
||||
auto handle = nanovdb::createNanoGrid(srcGrid, nanovdb::StatsMode::All);
|
||||
EXPECT_TRUE(handle);
|
||||
auto* meta = handle.gridMetaData();
|
||||
EXPECT_TRUE(meta);
|
||||
@@ -658,13 +632,13 @@ TEST_F(TestOpenVDB, OpenToNanoVDB_Model)
|
||||
{
|
||||
auto srcGrid = this->getSrcGrid(false);
|
||||
//mTimer.start("Generating NanoVDB grid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
//mTimer.start("Writing NanoVDB grid");
|
||||
nanovdb::io::writeGrid("data/test.nvdb", handle, this->getCodec());
|
||||
//mTimer.stop();
|
||||
|
||||
auto dstGrid = handle.grid<float>();
|
||||
EXPECT_TRUE(nanovdb::isAligned(dstGrid));
|
||||
EXPECT_TRUE(nanovdb::isValid(dstGrid));
|
||||
|
||||
auto kernel = [&](const openvdb::CoordBBox& bbox) {
|
||||
using CoordT = const nanovdb::Coord;
|
||||
@@ -705,9 +679,9 @@ TEST_F(TestOpenVDB, OpenToNanoVDB_Fp4)
|
||||
EXPECT_EQ(2.0f, srcAcc.getValue(openvdb::Coord(-10, 20,-50)));
|
||||
EXPECT_EQ(3.0f, srcAcc.getValue(openvdb::Coord( 50,-12, 30)));
|
||||
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> converter(srcGrid);
|
||||
nanovdb::CreateNanoGrid<openvdb::FloatGrid> converter(srcGrid);
|
||||
//converter.setVerbose();
|
||||
converter.setStats(nanovdb::tools::StatsMode::All);
|
||||
converter.setStats(nanovdb::StatsMode::All);
|
||||
auto handle = converter.getHandle<nanovdb::Fp4>();// (srcGrid);
|
||||
|
||||
EXPECT_TRUE(handle);
|
||||
@@ -759,7 +733,7 @@ TEST_F(TestOpenVDB, OpenToNanoVDB_Fp4)
|
||||
{// Model
|
||||
auto openGrid = this->getSrcGrid(false);
|
||||
const float tolerance = 0.5f*openGrid->voxelSize()[0];
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> converter(*openGrid);
|
||||
nanovdb::CreateNanoGrid<openvdb::FloatGrid> converter(*openGrid);
|
||||
converter.enableDithering();
|
||||
//converter.setVerbose(2);
|
||||
auto handle = converter.getHandle<nanovdb::Fp4>();
|
||||
@@ -800,8 +774,8 @@ TEST_F(TestOpenVDB, OpenToNanoVDB_Fp8)
|
||||
EXPECT_EQ(2.0f, srcAcc.getValue(openvdb::Coord(-10, 20,-50)));
|
||||
EXPECT_EQ(3.0f, srcAcc.getValue(openvdb::Coord( 50,-12, 30)));
|
||||
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> converter(srcGrid);
|
||||
converter.setStats(nanovdb::tools::StatsMode::All);
|
||||
nanovdb::CreateNanoGrid<openvdb::FloatGrid> converter(srcGrid);
|
||||
converter.setStats(nanovdb::StatsMode::All);
|
||||
auto handle = converter.getHandle<nanovdb::Fp8>();
|
||||
|
||||
EXPECT_TRUE(handle);
|
||||
@@ -842,7 +816,7 @@ TEST_F(TestOpenVDB, OpenToNanoVDB_Fp8)
|
||||
{// Model
|
||||
auto openGrid = this->getSrcGrid(false);
|
||||
const float tolerance = 0.05f*openGrid->voxelSize()[0];
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> converter(*openGrid);
|
||||
nanovdb::CreateNanoGrid<openvdb::FloatGrid> converter(*openGrid);
|
||||
auto handle = converter.getHandle<nanovdb::Fp8>();
|
||||
converter.enableDithering();
|
||||
//converter.setVerbose(2);
|
||||
@@ -884,9 +858,9 @@ TEST_F(TestOpenVDB, OpenToNanoVDB_Fp16)
|
||||
EXPECT_EQ(2.0f, srcAcc.getValue(openvdb::Coord(-10, 20,-50)));
|
||||
EXPECT_EQ(3.0f, srcAcc.getValue(openvdb::Coord( 50,-12, 30)));
|
||||
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> converter(srcGrid);
|
||||
nanovdb::CreateNanoGrid<openvdb::FloatGrid> converter(srcGrid);
|
||||
//converter.setVerbose(2);
|
||||
converter.setStats(nanovdb::tools::StatsMode::All);
|
||||
converter.setStats(nanovdb::StatsMode::All);
|
||||
auto handle = converter.getHandle<nanovdb::Fp16>();
|
||||
|
||||
EXPECT_TRUE(handle);
|
||||
@@ -928,7 +902,7 @@ TEST_F(TestOpenVDB, OpenToNanoVDB_Fp16)
|
||||
{// Model
|
||||
auto openGrid = this->getSrcGrid(false);
|
||||
const float tolerance = 0.005f*openGrid->voxelSize()[0];
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> converter(*openGrid);
|
||||
nanovdb::CreateNanoGrid<openvdb::FloatGrid> converter(*openGrid);
|
||||
converter.enableDithering();
|
||||
auto handle = converter.getHandle<nanovdb::Fp16>();
|
||||
//converter.setVerbose(2);
|
||||
@@ -970,8 +944,8 @@ TEST_F(TestOpenVDB, OpenToNanoVDB_FpN)
|
||||
EXPECT_EQ(2.0f, srcAcc.getValue(openvdb::Coord(-10, 20,-50)));
|
||||
EXPECT_EQ(3.0f, srcAcc.getValue(openvdb::Coord( 50,-12, 30)));
|
||||
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> converter(srcGrid);
|
||||
converter.setStats(nanovdb::tools::StatsMode::All);
|
||||
nanovdb::CreateNanoGrid<openvdb::FloatGrid> converter(srcGrid);
|
||||
converter.setStats(nanovdb::StatsMode::All);
|
||||
auto handle = converter.getHandle<nanovdb::FpN>();
|
||||
|
||||
EXPECT_TRUE(handle);
|
||||
@@ -1016,11 +990,11 @@ TEST_F(TestOpenVDB, OpenToNanoVDB_FpN)
|
||||
#else
|
||||
auto openGrid = this->getSrcGrid(true, 1, 1);// FOG volume of Disney cloud or cube
|
||||
#endif
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> converter(*openGrid);
|
||||
nanovdb::CreateNanoGrid<openvdb::FloatGrid> converter(*openGrid);
|
||||
//converter.setVerbose(2);
|
||||
|
||||
const float tolerance = 0.05f;
|
||||
nanovdb::tools::AbsDiff oracle(tolerance);
|
||||
nanovdb::AbsDiff oracle(tolerance);
|
||||
|
||||
auto handle = converter.getHandle<nanovdb::FpN>(oracle);
|
||||
auto* nanoGrid = handle.grid<nanovdb::FpN>();
|
||||
@@ -1040,13 +1014,13 @@ TEST_F(TestOpenVDB, OpenToNanoVDB_FpN)
|
||||
EXPECT_TRUE( oracle(exact, approx) );
|
||||
}
|
||||
};
|
||||
nanovdb::util::forEach(openGrid->evalActiveVoxelBoundingBox(), kernel);
|
||||
nanovdb::forEach(openGrid->evalActiveVoxelBoundingBox(), kernel);
|
||||
|
||||
handle = nanovdb::io::readGrid("data/test_fpN.nvdb");
|
||||
nanoGrid = handle.grid<nanovdb::FpN>();
|
||||
EXPECT_TRUE(nanoGrid);
|
||||
|
||||
nanovdb::util::forEach(openGrid->evalActiveVoxelBoundingBox(), kernel);
|
||||
nanovdb::forEach(openGrid->evalActiveVoxelBoundingBox(), kernel);
|
||||
}
|
||||
} // OpenToNanoVDB_FpN
|
||||
|
||||
@@ -1124,7 +1098,7 @@ TEST_F(TestOpenVDB, PointIndexGrid)
|
||||
EXPECT_EQ(pointCount, count);
|
||||
|
||||
//mTimer.start("Generating NanoVDB grid from PointIndexGrid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid, nanovdb::tools::StatsMode::All, nanovdb::CheckMode::Full);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid, nanovdb::StatsMode::All, nanovdb::ChecksumMode::Full);
|
||||
//mTimer.stop();
|
||||
EXPECT_TRUE(handle);
|
||||
auto* meta = handle.gridMetaData();
|
||||
@@ -1236,7 +1210,7 @@ TEST_F(TestOpenVDB, PointDataGridBasic)
|
||||
srcGrid->setName("PointDataGrid");
|
||||
|
||||
//mTimer.start("Generating NanoVDB grid from PointDataGrid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
//mTimer.stop();
|
||||
|
||||
EXPECT_TRUE(handle);
|
||||
@@ -1268,7 +1242,7 @@ TEST_F(TestOpenVDB, PointDataGridBasic)
|
||||
// Create a read-only AttributeHandle. Position always uses Vec3f.
|
||||
openvdb::points::AttributeHandle<openvdb::Vec3f> positionHandle(leafIter->constAttributeArray("P"));
|
||||
openvdb::Coord ijkSrc(openvdb::Coord::min());
|
||||
nanovdb::Coord ijkDst(nanovdb::math::Maximum<int>::value());
|
||||
nanovdb::Coord ijkDst(nanovdb::Maximum<int>::value());
|
||||
for (auto indexIter = leafIter->beginIndexOn(); indexIter; ++indexIter) {
|
||||
// Extract the local voxel-space position of the point relative to its occupying voxel ijk.
|
||||
const openvdb::Vec3f vxlSrc = positionHandle.get(*indexIter);
|
||||
@@ -1356,7 +1330,7 @@ TEST_F(TestOpenVDB, PointDataGridRandom)
|
||||
srcGrid->setName("PointDataGrid");
|
||||
|
||||
//mTimer.start("Generating NanoVDB grid from PointDataGrid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
//mTimer.stop();
|
||||
|
||||
EXPECT_TRUE(handle);
|
||||
@@ -1380,7 +1354,7 @@ TEST_F(TestOpenVDB, PointDataGridRandom)
|
||||
// Create a read-only AttributeHandle. Position always uses Vec3f.
|
||||
openvdb::points::AttributeHandle<openvdb::Vec3f> positionHandle(leafIter->constAttributeArray("P"));
|
||||
openvdb::Coord ijkSrc(openvdb::Coord::min());
|
||||
nanovdb::Coord ijkDst(nanovdb::math::Maximum<int>::value());
|
||||
nanovdb::Coord ijkDst(nanovdb::Maximum<int>::value());
|
||||
for (auto indexIter = leafIter->beginIndexOn(); indexIter; ++indexIter) {
|
||||
// Extract the local voxel-space position of the point relative to its occupying voxel ijk.
|
||||
const openvdb::Vec3f vxlSrc = positionHandle.get(*indexIter);
|
||||
@@ -1455,7 +1429,7 @@ TEST_F(TestOpenVDB, CNanoVDB)
|
||||
{
|
||||
auto srcGrid = this->getSrcGrid();
|
||||
//mTimer.start("Generating NanoVDB grid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
//mTimer.stop();
|
||||
EXPECT_TRUE(handle);
|
||||
EXPECT_TRUE(handle.data());
|
||||
@@ -1486,7 +1460,7 @@ TEST_F(TestOpenVDB, CNanoVDBTrilinear)
|
||||
{
|
||||
auto srcGrid = this->getSrcGrid();
|
||||
//mTimer.start("Generating NanoVDB grid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
//mTimer.stop();
|
||||
EXPECT_TRUE(handle);
|
||||
EXPECT_TRUE(handle.data());
|
||||
@@ -1528,7 +1502,7 @@ TEST_F(TestOpenVDB, CNanoVDBTrilinearStencil)
|
||||
{
|
||||
auto srcGrid = this->getSrcGrid();
|
||||
//mTimer.start("Generating NanoVDB grid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
//mTimer.stop();
|
||||
EXPECT_TRUE(handle);
|
||||
EXPECT_TRUE(handle.data());
|
||||
@@ -1569,13 +1543,13 @@ TEST_F(TestOpenVDB, CNanoVDBTrilinearStencil)
|
||||
|
||||
TEST_F(TestOpenVDB, NanoToOpenVDB_BuildGrid)
|
||||
{// test build::Grid -> NanoVDB -> OpenVDB
|
||||
nanovdb::tools::build::Grid<float> buildGrid(0.0f, "test", nanovdb::GridClass::LevelSet);
|
||||
nanovdb::build::Grid<float> buildGrid(0.0f, "test", nanovdb::GridClass::LevelSet);
|
||||
auto buildAcc = buildGrid.getAccessor();
|
||||
buildAcc.setValue(nanovdb::Coord(1, 2, 3), 1.0f);
|
||||
buildAcc.setValue(nanovdb::Coord(2, -2, 9), 2.0f);
|
||||
EXPECT_EQ(1.0f, buildAcc.getValue(nanovdb::Coord(1, 2, 3)));
|
||||
EXPECT_EQ(2.0f, buildAcc.getValue(nanovdb::Coord(2, -2, 9)));
|
||||
auto handle = nanovdb::tools::createNanoGrid(buildGrid);
|
||||
auto handle = nanovdb::createNanoGrid(buildGrid);
|
||||
EXPECT_TRUE(handle);
|
||||
auto* meta = handle.gridMetaData();
|
||||
EXPECT_TRUE(meta);
|
||||
@@ -1591,7 +1565,7 @@ TEST_F(TestOpenVDB, NanoToOpenVDB_BuildGrid)
|
||||
EXPECT_EQ(1.0f, nanoAcc.getValue(nanovdb::Coord(1, 2, 3)));
|
||||
EXPECT_EQ(2.0f, nanoAcc.getValue(nanovdb::Coord(2, -2, 9)));
|
||||
|
||||
auto openGrid = nanovdb::tools::nanoToOpenVDB(*nanoGrid);
|
||||
auto openGrid = nanovdb::nanoToOpenVDB(*nanoGrid);
|
||||
EXPECT_TRUE(openGrid);
|
||||
auto openAcc = openGrid->getAccessor();
|
||||
EXPECT_EQ(1.0f, openAcc.getValue(openvdb::Coord(1, 2, 3)));
|
||||
@@ -1620,7 +1594,7 @@ TEST_F(TestOpenVDB, NanoToOpenVDB)
|
||||
//std::cerr << "Grid name: " << srcGrid->gridName() << std::endl;
|
||||
|
||||
//mTimer.start("Deserializing NanoVDB grid");
|
||||
auto dstGrid = nanovdb::tools::nanoToOpenVDB(*srcGrid);
|
||||
auto dstGrid = nanovdb::nanoToOpenVDB(*srcGrid);
|
||||
//mTimer.stop();
|
||||
EXPECT_TRUE(dstGrid);
|
||||
|
||||
@@ -1704,13 +1678,13 @@ TEST_F(TestOpenVDB, MultiFile)
|
||||
grid.setName("Int32 grid");
|
||||
grid.tree().setValue(openvdb::Coord(-256), 10);
|
||||
EXPECT_EQ(1u, grid.activeVoxelCount());
|
||||
handles.push_back(nanovdb::tools::createNanoGrid(grid));
|
||||
handles.push_back(nanovdb::createNanoGrid(grid));
|
||||
}
|
||||
{ // 2: add an empty int32_t grid
|
||||
openvdb::Int32Grid grid(-4);
|
||||
grid.setName("Int32 grid, empty");
|
||||
EXPECT_EQ(0u, grid.activeVoxelCount());
|
||||
handles.push_back(nanovdb::tools::createNanoGrid(grid));
|
||||
handles.push_back(nanovdb::createNanoGrid(grid));
|
||||
}
|
||||
{ // 3: add a ValueMask grid
|
||||
openvdb::MaskGrid grid(false);
|
||||
@@ -1724,7 +1698,7 @@ TEST_F(TestOpenVDB, MultiFile)
|
||||
grid.tree().evalActiveVoxelBoundingBox(bbox);
|
||||
//std::cerr << bbox << std::endl;
|
||||
EXPECT_EQ(openvdb::CoordBBox(min, max), bbox);
|
||||
handles.push_back(nanovdb::tools::createNanoGrid(grid));
|
||||
handles.push_back(nanovdb::createNanoGrid(grid));
|
||||
}
|
||||
{ // 4: add a bool grid
|
||||
openvdb::BoolGrid grid(false);
|
||||
@@ -1733,7 +1707,7 @@ TEST_F(TestOpenVDB, MultiFile)
|
||||
EXPECT_EQ(1u, grid.activeVoxelCount());
|
||||
grid.tree().setValue(openvdb::Coord( 10, 450, 90), true);
|
||||
EXPECT_EQ(2u, grid.activeVoxelCount());
|
||||
handles.push_back(nanovdb::tools::createNanoGrid(grid));
|
||||
handles.push_back(nanovdb::createNanoGrid(grid));
|
||||
}
|
||||
{ // 5: add a Vec3f grid
|
||||
openvdb::Vec3fGrid grid(openvdb::Vec3f(0.0f, 0.0f, -1.0f));
|
||||
@@ -1742,7 +1716,7 @@ TEST_F(TestOpenVDB, MultiFile)
|
||||
EXPECT_EQ(0u, grid.activeVoxelCount());
|
||||
grid.tree().setValue(openvdb::Coord(-256), openvdb::Vec3f(1.0f, 0.0f, 0.0f));
|
||||
EXPECT_EQ(1u, grid.activeVoxelCount());
|
||||
handles.push_back(nanovdb::tools::createNanoGrid(grid));
|
||||
handles.push_back(nanovdb::createNanoGrid(grid));
|
||||
}
|
||||
{ // 6: add a Vec4f grid
|
||||
using OpenVDBVec4fGrid = openvdb::Grid<openvdb::tree::Tree4<openvdb::Vec4f, 5, 4, 3>::Type>;
|
||||
@@ -1753,7 +1727,7 @@ TEST_F(TestOpenVDB, MultiFile)
|
||||
EXPECT_EQ(0u, grid.activeVoxelCount());
|
||||
grid.tree().setValue(openvdb::Coord(-256), openvdb::Vec4f(1.0f, 0.0f, 0.0f, 0.0f));
|
||||
EXPECT_EQ(1u, grid.activeVoxelCount());
|
||||
handles.push_back(nanovdb::tools::createNanoGrid(grid));
|
||||
handles.push_back(nanovdb::createNanoGrid(grid));
|
||||
OpenVDBVec4fGrid::unregisterGrid();
|
||||
}
|
||||
{ // 7: add an int64_t grid
|
||||
@@ -1761,7 +1735,7 @@ TEST_F(TestOpenVDB, MultiFile)
|
||||
grid.setName("Int64 grid");
|
||||
grid.tree().setValue(openvdb::Coord(0), 10);
|
||||
EXPECT_EQ(1u, grid.activeVoxelCount());
|
||||
handles.push_back(nanovdb::tools::createNanoGrid(grid));
|
||||
handles.push_back(nanovdb::createNanoGrid(grid));
|
||||
}
|
||||
for (int i = 0; i < 10; ++i) {// 8 -> 17
|
||||
const float radius = 100.0f;
|
||||
@@ -1769,7 +1743,7 @@ TEST_F(TestOpenVDB, MultiFile)
|
||||
const openvdb::Vec3f center(i * 10.0f, 0.0f, 0.0f);
|
||||
auto srcGrid = openvdb::tools::createLevelSetSphere<openvdb::FloatGrid>(radius, center, voxelSize, width);
|
||||
srcGrid->setName("Level set sphere at (" + std::to_string(i * 10) + ",0,0)");
|
||||
handles.push_back(nanovdb::tools::createNanoGrid(*srcGrid));
|
||||
handles.push_back(nanovdb::createNanoGrid(*srcGrid));
|
||||
}
|
||||
{ // 18: add a double grid
|
||||
openvdb::DoubleGrid grid(0.0);
|
||||
@@ -1777,7 +1751,7 @@ TEST_F(TestOpenVDB, MultiFile)
|
||||
grid.setGridClass(openvdb::GRID_FOG_VOLUME);
|
||||
grid.tree().setValue(openvdb::Coord(6000), 1.0);
|
||||
EXPECT_EQ(1u, grid.activeVoxelCount());
|
||||
handles.push_back(nanovdb::tools::createNanoGrid(grid));
|
||||
handles.push_back(nanovdb::createNanoGrid(grid));
|
||||
}
|
||||
|
||||
nanovdb::io::writeGrids<nanovdb::HostBuffer, std::vector>("data/multi.nvdb", handles, this->getCodec());
|
||||
@@ -1824,15 +1798,15 @@ TEST_F(TestOpenVDB, MultiFile)
|
||||
EXPECT_EQ(1u, tree.nodeCount(2));
|
||||
auto mgrHandle = nanovdb::createNodeManager(*grid);
|
||||
auto *mgr = mgrHandle.mgr<int32_t>();
|
||||
EXPECT_TRUE(nanovdb::isAligned(mgr));
|
||||
EXPECT_TRUE(nanovdb::isValid(mgr));
|
||||
const auto& leaf = mgr->leaf(0);
|
||||
EXPECT_TRUE(nanovdb::isAligned(&leaf));
|
||||
EXPECT_TRUE(nanovdb::isValid(&leaf));
|
||||
EXPECT_EQ(bbox, leaf.bbox());
|
||||
const auto& node1 = mgr->lower(0);
|
||||
EXPECT_TRUE(nanovdb::isAligned(&node1));
|
||||
EXPECT_TRUE(nanovdb::isValid(&node1));
|
||||
EXPECT_EQ(bbox, node1.bbox());
|
||||
const auto& node2 = mgr->upper(0);
|
||||
EXPECT_TRUE(nanovdb::isAligned(&node2));
|
||||
EXPECT_TRUE(nanovdb::isValid(&node2));
|
||||
EXPECT_EQ(bbox, node2.bbox());
|
||||
EXPECT_FALSE(grid->isLevelSet());
|
||||
EXPECT_FALSE(grid->isFogVolume());
|
||||
@@ -2076,9 +2050,9 @@ TEST_F(TestOpenVDB, LongGridName)
|
||||
EXPECT_EQ(1u, srcGrid.activeVoxelCount());
|
||||
const bool isLong = length > limit;
|
||||
#if 1
|
||||
auto handle = nanovdb::tools::createNanoGrid(srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(srcGrid);
|
||||
#else
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> converter(srcGrid);
|
||||
nanovdb::CreateNanoGrid<openvdb::FloatGrid> converter(srcGrid);
|
||||
auto handle = converter.getHandle<float>();
|
||||
#endif
|
||||
auto* dstGrid = handle.grid<float>();
|
||||
@@ -2118,8 +2092,8 @@ TEST_F(TestOpenVDB, LevelSetFiles)
|
||||
foundModels.push_back(fileName.substr(pos, fileName.size() - pos - 4 ));
|
||||
|
||||
//mTimer.restart("Generating NanoVDB grid");
|
||||
//auto handle = nanovdb::tools::createNanoGrid(*srcGrid, nanovdb::tools::StatsMode::All, nanovdb::CheckMode::Partial);
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid, nanovdb::tools::StatsMode::BBox, nanovdb::CheckMode::Disable);
|
||||
//auto handle = nanovdb::createNanoGrid(*srcGrid, nanovdb::StatsMode::All, nanovdb::ChecksumMode::Partial);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid, nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Disable);
|
||||
//mTimer.restart("Writing NanoVDB grid");
|
||||
|
||||
nanovdb::io::writeGrid(os, handle, this->getCodec());
|
||||
@@ -2196,7 +2170,7 @@ TEST_F(TestOpenVDB, FogFiles)
|
||||
foundModels.push_back(fileName.substr(pos, fileName.size() - pos - 4 ));
|
||||
|
||||
//mTimer.restart("Generating NanoVDB grid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid, nanovdb::tools::StatsMode::All, nanovdb::CheckMode::Partial);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid, nanovdb::StatsMode::All, nanovdb::ChecksumMode::Partial);
|
||||
//mTimer.restart("Writing NanoVDB grid");
|
||||
nanovdb::io::writeGrid(os, handle, this->getCodec());
|
||||
|
||||
@@ -2271,7 +2245,7 @@ TEST_F(TestOpenVDB, PointFiles)
|
||||
EXPECT_TRUE(positionIndex != openvdb::points::AttributeSet::INVALID_POS);
|
||||
|
||||
//mTimer.restart("Generating NanoVDB grid from PointDataGrid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
//mTimer.restart("Writing NanoVDB grid");
|
||||
nanovdb::io::writeGrid(os, handle, this->getCodec());
|
||||
|
||||
@@ -2294,7 +2268,7 @@ TEST_F(TestOpenVDB, PointFiles)
|
||||
// Create a read-only AttributeHandle. Position always uses Vec3f.
|
||||
openvdb::points::AttributeHandle<openvdb::Vec3f> positionHandle(leafIter->constAttributeArray("P"));
|
||||
openvdb::Coord ijkSrc(openvdb::Coord::min());
|
||||
nanovdb::Coord ijkDst(nanovdb::math::Maximum<int>::value());
|
||||
nanovdb::Coord ijkDst(nanovdb::Maximum<int>::value());
|
||||
for (auto indexIter = leafIter->beginIndexOn(); indexIter; ++indexIter) {
|
||||
// Extract the index-space position of the point relative to its occupying voxel ijk.
|
||||
const openvdb::Vec3f vxlSrc = positionHandle.get(*indexIter);
|
||||
@@ -2352,7 +2326,7 @@ TEST_F(TestOpenVDB, Trilinear)
|
||||
acc.setValue(ijk, trilinear(srcGrid->indexToWorld(ijk)));
|
||||
}
|
||||
//mTimer.restart("Generating NanoVDB grid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
//mTimer.restart("Writing NanoVDB grid");
|
||||
nanovdb::io::writeGrid("data/tmp.nvdb", handle);
|
||||
//mTimer.stop();
|
||||
@@ -2375,11 +2349,11 @@ TEST_F(TestOpenVDB, Trilinear)
|
||||
//std::cerr << "Trilinear: exact = " << exact << ", approx = " << approx << std::endl;
|
||||
|
||||
auto dstAcc = dstGrid->getAccessor();
|
||||
auto sampler0 = nanovdb::math::createSampler<0>(dstAcc);
|
||||
auto sampler0 = nanovdb::createSampler<0>(dstAcc);
|
||||
//std::cerr << "0'th order: v = " << sampler0(ijk) << std::endl;
|
||||
EXPECT_EQ(approx, sampler0(ijk));
|
||||
|
||||
auto sampler1 = nanovdb::math::createSampler<1>(dstAcc); // faster since it's using an accessor!!!
|
||||
auto sampler1 = nanovdb::createSampler<1>(dstAcc); // faster since it's using an accessor!!!
|
||||
//std::cerr << "1'th order: v = " << sampler1(ijk) << std::endl;
|
||||
EXPECT_EQ(exact, sampler1(ijk));
|
||||
|
||||
@@ -2393,8 +2367,8 @@ TEST_F(TestOpenVDB, Trilinear)
|
||||
EXPECT_NEAR(6.7f, gradWorld[1], 1e-5);
|
||||
EXPECT_NEAR(-3.5f, gradWorld[2], 1e-5);
|
||||
|
||||
nanovdb::math::SampleFromVoxels<nanovdb::NanoTree<float>, 3> sampler3(dstGrid->tree());
|
||||
//auto sampler3 = nanovdb::math::createSampler<3>( dstAcc );
|
||||
nanovdb::SampleFromVoxels<nanovdb::NanoTree<float>, 3> sampler3(dstGrid->tree());
|
||||
//auto sampler3 = nanovdb::createSampler<3>( dstAcc );
|
||||
//std::cerr << "3'rd order: v = " << sampler3(ijk) << std::endl;
|
||||
EXPECT_EQ(exact, sampler3(ijk));
|
||||
} // Trilinear
|
||||
@@ -2418,7 +2392,7 @@ TEST_F(TestOpenVDB, Triquadratic)
|
||||
acc.setValue(ijk, triquadratic(srcGrid->indexToWorld(ijk)));
|
||||
}
|
||||
//mTimer.restart("Generating NanoVDB grid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
//mTimer.restart("Writing NanoVDB grid");
|
||||
nanovdb::io::writeGrid("data/tmp.nvdb", handle);
|
||||
//mTimer.stop();
|
||||
@@ -2440,21 +2414,21 @@ TEST_F(TestOpenVDB, Triquadratic)
|
||||
//std::cerr << "Trilinear: exact = " << exact << ", approx = " << approx << std::endl;
|
||||
auto dstAcc = dstGrid->getAccessor();
|
||||
|
||||
auto sampler0 = nanovdb::math::createSampler<0>(dstAcc);
|
||||
auto sampler0 = nanovdb::createSampler<0>(dstAcc);
|
||||
//std::cerr << "0'th order: v = " << sampler0(ijk) << std::endl;
|
||||
EXPECT_NEAR(approx, sampler0(ijk), 1e-6);
|
||||
|
||||
auto sampler1 = nanovdb::math::createSampler<1>(dstAcc);
|
||||
auto sampler1 = nanovdb::createSampler<1>(dstAcc);
|
||||
//std::cerr << "1'rd order: nanovdb = " << sampler1(ijk) << ", openvdb: " << openvdb::tools::Sampler<1>::sample(srcGrid->tree(), ijk) << std::endl;
|
||||
EXPECT_NE(exact, sampler1(ijk)); // it's non-linear
|
||||
EXPECT_NEAR(sampler1(ijk), openvdb::tools::Sampler<1>::sample(srcGrid->tree(), ijk), 1e-6);
|
||||
|
||||
auto sampler2 = nanovdb::math::createSampler<2>(dstAcc);
|
||||
auto sampler2 = nanovdb::createSampler<2>(dstAcc);
|
||||
//std::cerr << "2'rd order: nanovdb = " << sampler2(ijk) << ", openvdb: " << openvdb::tools::Sampler<2>::sample(srcGrid->tree(), ijk) << std::endl;
|
||||
EXPECT_NEAR(sampler2(ijk), openvdb::tools::Sampler<2>::sample(srcGrid->tree(), ijk), 1e-6);
|
||||
EXPECT_NEAR(exact, sampler2(ijk), 1e-5); // it's a 2nd order polynomial
|
||||
|
||||
auto sampler3 = nanovdb::math::createSampler<3>(dstAcc);
|
||||
auto sampler3 = nanovdb::createSampler<3>(dstAcc);
|
||||
//std::cerr << "3'rd order: v = " << sampler3(ijk) << std::endl;
|
||||
EXPECT_NEAR(exact, sampler3(ijk), 1e-4); // it's a 2nd order polynomial
|
||||
} // Triquadratic
|
||||
@@ -2477,7 +2451,7 @@ TEST_F(TestOpenVDB, Tricubic)
|
||||
acc.setValue(ijk, tricubic(srcGrid->indexToWorld(ijk)));
|
||||
}
|
||||
//mTimer.restart("Generating NanoVDB grid");
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
//mTimer.restart("Writing NanoVDB grid");
|
||||
nanovdb::io::writeGrid("data/tmp.nvdb", handle);
|
||||
//mTimer.stop();
|
||||
@@ -2499,21 +2473,21 @@ TEST_F(TestOpenVDB, Tricubic)
|
||||
//std::cerr << "Trilinear: exact = " << exact << ", approx = " << approx << std::endl;
|
||||
auto dstAcc = dstGrid->getAccessor();
|
||||
|
||||
auto sampler0 = nanovdb::math::createSampler<0>(dstAcc);
|
||||
auto sampler0 = nanovdb::createSampler<0>(dstAcc);
|
||||
//std::cerr << "0'th order: v = " << sampler0(ijk) << std::endl;
|
||||
EXPECT_NEAR(approx, sampler0(ijk), 1e-6);
|
||||
|
||||
auto sampler1 = nanovdb::math::createSampler<1>(dstAcc);
|
||||
auto sampler1 = nanovdb::createSampler<1>(dstAcc);
|
||||
//std::cerr << "1'rd order: nanovdb = " << sampler1(ijk) << ", openvdb: " << openvdb::tools::Sampler<1>::sample(srcGrid->tree(), ijk) << std::endl;
|
||||
EXPECT_NE(exact, sampler1(ijk)); // it's non-linear
|
||||
EXPECT_NEAR(sampler1(ijk), openvdb::tools::Sampler<1>::sample(srcGrid->tree(), ijk), 1e-6);
|
||||
|
||||
auto sampler2 = nanovdb::math::createSampler<2>(dstAcc);
|
||||
auto sampler2 = nanovdb::createSampler<2>(dstAcc);
|
||||
//std::cerr << "2'rd order: nanovdb = " << sampler2(ijk) << ", openvdb: " << openvdb::tools::Sampler<2>::sample(srcGrid->tree(), ijk) << std::endl;
|
||||
EXPECT_NEAR(sampler2(ijk), openvdb::tools::Sampler<2>::sample(srcGrid->tree(), ijk), 1e-6);
|
||||
EXPECT_NE(exact, sampler2(ijk)); // it's a 3nd order polynomial
|
||||
|
||||
auto sampler3 = nanovdb::math::createSampler<3>(dstAcc);
|
||||
auto sampler3 = nanovdb::createSampler<3>(dstAcc);
|
||||
//std::cerr << "3'rd order: v = " << sampler3(ijk) << std::endl;
|
||||
EXPECT_NEAR(exact, sampler3(ijk), 1e-4); // it's a 3nd order polynomial
|
||||
} // Tricubic
|
||||
@@ -2521,7 +2495,7 @@ TEST_F(TestOpenVDB, Tricubic)
|
||||
TEST_F(TestOpenVDB, GridValidator)
|
||||
{
|
||||
auto srcGrid = this->getSrcGrid();
|
||||
auto handle = nanovdb::tools::createNanoGrid(*srcGrid, nanovdb::tools::StatsMode::All, nanovdb::CheckMode::Full);
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid, nanovdb::StatsMode::All, nanovdb::ChecksumMode::Full);
|
||||
//mTimer.stop();
|
||||
EXPECT_TRUE(handle);
|
||||
EXPECT_TRUE(handle.data());
|
||||
@@ -2529,34 +2503,34 @@ TEST_F(TestOpenVDB, GridValidator)
|
||||
EXPECT_TRUE(grid);
|
||||
|
||||
//mTimer.start("isValid - detailed");
|
||||
EXPECT_TRUE(nanovdb::tools::isValid(grid, nanovdb::CheckMode::Full, true));
|
||||
EXPECT_TRUE(nanovdb::isValid(*grid, true, true));
|
||||
//mTimer.stop();
|
||||
|
||||
//mTimer.start("isValid - not detailed");
|
||||
EXPECT_TRUE(nanovdb::tools::isValid(grid, nanovdb::CheckMode::Partial, true));
|
||||
EXPECT_TRUE(nanovdb::isValid(*grid, false, true));
|
||||
//mTimer.stop();
|
||||
|
||||
//mTimer.start("Fast CRC");
|
||||
auto fastChecksum = nanovdb::tools::evalChecksum(grid, nanovdb::CheckMode::Full);
|
||||
auto fastChecksum = nanovdb::checksum(*grid, nanovdb::ChecksumMode::Full);
|
||||
//mTimer.stop();
|
||||
EXPECT_EQ(fastChecksum, nanovdb::tools::evalChecksum(grid, nanovdb::CheckMode::Full));
|
||||
EXPECT_EQ(fastChecksum, nanovdb::checksum(*grid, nanovdb::ChecksumMode::Full));
|
||||
|
||||
auto* leaf = grid->tree().getFirstLeaf();
|
||||
EXPECT_TRUE(nanovdb::isAligned(leaf));
|
||||
EXPECT_TRUE(nanovdb::isValid(leaf));
|
||||
leaf->data()->mValues[512 >> 1] += 0.00001f; // slightly modify a single voxel value
|
||||
|
||||
EXPECT_NE(fastChecksum, nanovdb::tools::evalChecksum(grid, nanovdb::CheckMode::Full));
|
||||
EXPECT_FALSE(nanovdb::tools::isValid(grid, nanovdb::CheckMode::Full, false));
|
||||
EXPECT_NE(fastChecksum, nanovdb::checksum(*grid, nanovdb::ChecksumMode::Full));
|
||||
EXPECT_FALSE(nanovdb::isValid(*grid, true, false));
|
||||
|
||||
leaf->data()->mValues[512 >> 1] -= 0.00001f; // change back the single voxel value to it's original value
|
||||
|
||||
EXPECT_EQ(fastChecksum, nanovdb::tools::evalChecksum(grid, nanovdb::CheckMode::Full));
|
||||
EXPECT_TRUE(nanovdb::tools::isValid(grid, nanovdb::CheckMode::Full, true));
|
||||
EXPECT_EQ(fastChecksum, nanovdb::checksum(*grid, nanovdb::ChecksumMode::Full));
|
||||
EXPECT_TRUE(nanovdb::isValid(*grid, true, true));
|
||||
|
||||
leaf->data()->mValueMask.toggle(512 >> 1); // change a single bit in a value mask
|
||||
|
||||
EXPECT_NE(fastChecksum, nanovdb::tools::evalChecksum(grid, nanovdb::CheckMode::Full));
|
||||
EXPECT_FALSE(nanovdb::tools::isValid(grid, nanovdb::CheckMode::Full, false));
|
||||
EXPECT_NE(fastChecksum, nanovdb::checksum(*grid, nanovdb::ChecksumMode::Full));
|
||||
EXPECT_FALSE(nanovdb::isValid(*grid, true, false));
|
||||
} // GridValidator
|
||||
|
||||
TEST_F(TestOpenVDB, BenchmarkHostBuffer)
|
||||
@@ -2590,8 +2564,8 @@ TEST_F(TestOpenVDB, DenseIndexGrid)
|
||||
// read openvdb::FloatGrid
|
||||
auto srcGrid = this->getSrcGrid(false, 0, 0);// level set of a dragon if available, else an octahedron
|
||||
auto& srcTree = srcGrid->tree();
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> builder(*srcGrid);
|
||||
builder.setStats(nanovdb::tools::StatsMode::All);
|
||||
nanovdb::CreateNanoGrid<openvdb::FloatGrid> builder(*srcGrid);
|
||||
builder.setStats(nanovdb::StatsMode::All);
|
||||
// openvdb::FloatGrid -> nanovdb::FloatGrid
|
||||
auto handle = builder.getHandle();
|
||||
EXPECT_TRUE(handle);
|
||||
@@ -2627,7 +2601,7 @@ TEST_F(TestOpenVDB, DenseIndexGrid)
|
||||
}
|
||||
//mTimer.stop();
|
||||
auto *idxLeaf0 = idxGrid->tree().getFirstNode<0>();
|
||||
nanovdb::util::forEach(nanovdb::util::Range1D(0,idxGrid->tree().nodeCount(0)),[&](const nanovdb::util::Range1D &r){
|
||||
nanovdb::forEach(nanovdb::Range1D(0,idxGrid->tree().nodeCount(0)),[&](const nanovdb::Range1D &r){
|
||||
auto fltAcc = fltGrid->getAccessor();// NOT thread-safe!
|
||||
for (auto i=r.begin(); i!=r.end(); ++i){
|
||||
auto *idxLeaf = idxLeaf0 + i;
|
||||
@@ -2649,7 +2623,7 @@ TEST_F(TestOpenVDB, SparseIndexGrid)
|
||||
auto srcGrid = this->getSrcGrid(false, 0, 0);// level set of a dragon if available, else an octahedron
|
||||
|
||||
// openvdb::FloatGrid -> nanovdb::IndexGrid
|
||||
nanovdb::tools::CreateNanoGrid<openvdb::FloatGrid> builder(*srcGrid);
|
||||
nanovdb::CreateNanoGrid<openvdb::FloatGrid> builder(*srcGrid);
|
||||
//mTimer.start("Create IndexGrid");
|
||||
auto handle2 = builder.getHandle<nanovdb::ValueIndex>(1u, false, false);
|
||||
//mTimer.stop();
|
||||
@@ -2677,25 +2651,25 @@ TEST_F(TestOpenVDB, SparseIndexGrid)
|
||||
TEST_F(TestOpenVDB, BuildNodeManager)
|
||||
{
|
||||
{// test NodeManager with build::Grid
|
||||
using GridT = nanovdb::tools::build::Grid<float>;
|
||||
using GridT = nanovdb::build::Grid<float>;
|
||||
GridT grid(0.0f);
|
||||
nanovdb::tools::build::NodeManager<GridT> mgr(grid);
|
||||
nanovdb::build::NodeManager<GridT> mgr(grid);
|
||||
using TreeT = GridT::TreeType;
|
||||
static const bool test = nanovdb::util::is_same<nanovdb::NodeTrait<TreeT,0>::type, TreeT::LeafNodeType>::value;
|
||||
static const bool test = nanovdb::is_same<nanovdb::NodeTrait<TreeT,0>::type, TreeT::LeafNodeType>::value;
|
||||
EXPECT_TRUE(test);
|
||||
}
|
||||
{// test NodeManager with openvdb::Grid
|
||||
using GridT = openvdb::FloatGrid;
|
||||
GridT grid(0.0f);
|
||||
nanovdb::tools::build::NodeManager<GridT> mgr(grid);
|
||||
nanovdb::build::NodeManager<GridT> mgr(grid);
|
||||
using TreeT = GridT::TreeType;
|
||||
static const bool test = nanovdb::util::is_same<nanovdb::NodeTrait<TreeT,0>::type, TreeT::LeafNodeType>::value;
|
||||
static const bool test = nanovdb::is_same<nanovdb::NodeTrait<TreeT,0>::type, TreeT::LeafNodeType>::value;
|
||||
EXPECT_TRUE(test);
|
||||
}
|
||||
{// test NodeTrait on nanovdb::Grid
|
||||
using GridT = nanovdb::NanoGrid<float>;
|
||||
using TreeT = GridT::TreeType;
|
||||
static const bool test = nanovdb::util::is_same<nanovdb::NodeTrait<TreeT,0>::type, TreeT::LeafNodeType>::value;
|
||||
static const bool test = nanovdb::is_same<nanovdb::NodeTrait<TreeT,0>::type, TreeT::LeafNodeType>::value;
|
||||
EXPECT_TRUE(test);
|
||||
}
|
||||
}// BuildNodeManager
|
||||
@@ -2719,7 +2693,7 @@ TEST_F(TestOpenVDB, Benchmark_OpenVDB_PointIndexGrid)
|
||||
{
|
||||
const double voxelSize = 0.5;
|
||||
|
||||
nanovdb::util::Timer timer("Generate sphere with points");
|
||||
nanovdb::CpuTimer timer("Generate sphere with points");
|
||||
auto pointsHandle = nanovdb::createPointSphere(8, 100.0, nanovdb::Vec3d(0.0), voxelSize);
|
||||
timer.stop();
|
||||
|
||||
@@ -2752,7 +2726,7 @@ TEST_F(TestOpenVDB, Benchmark_OpenVDB_PointDataGrid)
|
||||
{
|
||||
const double voxelSize = 0.5;
|
||||
|
||||
nanovdb::util::Timer timer("Generate sphere with points");
|
||||
nanovdb::CpuTimer timer("Generate sphere with points");
|
||||
auto pointsHandle = nanovdb::createPointSphere(8, 100.0, nanovdb::Vec3d(0.0), voxelSize);
|
||||
timer.stop();
|
||||
|
||||
|
||||
@@ -1,6 +1,83 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <nanovdb/util/Util.h>// for NANOVDB_DEPRECATED_HEADER
|
||||
#include <nanovdb/util/Timer.h>
|
||||
NANOVDB_DEPRECATED_HEADER("Include nanovdb/util/Timer.h instead.")
|
||||
/// @file CpuTimer.h
|
||||
///
|
||||
/// @author Ken Museth
|
||||
///
|
||||
/// @brief A simple timing class (in case openvdb::util::CpuTimer is unavailable)
|
||||
|
||||
#ifndef NANOVDB_CPU_TIMER_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_CPU_TIMER_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
class CpuTimer
|
||||
{
|
||||
std::chrono::high_resolution_clock::time_point mStart;
|
||||
public:
|
||||
/// @brief Default constructor
|
||||
CpuTimer() {}
|
||||
|
||||
/// @brief Constructor that starts the timer
|
||||
/// @param msg string message to be printed when timer is started
|
||||
/// @param os output stream for the message above
|
||||
CpuTimer(const std::string &msg, std::ostream& os = std::cerr) {this->start(msg, os);}
|
||||
|
||||
/// @brief Start the timer
|
||||
/// @param msg string message to be printed when timer is started
|
||||
/// @param os output stream for the message above
|
||||
void start(const std::string &msg, std::ostream& os = std::cerr)
|
||||
{
|
||||
os << msg << " ... " << std::flush;
|
||||
mStart = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
/// @brief elapsed time (since start) in miliseconds
|
||||
template <typename AccuracyT = std::chrono::milliseconds>
|
||||
auto elapsed()
|
||||
{
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
return std::chrono::duration_cast<AccuracyT>(end - mStart).count();
|
||||
}
|
||||
|
||||
/// @brief stop the timer
|
||||
/// @tparam AccuracyT Template parameter defining the accuracy of the reported times
|
||||
/// @param os output stream for the message above
|
||||
template <typename AccuracyT = std::chrono::milliseconds>
|
||||
void stop(std::ostream& os = std::cerr)
|
||||
{
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto diff = std::chrono::duration_cast<AccuracyT>(end - mStart).count();
|
||||
os << "completed in " << diff;
|
||||
if (std::is_same<AccuracyT, std::chrono::microseconds>::value) {// resolved at compile-time
|
||||
os << " microseconds" << std::endl;
|
||||
} else if (std::is_same<AccuracyT, std::chrono::milliseconds>::value) {
|
||||
os << " milliseconds" << std::endl;
|
||||
} else if (std::is_same<AccuracyT, std::chrono::seconds>::value) {
|
||||
os << " seconds" << std::endl;
|
||||
} else {
|
||||
os << " unknown time unit" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief stop and start the timer
|
||||
/// @tparam AccuracyT Template parameter defining the accuracy of the reported times
|
||||
/// @param msg string message to be printed when timer is started
|
||||
/// @param os output stream for the message above
|
||||
template <typename AccuracyT = std::chrono::milliseconds>
|
||||
void restart(const std::string &msg, std::ostream& os = std::cerr)
|
||||
{
|
||||
this->stop<AccuracyT>();
|
||||
this->start(msg, os);
|
||||
}
|
||||
|
||||
|
||||
};// CpuTimer
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_CPU_TIMER_HAS_BEEN_INCLUDED
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,185 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
//
|
||||
/// @author Jeff Lait
|
||||
///
|
||||
/// @date May 13, 2021
|
||||
///
|
||||
/// @file DitherLUT.h
|
||||
///
|
||||
/// @brief Defines look up table to do dithering of 8^3 leaf nodes.
|
||||
|
||||
#include <nanovdb/util/Util.h>// for NANOVDB_DEPRECATED_HEADER
|
||||
#include <nanovdb/math/DitherLUT.h>
|
||||
NANOVDB_DEPRECATED_HEADER("Include nanovdb/math/DitherLUT.h instead.")
|
||||
#ifndef NANOVDB_DITHERLUT_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_DITHERLUT_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/NanoVDB.h>// for __hostdev__, Vec3, Min, Max, Pow2, Pow3, Pow4
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
class DitherLUT
|
||||
{
|
||||
const bool mEnable;
|
||||
public:
|
||||
/// @brief Constructor with an optional scaling factor for the dithering
|
||||
__hostdev__ DitherLUT(bool enable = true) : mEnable(enable) {}
|
||||
|
||||
/// @brief Retrieves dither threshold for an offset within an 8^3 leaf nodes.
|
||||
///
|
||||
/// @param offset into the lookup table of size 512
|
||||
__hostdev__ float operator()(const int offset)
|
||||
{
|
||||
|
||||
// This table was generated with
|
||||
/**************
|
||||
|
||||
static constexpr inline uint32
|
||||
SYSwang_inthash(uint32 key)
|
||||
{
|
||||
// From http://www.concentric.net/~Ttwang/tech/inthash.htm
|
||||
key += ~(key << 16);
|
||||
key ^= (key >> 5);
|
||||
key += (key << 3);
|
||||
key ^= (key >> 13);
|
||||
key += ~(key << 9);
|
||||
key ^= (key >> 17);
|
||||
return key;
|
||||
}
|
||||
|
||||
static void
|
||||
ut_initDitherR(float *pattern, float offset,
|
||||
int x, int y, int z, int res, int goalres)
|
||||
{
|
||||
// These offsets are designed to maximize the difference between
|
||||
// dither values in nearby voxels within a given 2x2x2 cell, without
|
||||
// producing axis-aligned artifacts. The are organized in row-major
|
||||
// order.
|
||||
static const float theDitherOffset[] = {0,4,6,2,5,1,3,7};
|
||||
static const float theScale = 0.125F;
|
||||
int key = (((z << res) + y) << res) + x;
|
||||
|
||||
if (res == goalres)
|
||||
{
|
||||
pattern[key] = offset;
|
||||
return;
|
||||
}
|
||||
|
||||
// Randomly flip (on each axis) the dithering patterns used by the
|
||||
// subcells. This key is xor'd with the subcell index below before
|
||||
// looking up in the dither offset list.
|
||||
key = SYSwang_inthash(key) & 7;
|
||||
|
||||
x <<= 1;
|
||||
y <<= 1;
|
||||
z <<= 1;
|
||||
|
||||
offset *= theScale;
|
||||
for (int i = 0; i < 8; i++)
|
||||
ut_initDitherR(pattern, offset+theDitherOffset[i ^ key]*theScale,
|
||||
x+(i&1), y+((i&2)>>1), z+((i&4)>>2), res+1, goalres);
|
||||
}
|
||||
|
||||
// This is a compact algorithm that accomplishes essentially the same thing
|
||||
// as ut_initDither() above. We should eventually switch to use this and
|
||||
// clean the dead code.
|
||||
static fpreal32 *
|
||||
ut_initDitherRecursive(int goalres)
|
||||
{
|
||||
const int nfloat = 1 << (goalres*3);
|
||||
float *pattern = new float[nfloat];
|
||||
ut_initDitherR(pattern, 1.0F, 0, 0, 0, 0, goalres);
|
||||
|
||||
// This has built an even spacing from 1/nfloat to 1.0.
|
||||
// however, our dither pattern should be 1/(nfloat+1) to nfloat/(nfloat+1)
|
||||
// So we do a correction here. Note that the earlier calculations are
|
||||
// done with powers of 2 so are exact, so it does make sense to delay
|
||||
// the renormalization to this pass.
|
||||
float correctionterm = nfloat / (nfloat+1.0F);
|
||||
for (int i = 0; i < nfloat; i++)
|
||||
pattern[i] *= correctionterm;
|
||||
return pattern;
|
||||
}
|
||||
|
||||
theDitherMatrix = ut_initDitherRecursive(3);
|
||||
|
||||
for (int i = 0; i < 512/8; i ++)
|
||||
{
|
||||
for (int j = 0; j < 8; j ++)
|
||||
std::cout << theDitherMatrix[i*8+j] << "f, ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
**************/
|
||||
static const float LUT[512] =
|
||||
{
|
||||
0.14425f, 0.643275f, 0.830409f, 0.331384f, 0.105263f, 0.604289f, 0.167641f, 0.666667f,
|
||||
0.892788f, 0.393762f, 0.0818713f, 0.580897f, 0.853801f, 0.354776f, 0.916179f, 0.417154f,
|
||||
0.612086f, 0.11306f, 0.79922f, 0.300195f, 0.510721f, 0.0116959f, 0.947368f, 0.448343f,
|
||||
0.362573f, 0.861598f, 0.0506823f, 0.549708f, 0.261209f, 0.760234f, 0.19883f, 0.697856f,
|
||||
0.140351f, 0.639376f, 0.576998f, 0.0779727f, 0.522417f, 0.0233918f, 0.460039f, 0.959064f,
|
||||
0.888889f, 0.389864f, 0.327485f, 0.826511f, 0.272904f, 0.77193f, 0.709552f, 0.210526f,
|
||||
0.483431f, 0.982456f, 0.296296f, 0.795322f, 0.116959f, 0.615984f, 0.0545809f, 0.553606f,
|
||||
0.732943f, 0.233918f, 0.545809f, 0.0467836f, 0.865497f, 0.366472f, 0.803119f, 0.304094f,
|
||||
0.518519f, 0.0194932f, 0.45614f, 0.955166f, 0.729045f, 0.230019f, 0.54191f, 0.042885f,
|
||||
0.269006f, 0.768031f, 0.705653f, 0.206628f, 0.479532f, 0.978558f, 0.292398f, 0.791423f,
|
||||
0.237817f, 0.736842f, 0.424951f, 0.923977f, 0.136452f, 0.635478f, 0.323587f, 0.822612f,
|
||||
0.986355f, 0.487329f, 0.674464f, 0.175439f, 0.88499f, 0.385965f, 0.573099f, 0.0740741f,
|
||||
0.51462f, 0.0155945f, 0.202729f, 0.701754f, 0.148148f, 0.647174f, 0.834308f, 0.335283f,
|
||||
0.265107f, 0.764133f, 0.951267f, 0.452242f, 0.896686f, 0.397661f, 0.08577f, 0.584795f,
|
||||
0.8577f, 0.358674f, 0.920078f, 0.421053f, 0.740741f, 0.241715f, 0.678363f, 0.179337f,
|
||||
0.109162f, 0.608187f, 0.17154f, 0.670565f, 0.491228f, 0.990253f, 0.42885f, 0.927875f,
|
||||
0.0662768f, 0.565302f, 0.62768f, 0.128655f, 0.183236f, 0.682261f, 0.744639f, 0.245614f,
|
||||
0.814815f, 0.315789f, 0.378168f, 0.877193f, 0.931774f, 0.432749f, 0.495127f, 0.994152f,
|
||||
0.0350877f, 0.534113f, 0.97076f, 0.471735f, 0.214425f, 0.71345f, 0.526316f, 0.0272904f,
|
||||
0.783626f, 0.2846f, 0.222222f, 0.721248f, 0.962963f, 0.463938f, 0.276803f, 0.775828f,
|
||||
0.966862f, 0.467836f, 0.405458f, 0.904483f, 0.0701754f, 0.569201f, 0.881092f, 0.382066f,
|
||||
0.218324f, 0.717349f, 0.654971f, 0.155945f, 0.818713f, 0.319688f, 0.132554f, 0.631579f,
|
||||
0.0623782f, 0.561404f, 0.748538f, 0.249513f, 0.912281f, 0.413255f, 0.974659f, 0.475634f,
|
||||
0.810916f, 0.311891f, 0.499025f, 0.998051f, 0.163743f, 0.662768f, 0.226121f, 0.725146f,
|
||||
0.690058f, 0.191033f, 0.00389864f, 0.502924f, 0.557505f, 0.0584795f, 0.120858f, 0.619883f,
|
||||
0.440546f, 0.939571f, 0.752437f, 0.253411f, 0.307992f, 0.807018f, 0.869396f, 0.37037f,
|
||||
0.658869f, 0.159844f, 0.346979f, 0.846004f, 0.588694f, 0.0896686f, 0.152047f, 0.651072f,
|
||||
0.409357f, 0.908382f, 0.596491f, 0.0974659f, 0.339181f, 0.838207f, 0.900585f, 0.401559f,
|
||||
0.34308f, 0.842105f, 0.779727f, 0.280702f, 0.693957f, 0.194932f, 0.25731f, 0.756335f,
|
||||
0.592593f, 0.0935673f, 0.0311891f, 0.530214f, 0.444444f, 0.94347f, 0.506823f, 0.00779727f,
|
||||
0.68616f, 0.187135f, 0.124756f, 0.623782f, 0.288499f, 0.787524f, 0.350877f, 0.849903f,
|
||||
0.436647f, 0.935673f, 0.873294f, 0.374269f, 0.538012f, 0.0389864f, 0.60039f, 0.101365f,
|
||||
0.57115f, 0.0721248f, 0.758285f, 0.259259f, 0.719298f, 0.220273f, 0.532164f, 0.0331384f,
|
||||
0.321637f, 0.820663f, 0.00974659f, 0.508772f, 0.469786f, 0.968811f, 0.282651f, 0.781676f,
|
||||
0.539961f, 0.0409357f, 0.727096f, 0.22807f, 0.500975f, 0.00194932f, 0.563353f, 0.0643275f,
|
||||
0.290448f, 0.789474f, 0.477583f, 0.976608f, 0.251462f, 0.750487f, 0.31384f, 0.812865f,
|
||||
0.94152f, 0.442495f, 0.879142f, 0.380117f, 0.37232f, 0.871345f, 0.309942f, 0.808967f,
|
||||
0.192982f, 0.692008f, 0.130604f, 0.62963f, 0.621832f, 0.122807f, 0.559454f, 0.0604289f,
|
||||
0.660819f, 0.161793f, 0.723197f, 0.224172f, 0.403509f, 0.902534f, 0.840156f, 0.341131f,
|
||||
0.411306f, 0.910331f, 0.473684f, 0.97271f, 0.653021f, 0.153996f, 0.0916179f, 0.590643f,
|
||||
0.196881f, 0.695906f, 0.384016f, 0.883041f, 0.0955166f, 0.594542f, 0.157895f, 0.65692f,
|
||||
0.945419f, 0.446394f, 0.633528f, 0.134503f, 0.844055f, 0.345029f, 0.906433f, 0.407407f,
|
||||
0.165692f, 0.664717f, 0.103314f, 0.602339f, 0.126706f, 0.625731f, 0.189084f, 0.688109f,
|
||||
0.91423f, 0.415205f, 0.851852f, 0.352827f, 0.875244f, 0.376218f, 0.937622f, 0.438596f,
|
||||
0.317739f, 0.816764f, 0.255361f, 0.754386f, 0.996101f, 0.497076f, 0.933723f, 0.434698f,
|
||||
0.567251f, 0.0682261f, 0.504873f, 0.00584795f, 0.247563f, 0.746589f, 0.185185f, 0.684211f,
|
||||
0.037037f, 0.536062f, 0.0994152f, 0.598441f, 0.777778f, 0.278752f, 0.465887f, 0.964912f,
|
||||
0.785575f, 0.28655f, 0.847953f, 0.348928f, 0.0292398f, 0.528265f, 0.7154f, 0.216374f,
|
||||
0.39961f, 0.898636f, 0.961014f, 0.461988f, 0.0487329f, 0.547758f, 0.111111f, 0.610136f,
|
||||
0.649123f, 0.150097f, 0.212476f, 0.711501f, 0.797271f, 0.298246f, 0.859649f, 0.360624f,
|
||||
0.118908f, 0.617934f, 0.0565302f, 0.555556f, 0.329435f, 0.82846f, 0.516569f, 0.0175439f,
|
||||
0.867446f, 0.368421f, 0.805068f, 0.306043f, 0.578947f, 0.079922f, 0.267057f, 0.766082f,
|
||||
0.270955f, 0.76998f, 0.707602f, 0.208577f, 0.668616f, 0.169591f, 0.606238f, 0.107212f,
|
||||
0.520468f, 0.0214425f, 0.45809f, 0.957115f, 0.419103f, 0.918129f, 0.356725f, 0.855751f,
|
||||
0.988304f, 0.489279f, 0.426901f, 0.925926f, 0.450292f, 0.949318f, 0.512671f, 0.0136452f,
|
||||
0.239766f, 0.738791f, 0.676413f, 0.177388f, 0.699805f, 0.20078f, 0.263158f, 0.762183f,
|
||||
0.773879f, 0.274854f, 0.337232f, 0.836257f, 0.672515f, 0.173489f, 0.734893f, 0.235867f,
|
||||
0.0253411f, 0.524366f, 0.586745f, 0.0877193f, 0.423002f, 0.922027f, 0.48538f, 0.984405f,
|
||||
0.74269f, 0.243665f, 0.680312f, 0.181287f, 0.953216f, 0.454191f, 0.1423f, 0.641326f,
|
||||
0.493177f, 0.992203f, 0.430799f, 0.929825f, 0.204678f, 0.703704f, 0.890838f, 0.391813f,
|
||||
0.894737f, 0.395712f, 0.0838207f, 0.582846f, 0.0448343f, 0.54386f, 0.231969f, 0.730994f,
|
||||
0.146199f, 0.645224f, 0.832359f, 0.333333f, 0.793372f, 0.294347f, 0.980507f, 0.481481f,
|
||||
0.364522f, 0.863548f, 0.80117f, 0.302144f, 0.824561f, 0.325536f, 0.138402f, 0.637427f,
|
||||
0.614035f, 0.11501f, 0.0526316f, 0.551657f, 0.0760234f, 0.575049f, 0.88694f, 0.387914f,
|
||||
};
|
||||
return mEnable ? LUT[offset & 511] : 0.5f;// branch prediction should optimize this!
|
||||
}
|
||||
}; // DitherLUT class
|
||||
|
||||
} // end nanovdb namespace
|
||||
|
||||
#endif // NANOVDB_DITHERLUT_HAS_BEEN_INCLUDED
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/*!
|
||||
\file nanovdb/util/ForEach.h
|
||||
\file ForEach.h
|
||||
|
||||
\author Ken Museth
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
\brief A unified wrapper for tbb::parallel_for and a naive std::thread fallback
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_UTIL_FOREACH_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_UTIL_FOREACH_H_HAS_BEEN_INCLUDED
|
||||
#ifndef NANOVDB_FOREACH_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_FOREACH_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <nanovdb/util/Range.h>// for Range1D
|
||||
#include "Range.h"// for Range1D
|
||||
|
||||
#ifdef NANOVDB_USE_TBB
|
||||
#include <tbb/parallel_for.h>
|
||||
@@ -26,8 +26,6 @@
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace util {
|
||||
|
||||
/// @brief simple wrapper for tbb::parallel_for with a naive std fallback
|
||||
///
|
||||
/// @param range Range, CoordBBox, tbb::blocked_range, blocked_range2D, or blocked_range3D.
|
||||
@@ -85,32 +83,6 @@ inline void forEach(const ContainerT<T...> &c, size_t grainSize, const FuncT& fu
|
||||
forEach(Range1D(0, c.size(), grainSize), func);
|
||||
}
|
||||
|
||||
}// namespace util
|
||||
|
||||
/// @brief Simple wrapper for the function defined above
|
||||
template <typename FuncT>
|
||||
[[deprecated("Use nanovdb::util::forEach instead")]]
|
||||
inline void forEach(size_t begin, size_t end, size_t grainSize, const FuncT& func)
|
||||
{
|
||||
util::forEach(util::Range1D(begin, end, grainSize), func);
|
||||
}
|
||||
|
||||
/// @brief Simple wrapper for the function defined above, which works with std::containers
|
||||
template <template<typename...> class ContainerT, typename... T, typename FuncT>
|
||||
[[deprecated("Use nanovdb::util::forEach instead")]]
|
||||
inline void forEach(const ContainerT<T...> &c, const FuncT& func)
|
||||
{
|
||||
util::forEach(util::Range1D(0, c.size(), 1), func);
|
||||
}
|
||||
|
||||
/// @brief Simple wrapper for the function defined above, which works with std::containers
|
||||
template <template<typename...> class ContainerT, typename... T, typename FuncT>
|
||||
[[deprecated("Use nanovdb::util::forEach instead")]]
|
||||
inline void forEach(const ContainerT<T...> &c, size_t grainSize, const FuncT& func)
|
||||
{
|
||||
util::forEach(util::Range1D(0, c.size(), grainSize), func);
|
||||
}
|
||||
|
||||
}// namespace nanovdb
|
||||
|
||||
#endif // NANOVDB_UTIL_FOREACH_H_HAS_BEEN_INCLUDED
|
||||
#endif // NANOVDB_FOREACH_H_HAS_BEEN_INCLUDED
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user