Compare commits
230
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c81d904ded | ||
|
|
a89012d569 | ||
|
|
a14a735de9 | ||
|
|
9df9ec060d | ||
|
|
71da3a862e | ||
|
|
c814fae1fe | ||
|
|
2176f69923 | ||
|
|
83890bd879 | ||
|
|
eb0f1bfd83 | ||
|
|
c6353069c8 | ||
|
|
d19e483564 | ||
|
|
b2b4d30648 | ||
|
|
2a301b1290 | ||
|
|
e68ff4bc8e | ||
|
|
e679862fca | ||
|
|
568538e9ec | ||
|
|
c3ee2009c0 | ||
|
|
7946f17edb | ||
|
|
204fce09fb | ||
|
|
385584be80 | ||
|
|
2728de45a1 | ||
|
|
3f35101729 | ||
|
|
8a8ed7ca3e | ||
|
|
0377b2202d | ||
|
|
5eb1ec0c4d | ||
|
|
157c413b73 | ||
|
|
20615f1b31 | ||
|
|
e88f2f832e | ||
|
|
63d1878f30 | ||
|
|
d422d856d8 | ||
|
|
25715e5c18 | ||
|
|
6b05f50a67 | ||
|
|
ba9a2c0974 | ||
|
|
6717cca14a | ||
|
|
e538a0646b | ||
|
|
586dde77a0 | ||
|
|
bb597a2109 | ||
|
|
b795681895 | ||
|
|
230d640768 | ||
|
|
2ad18b6aeb | ||
|
|
b082b5ec7e | ||
|
|
8a91e1e096 | ||
|
|
457b671c34 | ||
|
|
111b12f9c1 | ||
|
|
f95115304e | ||
|
|
5b5e936723 | ||
|
|
e3351803dc | ||
|
|
d980ad9206 | ||
|
|
20f904e7cd | ||
|
|
7be8164055 | ||
|
|
75ed0cd57a | ||
|
|
92a4f48f8f | ||
|
|
1f885014b8 | ||
|
|
b7fc4fc7af | ||
|
|
56287f697a | ||
|
|
32dac53ce1 | ||
|
|
8e5b1bc07e | ||
|
|
332a57d9d6 | ||
|
|
b0b99d6ce7 | ||
|
|
1ca9bb066e | ||
|
|
50e1563078 | ||
|
|
59fcab450a | ||
|
|
65c9d4884c | ||
|
|
ad251341d0 | ||
|
|
06ad21bf70 | ||
|
|
2e30d61283 | ||
|
|
4775fa6079 | ||
|
|
f20dd2a50e | ||
|
|
fb4516e3e9 | ||
|
|
72a4bfe64e | ||
|
|
5fcaa5a347 | ||
|
|
44d5e657a7 | ||
|
|
9e6d47cb88 | ||
|
|
bbb19a9c27 | ||
|
|
04c16f94ad | ||
|
|
3cdaa03ba8 | ||
|
|
5bb2eba69d | ||
|
|
0e5fbf3c19 | ||
|
|
465599ddab | ||
|
|
992aace6cf | ||
|
|
fb1c692d05 | ||
|
|
42afea5d40 | ||
|
|
c3cde0fea6 | ||
|
|
a532de5526 | ||
|
|
acca53b4f8 | ||
|
|
ca21615c13 | ||
|
|
cf2f1493b9 | ||
|
|
b94efdd07d | ||
|
|
e332cc54d6 | ||
|
|
90c02c309d | ||
|
|
63a052f4b4 | ||
|
|
c1b7dd251a | ||
|
|
0dda5483b5 | ||
|
|
4ed2573179 | ||
|
|
a6a9dcca29 | ||
|
|
d8c9ee5c5a | ||
|
|
ff3709c618 | ||
|
|
c58736a68b | ||
|
|
31a6a29a6f | ||
|
|
defd4de2cf | ||
|
|
ecd3f084d6 | ||
|
|
d257946b18 | ||
|
|
bce1c3aaa1 | ||
|
|
0130f7a20b | ||
|
|
6d65d9e840 | ||
|
|
c157c94c14 | ||
|
|
f9754140ba | ||
|
|
2ea583eafd | ||
|
|
c7fee6b08a | ||
|
|
83c29033f2 | ||
|
|
153c7ce5b1 | ||
|
|
0e9bccd205 | ||
|
|
fdca38c531 | ||
|
|
bd6f6dafdf | ||
|
|
5b8d2a1532 | ||
|
|
1f46b91f30 | ||
|
|
0ae36dd0a8 | ||
|
|
54b8bf219a | ||
|
|
bbd6924f77 | ||
|
|
546347cad4 | ||
|
|
04cbd98322 | ||
|
|
da8a5ccaa4 | ||
|
|
a9079f5b2d | ||
|
|
a5c231cd55 | ||
|
|
a46e808da1 | ||
|
|
33401147f2 | ||
|
|
90729a6445 | ||
|
|
d43b5583ac | ||
|
|
8159467dd5 | ||
|
|
60fa1b861d | ||
|
|
cac4e20dcd | ||
|
|
84fc1e6ec4 | ||
|
|
226ac86da2 | ||
|
|
85bd0e52cd | ||
|
|
d68d0914fc | ||
|
|
c0afc0bf56 | ||
|
|
c372647a36 | ||
|
|
32dcb29a25 | ||
|
|
cbfcd732e7 | ||
|
|
716b3574a7 | ||
|
|
f4d870243d | ||
|
|
ffa10203b0 | ||
|
|
7191b429e6 | ||
|
|
bf87a9c112 | ||
|
|
d6b69a0ecd | ||
|
|
e7d62d92e8 | ||
|
|
31b11b0a02 | ||
|
|
61c21ccd4a | ||
|
|
1839dbdd82 | ||
|
|
b575a4250d | ||
|
|
03d071df7a | ||
|
|
e1cd518163 | ||
|
|
d5da5b0864 | ||
|
|
bab28c6ad5 | ||
|
|
e79508c7c8 | ||
|
|
48395b2b5c | ||
|
|
41c0f7a224 | ||
|
|
d23549f24a | ||
|
|
2f93980ad2 | ||
|
|
3976c554be | ||
|
|
fec59777b7 | ||
|
|
402b91251c | ||
|
|
a04cae04ce | ||
|
|
0a1d7e5030 | ||
|
|
8e595b7dae | ||
|
|
6eebb573d5 | ||
|
|
bc6b33366a | ||
|
|
b6db81bf06 | ||
|
|
226be9fa5d | ||
|
|
7ece1dae7a | ||
|
|
39a284988e | ||
|
|
cfba6dff78 | ||
|
|
297be1c341 | ||
|
|
482a795d6a | ||
|
|
a780ec029d | ||
|
|
a952846eab | ||
|
|
ec70b677da | ||
|
|
4a4d6730e3 | ||
|
|
522cecab27 | ||
|
|
a0a789ef8f | ||
|
|
f18950db05 | ||
|
|
05a658f5d9 | ||
|
|
55b94e2a79 | ||
|
|
3fa663d8d3 | ||
|
|
7f4a717e85 | ||
|
|
828641c552 | ||
|
|
3de00254b8 | ||
|
|
9fc4b8e0fe | ||
|
|
19013e3358 | ||
|
|
8ca4ac8a7f | ||
|
|
ae28d3ce14 | ||
|
|
0b3ce20f74 | ||
|
|
7fc793078c | ||
|
|
3733f2c372 | ||
|
|
0bb18adef6 | ||
|
|
41adf1baab | ||
|
|
0e29a1ff94 | ||
|
|
604ed38280 | ||
|
|
5a6714b67c | ||
|
|
c9293ed87c | ||
|
|
778e1efcb1 | ||
|
|
37c0c47bc4 | ||
|
|
2433964511 | ||
|
|
78b9cd84cf | ||
|
|
b8dd348aa5 | ||
|
|
6da1f9530a | ||
|
|
7c03e1f084 | ||
|
|
e9a1f1a3d2 | ||
|
|
f5a735a2af | ||
|
|
10da55d5c1 | ||
|
|
2901256875 | ||
|
|
a0df7e82fe | ||
|
|
c8ad233db1 | ||
|
|
687e6c405e | ||
|
|
7ba5bb1889 | ||
|
|
db301eb962 | ||
|
|
10aae7e90b | ||
|
|
5462395ae8 | ||
|
|
3fe53838b4 | ||
|
|
cd524c73ca | ||
|
|
ec28bf7d86 | ||
|
|
b8b2d78ba3 | ||
|
|
8884fd3e97 | ||
|
|
2892db60f8 | ||
|
|
ce6d2b5675 | ||
|
|
dfe40d98e8 | ||
|
|
763c09c57e | ||
|
|
154828954f | ||
|
|
3269bf764c | ||
|
|
c2ac9bf519 |
@@ -30,9 +30,6 @@ on:
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
schedule:
|
||||
# run this workflow every day 7am UTC
|
||||
- cron: '0 7 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
type:
|
||||
|
||||
+19
-14
@@ -19,6 +19,8 @@ on:
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
pull_request:
|
||||
# Add 'ready_for_review' to run CI when a draft PR is marked ready.
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
branches:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
@@ -32,9 +34,6 @@ on:
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
schedule:
|
||||
# run this workflow every day 7am UTC
|
||||
- cron: '0 7 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
type:
|
||||
@@ -56,9 +55,10 @@ jobs:
|
||||
# VFX platform jobs. These are run on the appropriate CentOS images from
|
||||
# the provided ASWF docker containers
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'linux'
|
||||
(github.event_name == 'push' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
|
||||
(github.event_name == 'workflow_dispatch' &&
|
||||
(github.event.inputs.type == 'all' || github.event.inputs.type == 'linux')))
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-22.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
name: >
|
||||
linux:${{ matrix.config.image }}-abi:${{ matrix.config.abi }}-cxx:${{ matrix.config.cxx }}-type:${{ matrix.config.build }}
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { cxx: clang++, image: '2026', abi: '13', build: 'Release', cmake: '-DOPENVDB_USE_FUTURE_ABI_13=ON' }
|
||||
- { cxx: clang++, image: '2026', abi: '13', build: 'Release', cmake: '-DOPENVDB_USE_FUTURE_ABI_13=ON -DOPENVDB_ENABLE_ASSERTS=ON' }
|
||||
- { cxx: g++, image: '2026', abi: '13', build: 'Release', cmake: '-DOPENVDB_USE_FUTURE_ABI_13=ON' }
|
||||
- { cxx: clang++, image: '2026', abi: '13', build: 'Debug', cmake: '-DOPENVDB_USE_FUTURE_ABI_13=ON' }
|
||||
- { cxx: clang++, image: '2025-clang19.1', abi: '12', build: 'Release', cmake: '' }
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
\"
|
||||
- name: test
|
||||
# Skip Debug on commits as they take a while.
|
||||
if: github.event_name == 'schedule'|| matrix.config.build == 'Release'
|
||||
if: github.event_name == 'workflow_dispatch' || matrix.config.build == 'Release'
|
||||
run: |
|
||||
cd build && ctest -V
|
||||
cd - && ./ci/test_install.sh
|
||||
@@ -122,9 +122,10 @@ jobs:
|
||||
windows:
|
||||
# Windows CI. Tests a dynamic build with MD.
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'win'
|
||||
(github.event_name == 'push' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
|
||||
(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
|
||||
env:
|
||||
@@ -142,6 +143,9 @@ jobs:
|
||||
- name: install
|
||||
shell: powershell
|
||||
run: .\ci\install_windows.ps1
|
||||
- name: install_numpy
|
||||
shell: powershell
|
||||
run: .\ci\install_windows_numpy.ps1
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
@@ -163,9 +167,10 @@ jobs:
|
||||
|
||||
macos:
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'mac'
|
||||
(github.event_name == 'push' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
|
||||
(github.event_name == 'workflow_dispatch' &&
|
||||
(github.event.inputs.type == 'all' || github.event.inputs.type == 'mac')))
|
||||
runs-on: ${{ matrix.config.image }}
|
||||
name: ${{ matrix.config.image }}
|
||||
env:
|
||||
|
||||
@@ -11,7 +11,6 @@ on:
|
||||
- 'CHANGES'
|
||||
- 'openvdb_maya/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
@@ -19,7 +18,6 @@ on:
|
||||
- 'CHANGES'
|
||||
- 'openvdb_maya/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
deploy:
|
||||
@@ -33,8 +31,25 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
doxygen:
|
||||
validate_docs:
|
||||
# Cheap static checks that run before the heavier doxygen build:
|
||||
# * every #include <...> referenced from a fenced code block in doc/ resolves
|
||||
# * every github.com/.../blob/<branch>/<path> URL points to a file on disk
|
||||
# Catches the class of docs-rot from issue #2161 (e.g. nanovdb::util ->
|
||||
# nanovdb::tools rename) before the doxygen job runs.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: set_up_python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: run_validator
|
||||
run: python3 ci/validate_docs_references.py
|
||||
|
||||
doxygen:
|
||||
needs: validate_docs
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-22.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
env:
|
||||
CXX: g++
|
||||
container:
|
||||
@@ -42,12 +57,15 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install_doxygen
|
||||
run: ./ci/install_doxygen.sh 1_8_11
|
||||
run: ./ci/install_doxygen.sh 1_14_0
|
||||
- name: nanobind
|
||||
run: ./ci/install_nanobind.sh 2.5.0
|
||||
- name: install_latex
|
||||
run: ./ci/install_texlive.sh
|
||||
- name: build
|
||||
# Doxygen's native WARN_AS_ERROR=FAIL_ON_WARNINGS gates the docs build:
|
||||
# any warning fails the job. FAIL_ON_WARNINGS still generates the docs and
|
||||
# writes the full warning log first, so the artifact step below captures
|
||||
# it for debugging. Enabled via OPENVDB_DOXYGEN_WARN_AS_ERROR (see
|
||||
# doc/CMakeLists.txt).
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--build-type=Release
|
||||
@@ -58,7 +76,18 @@ jobs:
|
||||
-DOPENVDB_PYTHON_WRAP_ALL_GRID_TYPES=ON
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF
|
||||
-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON
|
||||
-DOPENVDB_DOXYGEN_WARN_AS_ERROR=ON
|
||||
\"
|
||||
- name: upload_doxygen_warning_log
|
||||
# Always upload so contributors can debug failed PR builds without
|
||||
# having to reproduce the doxygen environment locally.
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: doxygen-warnings
|
||||
path: build/doc/doxygen.warnings.log
|
||||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
- name: pre_deploy
|
||||
# Overwrite global SSH configuration
|
||||
# https://github.com/peaceiris/actions-gh-pages/issues/719
|
||||
@@ -147,4 +176,3 @@ jobs:
|
||||
- Deployed from: AcademySoftwareFoundation/openvdb ${{ github.sha }}
|
||||
|
||||
Signed-off-by: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>"
|
||||
|
||||
|
||||
@@ -28,9 +28,6 @@ on:
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
schedule:
|
||||
# run this workflow every day 7am UTC
|
||||
- cron: '0 7 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
# Allow subsequent pushes to the same PR or REF to cancel any previous jobs.
|
||||
@@ -163,7 +160,7 @@ jobs:
|
||||
run: |
|
||||
./ci/build.sh -v \
|
||||
--build-type=Release \
|
||||
--components="core,hou,bin,view,render,python,test,axcore,axbin" \
|
||||
--components="core,hou,bin,view,render,test,axcore,axbin" \
|
||||
--cargs=\" \
|
||||
-DHOUDINI_ROOT=$HOME/houdini_install/hou \
|
||||
-DOPENVDB_BUILD_HOUDINI_ABITESTS=OFF \
|
||||
|
||||
@@ -18,6 +18,8 @@ on:
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
pull_request:
|
||||
# Add 'ready_for_review' to run CI when a draft PR is marked ready.
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
branches:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
@@ -30,9 +32,6 @@ on:
|
||||
- 'fvdb/**'
|
||||
- 'pendingchanges/**'
|
||||
- '**.md'
|
||||
schedule:
|
||||
# run this workflow every day 7am UTC
|
||||
- cron: '0 7 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
type:
|
||||
@@ -47,10 +46,12 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
linux-nanovdb:
|
||||
# push OR non-draft PR OR matching manual run
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'linux'
|
||||
(github.event_name == 'push' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
|
||||
(github.event_name == 'workflow_dispatch' &&
|
||||
(github.event.inputs.type == 'all' || github.event.inputs.type == 'linux')))
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-22.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
name: >
|
||||
linux-nanovdb:cxx:${{ matrix.config.cxx }}-${{ matrix.config.build }}
|
||||
@@ -72,20 +73,22 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/cuda-12/bin" >> $GITHUB_PATH
|
||||
echo "LD_LIBRARY_PATH=/usr/local/cuda-12/lib64:$LD_LIBRARY_PATH" >> $GITHUB_ENV
|
||||
- name: nanobind
|
||||
run: ./ci/install_nanobind.sh 2.5.0
|
||||
- name: build
|
||||
# NOTE: CMAKE_POSITION_INDEPENDENT_CODE set to fix default behaviour change in clang 14
|
||||
# https://github.com/AcademySoftwareFoundation/aswf-docker/issues/228
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--build-type=${{ matrix.config.build }}
|
||||
--components=core,nano,nanotest,nanoexam,nanobench,nanotool
|
||||
--components=core,nano,nanotest,nanoexam,nanobench,nanotool,nanopython,nanopytest
|
||||
--cargs=\'
|
||||
-DUSE_EXPLICIT_INSTANTIATION=OFF
|
||||
-DNANOVDB_USE_CUDA=ON
|
||||
-DCMAKE_CUDA_ARCHITECTURES="80"
|
||||
-DNANOVDB_USE_OPENVDB=ON
|
||||
-DCMAKE_INSTALL_PREFIX=`pwd`
|
||||
-DUSE_BLOSC=OFF
|
||||
-DUSE_BLOSC=ON
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
\'
|
||||
- name: test
|
||||
@@ -93,9 +96,10 @@ jobs:
|
||||
|
||||
windows-nanovdb:
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'win'
|
||||
(github.event_name == 'push' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
|
||||
(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: 'x64-windows'
|
||||
@@ -116,13 +120,16 @@ jobs:
|
||||
- name: install
|
||||
shell: powershell
|
||||
run: .\ci\install_windows.ps1
|
||||
- name: install_numpy
|
||||
shell: powershell
|
||||
run: .\ci\install_windows_numpy.ps1
|
||||
- name: build
|
||||
# nvcc doesn't set _WIN32 when run in bash so we need to set it manually
|
||||
shell: bash
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--config=Release
|
||||
--components=core,nano,nanotest,nanoexam,nanobench,nanotool
|
||||
--components=core,nano,nanotest,nanoexam,nanobench,nanotool,nanopython,nanopytest
|
||||
--cargs=\'
|
||||
-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF
|
||||
-DMSVC_COMPRESS_PDB=ON
|
||||
@@ -136,13 +143,14 @@ jobs:
|
||||
\'
|
||||
- name: test
|
||||
shell: bash
|
||||
run: cd build && ctest -V -E ".*cuda.*|.*mgpu.*"
|
||||
run: cd build && ctest -V -C Release -E ".*cuda.*|.*mgpu.*"
|
||||
|
||||
macos-nanovdb:
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'mac'
|
||||
(github.event_name == 'push' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
|
||||
(github.event_name == 'workflow_dispatch' &&
|
||||
(github.event.inputs.type == 'all' || github.event.inputs.type == 'mac')))
|
||||
runs-on: ${{ matrix.config.runner }}
|
||||
env:
|
||||
CXX: ${{ matrix.config.cxx }}
|
||||
@@ -162,16 +170,17 @@ jobs:
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
--build-type=${{ matrix.config.build }}
|
||||
--components=core,nano,nanotest,nanoexam,nanobench,nanotool
|
||||
--components=core,nano,nanotest,nanoexam,nanobench,nanotool,nanopython,nanopytest
|
||||
--cargs=\'-DUSE_EXPLICIT_INSTANTIATION=OFF -DNANOVDB_USE_CUDA=OFF -DNANOVDB_USE_OPENVDB=ON -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install \'
|
||||
- name: test
|
||||
run: cd build && ctest -V -E ".*cuda.*|.*mgpu.*"
|
||||
|
||||
nanovdb-lite:
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'linux'
|
||||
(github.event_name == 'push' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
|
||||
(github.event_name == 'workflow_dispatch' &&
|
||||
(github.event.inputs.type == 'all' || github.event.inputs.type == 'linux')))
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-22.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
container:
|
||||
image: aswf/ci-openvdb:2024-clang17.2
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
|
||||
name: TEACHME
|
||||
|
||||
# Compile-checks the NanoVDB TEACHME tutorial against the real headers. Runs
|
||||
# when EITHER the NanoVDB headers or the tutorial change, so an API change that
|
||||
# breaks a lesson code block is caught here (the whole point of the harness).
|
||||
# Compile-only: no GPU is required.
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'feature/**'
|
||||
- 'pr/**'
|
||||
paths:
|
||||
- 'nanovdb/nanovdb/**'
|
||||
- 'doc/nanovdb/TEACHME/**'
|
||||
- '.github/workflows/teachme.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
paths:
|
||||
- 'nanovdb/nanovdb/**'
|
||||
- 'doc/nanovdb/TEACHME/**'
|
||||
- '.github/workflows/teachme.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
# Allow subsequent pushes to the same PR or REF to cancel any previous jobs.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
teachme-docs-compile:
|
||||
runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-22.04-8c-32g-300h') || 'ubuntu-latest' }}
|
||||
name: teachme-docs-compile
|
||||
container:
|
||||
image: aswf/ci-openvdb:2024-clang17.2
|
||||
env:
|
||||
# Compile-only target; matches the arch used by the other NanoVDB jobs.
|
||||
CUDAARCHS: "80"
|
||||
# Each TU compiles the kitchen-sink fixture (most of NanoVDB's heavy
|
||||
# headers), so nvcc is memory-hungry. Cap parallel compiles to stay
|
||||
# within the runner's RAM — unbounded -j OOMs the 8-core/32 GB runner.
|
||||
TEACHME_JOBS: "4"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: setup_cuda_12
|
||||
run: |
|
||||
echo "/usr/local/cuda-12/bin" >> $GITHUB_PATH
|
||||
echo "LD_LIBRARY_PATH=/usr/local/cuda-12/lib64:$LD_LIBRARY_PATH" >> $GITHUB_ENV
|
||||
- name: run_harness
|
||||
# Reference-checks every #include / prose header path, then compiles
|
||||
# every tagged code block. The OpenVDB-dependent blocks are skipped
|
||||
# (still reference-checked) when OpenVDB is not installed in the image.
|
||||
run: bash doc/nanovdb/TEACHME/test/run.sh
|
||||
@@ -145,16 +145,15 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { name: 'all', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=ON -DUSE_ZLIB=ON -DUSE_EXR=ON -DUSE_PNG=ON' }
|
||||
- { name: 'lite', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=OFF -DUSE_ZLIB=OFF -DUSE_EXR=OFF -DUSE_PNG=OFF -DOPENVDB_USE_DELAYED_LOADING=OFF' }
|
||||
- { name: 'half', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=OFF -DUSE_IMATH_HALF=ON' }
|
||||
- { name: 'sse', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DOPENVDB_SIMD=SSE42' }
|
||||
- { name: 'avx', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DOPENVDB_SIMD=AVX' }
|
||||
- { name: 'pygrid', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DOPENVDB_PYTHON_WRAP_ALL_GRID_TYPES=ON' }
|
||||
- { name: 'asan', image: '2026', build: 'asan', components: 'core,test', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON -DNANOVDB_USE_OPENVDB=ON -DOPENVDB_AX_STATIC=OFF -DOPENVDB_CORE_STATIC=OFF -DUSE_BLOSC=OFF' } # We never called blosc_destroy(), so disable blosc to silence these errors
|
||||
- { name: 'ubsan', image: '2026', build: 'ubsan', components: 'core,test', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON -DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" ' }
|
||||
- { name: 'c++20', image: '2026', build: 'Release', components: 'core,test', cmake: '-DCMAKE_CXX_STANDARD=20' }
|
||||
- { name: 'conf', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON' }
|
||||
- { name: 'all', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=ON -DUSE_ZLIB=ON -DUSE_EXR=ON -DUSE_PNG=ON' }
|
||||
- { name: 'lite', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=OFF -DUSE_ZLIB=OFF -DUSE_EXR=OFF -DUSE_PNG=OFF -DOPENVDB_USE_DELAYED_LOADING=OFF' }
|
||||
- { name: 'half', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DUSE_BLOSC=OFF -DUSE_IMATH_HALF=ON' }
|
||||
- { name: 'simd', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DOPENVDB_X86_INSTRSET=7 -DUSE_VCL=ON' } # 7=AVX
|
||||
- { name: 'pygrid', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DOPENVDB_PYTHON_WRAP_ALL_GRID_TYPES=ON' }
|
||||
- { name: 'asan', image: '2026', build: 'asan', components: 'core,test', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON -DNANOVDB_USE_OPENVDB=ON -DOPENVDB_AX_STATIC=OFF -DOPENVDB_CORE_STATIC=OFF -DUSE_BLOSC=OFF' } # We never called blosc_destroy(), so disable blosc to silence these errors
|
||||
- { name: 'ubsan', image: '2026', build: 'ubsan', components: 'core,test', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON -DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" ' }
|
||||
- { name: 'c++20', image: '2026', build: 'Release', components: 'core,test', cmake: '-DCMAKE_CXX_STANDARD=20' }
|
||||
- { name: 'conf', image: '2026', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -241,9 +240,9 @@ jobs:
|
||||
# Boost as both shared and static libs are installed.
|
||||
# USE_EXPLICIT_INSTANTIATION is disabled for debug static libraries
|
||||
# due to disk space constraints
|
||||
# - { vc: 'x64-windows-static', components: 'core,bin,view,render,test', build: 'Release', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON' }
|
||||
- { vc: 'x64-windows-static', components: 'core,bin,view,render,test', build: 'Release', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_SHARED=OFF -DUSE_STATIC_DEPENDENCIES=ON -DBLOSC_USE_EXTERNAL_SOURCES=ON' }
|
||||
- { vc: 'x64-windows', components: 'core,bin,view,render,python,test', build: 'Release', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF' }
|
||||
# - { vc: 'x64-windows', components: 'core,bin,view,render,python,test', build: 'Debug', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF' }
|
||||
- { vc: 'x64-windows', components: 'core,bin,view,render,python,test', build: 'Debug', cmake: '-A x64 -G \"Visual Studio 17 2022\" -DOPENVDB_CORE_STATIC=OFF' }
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -256,6 +255,10 @@ jobs:
|
||||
- name: install
|
||||
shell: powershell
|
||||
run: .\ci\install_windows.ps1
|
||||
- name: install_numpy
|
||||
if: matrix.config.vc == 'x64-windows'
|
||||
shell: powershell
|
||||
run: .\ci\install_windows_numpy.ps1
|
||||
- name: build
|
||||
run: >
|
||||
./ci/build.sh -v
|
||||
@@ -446,7 +449,7 @@ jobs:
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
github.event.inputs.type == 'all' ||
|
||||
github.event.inputs.type == 'winnano'
|
||||
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 }}
|
||||
@@ -495,7 +498,7 @@ jobs:
|
||||
\'
|
||||
- name: test
|
||||
shell: bash
|
||||
run: cd build && ctest -V -E ".*cuda.*"
|
||||
run: cd build && ctest -V -E ".*cuda.*|.*mgpu.*"
|
||||
|
||||
#############################################################################
|
||||
################################## Blosc ####################################
|
||||
|
||||
@@ -4,10 +4,12 @@ name: Whitespace
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'ext/**'
|
||||
- 'pendingchanges/**'
|
||||
- 'tsc/meetings/**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'ext/**'
|
||||
- 'pendingchanges/**'
|
||||
- 'tsc/meetings/**'
|
||||
|
||||
@@ -25,7 +27,7 @@ jobs:
|
||||
- name: test
|
||||
run: |
|
||||
set +e
|
||||
git grep -n -I -E '^.+[ ]+$' -- ':!tsc/meetings/*' ':!pendingchanges/*' ':!*.wlt'
|
||||
git grep -n -I -E '^.+[ ]+$' -- ':!tsc/meetings/*' ':!pendingchanges/*' ':!ext/*' ':!*.wlt'
|
||||
test $? -eq 1
|
||||
|
||||
# Search for any tabs excluding meeting notes, image files and a few others
|
||||
@@ -36,5 +38,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/*' ':!ext/*' ':!*.wlt' ':!*.jpg' ':!*.gif' ':!*.mp4' ':!*.pt' ':!*.pth' ':!*.nvdb' ':!*.npz'
|
||||
test $? -eq 1
|
||||
|
||||
@@ -1,7 +1,150 @@
|
||||
OpenVDB Version History
|
||||
=======================
|
||||
|
||||
Version 12.1.2 - In development
|
||||
Version 13.0.1 - In Development
|
||||
|
||||
Version 13.0.0 - November 3, 2025
|
||||
|
||||
This version introduces ABI changes relative to older major releases,
|
||||
so to preserve ABI compatibility it might be necessary to define the
|
||||
macro OPENVDB_ABI_VERSION_NUMBER=N, where, for example, N is 11 for
|
||||
Houdini 20.5 and 12 for Houdini 21.0.
|
||||
|
||||
Support for reading .vdb files written prior to VDB 1.0 has been removed.
|
||||
To read older .vdb files, please use VDB 12.x or older and re-write them,
|
||||
so that they use the newer file format and can be read in VDB 13+.
|
||||
|
||||
Highlights:
|
||||
- OpenVDB now includes in-memory support for Half Grids and the file format
|
||||
has been extended to add I/O serialization for half grids. More work is
|
||||
planned on adapting algorithms when using Half Grids for better performance
|
||||
and improved precision.
|
||||
- NanoVDB is no longer limited to static applications, such as rendering, but
|
||||
can increasingly be applied to problems that involve dynamic topology, such
|
||||
as level set tracking, grid building, morphology operations and merging of
|
||||
grids. This is due to the fact that we keep adding more and more tools to
|
||||
NanoVDB that facilitates tree modification. However, unlike OpenVDB, these
|
||||
operations generate new NanoVDB grids (as oppose to in-palace modifications
|
||||
in OpenVDB), yet are typically much faster since they are performed on the
|
||||
GPU. Many of these exciting new features are the foundation of fVDB.
|
||||
|
||||
OpenVDB:
|
||||
New features:
|
||||
- Added native registration of HalfGrid type.
|
||||
- Added customization of the output transformation attribute for
|
||||
points::pca(). Can be output as a quaternion or as a combined affine
|
||||
transform with the scale/stretch ommitted.
|
||||
- Added support for input quaternions attributes and affine
|
||||
transformations (that include scale) in points::rasterizeSdf() for
|
||||
ellipsoidal rasterizations.
|
||||
|
||||
API Changes:
|
||||
- Added LeafBuffer::storage() for direct const access to the raw data
|
||||
buffer when using the bool LeafNode.
|
||||
- Removed Grid::evalMinMax(), use tools::minMax for superior threaded
|
||||
performance.
|
||||
- Removed points::rasterize() that took a custom filter and interrupter.
|
||||
- Removed LeafNode<bool, ...>() TopologyCopy constructor in favor of using
|
||||
explicit mask/buffer constructors.
|
||||
- Removed ValueAccessor NodeT0, NodeT1, NodeT2 aliases, use NodeTypeAtLevel
|
||||
instead.
|
||||
- Add InternalNode::setValueMaskUnsafe() to write a value mask directly.
|
||||
- LegacyFrustum and RootNodeMask have been removed. These classes were only
|
||||
used for reading old .vdb files, which is no longer supported.
|
||||
- io::writeCompressedValues() now takes a const ValueT* source buffer for
|
||||
improved const-correctness.
|
||||
|
||||
Bug Fixes:
|
||||
- Fix a bug in the LevelSetRayIntersector class where two of the methods
|
||||
were incorrectly adjusting the time even when an intersection was not
|
||||
found.
|
||||
- Fixed an assertion in math::Quat where a vector length check was using
|
||||
too small a tolerance.
|
||||
- Fixed a bug in the unit tests where detecting if assertions were enabled
|
||||
was using NDEBUG rather than OpenVDB assertions.
|
||||
|
||||
Build:
|
||||
- Removed the unnecessary USE_NUMPY build option for the OpenVDB Python
|
||||
module. All functions which take numpy arrays are now always generated
|
||||
(numpy is not required at compile time for this support).
|
||||
- Bumped minimum supported CMake version to 3.24, and new future minimum
|
||||
to 4.0.0.
|
||||
- Bumped minimum supported OpenEXR and Imath versions to 3.2, and new
|
||||
future minimum to 3.3.
|
||||
- Bumped minimum supported Boost version to 1.82, and new future minimum
|
||||
to 1.85.
|
||||
- Bumped minimum supported Python version to 3.11
|
||||
- Bumped minimum supported Log4cplus version to 2.0
|
||||
- Bumped minimum supported Houdini version to 20.5
|
||||
- Bumped minimum supported GLFW version to 3.3
|
||||
- Bumped future minimum for TBB to 2021 (oneapi).
|
||||
- Bumped future minimum for nanobind to 2.5.0.
|
||||
|
||||
OpenVDB AX:
|
||||
New:
|
||||
- Added support for LLVM 21.
|
||||
|
||||
NanoVDB:
|
||||
Highlights:
|
||||
- NanoVDB is no longer limited to static applications, such as rendering,
|
||||
but can increasingly be applied to problems that involve dynamic
|
||||
topology, such as level set tracking, grid building, morphology
|
||||
operations and merging of grids. This is due to the fact that we keep
|
||||
adding more and more tools to NanoVDB that facilitates tree modification.
|
||||
However, unlike OpenVDB, these operations generate new NanoVDB grids (as
|
||||
oppose to in-palace modifications in OpenVDB), yet are typically much
|
||||
faster since they are performed on the GPU. Many of these exciting new
|
||||
features are the foundation of fVDB.
|
||||
|
||||
New features:
|
||||
- Added nanovdb/tools/cuda/DilateGrid.cuh, which performs topology dilation
|
||||
on the GPU.
|
||||
- Added nanovdb/tools/cuda/MergeGrids.cuh, which merges the topology of two
|
||||
grids on the GPU.
|
||||
- Added nanovdb/tools/cuda/CoarsenGrid.cuh, which performs topological
|
||||
coarsening of grids on the GPU.
|
||||
- Added nanovdb/tools/cuda/RefineGrid.cuh, which performs topological
|
||||
refinement of grids on the GPU.
|
||||
- Added nanovdb/tools/cuda/PruneGrid.cuh, which prunes the topology of a
|
||||
grid using a sidecar mask on the GPU.
|
||||
- Added nanovdb/util/cuda/Injection.cuh, which facilitates sidecar data
|
||||
copying - on the GPU - in the overlap of two grids with different
|
||||
topology.
|
||||
- Added Mat classes and other math types to NanoVDB.
|
||||
- Major rewrite of nanovdb::NanoToOpenVDB, which now allows
|
||||
nanovdb::IndexGrids and blind (or sidecar) data to be converted into an
|
||||
openvdb grid.
|
||||
- Added support for LevelSet, FogVolume, and Staggered grid semantics.
|
||||
Added toGridClass and toSemantic to map Grid Blind data semantic to Grid
|
||||
class and vice versa.
|
||||
|
||||
Improvements:
|
||||
- Improvements to mGPU grid building and introduction of a separate test
|
||||
executable for mGPU tests
|
||||
- Added stand-alone function nanovdb::writeUncompressedGrid that allows
|
||||
nanovdb::IndexGrids and blind (or sidecar) data to be written directly to
|
||||
a stream.
|
||||
- nanovdb_print now (optionally) prints the blind data count per grid.
|
||||
- Extended nanovdb::GridBlindDataSemantic with nanovdb::GridClass types to
|
||||
support conversion between IndexGrid and regular grids.
|
||||
- Improvements and bugfixes to PNanoVDB.
|
||||
|
||||
API Changes:
|
||||
- Removed nanovdb::ValueIndexMask and nanovdb::ValueOnIndexMask since they
|
||||
are no longer used by fVDB (or elsewhere).
|
||||
- NanoVDB version is now 32.9.0, so the ABI of the grid is preserved but
|
||||
not the API. However, the API changes are very minor!
|
||||
|
||||
Bug Fixes:
|
||||
- Several fixes for compiler warnings.
|
||||
- Fixed error in nanovdb/io/IO.h related to missing throws.
|
||||
|
||||
Houdini:
|
||||
- Remove support for openvdb_houdini::Interrupter, use
|
||||
openvdb_houdini::HoudiniInterrupter instead.
|
||||
- Remove input-stealing via duplicateSourceStealable(), SideFX have since
|
||||
introduced verbification and compiled SOPs to provide zero-copy node
|
||||
evaluation.
|
||||
|
||||
|
||||
Version 12.1.1 - September 30, 2025
|
||||
|
||||
+53
-32
@@ -52,9 +52,9 @@ endif()
|
||||
|
||||
###### Version
|
||||
|
||||
set(OpenVDB_MAJOR_VERSION 12)
|
||||
set(OpenVDB_MINOR_VERSION 1)
|
||||
set(OpenVDB_PATCH_VERSION 2)
|
||||
set(OpenVDB_MAJOR_VERSION 13)
|
||||
set(OpenVDB_MINOR_VERSION 0)
|
||||
set(OpenVDB_PATCH_VERSION 1)
|
||||
set(OpenVDB_VERSION "${OpenVDB_MAJOR_VERSION}.${OpenVDB_MINOR_VERSION}.${OpenVDB_PATCH_VERSION}")
|
||||
|
||||
project(OpenVDB LANGUAGES CXX VERSION ${OpenVDB_VERSION})
|
||||
@@ -80,6 +80,10 @@ option(OPENVDB_BUILD_NANOVDB "Build the NanoVDB library. Turns ON if USE_NANOVDB
|
||||
# Global options
|
||||
option(OPENVDB_ENABLE_RPATH "Build with RPATH information" ON)
|
||||
option(OPENVDB_ENABLE_ASSERTS "Build with asserts in OpenVDB code enabled" OFF)
|
||||
option(OPENVDB_ENABLE_VEC_RELATIONAL_OPERATIONS [=[
|
||||
Enabled deprecated relational < and > operators on Vector types. This option
|
||||
will be switched to OFF and the feature will be subsequently removed in future
|
||||
releases.]=] ON)
|
||||
option(OPENVDB_USE_DELAYED_LOADING "Build the core OpenVDB library with delayed-loading." ON)
|
||||
option(OPENVDB_CXX_STRICT "Enable or disable pre-defined compiler warnings" OFF)
|
||||
cmake_dependent_option(OPENVDB_INSTALL_CMAKE_MODULES
|
||||
@@ -131,6 +135,30 @@ required.]=] ${USE_EXR})
|
||||
option(USE_PNG "Use PNG while building openvdb components." OFF)
|
||||
option(USE_AX "Use OpenVDB AX while building openvdb components." ${OPENVDB_BUILD_AX})
|
||||
option(USE_NANOVDB "Use NanoVDB while building openvdb components." ${OPENVDB_BUILD_NANOVDB})
|
||||
option(USE_VCL [=[
|
||||
Use the internally supplied copy of Agner Fog's vectorclass (VCL) SIMD wrapper
|
||||
library to instrument OpenVDB tools. This also requires VCL to be installed
|
||||
along with OpenVDB headers. Only applicable which compiling for x86 targets.]=] OFF)
|
||||
set(OPENVDB_X86_INSTRSET 0 CACHE STRING [=[
|
||||
The x86 instruction set to use. When unset, if USE_VCL is ON, this is
|
||||
automatically configured based on provided build flags. When unset and USE_VCL
|
||||
is OFF, defaults to 0. It is expected for you to provide this flag. Available
|
||||
values are:
|
||||
0: Unknown or unspecified
|
||||
1: SSE -msse
|
||||
2: SSE2 -msse2 # Minimum on x86_64
|
||||
3: SSE3 -msse3
|
||||
4: SSSE3 -mssse3
|
||||
5: SSE4.1 -msse4.1
|
||||
6: SSE4.2 -msse4.2 # Minimum recommended where supported
|
||||
7: AVX -mavx
|
||||
8: AVX2 -mavx2
|
||||
9: AVX512F -mavx512f
|
||||
10: AVX512BW/DQ/VL -mavx512bw -mavx512dq -mavx512vl
|
||||
Note that (while not strictly enforced by vendors) each option implies all
|
||||
preceeding flags. If you need more granular control you can provide your
|
||||
desired flags via CXX_FLAGS. Only applicable which compiling for x86 targets.
|
||||
]=])
|
||||
|
||||
cmake_dependent_option(OPENVDB_DISABLE_BOOST_IMPLICIT_LINKING
|
||||
"Disable the implicit linking of Boost libraries on Windows" ON "WIN32" OFF)
|
||||
@@ -172,19 +200,6 @@ elseif(NOT ${CONCURRENT_MALLOC} IN_LIST _CONCURRENT_MALLOC_OPTIONS)
|
||||
set(CONCURRENT_MALLOC Auto CACHE STRING FORCE)
|
||||
endif()
|
||||
|
||||
set(_OPENVDB_SIMD_OPTIONS None SSE42 AVX)
|
||||
if(NOT OPENVDB_SIMD)
|
||||
set(OPENVDB_SIMD None CACHE STRING
|
||||
"Choose whether to enable SIMD compiler flags or not, options are: None SSE42 AVX.
|
||||
Although not required, it is strongly recommended to enable SIMD. AVX implies SSE42.
|
||||
None is the default." FORCE
|
||||
)
|
||||
elseif(NOT ${OPENVDB_SIMD} IN_LIST _OPENVDB_SIMD_OPTIONS)
|
||||
message(WARNING "Unrecognized or unsupported value for OPENVDB_SIMD, "
|
||||
"using None instead.")
|
||||
set(OPENVDB_SIMD None CACHE STRING FORCE)
|
||||
endif()
|
||||
|
||||
if(USE_BLOSC AND NOT USE_ZLIB)
|
||||
message(WARNING "ZLib can only be disabled if Blosc is also disabled. Enabling ZLib.")
|
||||
endif()
|
||||
@@ -385,6 +400,28 @@ endif()
|
||||
|
||||
#########################################################################
|
||||
|
||||
# Still support -DOPENVDB_SIMD=SSE42 and -DOPENVDB_SIMD=AVX, these alias to
|
||||
# their respective OPENVDB_X86_INSTRSET value but, as a build option, are
|
||||
# deprecated. Not that the defines in version.h need to persist in case
|
||||
# downstream software was using them before including VDB headers
|
||||
set(_OPENVDB_SIMD_OPTIONS None SSE42 AVX)
|
||||
if(NOT OPENVDB_SIMD)
|
||||
set(OPENVDB_SIMD None CACHE STRING "Deprecated, see OPENVDB_X86_INSTRSET." FORCE)
|
||||
else()
|
||||
if(NOT ${OPENVDB_SIMD} IN_LIST _OPENVDB_SIMD_OPTIONS)
|
||||
message(WARNING "Unrecognized or unsupported value for OPENVDB_SIMD, using None instead.")
|
||||
set(OPENVDB_SIMD None CACHE STRING "Deprecated, see OPENVDB_X86_INSTRSET." FORCE)
|
||||
endif()
|
||||
message(DEPRECATION "The OPENVDB_SIMD option is deprecated and will be removed. Use OPENVDB_X86_INSTRSET.")
|
||||
if(OPENVDB_SIMD STREQUAL "AVX")
|
||||
set(OPENVDB_X86_INSTRSET 7)
|
||||
elseif(OPENVDB_SIMD STREQUAL "SSE42")
|
||||
set(OPENVDB_X86_INSTRSET 6)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#########################################################################
|
||||
|
||||
# Configure general CMake and CXX settings
|
||||
|
||||
include(cmake/config/OpenVDBCXX.cmake)
|
||||
@@ -404,22 +441,6 @@ if(CONCURRENT_MALLOC STREQUAL "Auto")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Configure SIMD. AVX implies SSE 4.2.
|
||||
|
||||
if(OPENVDB_SIMD STREQUAL "AVX")
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-mavx>")
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-msse4.2>")
|
||||
endif()
|
||||
add_compile_definitions("$<$<COMPILE_LANGUAGE:CXX>:OPENVDB_USE_AVX>")
|
||||
add_compile_definitions("$<$<COMPILE_LANGUAGE:CXX>:OPENVDB_USE_SSE42>")
|
||||
elseif(OPENVDB_SIMD STREQUAL "SSE42")
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-msse4.2>")
|
||||
endif()
|
||||
add_compile_definitions("$<$<COMPILE_LANGUAGE:CXX>:OPENVDB_USE_SSE42>")
|
||||
endif()
|
||||
|
||||
#########################################################################
|
||||
|
||||
# Configure our cmake modules to only search for static libraries
|
||||
|
||||
+1
-1
@@ -6,10 +6,10 @@
|
||||
|
||||
# Admin/General
|
||||
/.github/ @kmuseth @apradhana @jmlait @idclip @danrbailey @richhones
|
||||
/.github/workflows/nanovdb.yml @kmuseth @apradhana
|
||||
/ci/ @kmuseth @apradhana @jmlait @idclip @danrbailey @richhones
|
||||
/cmake/ @kmuseth @apradhana @jmlait @idclip @danrbailey @richhones
|
||||
/doc/ @kmuseth @apradhana @jmlait @idclip @danrbailey @richhones
|
||||
/pendingchanges/ @kmuseth @apradhana @jmlait @idclip @danrbailey @richhones
|
||||
/tsc/ @kmuseth @apradhana @jmlait @idclip @danrbailey @richhones
|
||||
CODEOWNERS @kmuseth @apradhana @jmlait @idclip @danrbailey @richhones
|
||||
LICENSE @kmuseth @apradhana @jmlait @idclip @danrbailey @richhones
|
||||
|
||||
@@ -57,6 +57,10 @@ code and has the right to release it under the
|
||||
[Apache 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.
|
||||
|
||||
#### Stale PRs
|
||||
|
||||
A PR that does not have a signed CLA or DCO sign-off will be given at least one week in which to resolve the issue and then will be closed. The TSC cannot review or merge PRs without these requirements having been met. Please re-open PRs that are now expected to pass once this criteria has been met.
|
||||
|
||||
## Maintainer
|
||||
|
||||
The maintainer role is the equivalent of the "Committer" role in the charter.
|
||||
|
||||
@@ -95,8 +95,6 @@ vcpkg install zlib:x64-windows
|
||||
vcpkg install blosc:x64-windows
|
||||
vcpkg install tbb:x64-windows
|
||||
vcpkg install boost-iostreams:x64-windows
|
||||
vcpkg install boost-any:x64-windows
|
||||
vcpkg install boost-algorithm:x64-windows
|
||||
vcpkg install boost-interprocess:x64-windows
|
||||
```
|
||||
|
||||
|
||||
+3
-1
@@ -73,6 +73,8 @@ COMPONENTS['nanotest']='NANOVDB_BUILD_UNITTESTS'
|
||||
COMPONENTS['nanoexam']='NANOVDB_BUILD_EXAMPLES'
|
||||
COMPONENTS['nanobench']='NANOVDB_BUILD_BENCHMARK'
|
||||
COMPONENTS['nanotool']='NANOVDB_BUILD_TOOLS'
|
||||
COMPONENTS['nanopython']='NANOVDB_BUILD_PYTHON_MODULE'
|
||||
COMPONENTS['nanopytest']='NANOVDB_BUILD_PYTHON_UNITTESTS'
|
||||
|
||||
################################################
|
||||
|
||||
@@ -196,8 +198,8 @@ 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_10=ON \
|
||||
-DOPENVDB_USE_DEPRECATED_ABI_11=ON \
|
||||
-DOPENVDB_USE_DEPRECATED_ABI_12=ON \
|
||||
-DOPENVDB_TOOL_USE_NANO=OFF \
|
||||
-DOPENVDB_BUILD_BINARIES=ON \
|
||||
-DOPENVDB_BUILD_PYTHON_UNITTESTS=ON \
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
set -ex
|
||||
|
||||
# Download the TeX Live installer
|
||||
wget https://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz
|
||||
wget https://mirrors.mit.edu/CTAN/systems/texlive/tlnet/install-tl-unx.tar.gz
|
||||
|
||||
# Extract
|
||||
tar xzf install-tl-unx.tar.gz
|
||||
@@ -13,10 +13,10 @@ rm install-tl-unx.tar.gz
|
||||
cd install-tl-*
|
||||
|
||||
# Run the installer with no docs or source installation
|
||||
./install-tl --no-interaction --no-doc-install --no-src-install --scheme=minimal
|
||||
./install-tl --no-interaction --no-doc-install --no-src-install --scheme=minimal --repository https://mirrors.mit.edu/CTAN/systems/texlive/tlnet
|
||||
|
||||
# Export the texlive PATH to GITHUB_PATH
|
||||
TEXLIVE_PATH=/usr/local/texlive/2025/bin/x86_64-linux
|
||||
TEXLIVE_PATH=$(echo /usr/local/texlive/*/bin/x86_64-linux)
|
||||
export PATH=${TEXLIVE_PATH}:${PATH}
|
||||
echo "${TEXLIVE_PATH}" >> $GITHUB_PATH
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Enable verbose and stop on error
|
||||
$ErrorActionPreference = "Stop"
|
||||
$VerbosePreference = "Continue"
|
||||
|
||||
# Install numpy for tests
|
||||
& "$env:VCPKG_INSTALLATION_ROOT\installed\$env:VCPKG_DEFAULT_TRIPLET\tools\python3\python.exe" -m ensurepip --upgrade
|
||||
& "$env:VCPKG_INSTALLATION_ROOT\installed\$env:VCPKG_DEFAULT_TRIPLET\tools\python3\python.exe" -m pip install numpy
|
||||
|
||||
Write-Host "pip numpy install completed successfully"
|
||||
Executable
+236
@@ -0,0 +1,236 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright Contributors to the OpenVDB Project
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Validate cross-references inside the OpenVDB documentation tree.
|
||||
|
||||
Run from the repo root (no arguments) or pass paths to validate explicitly.
|
||||
The script scans Markdown (`.md`) and Doxygen narrative (`.txt`) files under
|
||||
``doc/`` and checks two classes of cross-reference for staleness:
|
||||
|
||||
1. ``#include <path/to/header.h>`` lines that appear inside Markdown
|
||||
``cpp``/``cuda``/``c``/``c++`` fenced code blocks or inside Doxygen
|
||||
``@code``/``\\code`` blocks. Each include is resolved to a real file in
|
||||
``openvdb/``, ``openvdb_ax/``, ``openvdb_houdini/`` or ``nanovdb/``.
|
||||
|
||||
2. ``https://github.com/AcademySoftwareFoundation/openvdb/blob/<ref>/<path>``
|
||||
URLs. The ``<path>`` portion is checked against the local checkout.
|
||||
|
||||
The script exits non-zero on any unresolved reference and prints a
|
||||
``file:line`` style error so the failure is actionable in CI logs. It is
|
||||
intentionally dependency-free so it can run in a minimal Python container.
|
||||
|
||||
This guards against the class of bug tracked in issue #2161, where renames
|
||||
under ``nanovdb/util/ -> nanovdb/tools/`` and ``nanovdb/util/IO.h ->
|
||||
nanovdb/io/IO.h`` silently left the docs referencing paths that no longer
|
||||
exist.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Iterable, List, Sequence, Tuple
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Map a top-level include namespace to one or more on-disk directories that it
|
||||
# resolves against. ``#include <nanovdb/io/IO.h>`` resolves to
|
||||
# ``nanovdb/nanovdb/io/IO.h`` because the repo uses a double-named layout;
|
||||
# ``#include <houdini_utils/ParmFactory.h>`` resolves to
|
||||
# ``openvdb_houdini/openvdb_houdini/ParmFactory.h`` because the Houdini plugin
|
||||
# CMake configuration synthesises a ``houdini_utils/`` subtree at build time
|
||||
# (see ``openvdb_houdini/openvdb_houdini/CMakeLists.txt``).
|
||||
INCLUDE_NAMESPACE_ROOTS = {
|
||||
"openvdb": ("openvdb/openvdb", "openvdb"),
|
||||
"openvdb_ax": ("openvdb_ax/openvdb_ax", "openvdb_ax"),
|
||||
"openvdb_houdini": ("openvdb_houdini/openvdb_houdini", "openvdb_houdini"),
|
||||
"nanovdb": ("nanovdb/nanovdb", "nanovdb"),
|
||||
"houdini_utils": ("openvdb_houdini/openvdb_houdini",),
|
||||
}
|
||||
|
||||
# Top-level include path components that come from outside this repo. When a
|
||||
# header's leading component matches one of these, we skip resolution entirely.
|
||||
# Houdini SDK headers (``UT/``, ``GA/``, ``GEO_*``, etc.) live in the Houdini
|
||||
# install, not in our checkout.
|
||||
EXTERNAL_INCLUDE_PREFIXES = (
|
||||
"boost/",
|
||||
"tbb/",
|
||||
"cuda", # cuda_runtime.h, cuda.h, ...
|
||||
"thrust/",
|
||||
"Eigen/",
|
||||
"OpenEXR/",
|
||||
"Imath/",
|
||||
"openexr/",
|
||||
"GL/",
|
||||
# Houdini SDK prefixes
|
||||
"UT/",
|
||||
"GA/",
|
||||
"GU/",
|
||||
"GEO/",
|
||||
"GT/",
|
||||
"GR/",
|
||||
"OP/",
|
||||
"PRM/",
|
||||
"SOP/",
|
||||
"SYS/",
|
||||
"CH/",
|
||||
"DEP/",
|
||||
"PI/",
|
||||
"RE/",
|
||||
# Standard library headers (no '/' is the usual signal, but list a few
|
||||
# explicitly to be safe).
|
||||
"stdio.h",
|
||||
"iostream",
|
||||
"vector",
|
||||
"string",
|
||||
"memory",
|
||||
"cstdint",
|
||||
)
|
||||
|
||||
# Match a Markdown fenced code block whose language is one of cpp/cuda/c/c++.
|
||||
_MARKDOWN_FENCE_RE = re.compile(
|
||||
r"^```(?:cpp|cuda|c\+\+|c)\s*$(?P<body>.*?)^```\s*$",
|
||||
re.DOTALL | re.MULTILINE,
|
||||
)
|
||||
|
||||
# Doxygen ``@code``/``\code`` ... ``@endcode``/``\endcode`` blocks. The
|
||||
# language hint (``@code{.cpp}``) is optional.
|
||||
_DOXY_CODE_RE = re.compile(
|
||||
r"[@\\]code(?:\{[^}]*\})?\s*$(?P<body>.*?)^[@\\]endcode\s*$",
|
||||
re.DOTALL | re.MULTILINE,
|
||||
)
|
||||
|
||||
_INCLUDE_RE = re.compile(r"#\s*include\s*<([^>\n]+)>")
|
||||
|
||||
# Match URLs pointing into a blob in this repo. ``<ref>`` may be a branch
|
||||
# name or a sha; ``<path>`` is the portion that must exist on disk today.
|
||||
_GITHUB_BLOB_RE = re.compile(
|
||||
r"https?://github\.com/AcademySoftwareFoundation/openvdb/blob/"
|
||||
r"(?P<ref>[^/\s]+)/(?P<path>[^\s)\"'#?]+)"
|
||||
)
|
||||
|
||||
|
||||
def is_external_include(header: str) -> bool:
|
||||
"""Return True for headers we cannot resolve from this repo."""
|
||||
head, _, _ = header.partition("/")
|
||||
if head in INCLUDE_NAMESPACE_ROOTS:
|
||||
return False
|
||||
return any(header.startswith(p) for p in EXTERNAL_INCLUDE_PREFIXES) or "/" not in header
|
||||
|
||||
|
||||
def resolve_include(header: str) -> Path | None:
|
||||
"""Resolve ``#include <header>`` to a path on disk, or None if not found."""
|
||||
head, _, rest = header.partition("/")
|
||||
roots = INCLUDE_NAMESPACE_ROOTS.get(head)
|
||||
if roots is None or not rest:
|
||||
return None
|
||||
for root in roots:
|
||||
candidate = REPO_ROOT / root / rest
|
||||
if candidate.is_file():
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
def iter_code_block_lines(text: str) -> Iterable[Tuple[int, str]]:
|
||||
"""Yield (line_number, line_text) for every line inside a code block.
|
||||
|
||||
Line numbers are 1-based and refer to the original ``text``. Handles both
|
||||
Markdown fences (```cpp ...```) and Doxygen ``@code``/``\\code`` blocks.
|
||||
"""
|
||||
for match in _MARKDOWN_FENCE_RE.finditer(text):
|
||||
body_start = match.start("body")
|
||||
yield from _lines_with_offsets(text, match.group("body"), body_start)
|
||||
for match in _DOXY_CODE_RE.finditer(text):
|
||||
body_start = match.start("body")
|
||||
yield from _lines_with_offsets(text, match.group("body"), body_start)
|
||||
|
||||
|
||||
def _lines_with_offsets(full_text: str, body: str, body_offset: int) -> Iterable[Tuple[int, str]]:
|
||||
base_line = full_text.count("\n", 0, body_offset) + 1
|
||||
for i, line in enumerate(body.splitlines()):
|
||||
yield base_line + i, line
|
||||
|
||||
|
||||
def find_doc_files(extra_paths: Sequence[str]) -> List[Path]:
|
||||
"""Return the list of doc files to scan.
|
||||
|
||||
Without arguments, scan ``doc/**/*.md`` and ``doc/**/*.txt``. With
|
||||
arguments, scan only those files (resolved relative to the repo root).
|
||||
"""
|
||||
if extra_paths:
|
||||
return [
|
||||
(Path(p) if Path(p).is_absolute() else REPO_ROOT / p).resolve()
|
||||
for p in extra_paths
|
||||
]
|
||||
doc_dir = REPO_ROOT / "doc"
|
||||
return sorted(
|
||||
list(doc_dir.rglob("*.md")) + list(doc_dir.rglob("*.txt"))
|
||||
)
|
||||
|
||||
|
||||
def check_file(path: Path) -> List[str]:
|
||||
"""Return a list of human-readable error strings for the given doc file."""
|
||||
errors: List[str] = []
|
||||
try:
|
||||
text = path.read_text(encoding="utf-8")
|
||||
except (OSError, UnicodeDecodeError) as exc:
|
||||
return [f"{path}: failed to read: {exc}"]
|
||||
|
||||
rel = path.relative_to(REPO_ROOT) if path.is_relative_to(REPO_ROOT) else path
|
||||
|
||||
for line_no, line in iter_code_block_lines(text):
|
||||
for include_match in _INCLUDE_RE.finditer(line):
|
||||
header = include_match.group(1).strip()
|
||||
if is_external_include(header):
|
||||
continue
|
||||
if resolve_include(header) is None:
|
||||
errors.append(
|
||||
f"{rel}:{line_no}: include <{header}> does not resolve "
|
||||
"to a file in this checkout"
|
||||
)
|
||||
|
||||
for url_match in _GITHUB_BLOB_RE.finditer(text):
|
||||
ref = url_match.group("ref")
|
||||
url_path = url_match.group("path")
|
||||
on_disk = REPO_ROOT / url_path
|
||||
if not on_disk.exists():
|
||||
# Compute the 1-based line number of the URL for the error report.
|
||||
line_no = text.count("\n", 0, url_match.start()) + 1
|
||||
errors.append(
|
||||
f"{rel}:{line_no}: github URL "
|
||||
f"AcademySoftwareFoundation/openvdb/blob/{ref}/{url_path} "
|
||||
"does not exist in this checkout"
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> int:
|
||||
files = find_doc_files(argv[1:])
|
||||
total_errors: List[str] = []
|
||||
for path in files:
|
||||
if not path.exists():
|
||||
total_errors.append(f"{path}: file does not exist")
|
||||
continue
|
||||
total_errors.extend(check_file(path))
|
||||
|
||||
if total_errors:
|
||||
print("Documentation reference validation FAILED.", file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
for err in total_errors:
|
||||
print(err, file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print(
|
||||
f"{len(total_errors)} broken reference(s) across "
|
||||
f"{len(files)} file(s).",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
print(f"OK: {len(files)} doc file(s) validated, no broken references.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
@@ -419,9 +419,11 @@ if(NOT TARGET Blosc::blosc)
|
||||
set(${BLOSC_EXTERNAL_LIB}_LIBRARIES ${${BLOSC_EXTERNAL_LIB}_LIBRARY_DEBUG})
|
||||
endif()
|
||||
|
||||
target_link_libraries(Blosc::blosc INTERFACE
|
||||
$<$<CONFIG:Release>:${${BLOSC_EXTERNAL_LIB}_LIBRARY_RELEASE}>
|
||||
$<$<CONFIG:Debug>:${${BLOSC_EXTERNAL_LIB}_LIBRARY_DEBUG}>)
|
||||
if(${BLOSC_EXTERNAL_LIB}_LIBRARY_RELEASE OR ${BLOSC_EXTERNAL_LIB}_LIBRARY_DEBUG)
|
||||
target_link_libraries(Blosc::blosc INTERFACE
|
||||
$<$<CONFIG:Release>:${${BLOSC_EXTERNAL_LIB}_LIBRARY_RELEASE}>
|
||||
$<$<CONFIG:Debug>:${${BLOSC_EXTERNAL_LIB}_LIBRARY_DEBUG}>)
|
||||
endif()
|
||||
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
+116
-4
@@ -83,8 +83,8 @@ function(OPENVDB_GET_VERSION_DEFINE HEADER KEY VALUE)
|
||||
endfunction()
|
||||
|
||||
|
||||
########################################################################
|
||||
########################################################################
|
||||
###############################################################################
|
||||
###############################################################################
|
||||
|
||||
|
||||
function(OPENVDB_VERSION_FROM_HEADER OPENVDB_VERSION_FILE)
|
||||
@@ -120,8 +120,8 @@ function(OPENVDB_VERSION_FROM_HEADER OPENVDB_VERSION_FILE)
|
||||
endfunction()
|
||||
|
||||
|
||||
########################################################################
|
||||
########################################################################
|
||||
###############################################################################
|
||||
###############################################################################
|
||||
|
||||
|
||||
function(OPENVDB_ABI_VERSION_FROM_PRINT OPENVDB_PRINT)
|
||||
@@ -162,3 +162,115 @@ function(OPENVDB_ABI_VERSION_FROM_PRINT OPENVDB_PRINT)
|
||||
set(${_VDB_ABI} ${_OpenVDB_ABI} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
###############################################################################
|
||||
## @brief Compute the enumerated ISA value for x86/x86_64 architectures that
|
||||
## VCL will use during compilation of OpenVDB. The value is stored in the
|
||||
## VCL_INSTRSET variable. Possible values are:
|
||||
## 0: Unknown or error
|
||||
## 2: SSE2
|
||||
## 3: SSE3
|
||||
## 4: SSSE3
|
||||
## 5: SSE4.1
|
||||
## 6: SSE4.2
|
||||
## 7: AVX
|
||||
## 8: AVX2
|
||||
## 9: AVX512F
|
||||
## 10: AVX512BW/DQ/VL
|
||||
function(OPENVDB_COMPUTE_X86_INSTRSET_FROM_VCL VCL_INSTRSET)
|
||||
# Append project specific flags
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
if(OPENVDB_SIMD STREQUAL "AVX")
|
||||
set(CMAKE_REQUIRED_FLAGS "-mavx;-msse4.2")
|
||||
elseif(OPENVDB_SIMD STREQUAL "SSE42")
|
||||
set(CMAKE_REQUIRED_FLAGS "-msse4.2")
|
||||
endif()
|
||||
|
||||
# Figure out that the INSTRSET macro would expand to.
|
||||
# @note would be much faster with try_run but this way doesn't involve
|
||||
# invoking bad instruction sets on unsupported ISAs on the build machine.
|
||||
include(CheckCXXSourceCompiles)
|
||||
check_cxx_source_compiles([[
|
||||
#include "${CMAKE_SOURCE_DIR}/ext/vcl/openvdb/ext/vcl/instrset.h"
|
||||
int main() { static_assert(INSTRSET == 10); return 0; } ]] COMPUTE_VCL_INSTRSET__AVX512VL_DQ_BW__)
|
||||
if(COMPUTE_VCL_INSTRSET__AVX512VL_DQ_BW__)
|
||||
set(${VCL_INSTRSET} 10 PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
return()
|
||||
endif()
|
||||
check_cxx_source_compiles([[
|
||||
#include "${CMAKE_SOURCE_DIR}/ext/vcl/openvdb/ext/vcl/instrset.h"
|
||||
int main() { static_assert(INSTRSET == 9); return 0; } ]] COMPUTE_VCL_INSTRSET__AVX512F__)
|
||||
if(COMPUTE_VCL_INSTRSET__AVX512F__)
|
||||
set(${VCL_INSTRSET} 9 PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
return()
|
||||
endif()
|
||||
check_cxx_source_compiles([[
|
||||
#include "${CMAKE_SOURCE_DIR}/ext/vcl/openvdb/ext/vcl/instrset.h"
|
||||
int main() { static_assert(INSTRSET == 8); return 0; } ]] COMPUTE_VCL_INSTRSET__AVX2__)
|
||||
if(COMPUTE_VCL_INSTRSET__AVX2__)
|
||||
set(${VCL_INSTRSET} 8 PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
return()
|
||||
endif()
|
||||
check_cxx_source_compiles([[
|
||||
#include "${CMAKE_SOURCE_DIR}/ext/vcl/openvdb/ext/vcl/instrset.h"
|
||||
int main() { static_assert(INSTRSET == 7); return 0; } ]] COMPUTE_VCL_INSTRSET__AVX__)
|
||||
if(COMPUTE_VCL_INSTRSET__AVX__)
|
||||
set(${VCL_INSTRSET} 7 PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
return()
|
||||
endif()
|
||||
check_cxx_source_compiles([[
|
||||
#include "${CMAKE_SOURCE_DIR}/ext/vcl/openvdb/ext/vcl/instrset.h"
|
||||
int main() { static_assert(INSTRSET == 6); return 0; } ]] COMPUTE_VCL_INSTRSET__SSE4_2__)
|
||||
if(COMPUTE_VCL_INSTRSET__SSE4_2__)
|
||||
set(${VCL_INSTRSET} 6 PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
return()
|
||||
endif()
|
||||
check_cxx_source_compiles([[
|
||||
#include "${CMAKE_SOURCE_DIR}/ext/vcl/openvdb/ext/vcl/instrset.h"
|
||||
int main() { static_assert(INSTRSET == 5); return 0; } ]] COMPUTE_VCL_INSTRSET__SSE4_1__)
|
||||
if(COMPUTE_VCL_INSTRSET__SSE4_1__)
|
||||
set(${VCL_INSTRSET} 5 PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
return()
|
||||
endif()
|
||||
check_cxx_source_compiles([[
|
||||
#include "${CMAKE_SOURCE_DIR}/ext/vcl/openvdb/ext/vcl/instrset.h"
|
||||
int main() { static_assert(INSTRSET == 4); return 0; } ]] COMPUTE_VCL_INSTRSET__SSSE3__)
|
||||
if(COMPUTE_VCL_INSTRSET__SSSE3__)
|
||||
set(${VCL_INSTRSET} 4 PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
return()
|
||||
endif()
|
||||
check_cxx_source_compiles([[
|
||||
#include "${CMAKE_SOURCE_DIR}/ext/vcl/openvdb/ext/vcl/instrset.h"
|
||||
int main() { static_assert(INSTRSET == 3); return 0; } ]] COMPUTE_VCL_INSTRSET__SSE3__)
|
||||
if(COMPUTE_VCL_INSTRSET__SSE3__)
|
||||
set(${VCL_INSTRSET} 3 PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
return()
|
||||
endif()
|
||||
check_cxx_source_compiles([[
|
||||
#include "${CMAKE_SOURCE_DIR}/ext/vcl/openvdb/ext/vcl/instrset.h"
|
||||
int main() { static_assert(INSTRSET == 2); return 0; } ]] COMPUTE_VCL_INSTRSET__SSE2__)
|
||||
if(COMPUTE_VCL_INSTRSET__SSE2__)
|
||||
set(${VCL_INSTRSET} 2 PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
return()
|
||||
endif()
|
||||
check_cxx_source_compiles([[
|
||||
#include "${CMAKE_SOURCE_DIR}/ext/vcl/openvdb/ext/vcl/instrset.h"
|
||||
int main() { static_assert(INSTRSET == 1); return 0; } ]] COMPUTE_VCL_INSTRSET__SSE__)
|
||||
if(COMPUTE_VCL_INSTRSET__SSE__)
|
||||
set(${VCL_INSTRSET} 1 PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
return()
|
||||
endif()
|
||||
# Unknown or error
|
||||
set(${VCL_INSTRSET} 0 PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
endfunction()
|
||||
|
||||
@@ -127,6 +127,47 @@ endif()
|
||||
|
||||
###############################################################################
|
||||
|
||||
# x86 ISA selection
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
|
||||
if(OPENVDB_X86_INSTRSET GREATER_EQUAL 1)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-msse>")
|
||||
endif()
|
||||
if(OPENVDB_X86_INSTRSET GREATER_EQUAL 2)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-msse2>")
|
||||
endif()
|
||||
if(OPENVDB_X86_INSTRSET GREATER_EQUAL 3)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-msse3>")
|
||||
endif()
|
||||
if(OPENVDB_X86_INSTRSET GREATER_EQUAL 4)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-mssse3>")
|
||||
endif()
|
||||
if(OPENVDB_X86_INSTRSET GREATER_EQUAL 5)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-msse4.1>")
|
||||
endif()
|
||||
if(OPENVDB_X86_INSTRSET GREATER_EQUAL 6)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-msse4.2>")
|
||||
endif()
|
||||
if(OPENVDB_X86_INSTRSET GREATER_EQUAL 7)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-mavx>")
|
||||
endif()
|
||||
if(OPENVDB_X86_INSTRSET GREATER_EQUAL 8)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-mavx2>")
|
||||
endif()
|
||||
if(OPENVDB_X86_INSTRSET GREATER_EQUAL 9)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-mavx512f>")
|
||||
endif()
|
||||
if(OPENVDB_X86_INSTRSET GREATER_EQUAL 10)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-mavx512bw>")
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-mavx512dq>")
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-mavx512vl>")
|
||||
endif()
|
||||
elseif(OPENVDB_X86_INSTRSET GREATER 0)
|
||||
message(WARNING "OPENVDB_X86_INSTRSET has been set but has no effect when building for ${CMAKE_SYSTEM_PROCESSOR}.")
|
||||
endif()
|
||||
|
||||
###############################################################################
|
||||
|
||||
# Increase the number of sections that an object file can contain
|
||||
add_compile_options("$<$<COMPILE_LANG_AND_ID:CXX,MSVC>:/bigobj>")
|
||||
# Enable MSVC options that make it behave like other compilers
|
||||
|
||||
+99
-21
@@ -25,8 +25,16 @@ option(OPENVDB_DOXYGEN_NANOVDB "Build NanoVDB documentation" ON)
|
||||
option(OPENVDB_DOXYGEN_INTERNAL [=[
|
||||
Enable documentation of methods and classes which have been explicitly marked as internal
|
||||
]=] OFF)
|
||||
# When enabled, Doxygen's WARN_AS_ERROR=FAIL_ON_WARNINGS makes any warning fail
|
||||
# the docs build (it still generates the docs and writes the full warning log
|
||||
# first, then exits non-zero). CI turns this ON -- see .github/workflows/docs.yml
|
||||
# -- so net-new doc warnings fail the PR that introduces them. Defaults OFF so a
|
||||
# local doc build isn't blocked by a stray warning from a different Doxygen
|
||||
# version; pass -DOPENVDB_DOXYGEN_WARN_AS_ERROR=ON to gate locally too.
|
||||
option(OPENVDB_DOXYGEN_WARN_AS_ERROR
|
||||
"Treat Doxygen warnings as errors" OFF)
|
||||
|
||||
find_package(Doxygen REQUIRED doxygen)
|
||||
find_package(Doxygen 1.0...<1.15.0 REQUIRED doxygen)
|
||||
if(MINIMUM_DOXYGEN_VERSION)
|
||||
if(DOXYGEN_VERSION VERSION_LESS MINIMUM_DOXYGEN_VERSION)
|
||||
message(WARNING "The doxygen-config doxyfile has been generated with version "
|
||||
@@ -41,12 +49,11 @@ set(DOXY_FILES
|
||||
openvdb/openvdb/io
|
||||
openvdb/openvdb/math
|
||||
openvdb/openvdb/points
|
||||
openvdb/openvdb/points/impl
|
||||
openvdb/openvdb/python/pyopenvdb.h
|
||||
openvdb/openvdb/thread
|
||||
openvdb/openvdb/tools
|
||||
openvdb/openvdb/tools/impl
|
||||
openvdb/openvdb/tree
|
||||
openvdb/openvdb/util
|
||||
ext/imath/openvdb/math
|
||||
doc/build.txt
|
||||
doc/changes.txt
|
||||
doc/codingstyle.txt
|
||||
@@ -58,28 +65,93 @@ set(DOXY_FILES
|
||||
doc/points.txt
|
||||
doc/python.txt)
|
||||
|
||||
if(OPENVDB_DOXYGEN_INTERNAL)
|
||||
list(APPEND
|
||||
openvdb/openvdb/points/impl
|
||||
openvdb/openvdb/tools/impl)
|
||||
endif()
|
||||
|
||||
if(NOT OPENVDB_DOXYGEN_INTERNAL)
|
||||
# Hide implementation-detail symbols in the lowercase `internal` namespaces
|
||||
# (the project convention for non-public helpers). Note: do NOT add a
|
||||
# capital-I "*Internal*" glob here -- it also matches the public
|
||||
# tree::InternalNode (and nanovdb::InternalNode) class, silently dropping it
|
||||
# from the docs and breaking every \link to it (e.g. InternalNode::childCount
|
||||
# in doc/changes.txt) while its RootNode/LeafNode siblings resolve fine.
|
||||
set(DOXYGEN_EXCLUDE_SYMBOLS
|
||||
"*internal*")
|
||||
endif()
|
||||
|
||||
set(DOXYGEN_PROJECT_NAME "OpenVDB")
|
||||
set(DOXYGEN_PROJECT_LOGO "${CMAKE_CURRENT_SOURCE_DIR}/img/openvdb_logo.png")
|
||||
set(DOXYGEN_PROJECT_NUMBER "${OpenVDB_VERSION}")
|
||||
set(DOXYGEN_PROJECT_BRIEF "")
|
||||
set(DOXYGEN_FILE_PATTERNS *.h *h.in) # headers only
|
||||
set(DOXYGEN_EXTENSION_MAPPING .in=C) # parse CMake config headers as C
|
||||
set(DOXYGEN_IMAGE_PATH "doc/img")
|
||||
set(DOXYGEN_IMAGE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/img")
|
||||
set(DOXYGEN_RECURSIVE NO)
|
||||
set(DOXYGEN_QUIET YES)
|
||||
set(DOXYGEN_NUM_PROC_THREADS 0) # detects from system
|
||||
|
||||
# Enable \snippet resolution against the on-disk example sources. Paths in
|
||||
# \snippet directives are resolved relative to EXAMPLE_PATH, so referring to
|
||||
# `nanovdb/nanovdb/examples/ex_xxx/yyy.cc` from a doc page works as written.
|
||||
set(DOXYGEN_EXAMPLE_PATH "${CMAKE_SOURCE_DIR}")
|
||||
set(DOXYGEN_EXAMPLE_PATTERNS *.cc *.cpp *.cu *.cuh *.h *.txt)
|
||||
set(DOXYGEN_EXAMPLE_RECURSIVE YES)
|
||||
|
||||
# Always capture Doxygen warnings to a logfile so CI can publish it as an
|
||||
# artifact and contributors can see exactly what's broken.
|
||||
set(DOXYGEN_WARN_LOGFILE "${CMAKE_CURRENT_BINARY_DIR}/doxygen.warnings.log")
|
||||
if(OPENVDB_DOXYGEN_WARN_AS_ERROR)
|
||||
# FAIL_ON_WARNINGS keeps generating docs but exits non-zero at the end if
|
||||
# any warning was emitted. Requires Doxygen >= 1.9.x.
|
||||
set(DOXYGEN_WARN_AS_ERROR FAIL_ON_WARNINGS)
|
||||
# Don't suppress messages so contributors can see warnings during the build.
|
||||
set(DOXYGEN_QUIET NO)
|
||||
else()
|
||||
set(DOXYGEN_QUIET YES)
|
||||
endif()
|
||||
|
||||
set(DOXYGEN_GENERATE_HTML YES)
|
||||
set(DOXYGEN_GENERATE_MAN NO)
|
||||
set(DOXYGEN_GENERATE_LATEX NO)
|
||||
set(DOXYGEN_GENERATE_TODOLIST NO)
|
||||
|
||||
set(DOXYGEN_HTML_COLORSTYLE_HUE 4)
|
||||
set(DOXYGEN_HTML_COLORSTYLE_SAT 222)
|
||||
set(DOXYGEN_HTML_TIMESTAMP NO) # for simpler website diffs
|
||||
|
||||
# Render math formulas via MathJax instead of LaTeX -> PNG. This removes the
|
||||
# entire `latex`/`dvips`/`gs` toolchain dependency from both local doc builds
|
||||
# and CI (see .github/workflows/docs.yml), eliminates a class of cross-platform
|
||||
# install pain, and produces crisper, selectable math in the rendered HTML.
|
||||
set(DOXYGEN_USE_MATHJAX YES)
|
||||
set(DOXYGEN_MATHJAX_VERSION MathJax_3) # default JS bundle loaded from a CDN
|
||||
|
||||
set(DOXYGEN_EXTRACT_ALL YES)
|
||||
set(DOXYGEN_EXTRACT_STATIC YES)
|
||||
set(DOXYGEN_EXTRACT_LOCAL_CLASSES NO)
|
||||
|
||||
# Configuration for doxygen-awesome-css
|
||||
# {
|
||||
set(DOXYGEN_HTML_COLORSTYLE_HUE 209)
|
||||
set(DOXYGEN_HTML_COLORSTYLE_SAT 255)
|
||||
set(DOXYGEN_HTML_COLORSTYLE_GAMMA 113)
|
||||
set(DOXYGEN_GENERATE_TREEVIEW YES)
|
||||
set(DOXYGEN_DISABLE_INDEX NO)
|
||||
set(DOXYGEN_FULL_SIDEBAR NO)
|
||||
set(DOXYGEN_HTML_HEADER
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/header.html")
|
||||
set(DOXYGEN_HTML_EXTRA_STYLESHEET
|
||||
"${CMAKE_SOURCE_DIR}/ext/doxygen-awesome/doxygen-awesome.css"
|
||||
"${CMAKE_SOURCE_DIR}/ext/doxygen-awesome/doxygen-awesome-sidebar-only.css"
|
||||
"${CMAKE_SOURCE_DIR}/ext/doxygen-awesome/doxygen-awesome-sidebar-only-darkmode-toggle.css"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/openvdb.css")
|
||||
set(DOXYGEN_HTML_EXTRA_FILES
|
||||
"${CMAKE_SOURCE_DIR}/ext/doxygen-awesome/doxygen-awesome-paragraph-link.js"
|
||||
"${CMAKE_SOURCE_DIR}/ext/doxygen-awesome/doxygen-awesome-fragment-copy-button.js"
|
||||
"${CMAKE_SOURCE_DIR}/ext/doxygen-awesome/doxygen-awesome-darkmode-toggle.js")
|
||||
set(DOXYGEN_HTML_COPY_CLIPBOARD NO) # required with Doxygen >= 1.10.0
|
||||
set(DOXYGEN_HTML_COLORSTYLE LIGHT) # required with Doxygen >= 1.9.5
|
||||
# }
|
||||
|
||||
# Allow for root directory paths to be listed (e.g. openvdb/io/File.h)
|
||||
# Rather than just the file names (e.g. File.h). This makes it far
|
||||
# easier to navigate the docs
|
||||
@@ -110,12 +182,13 @@ set(DOXYGEN_HIDE_SCOPE_NAMES YES)
|
||||
set(DOXYGEN_INLINE_INHERITED_MEMB YES)
|
||||
set(DOXYGEN_MACRO_EXPANSION YES)
|
||||
# @todo use dot with class diagrams for relevant classes
|
||||
set(DOXYGEN_HAVE_DOT NO)
|
||||
set(DOXYGEN_CLASS_GRAPH NO)
|
||||
set(DOXYGEN_CLASS_DIAGRAMS NO)
|
||||
|
||||
|
||||
set(DOXYGEN_ALIASES
|
||||
[[ijk="(<i>i</i>, <i>j</i>, <i>k</i>)"]]
|
||||
[[xyz="(<i>x</i>, <i>y</i>, <i>z</i>)"]]
|
||||
[[ijk="(<i>i</i>, \;<i>j</i>, \;<i>k</i>)"]]
|
||||
[[xyz="(<i>x</i>, \;<i>y</i>, \;<i>z</i>)"]]
|
||||
[[const="<tt>const</tt>"]]
|
||||
"\"vdbnamespace=openvdb::v${OpenVDB_MAJOR_VERSION}_${OpenVDB_MINOR_VERSION}\""
|
||||
[["hunamespace=houdini_utils"]]
|
||||
@@ -143,12 +216,18 @@ set(DOXYGEN_PREDEFINED
|
||||
"OPENVDB_VERSION_NAME=v${OpenVDB_MAJOR_VERSION}_${OpenVDB_MINOR_VERSION}"
|
||||
"OPENVDB_ABI_VERSION_NUMBER=${OpenVDB_MAJOR_VERSION}"
|
||||
[[__declspec(x):= __attribute__(x):=]]
|
||||
# Strip the symbol-visibility/export macro so Doxygen doesn't fold it into a
|
||||
# declaration's name. Doxygen tolerates `class OPENVDB_API X` but mis-parses
|
||||
# `enum OPENVDB_API X` (e.g. half::FromBitsTag in ext/imath/.../Half.h),
|
||||
# reporting the enum as a documented-but-undeclared symbol.
|
||||
"OPENVDB_API="
|
||||
# Replace the deprecated macros with doxygen deprecated comments. This ensures
|
||||
# that every bit of deprecated C++ is listed in the doxygen deprecated list,
|
||||
# even if it was not properly tagged
|
||||
[["OPENVDB_DEPRECATED=/** \deprecated */"]]
|
||||
[["OPENVDB_DEPRECATED_MESSAGE(x)=/** \deprecated x */"]]
|
||||
"OPENVDB_USE_LOG4CPLUS=")
|
||||
"OPENVDB_USE_LOG4CPLUS="
|
||||
"OPENVDB_UBSAN_SUPPRESS=")
|
||||
|
||||
set(DOXYGEN_ENABLED_SECTIONS "")
|
||||
|
||||
@@ -190,9 +269,9 @@ if(OPENVDB_DOXYGEN_HOUDINI)
|
||||
list(APPEND DOXY_FILES
|
||||
doc/houdini.txt
|
||||
openvdb_houdini/openvdb_houdini/AttributeTransferUtil.h
|
||||
openvdb_houdini/openvdb_houdini/GEO_PrimVDB.h
|
||||
openvdb_houdini/openvdb_houdini/reference/GEO_PrimVDB.h
|
||||
openvdb_houdini/openvdb_houdini/reference/GU_PrimVDB.h
|
||||
openvdb_houdini/openvdb_houdini/GT_GEOPrimCollectVDB.h
|
||||
openvdb_houdini/openvdb_houdini/GU_PrimVDB.h
|
||||
openvdb_houdini/openvdb_houdini/GU_VDBPointTools.h
|
||||
openvdb_houdini/openvdb_houdini/GeometryUtil.h
|
||||
openvdb_houdini/openvdb_houdini/ParmFactory.h
|
||||
@@ -205,7 +284,9 @@ if(OPENVDB_DOXYGEN_HOUDINI)
|
||||
endif()
|
||||
|
||||
if(OPENVDB_DOXYGEN_AX)
|
||||
set(DOXYGEN_EXTRA_PACKAGES amsmath) # for math formatting
|
||||
# AX docs use @f$ inline and @f[ block math. Formula font size also applies
|
||||
# under MathJax (set above). The amsmath package the AX docs assume is
|
||||
# bundled by default in MathJax v3, so no extra extensions are required.
|
||||
set(DOXYGEN_FORMULA_FONTSIZE 14)
|
||||
list(APPEND DOXYGEN_ENABLED_SECTIONS "OPENVDB_AX")
|
||||
|
||||
@@ -221,8 +302,7 @@ if(OPENVDB_DOXYGEN_AX)
|
||||
openvdb_ax/openvdb_ax/compiler
|
||||
openvdb_ax/openvdb_ax/grammar
|
||||
openvdb_ax/openvdb_ax/grammar/generated
|
||||
openvdb_ax/openvdb_ax/math
|
||||
openvdb_ax/openvdb_ax/test)
|
||||
openvdb_ax/openvdb_ax/math)
|
||||
endif()
|
||||
|
||||
if(OPENVDB_DOXYGEN_NANOVDB)
|
||||
@@ -236,7 +316,6 @@ if(OPENVDB_DOXYGEN_NANOVDB)
|
||||
doc/nanovdb/HelloWorld.md
|
||||
nanovdb/nanovdb
|
||||
nanovdb/nanovdb/util
|
||||
nanovdb/nanovdb/examples/benchmark
|
||||
nanovdb/nanovdb/examples/ex_bump_pool_buffer
|
||||
nanovdb/nanovdb/examples/ex_collide_level_set
|
||||
nanovdb/nanovdb/examples/ex_make_custom_nanovdb
|
||||
@@ -255,8 +334,7 @@ if(OPENVDB_DOXYGEN_NANOVDB)
|
||||
nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor_cuda
|
||||
nanovdb/nanovdb/examples/ex_util
|
||||
nanovdb/nanovdb/examples/ex_vox_to_nanovdb
|
||||
nanovdb/nanovdb/examples/ex_write_nanovdb_grids
|
||||
nanovdb/nanovdb/unittest)
|
||||
nanovdb/nanovdb/examples/ex_write_nanovdb_grids)
|
||||
endif()
|
||||
|
||||
set(DOXYGEN_VERBATIM_VARS DOXYGEN_ALIASES DOXYGEN_PREDEFINED)
|
||||
|
||||
+225
-287
@@ -1,14 +1,13 @@
|
||||
/**
|
||||
<div style="width:800px;text-align:justify;">
|
||||
|
||||
@page ax AX
|
||||
@page ax AX Language Documentation
|
||||
|
||||
@section axlanguage AX Language Documentation
|
||||
@section axlanguage Intro
|
||||
@par
|
||||
Welcome to the AX Language documentation. These docs provide detailed
|
||||
information on the AX language including syntax, data types, available
|
||||
functionality (including OpenVDB specific methods) execution structure and
|
||||
control flow. See the @subpage axcplusplus documentation for the C++ developer
|
||||
control flow. See the @ref axcplusplus documentation for the C++ developer
|
||||
documentation.
|
||||
@note
|
||||
Some sections of this document are still to be completed. Please get in touch
|
||||
@@ -108,8 +107,8 @@ function (see @ref axprogramexample) and whilst there are ways to achieve
|
||||
reductions, it is an example which would be better suited to a custom C++
|
||||
implementation.
|
||||
@par
|
||||
For details of extending AX for custom geometry, see the @subpage axcplusplus
|
||||
developer documentation. Note that custom geometry support requires C++ extension.
|
||||
For details of extending AX for custom geometry, see the @ref axcplusplus
|
||||
developer documentation. Note that custom geometry support requires `C++` extension.
|
||||
|
||||
|
||||
@subsection axprogramexample A Program Example
|
||||
@@ -133,20 +132,20 @@ The following explains the above example in relation to OpenVDB data; OpenVDB
|
||||
points and OpenVDB volumes. The example demonstrates reading from a value on
|
||||
some input, either a point attribute or a voxel value, and storing the
|
||||
result in a @ref axdecls "local variable". Importantly, the attribute being
|
||||
accessed has the name @b `myattribute` and the type @b `float`. The first statement:
|
||||
accessed has the name `myattribute` and the type `float`. The first statement:
|
||||
<!-- A -->@code{.c}
|
||||
float temp = float@myattribute;
|
||||
@endcode
|
||||
Invokes a @b read from this attribute (see @ref axattribaccess), retrieving the
|
||||
value from the geometry and storing it in the variable @b `temp` to be used in
|
||||
value from the geometry and storing it in the variable `temp` to be used in
|
||||
the AX program. The second statement:
|
||||
@code{.c}
|
||||
if (temp > 0.0f) float@myattribute = 0.0f;
|
||||
@endcode
|
||||
Performs a comparison of this value with @b `0.0f` and, if the value is greater
|
||||
than @b `0.0f`, performs a @b write to the geometry in the form of
|
||||
<b>`float@myattribute = 0.0f;`</b> which sets the value of this attribute on the
|
||||
geometry to @b `0.0f`.
|
||||
Performs a comparison of this value with `0.0f` and, if the value is greater
|
||||
than `0.0f`, performs a @b write to the geometry in the form of
|
||||
`float@myattribute = 0.0f;` which sets the value of this attribute on the
|
||||
geometry to `0.0f`.
|
||||
@par
|
||||
For OpenVDB Points, this kernel is run over every point in an OpenVDB Points
|
||||
Grid. In simple pseudo code:
|
||||
@@ -155,7 +154,7 @@ foreach(point in pointgrid)
|
||||
run_ax_kernel(point)
|
||||
done()
|
||||
@endcode
|
||||
Where @b `pointgrid` is a single Point Data Grid. For OpenVDB volumes the kernel
|
||||
Where `pointgrid` is a single Point Data Grid. For OpenVDB volumes the kernel
|
||||
is run over each voxel in a provided grid:
|
||||
@code{.unparsed}
|
||||
foreach(grid in grids)
|
||||
@@ -165,8 +164,8 @@ foreach(grid in grids)
|
||||
done()
|
||||
@endcode
|
||||
@par
|
||||
Where @b `grids` can comprise of any number of OpenVDB volumes. In this program,
|
||||
only a floating point grid with the name @b `myattribute` will be updated. Each
|
||||
Where `grids` can comprise of any number of OpenVDB volumes. In this program,
|
||||
only a floating point grid with the name `myattribute` will be updated. Each
|
||||
voxel in this grid will have it's value compared in the same way as the points
|
||||
example given above.
|
||||
|
||||
@@ -282,9 +281,9 @@ missing types from OpenVDB.
|
||||
|
||||
@subsection axscalars Scalars
|
||||
@par
|
||||
AX Supports boolean, integer and floating point scalar types. @b `int` and
|
||||
@b `int32` represent 32-bit integer values and @b `int64` represents a 64-bit
|
||||
value. @b `float` and @b `double` represent 32-bit and 64-bit floating point
|
||||
AX Supports boolean, integer and floating point scalar types. `int` and
|
||||
`int32` represent 32-bit integer values and `int64` represents a 64-bit
|
||||
value. `float` and `double` represent 32-bit and 64-bit floating point
|
||||
values respectively. All scalars, excluding bools, are signed; there are no
|
||||
other unsigned scalar types in AX.
|
||||
@par Integer Overflow and Floating Point Truncation
|
||||
@@ -309,10 +308,10 @@ int64 a = 2147483648l; // one more than can be held in an int (note the last let
|
||||
int b = a; // implicit conversion. "b" is set to -2147483648
|
||||
@endcode
|
||||
@par Infinite and NaN values
|
||||
Floating point values have two additional intrinsic states; @b `inf` and @b `nan`.
|
||||
Floating point values have two additional intrinsic states; `inf` and `nan`.
|
||||
These states can occur during invalid floating point arithmetic. AX performs no
|
||||
checks on arithmetic to catch these values. A typical example of both @b `inf`
|
||||
and @b `nan` values are division by zero expressions:
|
||||
checks on arithmetic to catch these values. A typical example of both `inf`
|
||||
and `nan` values are division by zero expressions:
|
||||
@code
|
||||
float a = 1.0f/0.0f;
|
||||
print(a); // prints "inf"
|
||||
@@ -337,16 +336,16 @@ There is currently support for vectors of sizes 2, 3 and 4 elements. The suffix
|
||||
letter on the vector types denotes the contained elements precision, with the
|
||||
number corresponding to the vector size. For example, @b `vec2i` denotes a vector
|
||||
of two 32-bit integer elements. There are no native types for vectors with
|
||||
@b `bool`, @b `int16` or @b `int64` elements, so these are not supported.
|
||||
`bool`, `int16` or `int64` elements, so these are not supported.
|
||||
@par Matrices
|
||||
Matrices are stored in row major layout (lexographical access order) which
|
||||
matches the representation of a matrix in OpenVDB. Matrix support consists of
|
||||
@b `float` and @b `double` precision elements with dimensions either @b `3x3` or
|
||||
@b `4x4` (total size of 9 and 16 elements respectively). There are no integer or
|
||||
`float` and `double` precision elements with dimensions either `3x3` or
|
||||
`4x4` (total size of 9 and 16 elements respectively). There are no integer or
|
||||
boolean matrix types. Similiar to vectors, the suffix letter on the type denotes
|
||||
the contained elements precision, with the number corresponding to the matrix
|
||||
dimension. A matrix of type @b `mat3d` therefor corresponds to a @b `3x3` matrix
|
||||
(9 elements) with @b `double` precision elements.
|
||||
dimension. A matrix of type `mat3d` therefor corresponds to a `3x3` matrix
|
||||
(9 elements) with `double` precision elements.
|
||||
@par Element Access
|
||||
As elements of all matrix and vector types are stored contiguously, they can be
|
||||
accessed by the @ref axvecaccessop "[] operator". Vector elements can also be
|
||||
@@ -370,10 +369,10 @@ performing matrix projections (transformations) on vector arguments.
|
||||
Strings are an array of characters stored in a unique AX type which is
|
||||
incompatible with @ref axopaccess "container accesses". As such, string support
|
||||
is currently fairly limited. Characters themselves (users may know this as a
|
||||
@b `char` type) have no frontend type, so a @b `string` type must be used for
|
||||
`char` type) have no frontend type, so a `string` type must be used for
|
||||
any number of characters (including the empty string). String literals are
|
||||
represented by enclosing any number of @ref axtokens "supported AX characters"
|
||||
by quotes <b>\" \"</b>.
|
||||
by quotes `" "`.
|
||||
|
||||
@subsection axtypeprecedence Implicit conversion / type precedence
|
||||
@par
|
||||
@@ -441,9 +440,9 @@ depending on the operand types.
|
||||
<th colspan="9"> Operators
|
||||
</th></tr>
|
||||
<tr style="text-align:center">
|
||||
<td bgcolor="#ffc4c4" style="border-right-width: thick" colspan="4"> @ref axopbinary </td>
|
||||
<td bgcolor="#ffc4c4" style="border-right-width: thick" colspan="3"> @ref axopunary </td>
|
||||
<td bgcolor="#ffc4c4" colspan="2"> Other </td></tr>
|
||||
<td style="border-right-width: thick" colspan="4"> @ref axopbinary </td>
|
||||
<td style="border-right-width: thick" colspan="3"> @ref axopunary </td>
|
||||
<td colspan="2"> Other </td></tr>
|
||||
<tr style="text-align:center">
|
||||
<td> @ref axopassignment </td>
|
||||
<td> @ref axopbinarithmetic </td>
|
||||
@@ -456,50 +455,33 @@ depending on the operand types.
|
||||
<td> @ref axopother </td></tr>
|
||||
<tr style="text-align:center">
|
||||
<td><p> <!-- *********************** COLUMN ASSIGNMENT *********************** -->
|
||||
<code>
|
||||
a = b<br/>a += b<br/>a -= b<br/>a *= b<br/>a /= b<br/>a %= b<br/>
|
||||
a &= b<br/>a |= b<br/>a ^= b<br/>a <<= b<br/>a >>= b
|
||||
</code>
|
||||
`a = b`<br/>`a += b`<br/>`a -= b`<br/>`a *= b`<br/>`a /= b`<br/>`a %= b`<br/>
|
||||
`a &= b`<br/>`a |= b`<br/>`a ^= b`<br/>`a <<= b`<br/>`a >>= b`
|
||||
</p></td>
|
||||
<td><p> <!-- *********************** COLUMN ARITH *********************** -->
|
||||
<code>
|
||||
a + b<br/>a - b<br/>a * b<br/>a / b<br/>a % b<br/>a & b<br/>a | b<br/>
|
||||
a ^ b<br/>a << b<br/>a >> b
|
||||
</code></p></td>
|
||||
`a + b`<br/>`a - b`<br/>`a * b`<br/>`a / b`<br/>`a % b`<br/>`a & b`<br/>`a | b`<br/>
|
||||
`a ^ b`<br/>`a << b`<br/>`a >> b`
|
||||
</p></td>
|
||||
<td><p> <!-- *********************** COLUMN COMP *********************** -->
|
||||
<code>
|
||||
a == b<br/>a != b<br/>a < b<br/>a > b<br/>a <= b<br/>a >= b
|
||||
</code>
|
||||
`a == b`<br/>`a != b`<br/>`a < b`<br/>`a > b`<br/>`a <= b`<br/>`a >= b`
|
||||
</p></td>
|
||||
<td style="border-right-width: thick"><p> <!-- *********************** COLUMN LOGICAL *********************** -->
|
||||
<code>
|
||||
a && b<br/>a || b
|
||||
</code>
|
||||
`a && b`<br/>`a || b`
|
||||
</p></td>
|
||||
<td><p> <!-- *********************** COLUMN ARITH *********************** -->
|
||||
<code>
|
||||
+a<br/>-a<br/>~a
|
||||
</code>
|
||||
`+a`<br/>`-a`<br/>`~a`
|
||||
</p></td>
|
||||
<td><p> <!-- *********************** COLUMN LOGICAL *********************** -->
|
||||
<code>
|
||||
!a
|
||||
</code>
|
||||
`!a`
|
||||
</p></td>
|
||||
<td style="border-right-width: thick"><p> <!-- *********************** COLUMN INC/DEC *********************** -->
|
||||
<code>
|
||||
++a<br/>\-\-a<br/>a++<br/>a\-\-
|
||||
</code>
|
||||
`++a`<br/>`--a`<br/>`a++`<br/>`a--`
|
||||
</p></td>
|
||||
<td><p> <!-- *********************** COLUMN OTHER *********************** -->
|
||||
<code>
|
||||
a[]<br/>a[,]<br/>a.b
|
||||
</code>
|
||||
`a[]`<br/>`a[,]`<br/>`a.b`
|
||||
</p></td>
|
||||
<td><p> <!-- *********************** COLUMN OTHER *********************** -->
|
||||
<code>
|
||||
a(...)<br/>a, b<br/>a ? b : c<br/>{ a,b ... }
|
||||
</code>
|
||||
`a(...)`<br/>`a, b`<br/>`a ? b : c`<br/>`a ?: c`<br/>`{ a,b ... }`
|
||||
</p></td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -522,34 +504,34 @@ assignment operators have the following forms:
|
||||
@par
|
||||
<table>
|
||||
<tr><th>Operator Name</th><th>Operator Syntax</th><th>Returns</th></tr>
|
||||
<tr><td>Simple assignment</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`=`</b> @a `rhs` </td><td>Reference to @a `lhs`</td></tr>
|
||||
<tr><td>Addition assignment</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`+=`</b> @a `rhs` </td><td>Reference to @a `lhs`</td></tr>
|
||||
<tr><td>Subtraction assignment</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`-=`</b> @a `rhs` </td><td>Reference to @a `lhs`</td></tr>
|
||||
<tr><td>Multiplication assignment</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`*=`</b> @a `rhs` </td><td>Reference to @a `lhs`</td></tr>
|
||||
<tr><td>Division assignment</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`/=`</b> @a `rhs` </td><td>Reference to @a `lhs`</td></tr>
|
||||
<tr><td>Modulo assignment</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`%=`</b> @a `rhs` </td><td>Reference to @a `lhs`</td></tr>
|
||||
<tr><td>Bitwise AND assignment</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`&=`</b> @a `rhs` </td><td>Reference to @a `lhs`</td></tr>
|
||||
<tr><td>Bitwise OR assignment</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`|=`</b> @a `rhs` </td><td>Reference to @a `lhs`</td></tr>
|
||||
<tr><td>Bitwise XOR assignment</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`^=`</b> @a `rhs` </td><td>Reference to @a `lhs`</td></tr>
|
||||
<tr><td>Bitwise shift left assignment</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`<<=`</b> @a `rhs` </td><td>Reference to @a `lhs`</td></tr>
|
||||
<tr><td>Bitwise shift right assignment</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`>>=`</b> @a `rhs` </td><td>Reference to @a `lhs`</td></tr>
|
||||
<tr><td>Simple assignment</td><td> @a lhs <b>`=`</b> @a rhs </td>
|
||||
<td rowspan="11">Reference to @a lhs</td></tr>
|
||||
<tr><td>Addition assignment</td><td> @a lhs <b>`+=`</b> @a rhs </td></tr>
|
||||
<tr><td>Subtraction assignment</td><td> @a lhs <b>`-=`</b> @a rhs </td></tr>
|
||||
<tr><td>Multiplication assignment</td><td> @a lhs <b>`*=`</b> @a rhs </td></tr>
|
||||
<tr><td>Division assignment</td><td> @a lhs <b>`/=`</b> @a rhs </td></tr>
|
||||
<tr><td>Modulo assignment</td><td> @a lhs <b>`%=`</b> @a rhs </td></tr>
|
||||
<tr><td>Bitwise AND assignment</td><td> @a lhs <b>`&=`</b> @a rhs </td></tr>
|
||||
<tr><td>Bitwise OR assignment</td><td> @a lhs <b>`|=`</b> @a rhs </td></tr>
|
||||
<tr><td>Bitwise XOR assignment</td><td> @a lhs <b>`^=`</b> @a rhs </td></tr>
|
||||
<tr><td>Bitwise shift left assignment</td><td> @a lhs <b>`<<=`</b> @a rhs </td></tr>
|
||||
<tr><td>Bitwise shift right assignment</td><td> @a lhs <b>`>>=`</b> @a rhs </td></tr>
|
||||
</table>
|
||||
@par
|
||||
These can be further categorised into two types; direct assignment, which is
|
||||
comprised only of the <b>simple assignment</b> operator, and compound assignments,
|
||||
which is the set of all other assignment operators.
|
||||
@par Direct Assignment
|
||||
Direct assignments replace the contents of the @b `lhs` with a copy of the @b
|
||||
`rhs`:
|
||||
Direct assignments replace the contents of the `lhs` with a copy of the `rhs`:
|
||||
@par
|
||||
<table><tr>
|
||||
<td bgcolor="#ffc4c4"> @a `lhs` <b>= </b> @a `rhs` </td>
|
||||
<td> @a lhs <b>=</b> @a rhs </td>
|
||||
</tr></table>
|
||||
@par
|
||||
The @b `rhs` side is not modified and the @b `lhs` is not read - only written
|
||||
to. If the @b `rhs` type does not match the @b `lhs` type, the @b `rhs` is first
|
||||
implicitly converted into a new temporary with the @b `lhs` type before being
|
||||
copied into the @b `lhs`. The following combination of AX type categories are
|
||||
The `rhs` side is not modified and the `lhs` is not read - only written
|
||||
to. If the `rhs` type does not match the `lhs` type, the `rhs` is first
|
||||
implicitly converted into a new temporary with the `lhs` type before being
|
||||
copied into the `lhs`. The following combination of AX type categories are
|
||||
supported for direct assignment (if an operation is not listed, it is not
|
||||
supported):
|
||||
@par
|
||||
@@ -569,15 +551,14 @@ supported):
|
||||
<td style="border-top-width: thick">@ref axscalars "scalar"</td>
|
||||
<td style="border-top-width:thick;text-align:left">
|
||||
Each element of the vector is set to the right hand side scalar. i.e.
|
||||
<br/> `a[0] = b; ... a[n-1] = b;` <br/>
|
||||
where `n` is the size of the vector. If the scalar type does not match
|
||||
element type of vector, the scalar is copied and @ref axtypeprecedence
|
||||
"implicitly cast" to that type.</td>
|
||||
`a[0] = b; ... a[n-1] = b;` where `n` is the size of the vector. If the
|
||||
scalar type does not match element type of vector, the scalar is copied and
|
||||
@ref axtypeprecedence "implicitly cast" to that type.</td>
|
||||
</tr>
|
||||
<tr style="text-align:center"><td>@ref axvecmats "vector"</td>
|
||||
<td style="text-align:left">Component wise assignment i.e.
|
||||
<br/> `a[0] = b; ... a[n-1] = b;` <br/>
|
||||
where `n` is the size of the vector.
|
||||
<td style="text-align:left">
|
||||
Component wise assignment i.e. `a[0] = b; ... a[n-1] = b;` where `n` is the size
|
||||
of the vector.
|
||||
<!-- A -->@code{.c}
|
||||
vec3f a = 0, b = 1;
|
||||
a = b;
|
||||
@@ -612,7 +593,7 @@ of matrix, the scalar is copied and @ref axtypeprecedence "implicitly cast" to
|
||||
that type.</td>
|
||||
<tr style="text-align:center"><td>@ref axvecmats "matrix"</td>
|
||||
<td style="text-align:left">Component wise assignment i.e.
|
||||
<br/> `a[0] = b[0]; ... a[n-1] = b[n-1];` <br/>
|
||||
<br/> `a[0] = b[0]; ... a[n-1] = b[n-1];` <br/>
|
||||
where `n` is the @b total size of the matrix.
|
||||
<!-- A -->@code{.c}
|
||||
mat4f a = 0, b = 1;
|
||||
@@ -645,25 +626,27 @@ int a = 1; // assign a from an integer literal of value 1
|
||||
c = a; // assign components of c (x/y/z) to the result of float(a)
|
||||
@endcode
|
||||
@par Compound Assignment
|
||||
Compound assignments replace the the contents of the @b `lhs` with the result of
|
||||
a @ref axopbinarithmetic "binary arithmetic" operation of the @b `lhs` with the
|
||||
@b `rhs`:
|
||||
Compound assignments replace the the contents of the `lhs` with the result of
|
||||
a @ref axopbinarithmetic "binary arithmetic" operation of the `lhs` with the
|
||||
`rhs`:
|
||||
@par
|
||||
<table>
|
||||
<tr><td bgcolor="#ffc4c4"> @a `lhs` @b op @a `rhs` </td></tr>
|
||||
<tr><td> @a lhs @b op @a rhs </td></tr>
|
||||
</table>
|
||||
Where @b op is one of :
|
||||
|
||||
Where @b op is one of:
|
||||
|
||||
<table><tr>
|
||||
<td bgcolor="#ffc4c4"> <b>`+=`</b></td>
|
||||
<td bgcolor="#ffc4c4"> <b>`-=`</b></td>
|
||||
<td bgcolor="#ffc4c4"> <b>`*=`</b></td>
|
||||
<td bgcolor="#ffc4c4"> <b>`/=`</b></td>
|
||||
<td bgcolor="#ffc4c4"> <b>`%=`</b></td>
|
||||
<td bgcolor="#ffc4c4"> <b>`&=`</b></td>
|
||||
<td bgcolor="#ffc4c4"> <b>`|=`</b></td>
|
||||
<td bgcolor="#ffc4c4"> <b>`^=`</b></td>
|
||||
<td bgcolor="#ffc4c4"> <b>`<<=`</b></td>
|
||||
<td bgcolor="#ffc4c4"> <b>`>>= `</b></td>
|
||||
<td> <b>`+=`</b></td>
|
||||
<td> <b>`-=`</b></td>
|
||||
<td> <b>`*=`</b></td>
|
||||
<td> <b>`/=`</b></td>
|
||||
<td> <b>`%=`</b></td>
|
||||
<td> <b>`&=`</b></td>
|
||||
<td> <b>`|=`</b></td>
|
||||
<td> <b>`^=`</b></td>
|
||||
<td> <b>`<<=`</b></td>
|
||||
<td> <b>`>>= `</b></td>
|
||||
</tr></table>
|
||||
@par
|
||||
The behaviour of a given compound assignment (for example `a += b`) is similar
|
||||
@@ -680,8 +663,8 @@ int a = 1;
|
||||
++a += 1; // equal to a = ++a + 1 which is equal to 3
|
||||
@endcode
|
||||
@par
|
||||
The @b `rhs` side is not modified, however the @b `lhs` is both read from and
|
||||
written to. Note that the arithmetic operation may cast either @b `lhs` or @b
|
||||
The `rhs` side is not modified, however the `lhs` is both read from and
|
||||
written to. Note that the arithmetic operation may cast either `lhs` or
|
||||
`rhs` following the rules of AX's @ref axopbinarithmetic
|
||||
"arithmetic type precedence". See the @ref axopbinarithmetic
|
||||
"arithmetic operations" section for more information on arithmetic type
|
||||
@@ -720,7 +703,7 @@ b = c; // b becomes 4
|
||||
a = b; // a becomes float(4)
|
||||
@endcode
|
||||
@par
|
||||
Importantly, @b `a` receives the result of any previous implicit casts to the
|
||||
Importantly, `a` receives the result of any previous implicit casts to the
|
||||
right hand side of its binary assignment.
|
||||
|
||||
|
||||
@@ -737,20 +720,20 @@ container types). Binary arithmetic operations have the following forms:
|
||||
@par
|
||||
<table>
|
||||
<tr><th>Operator Name</th><th>Operator Syntax</th><th>Returns</th></tr>
|
||||
<tr><td>Addition</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`+`</b> @a `rhs` </td><td>The sum of both operands</td></tr>
|
||||
<tr><td>Subtraction</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`-`</b> @a `rhs` </td><td>The first operand minus the second operand</td></tr>
|
||||
<tr><td>Multiplication</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`*`</b> @a `rhs` </td><td>The product of both operands</td></tr>
|
||||
<tr><td>Division</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`/`</b> @a `rhs` </td><td>The first operand divided by the second operand</td></tr>
|
||||
<tr><td>Modulo</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`%`</b> @a `rhs` </td><td>The @b floored modulo operator. See @ref axbinmultop "Multiplicative operands"</td></tr>
|
||||
<tr><td>Bitwise AND</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`&`</b> @a `rhs` </td>
|
||||
<tr><td>Addition</td><td> @a lhs <b>`+`</b> @a rhs </td><td>The sum of both operands</td></tr>
|
||||
<tr><td>Subtraction</td><td> @a lhs <b>`-`</b> @a rhs </td><td>The first operand minus the second operand</td></tr>
|
||||
<tr><td>Multiplication</td><td> @a lhs <b>`*`</b> @a rhs </td><td>The product of both operands</td></tr>
|
||||
<tr><td>Division</td><td> @a lhs <b>`/`</b> @a rhs </td><td>The first operand divided by the second operand</td></tr>
|
||||
<tr><td>Modulo</td><td> @a lhs <b>`%`</b> @a rhs </td><td>The @b floored modulo operator. See @ref axbinmultop "Multiplicative operands"</td></tr>
|
||||
<tr><td>Bitwise AND</td><td> @a lhs <b>`&`</b> @a rhs </td>
|
||||
<td>The @b integral bitwise AND result of each bit in the first operand applied to the bit at the same location in the second operand</td></tr>
|
||||
<tr><td>Bitwise OR</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`|`</b> @a `rhs` </td>
|
||||
<tr><td>Bitwise OR</td><td> @a lhs <b>`|`</b> @a rhs </td>
|
||||
<td>The @b integral bitwise OR result of each bit in the first operand applied to the bit at the same location in the second operand</td></tr>
|
||||
<tr><td>Bitwise XOR</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`^`</b> @a `rhs` </td>
|
||||
<tr><td>Bitwise XOR</td><td> @a lhs <b>`^`</b> @a rhs </td>
|
||||
<td>The @b integral bitwise XOR result of each bit in the first operand applied to the bit at the same location in the second operand</td></tr>
|
||||
<tr><td>Bitwise shift left</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`<<`</b> @a `rhs` </td>
|
||||
<tr><td>Bitwise shift left</td><td> @a lhs <b>`<<`</b> @a rhs </td>
|
||||
<td>The @b integral bitwise left shift of the first operand by the second operand</td></tr>
|
||||
<tr><td>Bitwise shift right</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`>>`</b> @a `rhs` </td>
|
||||
<tr><td>Bitwise shift right</td><td> @a lhs <b>`>>`</b> @a rhs </td>
|
||||
<td>The @b integral bitwise right shift of the first operand by the second operand</td></tr>
|
||||
</table>
|
||||
@anchor axbinadditiveop
|
||||
@@ -759,13 +742,13 @@ Binary additive operations perform summations on the input arguments. They have
|
||||
the following forms:
|
||||
@par
|
||||
<table>
|
||||
<tr><td bgcolor="#ffc4c4"> @a `lhs` @b `+` @a `rhs` </td></tr>
|
||||
<tr><td bgcolor="#ffc4c4"> @a `lhs` @b `-` @a `rhs` </td></tr>
|
||||
<tr><td> @a lhs @b + @a rhs </td></tr>
|
||||
<tr><td> @a lhs @b - @a rhs </td></tr>
|
||||
</table>
|
||||
@par
|
||||
After @ref axtypeprecedence "implicit conversion", the result of the binary
|
||||
operation with the arithmetic @b `+` (@ref axtokens "plus token") is the sum of
|
||||
the operands, and the result of the binary operation with the arithmetic @b `-`
|
||||
operation with the arithmetic `+` (@ref axtokens "plus token") is the sum of
|
||||
the operands, and the result of the binary operation with the arithmetic `-`
|
||||
(@ref axtokens "minus token") token is the second operand subtracted from the
|
||||
first operand. The following combination of AX type categories are supported for
|
||||
additive operations (if an operation is not listed, it is not supported):
|
||||
@@ -870,19 +853,19 @@ c - b; // valid vec - scalar
|
||||
The binary multiplicative operands have the following forms:
|
||||
@par
|
||||
<table>
|
||||
<tr><td bgcolor="#ffc4c4"> @a `lhs` @b `*` @a `rhs` </td></tr>
|
||||
<tr><td bgcolor="#ffc4c4"> @a `lhs` @b `/` @a `rhs` </td></tr>
|
||||
<tr><td bgcolor="#ffc4c4"> @a `lhs` @b `%` @a `rhs` </td></tr>
|
||||
<tr><td> @a lhs `*` @a rhs </td></tr>
|
||||
<tr><td> @a lhs `/` @a rhs </td></tr>
|
||||
<tr><td> @a lhs `%` @a rhs </td></tr>
|
||||
</table>
|
||||
@par
|
||||
After @ref axtypeprecedence "implicit conversion", the result of the binary
|
||||
operation:
|
||||
- with the arithmetic @b `*` (@ref axtokens "asterisk token") is:
|
||||
- with the arithmetic `*` (@ref axtokens "asterisk token") is:
|
||||
- the product of the operands
|
||||
- @b OR performs matrix multiplication for vector and matrix types.
|
||||
- with the arithmetic @b `/` (@ref axtokens "forward slash token") is the first
|
||||
- with the arithmetic `/` (@ref axtokens "forward slash token") is the first
|
||||
operand divided by the second operand.
|
||||
- with the arithmetic @b `%` (@ref axtokens "percentage token") is the
|
||||
- with the arithmetic `%` (@ref axtokens "percentage token") is the
|
||||
@b floored modulo operations .i.e. the expression `d % D` returns the result
|
||||
`D - d * floor(D/d)`. This is in contrast to truncated modulo operations
|
||||
`D - d * (D/d)` where the division is truncated.
|
||||
@@ -894,7 +877,7 @@ mean that the relationship properties between `%` and `/` differ when either
|
||||
@b `d` or @b `D` is negative. i.e.:
|
||||
`(d/D)*D + (d%D) != d`
|
||||
@note
|
||||
The multiplicative operation @b `*` has important behaviour for vectors and
|
||||
The multiplicative operation `*` has important behaviour for vectors and
|
||||
matrix types.
|
||||
@par
|
||||
The following combination of AX type categories are supported for multiplicative
|
||||
@@ -907,7 +890,7 @@ operations (if an operation is not listed, it is not supported):
|
||||
<tr style="text-align:center"><td rowspan="3">@ref axscalars "scalar"</td><td rowspan="2">`* / %`</td><td rowspan="1">@ref axscalars "scalar"</td>
|
||||
<td style="text-align:left">Returns the result of the scalar multiplication, division or remainder of the division respectively. </td>
|
||||
</tr>
|
||||
<tr style="text-align:center"><td>@ref axvecmats "vector"</td></td>
|
||||
<tr style="text-align:center"><td>@ref axvecmats "vector"</td>
|
||||
<td style="text-align:left" rowspan="1">
|
||||
Performs per component binary operations (after
|
||||
@ref axtypeprecedence "implicit conversion") with the left hand side scalar to
|
||||
@@ -1096,27 +1079,27 @@ Floating point multiplication is communicative (`a * b = b * a`) but is not
|
||||
Binary bitwise operations have the following forms:
|
||||
@par
|
||||
<table>
|
||||
<tr><td bgcolor="#ffc4c4"> @a `lhs` @b `&` @a `rhs` </td></tr>
|
||||
<tr><td bgcolor="#ffc4c4"> @a `lhs` <b> `|` </b> @a `rhs` </td></tr>
|
||||
<tr><td bgcolor="#ffc4c4"> @a `lhs` @b `^` @a `rhs` </td></tr>
|
||||
<tr><td bgcolor="#ffc4c4"> @a `lhs` @b `<<` @a `rhs` </td></tr>
|
||||
<tr><td bgcolor="#ffc4c4"> @a `lhs` @b `>>` @a `rhs` </td></tr>
|
||||
<tr><td> @a lhs <b> `&` </b> @a rhs </td></tr>
|
||||
<tr><td> @a lhs <b> `|` </b> @a rhs </td></tr>
|
||||
<tr><td> @a lhs <b> `^` </b> @a rhs </td></tr>
|
||||
<tr><td> @a lhs <b> `<<` </b> @a rhs </td></tr>
|
||||
<tr><td> @a lhs <b> `>>` </b> @a rhs </td></tr>
|
||||
</table>
|
||||
@par
|
||||
These operations only run on @b integral operands. After @ref axtypeprecedence
|
||||
"implicit conversion", the result of the binary operation:
|
||||
- with the @b `&` (@ref axtokens "ampersand token") is the result of the
|
||||
- with the @b & (@ref axtokens "ampersand token") is the result of the
|
||||
logical AND operation on each pair of the corresponding bits of the input
|
||||
operands.
|
||||
- with the <b> `|` </b> (@ref axtokens "pipe token") is the result of the
|
||||
- with the <b> | </b> (@ref axtokens "pipe token") is the result of the
|
||||
logical OR operation on each pair of the corresponding bits of the input
|
||||
operands.
|
||||
- with the @b `^` (@ref axtokens "hat token") is the result of the
|
||||
- with the @b ^ (@ref axtokens "hat token") is the result of the
|
||||
logical XOR operation on each pair of the corresponding bits of the input
|
||||
operands.
|
||||
- with the @b `<<` (@ref axtokens "less than tokens") is the value shifted left
|
||||
- with the @b << (@ref axtokens "less than tokens") is the value shifted left
|
||||
with zeros shifted in on the right.
|
||||
- with the @b `>>` (@ref axtokens "greater than tokens") is the value shifted
|
||||
- with the @b >> (@ref axtokens "greater than tokens") is the value shifted
|
||||
right with zeros shifted in on the left.
|
||||
@par
|
||||
<table>
|
||||
@@ -1144,17 +1127,17 @@ is of type @b `bool`. Binary comparison operations have the following forms:
|
||||
@par
|
||||
<table>
|
||||
<tr><th>Operator Name</th><th>Operator Syntax</th><th>Returns</th></tr>
|
||||
<tr><td>Equal to</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`==`</b> @a `rhs` </td>
|
||||
<tr><td>Equal to</td><td> @a lhs <b>`==`</b> @a rhs </td>
|
||||
<td>Returns `true` if both operands are equal</td></tr>
|
||||
<tr><td>Not equal to</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`!=`</b> @a `rhs` </td>
|
||||
<tr><td>Not equal to</td><td> @a lhs <b>`!=`</b> @a rhs </td>
|
||||
<td>Returns `true` if operands are not equal</td></tr>
|
||||
<tr><td>Less than</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`<`</b> @a `rhs` </td>
|
||||
<tr><td>Less than</td><td> @a lhs <b>`<`</b> @a rhs </td>
|
||||
<td>Returns `true` the left hand side value is less than the right hand side</td></tr>
|
||||
<tr><td>Greater than</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`>`</b> @a `rhs` </td>
|
||||
<tr><td>Greater than</td><td> @a lhs <b>`>`</b> @a rhs </td>
|
||||
<td>Returns `true` the left hand side value is greater than the right hand side</td></tr>
|
||||
<tr><td>Less than or equal to</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`<=`</b> @a `rhs` </td>
|
||||
<tr><td>Less than or equal to</td><td> @a lhs <b>`<=`</b> @a rhs </td>
|
||||
<td>Returns `true` the left hand side value is less than or equal to the right hand side</td></tr>
|
||||
<tr><td>Greater than or equal to</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`>=`</b> @a `rhs` </td>
|
||||
<tr><td>Greater than or equal to</td><td> @a lhs <b>`>=`</b> @a rhs </td>
|
||||
<td>Returns `true` the left hand side value is greater than or equal to the right hand side</td></tr>
|
||||
</table>
|
||||
@par
|
||||
@@ -1264,10 +1247,10 @@ They have the following forms:
|
||||
@par
|
||||
<table>
|
||||
<tr><th>Operator Name</th><th>Operator Syntax</th><th>Returns</th></tr>
|
||||
<tr><td>AND</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`&&`</b> @a `rhs` </td>
|
||||
<tr><td>AND</td><td> @a lhs <b>`&&`</b> @a rhs </td>
|
||||
<td>Returns true if both operands are true. Otherwise, the result is false. This
|
||||
operator is short-circuiting.</td></tr>
|
||||
<tr><td>Inclusive OR</td><td bgcolor="#ffc4c4"> @a `lhs` <b>`||`</b> @a `rhs` </td>
|
||||
<tr><td>Inclusive OR</td><td> @a lhs <b>`||`</b> @a rhs </td>
|
||||
<td>Returns true if either the first or the second operand is true. This operator
|
||||
is short-circuiting.</td></tr>
|
||||
</table>
|
||||
@@ -1300,14 +1283,14 @@ Arithmetic unary operators are comprised of arithemtic and bitwise operators.
|
||||
@par
|
||||
<table>
|
||||
<tr><th>Operator Name</th><th>Operator Syntax</th><th>Returns</th></tr>
|
||||
<tr><td>Unary plus</td><td bgcolor="#ffc4c4"> <b>`+`</b>@a `a` </td>
|
||||
<td>Returns the value of its operand, @b `a`</td>
|
||||
<tr><td>Unary plus</td><td> `+a` </td>
|
||||
<td>Returns the value of its operand, `a`</td>
|
||||
</tr>
|
||||
<tr><td>Unary minus</td><td bgcolor="#ffc4c4"> <b>`-`</b>@a `a` </td>
|
||||
<td>Returns the negative representation of @b `a`</td>
|
||||
<tr><td>Unary minus</td><td> `-a` </td>
|
||||
<td>Returns the negative representation of `a`</td>
|
||||
</tr>
|
||||
<tr><td>Bitwise NOT</td><td bgcolor="#ffc4c4"> <b>`~`</b>@a `a` </td>
|
||||
<td>Returns the bitwise NOT (one's complement) value of @b `a`. This operator
|
||||
<tr><td>Bitwise NOT</td><td> `~a` </td>
|
||||
<td>Returns the bitwise NOT (one's complement) value of `a`. This operator
|
||||
is only valid on integral element types.</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -1365,7 +1348,7 @@ Logical unary operators consist of a single operand.
|
||||
@par
|
||||
<table>
|
||||
<tr><th>Operator Name</th><th>Operator Syntax</th><th>Returns</th></tr>
|
||||
<tr><td>Logical NOT</td><td bgcolor="#ffc4c4"> <b>`!`</b>@a `a` </td>
|
||||
<tr><td>Logical NOT</td><td> `!a` </td>
|
||||
<td>Returns true if the operand is false. Otherwise, returns false.</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -1409,10 +1392,10 @@ each component of a vector or matrix. They have the following forms:
|
||||
@par
|
||||
<table>
|
||||
<tr><th>Operator Name</th><th>Operator Syntax</th><th>Returns</th></tr>
|
||||
<tr><td>Pre-increment</td><td bgcolor="#ffc4c4"> <b>`++`</b>@a `a` </td><td>Returns a reference to the incremented result.</td>
|
||||
<tr><td>Post-increment</td><td bgcolor="#ffc4c4">@a `a`<b>`++`</b> </td><td>Returns a copy of the incremented result.</td>
|
||||
<tr><td>Pre-decrement</td><td bgcolor="#ffc4c4"> <b>`--`</b>@a `a` </td><td>Returns a reference to the decremented result.</td>
|
||||
<tr><td>Post-decrement</td><td bgcolor="#ffc4c4"> @a `a`<b>`--`</b> </td><td>Returns a copy of the decremented result.</td>
|
||||
<tr><td>Pre-increment</td><td> `++a` </td><td>Returns a reference to the incremented result.</td>
|
||||
<tr><td>Post-increment</td><td> `a++` </td><td>Returns a copy of the incremented result.</td>
|
||||
<tr><td>Pre-decrement</td><td> `--a` </td><td>Returns a reference to the decremented result.</td>
|
||||
<tr><td>Post-decrement</td><td> `a--` </td><td>Returns a copy of the decremented result.</td>
|
||||
</tr>
|
||||
</table>
|
||||
@par
|
||||
@@ -1444,20 +1427,20 @@ the following forms:
|
||||
<tr><th>Operator Name</th><th>Operator Syntax</th><th>Returns</th></tr>
|
||||
<tr style="text-align:left">
|
||||
<td>Dot Component Access</td>
|
||||
<td bgcolor="#ffc4c4"> <em>`vector`</em> <b>.</b> @a `component` </td>
|
||||
<td>Reference to component @a `component` of @a `vector`</td>
|
||||
<td> <em>`vector`</em> <b>.</b> `component` </td>
|
||||
<td>Reference to component `component` of `vector`</td>
|
||||
</tr>
|
||||
<tr style="text-align:left">
|
||||
<td>Container Component Access</td>
|
||||
<td bgcolor="#ffc4c4"> <em>`container`</em> <b>[</b> @a `exp` <b>]</b> </td>
|
||||
<td>Reference to component at index @a `exp` of @a `container`</td>
|
||||
<td> <em>`container`</em> <b>[</b> `exp` <b>]</b> </td>
|
||||
<td>Reference to component at index `exp` of `container`</td>
|
||||
</tr>
|
||||
<tr style="text-align:left">
|
||||
<td>Matrix Component Access</td>
|
||||
<td bgcolor="#ffc4c4"> <em>`matrix`</em> <b>[</b> @a `exp1`, <em>`exp2`</em> <b>]</b> </td>
|
||||
<td> <em>`matrix`</em> <b>[</b> `exp1`, <em>`exp2`</em> <b>]</b> </td>
|
||||
<td>Reference to component in row <b><em>`exp1`</em></b>, column <b><em>`exp2`</em></b>.
|
||||
Also equal to returning a reference to component at index: <b><em> `[exp1 * dimension + exp2]` </em></b>
|
||||
of @a `matrix` where @a `dimension` is the the size of the matrix</td>
|
||||
Also equal to returning a reference to component at index: <b><em> `[exp1 * dimension + exp2]` </em></b>
|
||||
of `matrix` where `dimension` is the the size of the matrix</td>
|
||||
</tr>
|
||||
</table>
|
||||
@par
|
||||
@@ -1471,11 +1454,11 @@ container.
|
||||
The dot operator <b>.</b> is only valid for vector types. It has the form:
|
||||
@par
|
||||
<table><tr>
|
||||
<td bgcolor="#ffc4c4"> @a `vector` <b>.</b> @a `component` </td>
|
||||
<td> `vector` <b>.</b> `component` </td>
|
||||
</tr></table>
|
||||
@par
|
||||
Where component is one of the below supported component letters. It only provides
|
||||
access for the first three elements of any vector (for @b `vec2` types, only the
|
||||
access for the first three elements of any vector (for `vec2` types, only the
|
||||
first 2 components are accessible). Accessing a vector using the dot operator is
|
||||
fundamentally the same as using the <b>`[]`</b> operator with the corresponding
|
||||
integer index.
|
||||
@@ -1492,12 +1475,12 @@ a_{0}\quad a_{1}\quad a_{2}
|
||||
@f]
|
||||
| Component | Access Index | Result |
|
||||
|:-----------:|:--------------:|:-------------:|
|
||||
| @b `A.x` | 0 | @f$ a_{0} @f$ |
|
||||
| @b `A.r` | 0 | @f$ a_{0} @f$ |
|
||||
| @b `A.y` | 1 | @f$ a_{1} @f$ |
|
||||
| @b `A.g` | 1 | @f$ a_{1} @f$ |
|
||||
| @b `A.z` | 2 | @f$ a_{2} @f$ |
|
||||
| @b `A.b` | 2 | @f$ a_{2} @f$ |
|
||||
| `A.x` | 0 | @f$ a_{0} @f$ |
|
||||
| `A.r` | 0 | @f$ a_{0} @f$ |
|
||||
| `A.y` | 1 | @f$ a_{1} @f$ |
|
||||
| `A.g` | 1 | @f$ a_{1} @f$ |
|
||||
| `A.z` | 2 | @f$ a_{2} @f$ |
|
||||
| `A.b` | 2 | @f$ a_{2} @f$ |
|
||||
</center>
|
||||
@par
|
||||
This operator can provide more immediately readability when working with
|
||||
@@ -1507,12 +1490,12 @@ expressions that focus on colour or Cartesian coordinate systems.
|
||||
The <b>`[]`</b> operator is valid for both vectors and matrices. It has the form:
|
||||
@par
|
||||
<table><tr>
|
||||
<td bgcolor="#ffc4c4"> @a `container` <b>[</b><em>`exp`</em><b>]</b> </td>
|
||||
<td> `container` <b>[</b><em>`exp`</em><b>]</b> </td>
|
||||
</tr></table>
|
||||
@par
|
||||
@a `exp` is expected to evaluate to a value which is castable to an integer
|
||||
`exp` is expected to evaluate to a value which is castable to an integer
|
||||
following the @ref axopassignment "assignment casting" rules and falls within
|
||||
the valid size of the @a `container`. <b>No bounds checking is performed</b>.
|
||||
the valid size of the `container`. <b>No bounds checking is performed</b>.
|
||||
@par
|
||||
As matrices are also an array of contigous values, they can be accessed in the
|
||||
same way. This can make some operations much simplier; a typical example being
|
||||
@@ -1533,16 +1516,16 @@ for (int i = 0; i < 16; ++i) b[i] = i;
|
||||
The <b>`[,]`</b> operator is only valid for matrices. It has the form:
|
||||
@par
|
||||
<table><tr>
|
||||
<td bgcolor="#ffc4c4"> @a `matrix` <b>[</b><em>`exp1`</em>, <em>`exp2`</em><b>]</b> </td>
|
||||
<td> `matrix` <b>[</b><em>`exp1`</em>, <em>`exp2`</em><b>]</b> </td>
|
||||
</tr></table>
|
||||
@par
|
||||
@a `exp1` and @a `exp2` are expected to evaluate to a value which is castable to
|
||||
`exp1` and `exp2` are expected to evaluate to a value which is castable to
|
||||
an integer following the @ref axopassignment "assignment casting" rules and
|
||||
falls within the valid size of the @a `matrix` dimensions. <b>No bounds checking
|
||||
falls within the valid size of the `matrix` dimensions. <b>No bounds checking
|
||||
is performed</b>. In general it is more readable to use this operator when
|
||||
accessing specific elements, where @b `exp1` is the row index and @b `exp2` is
|
||||
accessing specific elements, where `exp1` is the row index and `exp2` is
|
||||
the column index. The tables below show the different access patterns for
|
||||
@b `3x3` and @b `4x4` matrices.
|
||||
`3x3` and `4x4` matrices.
|
||||
@par
|
||||
<!-- A -->@code{.c}
|
||||
mat3f a;
|
||||
@@ -1555,22 +1538,26 @@ for (int i = 0; i < 3; ++i)
|
||||
<tr style="text-align:center">
|
||||
<td style="border:none" colspan=3>
|
||||
@f[
|
||||
{
|
||||
A=\begin{bmatrix}
|
||||
a_{00}\quad a_{01}\quad a_{02}\\
|
||||
a_{10}\quad a_{11}\quad a_{12}\\
|
||||
a_{20}\quad a_{21}\quad a_{22}\\
|
||||
\end{bmatrix}
|
||||
}
|
||||
@f]
|
||||
</td>
|
||||
<td style="border:none"></td>
|
||||
<td style="border:none" colspan=3>
|
||||
@f[
|
||||
{
|
||||
A=\begin{bmatrix}
|
||||
a_{00}\quad a_{01}\quad a_{02}\quad a_{03}\\
|
||||
a_{10}\quad a_{11}\quad a_{12}\quad a_{13}\\
|
||||
a_{20}\quad a_{21}\quad a_{22}\quad a_{23}\\
|
||||
a_{30}\quad a_{31}\quad a_{32}\quad a_{33}\\
|
||||
\end{bmatrix}
|
||||
}
|
||||
@f]
|
||||
</td>
|
||||
</tr>
|
||||
@@ -1584,112 +1571,62 @@ a_{30}\quad a_{31}\quad a_{32}\quad a_{33}\\
|
||||
<th>Result</th>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[0]</code></td>
|
||||
<td><code>A[0,0]</code></td>
|
||||
<td>@f$ a_{00} @f$</td>
|
||||
<td><code>A[0]</code></td>
|
||||
<td><code>A[0,0]</code></td>
|
||||
<td>@f$ a_{00} @f$</td>
|
||||
<td>`A[0]`</td><td>`A[0,0]`</td><td>@f$ a_{00} @f$</td>
|
||||
<td>`A[0]`</td><td>`A[0,0]`</td><td>@f$ a_{00} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[1]</code></td>
|
||||
<td><code>A[0,1]</code></td>
|
||||
<td>@f$ a_{01} @f$</td>
|
||||
<td><code>A[1]</code></td>
|
||||
<td><code>A[0,1]</code></td>
|
||||
<td>@f$ a_{01} @f$</td>
|
||||
<td>`A[1]`</td><td>`A[0,1]`</td><td>@f$ a_{01} @f$</td>
|
||||
<td>`A[1]`</td><td>`A[0,1]`</td><td>@f$ a_{01} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[2]</code></td>
|
||||
<td><code>A[0,2]</code></td>
|
||||
<td>@f$ a_{02} @f$</td>
|
||||
<td><code>A[2]</code></td>
|
||||
<td><code>A[0,2]</code></td>
|
||||
<td>@f$ a_{02} @f$</td>
|
||||
<td>`A[2]`</td><td>`A[0,2]`</td><td>@f$ a_{02} @f$</td>
|
||||
<td>`A[2]`</td><td>`A[0,2]`</td><td>@f$ a_{02} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[3]</code></td>
|
||||
<td><code>A[1,0]</code></td>
|
||||
<td>@f$ a_{10} @f$</td>
|
||||
<td><code>A[3]</code></td>
|
||||
<td><code>A[0,3]</code></td>
|
||||
<td>@f$ a_{03} @f$</td>
|
||||
<td>`A[3]`</td><td>`A[1,0]`</td><td>@f$ a_{10} @f$</td>
|
||||
<td>`A[3]`</td><td>`A[0,3]`</td><td>@f$ a_{03} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[4]</code></td>
|
||||
<td><code>A[1,1]</code></td>
|
||||
<td>@f$ a_{10} @f$</td>
|
||||
<td><code>A[4]</code></td>
|
||||
<td><code>A[1,0]</code></td>
|
||||
<td>@f$ a_{10} @f$</td>
|
||||
<td>`A[4]`</td><td>`A[1,1]`</td><td>@f$ a_{10} @f$</td>
|
||||
<td>`A[4]`</td><td>`A[1,0]`</td><td>@f$ a_{10} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[5]</code></td>
|
||||
<td><code>A[1,2]</code></td>
|
||||
<td>@f$ a_{12} @f$</td>
|
||||
<td><code>A[5]</code></td>
|
||||
<td><code>A[1,1]</code></td>
|
||||
<td>@f$ a_{11} @f$</td>
|
||||
<td>`A[5]`</td><td>`A[1,2]`</td><td>@f$ a_{12} @f$</td>
|
||||
<td>`A[5]`</td><td>`A[1,1]`</td><td>@f$ a_{11} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[6]</code></td>
|
||||
<td><code>A[2,0]</code></td>
|
||||
<td>@f$ a_{20} @f$</td>
|
||||
<td><code>A[6]</code></td>
|
||||
<td><code>A[1,2]</code></td>
|
||||
<td>@f$ a_{12} @f$</td>
|
||||
<td>`A[6]`</td><td>`A[2,0]`</td><td>@f$ a_{20} @f$</td>
|
||||
<td>`A[6]`</td><td>`A[1,2]`</td><td>@f$ a_{12} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[7]</code></td>
|
||||
<td><code>A[2,1]</code></td>
|
||||
<td>@f$ a_{21} @f$</td>
|
||||
<td><code>A[7]</code></td>
|
||||
<td><code>A[1,3]</code></td>
|
||||
<td>@f$ a_{13} @f$</td>
|
||||
<td>`A[7]`</td><td>`A[2,1]`</td><td>@f$ a_{21} @f$</td>
|
||||
<td>`A[7]`</td><td>`A[1,3]`</td><td>@f$ a_{13} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[8]</code></td>
|
||||
<td><code>A[2,2]</code></td>
|
||||
<td>@f$ a_{22} @f$</td>
|
||||
<td><code>A[8]</code></td>
|
||||
<td><code>A[2,0]</code></td>
|
||||
<td>@f$ a_{20} @f$</td>
|
||||
<td>`A[8]`</td><td>`A[2,2]`</td><td>@f$ a_{22} @f$</td>
|
||||
<td>`A[8]`</td><td>`A[2,0]`</td><td>@f$ a_{20} @f$</td>
|
||||
</tr> <!-- ******************** MAT4 DATA ONLY ******************** -->
|
||||
<tr style="text-align:center">
|
||||
<td style="border:none;" colspan="3" rowspan="7"></td>
|
||||
<td><code>A[9]</code></td>
|
||||
<td><code>A[2,1]</code></td>
|
||||
<td>@f$ a_{21} @f$</td>
|
||||
<td style="border:right;" colspan="4" rowspan="7"></td>
|
||||
<td>`A[9]`</td><td>`A[2,1]`</td><td>@f$ a_{21} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[10]</code></td>
|
||||
<td><code>A[2,2]</code></td>
|
||||
<td>@f$ a_{22} @f$</td>
|
||||
<td>`A[10]`</td><td>`A[2,2]`</td><td>@f$ a_{22} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[11]</code></td>
|
||||
<td><code>A[2,3]</code></td>
|
||||
<td>@f$ a_{23} @f$</td>
|
||||
<td>`A[11]`</td><td>`A[2,3]`</td><td>@f$ a_{23} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[12]</code></td>
|
||||
<td><code>A[3,0]</code></td>
|
||||
<td>@f$ a_{30} @f$</td>
|
||||
<td>`A[12]`</td><td>`A[3,0]`</td><td>@f$ a_{30} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[13]</code></td>
|
||||
<td><code>A[3,1]</code></td>
|
||||
<td>@f$ a_{31} @f$</td>
|
||||
<td>`A[13]`</td><td>`A[3,1]`</td><td>@f$ a_{31} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[14]</code></td>
|
||||
<td><code>A[3,2]</code></td>
|
||||
<td>@f$ a_{32} @f$</td>
|
||||
<td>`A[14]`</td><td>`A[3,2]`</td><td>@f$ a_{32} @f$</td>
|
||||
</tr>
|
||||
<tr style="text-align:center">
|
||||
<td><code>A[15]</code></td>
|
||||
<td><code>A[3,3]</code></td>
|
||||
<td>@f$ a_{33} @f$</td>
|
||||
<td>`A[15]`</td><td>`A[3,3]`</td><td>@f$ a_{33} @f$</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -1703,22 +1640,27 @@ section. They have the following forms:
|
||||
<tr><th>Operator Name</th><th>Operator Syntax</th><th>Returns</th></tr>
|
||||
<tr style="text-align:left">
|
||||
<td>Call / Explicit Cast</td>
|
||||
<td bgcolor="#ffc4c4"> <em>`a`</em><b>`(...)`</b></td>
|
||||
<td> `a(...)` </td>
|
||||
<td>Returns the result of a function call or inbuilt explicit cast</td>
|
||||
</tr>
|
||||
<tr style="text-align:left">
|
||||
<td>Comma</td>
|
||||
<td bgcolor="#ffc4c4"> <em>`a`</em><b>,</b> <em>`b`</em></td>
|
||||
<td>Returns the value of @b `b` after chained evaluation</td>
|
||||
<td> `a, b, ...` </td>
|
||||
<td>Returns the value of the last expression after chained evaluation</td>
|
||||
</tr>
|
||||
<tr style="text-align:left">
|
||||
<td>Ternary Conditional</td>
|
||||
<td bgcolor="#ffc4c4"> <em>`a`</em> <b>`?`</b> <em>`b`</em> <b>`:`</b><em>`c`</em></td>
|
||||
<td>@b `b` if @b `a` is @b true, @b `c` otherwise.</td>
|
||||
<td> `a ? b : c` </td>
|
||||
<td> `b` if `a` is true, `c` otherwise.</td>
|
||||
</tr>
|
||||
<tr style="text-align:left">
|
||||
<td>Ternary Conditional</td>
|
||||
<td> `a ? : c` </td>
|
||||
<td> `a` if `a` is true, `c` otherwise.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Vector/Matrix Initializer</td>
|
||||
<td bgcolor="#ffc4c4"> <b>`{`</b> <em> `a, `</em> `b `<b>`... }`</b> </td>
|
||||
<td>`{ a, b, ... }`</td>
|
||||
<td>Returns a temporary vector or matrix</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -1728,10 +1670,10 @@ The function call operator provides the ability to invoke functions @b or invoke
|
||||
a supported explicit cast. It has the form:
|
||||
@par
|
||||
<table><tr>
|
||||
<td bgcolor="#ffc4c4"> <em> `func(a, b, c, ...)` </em></td>
|
||||
<td> `func(a, b, c, ...)` </td>
|
||||
</tr></table>
|
||||
@par
|
||||
Where @b `func` is a function name @b or a valid explicit cast typename. Explicit
|
||||
Where `func` is a function name @b or a valid explicit cast typename. Explicit
|
||||
casts are only supported for @ref axscalars "scalar types". For example:
|
||||
<!-- A -->@code{.c}
|
||||
int a = int(1.1f);
|
||||
@@ -1740,7 +1682,7 @@ a = int(b.x);
|
||||
@endcode
|
||||
@par
|
||||
Whilst explicit casts take only a single argument, the operator itself supports
|
||||
multiple or empty arguments for function calls. Whe multiple arguments are
|
||||
multiple or empty arguments for function calls. When multiple arguments are
|
||||
provided, they are parsed from first to last and processed in reverse order.
|
||||
@b However, the order of runtime evaluation is @b unspecified i.e. they may
|
||||
evaluate in any order. Each function parameter is initialized with its
|
||||
@@ -1753,10 +1695,10 @@ functions.
|
||||
The Comma operator has the form:
|
||||
@par
|
||||
<table><tr>
|
||||
<td bgcolor="#ffc4c4"><em>`a`</em><b>`,` </b><em>`b`</em></td>
|
||||
<td>`a, b, ...`</td>
|
||||
</tr></table>
|
||||
@par
|
||||
Where @b `a` and @b `b` are expressions. Chained expressions evaluate each
|
||||
Where `a` and `b` are expressions. Chained expressions evaluate each
|
||||
expression in syntactical order (first to last), discarding any return value
|
||||
except for the last expression, which is returned from the entire list. Note that
|
||||
although return values for any expression but the last are discarded, their side
|
||||
@@ -1780,32 +1722,33 @@ The ternary conditional operator can be thought of as an in-line @ref axbranchin
|
||||
a single expression that will be returned. It has the form:
|
||||
@par
|
||||
<table><tr>
|
||||
<td bgcolor="#ffc4c4"><em>`a`</em> <b>`?`</b> <em>`b`</em> <b>`:`</b><em>`c`</em></td>
|
||||
<td>`a ? b : c`</td>
|
||||
</tr></table>
|
||||
Where @b `a` is the condition, evaluated and converted to @b bool, and @b `b` and @b
|
||||
`c` are expressions of the same or implicit-castable types. @b `b` is evaluated and
|
||||
returned if the condition is @b true, @b `c` is evaluated and returned if the
|
||||
condition is @b false. Only the expression out of @b `b` and @b `c` that is
|
||||
returned will be evaluated. Expressions with no return value (a.k.a @b `void`)
|
||||
are supported as long as @b `b` and @b `c` are both of this type.
|
||||
@par
|
||||
Also supported is the 'Elvis' operator, <b>`a` `?` `:` `c`</b> (or `?:`).
|
||||
Here, @b `a` is evaluated once, and if when converted to @b bool is @b true, @b `a` is
|
||||
returned, otherwise @b `c` is evaluated and returned. In this case, @b `a` and @b
|
||||
Where `a` is the condition, evaluated and converted to @b bool, and `b` and
|
||||
`c` are expressions of the same or implicit-castable types. `b` is evaluated and
|
||||
returned if the condition is @b true, `c` is evaluated and returned if the
|
||||
condition is @b false. Only the expression out of `b` and `c` that is
|
||||
returned will be evaluated. Expressions with no return value (a.k.a `void`)
|
||||
are supported as long as `b` and `c` are both of this type.
|
||||
@par
|
||||
Also supported is the 'Elvis' operator, `a ? : c` (or `?:`).
|
||||
Here, `a` is evaluated once, and if when converted to @b bool is @b true, `a` is
|
||||
returned, otherwise `c` is evaluated and returned. In this case, `a` and
|
||||
`c` must be the same or implicit-castable types, and both implicit-castable to @b bool.
|
||||
|
||||
@anchor axvecmatinit
|
||||
@par Vector/Matrix Initializer
|
||||
@ref axvecmats "Vectors and matrices" can be represented as local variables (e.g.
|
||||
@b `vec3f`), external attributes or parameters (e.g. @b `vec3f\@attrib`) or as
|
||||
`vec3f`), external attributes or parameters (e.g. `vec3f@attrib`) or as
|
||||
temporary values using the vector/matrix initializer syntax. This operator has
|
||||
the form:
|
||||
@par
|
||||
<table><tr>
|
||||
<td bgcolor="#ffc4c4"> <b>`{`</b> <em>`a, b `</em><b>`... }`</b> </td>
|
||||
<td> `{ a, b, ... }`</td>
|
||||
</tr></table>
|
||||
@par
|
||||
Where @b `a` and @b `b` are expressions returning a scalar value. When this
|
||||
Where `a` and `b` are expressions returning a scalar value. When this
|
||||
operator is invoked with a specific number of arguments, a vector or matrix is
|
||||
implicitly created with a copy of those arguments which can be assigned to an
|
||||
existing container of matching size. Expression in the operator are evaluated
|
||||
@@ -1833,7 +1776,7 @@ The temporary container evaluates all expressions and uses the @b highest rankin
|
||||
type (as defined by @ref axtypeprecedence "arithmetic type precedence") to infer
|
||||
the element type of the container, implicitly casting all other types to that
|
||||
type where necessary. In the above example, the temporary created forms a
|
||||
@b `mat3d`, before being implicitly cast to a @b `mat3f` due to the assignment.
|
||||
`mat3d`, before being implicitly cast to a `mat3f` due to the assignment.
|
||||
|
||||
|
||||
@subsection axopprecedence Operator Precedence
|
||||
@@ -2004,10 +1947,10 @@ can be used to assign any scalar a value.
|
||||
@par
|
||||
<table>
|
||||
<tr><th>Type</th><th>Literal Tokens</th></tr>
|
||||
<tr><td>`bool`</td><td>Tokens @b `true` and @b `false`</td></tr>
|
||||
<tr><td>`bool`</td><td>Tokens `true` and `false`</td></tr>
|
||||
<tr><td>`int32`</td><td>No suffix, automatically infered from integral literals</td></tr>
|
||||
<tr><td>`int64`</td><td>The letter @b `l` e.g. @code int64 a = 10l; @endcode</td></tr>
|
||||
<tr><td>`float`</td><td>The letter @b `f` e.g. @code float a = 0.0f; @endcode</td></tr>
|
||||
<tr><td>`int64`</td><td>The letter `l` e.g. @code int64 a = 10l; @endcode</td></tr>
|
||||
<tr><td>`float`</td><td>The letter `f` e.g. @code float a = 0.0f; @endcode</td></tr>
|
||||
<tr><td>`double`</td><td>No suffix, automatically infered from floating point literals.</td></tr>
|
||||
<tr><td>`string`</td><td>Character strings wrapped in double quotes @b \" @b \"</td></tr>
|
||||
</table>
|
||||
@@ -2025,7 +1968,6 @@ can be used to assign any scalar a value.
|
||||
@subsection axattribaccess Attribute Access
|
||||
<!-- codecs not supported, how are points and volumes sampled (point vs volume) -->
|
||||
|
||||
<!--
|
||||
Further, the active states of
|
||||
OpenVDB voxels change which voxels AX will process. The defined behavior for all
|
||||
cases is shown below.
|
||||
@@ -2064,8 +2006,6 @@ foreach(ActiveVoxel in surface)
|
||||
DensityIndexSpaceCoord = density.transformToIndex(SurfaceWorldSpaceCoord);
|
||||
ActiveVoxel = density.getValue(DensityIndexSpaceCoord);
|
||||
@endcode
|
||||
done()
|
||||
-->
|
||||
|
||||
|
||||
@subsection axexternalaccess External Parameter Access
|
||||
@@ -2108,6 +2048,4 @@ AX Roadmap as a near future feature.
|
||||
|
||||
@section axvexsupport AX VEX Support
|
||||
|
||||
</div>
|
||||
|
||||
*/
|
||||
|
||||
+9
-12
@@ -1,9 +1,8 @@
|
||||
/**
|
||||
<div style="width:800px;text-align:justify;">
|
||||
|
||||
@page axcplusplus OpenVDB AX
|
||||
@page axcplusplus AX C++ Documentation
|
||||
|
||||
@section axcdocs AX C++ Documentation
|
||||
@section axcdocs Intro
|
||||
@par
|
||||
The following documentation focuses on the C++ design and API of OpenVDB AX,
|
||||
@b not the front end AX language. For the latter, see the
|
||||
@@ -27,7 +26,6 @@ API doxygen comments or contact us for more information.
|
||||
- @ref vdbaxcompiler
|
||||
- @ref vdbaxbinary
|
||||
- @ref vdbaxtoaxtypes
|
||||
- @ref vdbaxextend
|
||||
|
||||
<hr>
|
||||
|
||||
@@ -118,7 +116,7 @@ parser is accessed through the @b ast module.
|
||||
@par
|
||||
The @b ast module provides:
|
||||
- The complete implemenation of an @ref AST.h "AX abstract syntax tree"
|
||||
- The openvdb::ax::ast::parse() method
|
||||
- The @vdblink::ax::ast::parse ax::ast::parse()@endlink method
|
||||
- The @vdblink::ax::ast::Visitor AST Visitor@endlink
|
||||
- AST scanners and tools
|
||||
@par
|
||||
@@ -128,7 +126,7 @@ The complete AST hierarchy is visualized by the @vdblink::ax::ast::Node doxygen
|
||||
class graph for an openvdb::ax::ast::Node@endlink, which represents the top most
|
||||
node in the AST. The AST API exists in a nested namespace (`openvdb::ax::ast`)
|
||||
and can be used by clients for more granular control and modifications to an AST.
|
||||
Importantly, the AST API contains the openvdb::ax::ast::parse() method, which
|
||||
Importantly, the AST API contains the `openvdb::ax::ast::parse()` method, which
|
||||
invokes the C/C++ functions built by the grammar to iteratively construct an
|
||||
AST from a provided character string.
|
||||
@par
|
||||
@@ -139,7 +137,7 @@ branches. All these methods use the @vdblink::ax::ast::Visitor AX visitor
|
||||
framework@endlink, a visitor pattern traversal class strongly influenced by
|
||||
<A HREF="https://clang.llvm.org/doxygen/classclang_1_1RecursiveASTVisitor.html">
|
||||
Clang's RecursiveVisitor</A>. Clients can use the AX visitor to implement their
|
||||
own AST analysis or modifications (see @ref vdbaxextend).
|
||||
own AST analysis or modifications.
|
||||
|
||||
@subsubsection vdbaxastscanners Scanners
|
||||
@par
|
||||
@@ -285,11 +283,11 @@ AX ASTs to verify access patterns of your data.
|
||||
@subsubsection vdbaxcompilercustomdata Custom Data
|
||||
@par
|
||||
The AX language provides two main tokens to access memory which has not been
|
||||
allocated by AX itself. The first token, @b \@ (the "at" symbol), is designed to
|
||||
allocated by AX itself. The first token, `@` (the "at" symbol), is designed to
|
||||
provide read/write access to geometry attributes. The second token is the dollar
|
||||
character @b $. This allows <b>read only</b> access to custom data that can be
|
||||
character `$`. This allows <b>read only</b> access to custom data that can be
|
||||
modified in C++ i.e. outside the scope of an AX program. In the same way as the
|
||||
@b \@attribute syntax, the value of this data can change without the need for AX
|
||||
`@attribute` syntax, the value of this data can change without the need for AX
|
||||
to re-compile the program. It is, however, solely on the C++ clients of AX to
|
||||
make sure that this data is setup correctly when integrating AX into their
|
||||
applications.
|
||||
@@ -487,10 +485,9 @@ Vec4IGrid::registerGrid();
|
||||
@endcode
|
||||
|
||||
<hr>
|
||||
@section vdbaxextend Extending OpenVDB AX
|
||||
<!--@section vdbaxextend Extending OpenVDB AX-->
|
||||
<!--@subsection vdbaxcustomtraversal Custom AST Traversal-->
|
||||
<!--@subsection vdbaxcustomtraversal Designing an Executable-->
|
||||
<!--@subsection vdbaxcustomtraversal Geometry Callbacks-->
|
||||
<!--@subsection vdbaxcustomtraversal Custom Code Generation-->
|
||||
</div>
|
||||
*/
|
||||
|
||||
+14
-16
@@ -1,5 +1,4 @@
|
||||
/**
|
||||
<div style="width:800px;text-align:justify;">
|
||||
|
||||
@page axexamples AX Code Examples
|
||||
|
||||
@@ -30,12 +29,12 @@ These examples demonstrate how to use AX on OpenVDB points grids.
|
||||
@subsection axexamplepointbasic Basic point attributes
|
||||
@par
|
||||
Below is a small example which demonstrates working with a few point attributes.
|
||||
The @ symbol is the identifier for an AX attribute. The type of each attribute
|
||||
is specified before the @ symbol and the name is specified afterwards. For
|
||||
example: @b `int\@count` would imply an @b integer attribute called @b count.
|
||||
The `@` symbol is the identifier for an AX attribute. The type of each attribute
|
||||
is specified before the `@` symbol and the name is specified afterwards. For
|
||||
example: `int@count` would imply an @b integer attribute called @b count.
|
||||
@par
|
||||
In this example there are three point attributes: a @b float attribute @b speed,
|
||||
a @b vec3f float attribute @b velocity and a @b vec3f float attribute @b colour.
|
||||
a `vec3f` float attribute @b velocity and a `vec3f` float attribute @b colour.
|
||||
@par
|
||||
This snippet uses the functions @ref axlength "length" and @ref axfit "fit" to
|
||||
give points a colour between black and white based on their speed.
|
||||
@@ -68,8 +67,8 @@ special attribute.
|
||||
@subsection axexamplepointdelete Deleting Points
|
||||
@par
|
||||
This example demonstrates a simple way of removing points from a VDB points grid.
|
||||
It uses a combination of @ref axrand and @ref axdeletepoint to randomly delete
|
||||
roughly half of the points in the input VDB points grid.
|
||||
It uses a combination of @ref axrand "rand()" and @ref axdeletepoint "deletepoint()"
|
||||
to randomly delete roughly half of the points in the input VDB points grid.
|
||||
@par
|
||||
<!-- P -->@code{.c}
|
||||
float threshold = 0.5f;
|
||||
@@ -83,7 +82,7 @@ if (rand(int64@id) > threshold) {
|
||||
@endcode
|
||||
@par
|
||||
The @b id attribute in this example is assumed to be a unique @b integer for
|
||||
each point in the VDB points grid such that @ref axrand will receive a
|
||||
each point in the VDB points grid such that @ref axrand "rand()" will receive a
|
||||
different seed value per point producing a good distribution of random values.
|
||||
|
||||
@subsection axexamplepointdrag Applying Drag
|
||||
@@ -114,7 +113,7 @@ v@P += v@v * dt;
|
||||
@endcode
|
||||
@par
|
||||
The final AX command, `v@P += v@v * dt;` writes to the position attribute of
|
||||
the points @b `v\@P`. Whenever position is written to in this way, the AX
|
||||
the points `v@P`. Whenever position is written to in this way, the AX
|
||||
`PointExecutable` will move (re-bucket) points in PointDataGrids.
|
||||
|
||||
@subsection axexamplepointcurlnoise Curl Noise
|
||||
@@ -198,12 +197,12 @@ These examples demonstrate how to use AX on VDB volume grids.
|
||||
@subsection axexamplevolumeclamp Volume Clamping
|
||||
@par
|
||||
Below is a small example of reading adn writing to voxels within a VDB volume.
|
||||
As with points, the @ symbol specifies an access to a named volume, with the
|
||||
As with points, the `@` symbol specifies an access to a named volume, with the
|
||||
type of each volume preceding it and the name appearing as a suffix. For
|
||||
example: `float@density` would imply a @b float volume called @b density.
|
||||
@par
|
||||
This snippet uses @ref axclamp function to constrain the @b density attribute
|
||||
between zero and one.
|
||||
This snippet uses @ref axclamp "clamp()" function to constrain the @b density
|
||||
attribute between zero and one.
|
||||
@par
|
||||
<!-- V -->@code{.c}
|
||||
float@density = clamp(float@density, 0.f, 1.f);
|
||||
@@ -234,10 +233,10 @@ vec3f@vel += dt * massInverse * vec3f@force;
|
||||
@par
|
||||
Here we demonstrate how AX can be used to blend two volumes together. Note that
|
||||
only the volumes which are written to are executed over. In the below code,
|
||||
only @b `\@surface_a` is written to. This means that the final blended result,
|
||||
only `@surface_a` is written to. This means that the final blended result,
|
||||
stored in `surface_a`, will only be updated in `surface_a`'s topology. Should
|
||||
we want a combined blend of @b non @b overlapping areas of both surface a and b,
|
||||
we would need to activate a's topology with respect to b first.
|
||||
we want a combined blend of @b non @b overlapping areas of both surface `a` and
|
||||
`b`, we would need to activate `a`'s topology with respect to `b` first.
|
||||
@par
|
||||
<!-- V -->@code{.c}
|
||||
// time constants for each run.
|
||||
@@ -263,5 +262,4 @@ mask = clamp(mask, 0.0f, 1.0f);
|
||||
@surface_a = a + (b-a) * mask;
|
||||
@endcode
|
||||
|
||||
</div>
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/**
|
||||
<div style="width:800px;text-align:justify;">
|
||||
|
||||
@page axfunctionlist AX Supported Functions
|
||||
|
||||
@@ -721,7 +720,7 @@ double(int64 seed);
|
||||
@anchor axrand32
|
||||
@par rand32
|
||||
Creates a random number based on the provided 32 bit seed. The number will be in the range of 0 to
|
||||
1. The same number is produced for the same seed. NOTE: This function does not share the same
|
||||
1\. The same number is produced for the same seed. NOTE: This function does not share the same
|
||||
random number generator as rand(). rand32() may provide a higher throughput on some
|
||||
architectures, but will produce different results to rand(). NOTE: If rand32 is called without
|
||||
a seed the previous state of the random number generator is advanced for the currently
|
||||
@@ -881,6 +880,4 @@ int64(int64 dividend; int64 divisor);
|
||||
int32(int32 dividend; int32 divisor);
|
||||
@endcode
|
||||
|
||||
|
||||
</div>
|
||||
*/
|
||||
|
||||
+1
-3
@@ -1,5 +1,4 @@
|
||||
/**
|
||||
<div style="width:800px;text-align:justify;">
|
||||
|
||||
@page openvdbax OpenVDB AX
|
||||
|
||||
@@ -41,7 +40,6 @@ provided:
|
||||
@par
|
||||
- @subpage axexamples "Code Examples" for examples of OpenVDB AX in action.
|
||||
- @subpage axfunctionlist "AX Functions" for a list of all available AX functions.
|
||||
- @subpage build "Building OpenVDB" and @subpage dependencies "OpenVDB Dependencies" for information on building OpenVDB and <b>OpenVDB AX</b>.
|
||||
- @ref build "Building OpenVDB" and @ref dependencies "OpenVDB Dependencies" for information on building OpenVDB and <b>OpenVDB AX</b>.
|
||||
|
||||
</div>
|
||||
*/
|
||||
|
||||
+10
-11
@@ -288,7 +288,7 @@ Required:
|
||||
|
||||
Optional:
|
||||
|
||||
- @b [CMAKE_INSTALL_PREFIX](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html)
|
||||
- [CMAKE_INSTALL_PREFIX](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html)
|
||||
=`/path/to/install/dir` # The location you wish to install OpenVDB to. See
|
||||
the link for default values.
|
||||
- @b OPENVDB_HOUDINI_INSTALL_PREFIX =`/path/to/install/dir` # The location to
|
||||
@@ -381,7 +381,7 @@ Required:
|
||||
|
||||
Optional:
|
||||
|
||||
- @b [CMAKE_INSTALL_PREFIX](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html)
|
||||
- [CMAKE_INSTALL_PREFIX](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html)
|
||||
=`/path/to/install/dir` # The location you wish to install OpenVDB to. See
|
||||
the link for default values.
|
||||
- @b OPENVDB_MAYA_INSTALL_PREFIX =`/path/to/install/dir` # The location to
|
||||
@@ -449,7 +449,7 @@ Now you can call CMake by providing the absolute or relative path to the root
|
||||
of the OpenVDB source tree. Below are some common options you may want to
|
||||
provide:
|
||||
|
||||
- @b [CMAKE_INSTALL_PREFIX](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html)
|
||||
- [CMAKE_INSTALL_PREFIX](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html)
|
||||
=`/path/to/install/dir` # The location you wish to install OpenVDB to. See
|
||||
the link for default values.
|
||||
- @b Xxx_ROOT =`/path/to/Xxx/` # Give CMake a path to where a package has been
|
||||
@@ -579,8 +579,7 @@ bypass these checks with `-D DISABLE_DEPENDENCY_VERSION_CHECKS=ON` if necessary.
|
||||
Note that using older or untested dependencies may produce undesired behavior.
|
||||
Older versions in particular are not supported by OpenVDB.
|
||||
|
||||
@b Note: Boost will produce a version error in the format `Detected version of
|
||||
Boost is too old.`
|
||||
@b Note: Boost will produce a version error in the format: `Detected version of Boost is too old.`
|
||||
|
||||
@subsection troubleshoot3 CMake warnings/errors in FindBoost.cmake
|
||||
|
||||
@@ -632,9 +631,9 @@ Note that you can provide a value for `CMAKE_MSVC_RUNTIME_LIBRARY` which is a
|
||||
native CMake variable to override any CRT selection done by OpenVDB.
|
||||
|
||||
For more information, see the following articles:
|
||||
Description of CRT selection: https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library
|
||||
In depth descriptopn of the CRT on Windows: https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features
|
||||
Related errors: https://docs.microsoft.com/en-us/cpp/error-messages/tool-errors/linker-tools-error-lnk2038
|
||||
- Description of CRT selection: https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library
|
||||
- In depth descriptopn of the CRT on Windows: https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features
|
||||
- Related errors: https://docs.microsoft.com/en-us/cpp/error-messages/tool-errors/linker-tools-error-lnk2038
|
||||
`CMAKE_MSVC_RUNTIME_LIBRARY` docs: https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html
|
||||
|
||||
@subsection troubleshoot5 Unexpected value for the Windows CRT with target build artifacts.
|
||||
@@ -656,9 +655,9 @@ usage, it is strongly encouraged to match the selection against your desired
|
||||
build type.
|
||||
|
||||
For more information, see the following articles:
|
||||
Description of CRT selection: https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library
|
||||
In depth descriptopn of the CRT on Windows: https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features
|
||||
Related errors: https://docs.microsoft.com/en-us/cpp/error-messages/tool-errors/linker-tools-error-lnk2038
|
||||
- Description of CRT selection: https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library
|
||||
- In depth descriptopn of the CRT on Windows: https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features
|
||||
- Related errors: https://docs.microsoft.com/en-us/cpp/error-messages/tool-errors/linker-tools-error-lnk2038
|
||||
`CMAKE_MSVC_RUNTIME_LIBRARY` docs: https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html
|
||||
|
||||
@subsection troubleshoot6 error LNK2038: mismatch detected for 'RuntimeLibrary'
|
||||
|
||||
+204
-45
@@ -2,9 +2,170 @@
|
||||
|
||||
@page changes Release Notes
|
||||
|
||||
@htmlonly <a name="v12_1_2_changes"></a>@endhtmlonly
|
||||
@htmlonly <a name="v13_0_1_changes"></a>@endhtmlonly
|
||||
@par
|
||||
<B>Version 12.1.2</B> - <I>In development</I>
|
||||
<B>Version 13.0.1</B> - <I>In Development</I>
|
||||
|
||||
@htmlonly <a name="v13_0_0_changes"></a>@endhtmlonly
|
||||
@par
|
||||
<B>Version 13.0.0</B> - <I>November 3, 2025</I>
|
||||
|
||||
@par
|
||||
<BLOCKQUOTE>
|
||||
This version introduces ABI changes relative to older major releases, so to
|
||||
preserve ABI compatibility it might be necessary to define the macro
|
||||
<TT>OPENVDB_ABI_VERSION_NUMBER=</TT><I>N</I>, where, for example,
|
||||
<I>N</I> is 11 for Houdini 20.5 and 12 for Houdini 21.0.
|
||||
</BLOCKQUOTE>
|
||||
|
||||
@par
|
||||
<BLOCKQUOTE>
|
||||
Support for reading <TT>.vdb</TT> files written prior to VDB 1.0 has been
|
||||
removed. To read older <TT>.vdb</TT> files, please use VDB 12.x or older
|
||||
and re-write them, so that they use the newer file format and can be read in
|
||||
VDB 13+.
|
||||
</BLOCKQUOTE>
|
||||
|
||||
@par
|
||||
Highlights:
|
||||
- OpenVDB now includes in-memory support for Half Grids and the file format
|
||||
has been extended to add I/O serialization for half grids. More work is
|
||||
planned on adapting algorithms when using Half Grids for better performance
|
||||
and improved precision.
|
||||
- NanoVDB is no longer limited to static applications, such as rendering, but
|
||||
can increasingly be applied to problems that involve dynamic topology, such
|
||||
as level set tracking, grid building, morphology operations and merging of
|
||||
grids. This is due to the fact that we keep adding more and more tools to
|
||||
NanoVDB that facilitates tree modification. However, unlike OpenVDB, these
|
||||
operations generate new NanoVDB grids (as oppose to in-palace modifications
|
||||
in OpenVDB), yet are typically much faster since they are performed on the
|
||||
GPU. Many of these exciting new features are the foundation of fVDB.
|
||||
|
||||
@par
|
||||
OpenVDB:
|
||||
- New features:
|
||||
- Added native registration of HalfGrid type.
|
||||
- Added customization of the output transformation attribute for
|
||||
@vdblink::points::pca points::pca@endlink. Can be output as a quaternion
|
||||
or as a combined affine transform with the scale/stretch ommitted.
|
||||
- Added support for input quaternions attributes and affine transformations
|
||||
(that include scale) in
|
||||
@vdblink::points::rasterizeSdf points::rasterizeSdf@endlink for
|
||||
ellipsoidal rasterizations.
|
||||
|
||||
- API Changes:
|
||||
- Added @c openvdb::tree::LeafBuffer::storage()
|
||||
for direct const access to the raw data buffer when using the bool LeafNode.
|
||||
- Removed Grid::evalMinMax(), use
|
||||
@vdblink::tools::minMax tools::minMax@endlink for superior threaded
|
||||
performance.
|
||||
- Removed points::rasterize() that took a custom filter and interrupter.
|
||||
- Removed LeafNodeBool TopologyCopy constructor in favor of using explicit
|
||||
mask/buffer constructors.
|
||||
- Removed @vdblink::tree::ValueAccessor ValueAccessor@endlink NodeT0,
|
||||
NodeT1, NodeT2 aliases, use NodeTypeAtLevel instead.
|
||||
- Add @vdblink::tree::InternalNode::setValueMaskUnsafe() InternalNode::setValueMaskUnsafe()@endlink
|
||||
to write a value mask directly.
|
||||
- LegacyFrustum and RootNodeMask have been removed. These classes were
|
||||
only used for reading old .vdb files, which is no longer supported.
|
||||
- @vdblink::io::writeCompressedValues() io::writeCompressedValues()@endlink
|
||||
now takes a const ValueT* source buffer for improved const-correctness.
|
||||
|
||||
- Bug Fixes:
|
||||
- Fix a bug in the
|
||||
@vdblink::tools::LevelSetRayIntersector LevelSetRayIntersector@endlink class
|
||||
where two of the methods were incorrectly adjusting the time even when an
|
||||
intersection was not found.
|
||||
- Fixed an assertion in @vdblink::math::Quat math::Quat@endlink where a
|
||||
vector length check was using too small a tolerance.
|
||||
- Fixed a bug in the unit tests where detecting if assertions were enabled
|
||||
was using @c NDEBUG rather than OpenVDB assertions.
|
||||
|
||||
- Build:
|
||||
- Removed the unnecessary @c USE_NUMPY build option for the OpenVDB Python
|
||||
module. All functions which take numpy arrays are now always generated
|
||||
(numpy is not required at compile time for this support).
|
||||
- Bumped minimum supported CMake version to 3.24, and new future minimum to
|
||||
4.0.0.
|
||||
- Bumped minimum supported OpenEXR and Imath versions to 3.2, and new
|
||||
future minimum to 3.3.
|
||||
- Bumped minimum supported Boost version to 1.82, and new future minimum to
|
||||
1.85.
|
||||
- Bumped minimum supported Python version to 3.11
|
||||
- Bumped minimum supported Log4cplus version to 2.0
|
||||
- Bumped minimum supported Houdini version to 20.5
|
||||
- Bumped minimum supported GLFW version to 3.3
|
||||
- Bumped future minimum for TBB to 2021 (oneapi).
|
||||
- Bumped future minimum for nanobind to 2.5.0.
|
||||
|
||||
@par
|
||||
OpenVDB AX:
|
||||
- New:
|
||||
- Added support for LLVM 21.
|
||||
|
||||
@par
|
||||
NanoVDB:
|
||||
- Highlights:
|
||||
- NanoVDB is no longer limited to static applications, such as rendering,
|
||||
but can increasingly be applied to problems that involve dynamic topology,
|
||||
such as level set tracking, grid building, morphology operations and
|
||||
merging of grids. This is due to the fact that we keep adding more and
|
||||
more tools to NanoVDB that facilitates tree modification. However, unlike
|
||||
OpenVDB, these operations generate new NanoVDB grids (as oppose to
|
||||
in-palace modifications in OpenVDB), yet are typically much faster since
|
||||
they are performed on the GPU. Many of these exciting new features are the
|
||||
foundation of fVDB.
|
||||
|
||||
- New features:
|
||||
- Added <TT>nanovdb/tools/cuda/DilateGrid.cuh</TT>, which performs topology
|
||||
dilation on the GPU.
|
||||
- Added <TT>nanovdb/tools/cuda/MergeGrids.cuh</TT>, which merges the topology
|
||||
of two grids on the GPU.
|
||||
- Added <TT>nanovdb/tools/cuda/CoarsenGrid.cuh</TT>, which performs topological
|
||||
coarsening of grids on the GPU.
|
||||
- Added <TT>nanovdb/tools/cuda/RefineGrid.cuh</TT>, which performs topological
|
||||
refinement of grids on the GPU.
|
||||
- Added <TT>nanovdb/tools/cuda/PruneGrid.cuh</TT>, which prunes the topology of
|
||||
a grid using a sidecar mask on the GPU.
|
||||
- Added <TT>nanovdb/util/cuda/Injection.cuh</TT>, which facilitates sidecar
|
||||
data copying - on the GPU - in the overlap of two grids with different
|
||||
topology.
|
||||
- Added Mat classes and other math types to NanoVDB.
|
||||
- Major rewrite of nanovdb::NanoToOpenVDB, which now allows
|
||||
nanovdb::IndexGrids and blind (or sidecar) data to be converted into
|
||||
an openvdb grid.
|
||||
- Added support for LevelSet, FogVolume, and Staggered grid semantics.
|
||||
Added toGridClass and toSemantic to map Grid Blind data semantic to
|
||||
Grid class and vice versa.
|
||||
|
||||
- Improvements:
|
||||
- Improvements to mGPU grid building and introduction of a separate test
|
||||
executable for mGPU tests
|
||||
- Added stand-alone function nanovdb::writeUncompressedGrid that allows
|
||||
nanovdb::IndexGrids and blind (or sidecar) data to be written directly
|
||||
to a stream.
|
||||
- @c nanovdb_print now (optionally) prints the blind data count per grid.
|
||||
- Extended nanovdb::GridBlindDataSemantic with nanovdb::GridClass types
|
||||
to support conversion between IndexGrid and regular grids.
|
||||
- Improvements and bugfixes to PNanoVDB.
|
||||
|
||||
- API Changes:
|
||||
- Removed nanovdb::ValueIndexMask and nanovdb::ValueOnIndexMask since
|
||||
they are no longer used by fVDB (or elsewhere).
|
||||
- NanoVDB version is now 32.9.0, so the ABI of the grid is preserved
|
||||
but not the API. However, the API changes are very minor!
|
||||
|
||||
- Bug Fixes:
|
||||
- Several fixes for compiler warnings.
|
||||
- Fixed error in <TT>nanovdb/io/IO.h</TT> related to missing throws.
|
||||
|
||||
@par
|
||||
Houdini:
|
||||
- Remove support for openvdb_houdini::Interrupter, use
|
||||
openvdb_houdini::HoudiniInterrupter instead.
|
||||
- Remove input-stealing via duplicateSourceStealable(), SideFX have since
|
||||
introduced verbification and compiled SOPs to provide zero-copy node
|
||||
evaluation.
|
||||
|
||||
|
||||
@htmlonly <a name="v12_1_1_changes"></a>@endhtmlonly
|
||||
@@ -20,7 +181,7 @@ OpenVDB AX:
|
||||
@par
|
||||
Houdini:
|
||||
- Fixed a compilation issue with the OpenVDB Activate SOP with Houdini 21
|
||||
[Reported by @callen128]
|
||||
[Reported by \@callen128]
|
||||
|
||||
|
||||
@htmlonly <a name="v12_1_0_changes"></a>@endhtmlonly
|
||||
@@ -190,7 +351,7 @@ NanoVDB:
|
||||
- Fixed a call to a non-existent method in GridBuild.h
|
||||
|
||||
|
||||
@htmlonly <a name="v1_changes"></a>@endhtmlonly
|
||||
@htmlonly <a name="v12_0_0_changes"></a>@endhtmlonly
|
||||
@par
|
||||
<B>Version 12.0.0</B> - <I>October 31, 2024</I>
|
||||
|
||||
@@ -579,10 +740,10 @@ OpenVDB:
|
||||
- Fixed a bug with LeafManager which wouldn't correctly
|
||||
initialize its LeafNode array for single level Tree configurations
|
||||
i.e. RootNode<LeafNode> (bug introduced in 7.2.0).
|
||||
[Reported by @lanwatch]
|
||||
[Reported by \@lanwatch]
|
||||
- Fixed a bug with LeafNodeBool Topology constructor with designated
|
||||
on/off values which wouldn't apply them correctly.
|
||||
[Reported by @hozhaoea]
|
||||
[Reported by \@hozhaoea]
|
||||
|
||||
@par
|
||||
OpenVDB AX:
|
||||
@@ -2641,13 +2802,13 @@ Improvements:
|
||||
util::PagedArray@endlink.
|
||||
- Added an @c install_lib build target to the Makefile.
|
||||
<I>[Contributed by Double Negative]</I>
|
||||
- Added @subpage points "documentation" and Cookbook
|
||||
- Added @ref points "documentation" and Cookbook
|
||||
@ref openvdbPointsHelloWorld "examples" for OpenVDB Points.
|
||||
<I>[Contributed by Double Negative]</I>
|
||||
- Registration of OpenVDB Points grid and attribute types is now
|
||||
handled in @vdblink::initialize() openvdb::initialize@endlink,
|
||||
and @vdblink::points::initialize() points::initialize@endlink
|
||||
and @vdblink::points::uninitialize() points::uninitialize@endlink
|
||||
and @c openvdb::points::initialize()
|
||||
and @c openvdb::points::uninitialize()
|
||||
are therefore deprecated.
|
||||
- Extended multi-pass I/O to handle a variable number of passes per leaf node.
|
||||
<I>[Contributed by Double Negative]</I>
|
||||
@@ -3365,11 +3526,10 @@ Houdini:
|
||||
- @vdblink::tools::volumeToMesh() tools::volumeToMesh@endlink now compiles
|
||||
for all grid types but throws an exception if the input grid does not
|
||||
have a scalar value type.
|
||||
- Added a
|
||||
@vdblink::io::File::readGrid(const Name&, const BBoxd&) File::readGrid@endlink
|
||||
overload and @vdblink::GridBase::readBuffers(std::istream&, const CoordBBox&)
|
||||
readBuffers@endlink overloads to the grid, tree and node classes that allow
|
||||
one to specify a bounding box against which to clip a grid while reading it.
|
||||
- Added an <TT>openvdb::io::File::readGrid(const Name&, const BBoxd&)</TT>
|
||||
overload and <TT>openvdb::GridBase::readBuffers(std::istream&, const CoordBBox&)</TT>
|
||||
overloads to the grid, tree and node classes that allow one to specify a
|
||||
bounding box against which to clip a grid while reading it.
|
||||
For large grids, clipping while reading can result in significantly lower
|
||||
memory usage than clipping after reading.
|
||||
- Added @vdblink::GridBase::clipGrid() GridBase::clipGrid@endlink, which
|
||||
@@ -3428,10 +3588,11 @@ Houdini:
|
||||
- Added constructors and methods to both
|
||||
@vdblink::math::RandInt RandInt@endlink and
|
||||
@vdblink::math::Rand01 Rand01@endlink to set and reset the random seed value.
|
||||
- Added convenience methods for
|
||||
@vdblink::math::Transform::indexToWorld(const BBoxd&) const transforming@endlink
|
||||
@vdblink::math::Transform::worldToIndex(const BBoxd&) const bounding@endlink
|
||||
@vdblink::math::Transform::worldToIndexCellCentered(const BBoxd&) const boxes@endlink
|
||||
- Added convenience methods
|
||||
<TT>openvdb::math::Transform::indexToWorld(const BBoxd&)</TT>,
|
||||
<TT>openvdb::math::Transform::worldToIndex(const BBoxd&)</TT> and
|
||||
<TT>openvdb::math::Transform::worldToIndexCellCentered(const BBoxd&)</TT>
|
||||
for transforming bounding boxes
|
||||
to @vdblink::math::Transform math::Transform@endlink.
|
||||
- @c vdb_view is now compatible with both GLFW 2 and GLFW 3.
|
||||
- Made many small changes to address type conversion and other warnings
|
||||
@@ -3451,9 +3612,9 @@ Houdini:
|
||||
|
||||
@par
|
||||
API changes:
|
||||
- The addition of a
|
||||
@vdblink::GridBase::readBuffers(std::istream&, const CoordBBox&)
|
||||
GridBase::readBuffers@endlink virtual function overload and the
|
||||
- The addition of an
|
||||
<TT>openvdb::GridBase::readBuffers(std::istream&, const CoordBBox&)</TT>
|
||||
virtual function overload and the
|
||||
@vdblink::GridBase::clip() GridBase::clip@endlink
|
||||
@vdblink::GridBase::readNonresidentBuffers()
|
||||
GridBase::readNonresidentBuffers@endlink and
|
||||
@@ -3558,16 +3719,15 @@ Houdini:
|
||||
extractSparseTreeWithMask@endlink, which copies data from the index-space
|
||||
intersection of a sparse tree and a dense input grid.
|
||||
- Added copy constructors to the
|
||||
@vdblink::Grid::Grid(const Grid<OtherTreeType>&) Grid@endlink,
|
||||
@vdblink::tree::Tree::Tree(const Tree<OtherRootType>&) Tree@endlink,
|
||||
@vdblink::tree::RootNode::RootNode(const RootNode<OtherChildType>&)
|
||||
RootNode@endlink,
|
||||
@vdblink::tree::InternalNode::InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>&)
|
||||
InternalNode@endlink and
|
||||
@vdblink::tree::LeafNode::LeafNode(const LeafNode<OtherValueType, Log2Dim>&) LeafNode@endlink
|
||||
<TT>openvdb::Grid::Grid(const Grid<OtherTreeType>&)</TT>,
|
||||
<TT>openvdb::tree::Tree::Tree(const Tree<OtherRootType>&)</TT>,
|
||||
<TT>openvdb::tree::RootNode::RootNode(const RootNode<OtherChildType>&)</TT>,
|
||||
<TT>openvdb::tree::InternalNode::InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>&)</TT>
|
||||
and
|
||||
<TT>openvdb::tree::LeafNode::LeafNode(const LeafNode<OtherValueType, Log2Dim>&)</TT>
|
||||
classes, and an assignment operator overload to
|
||||
@vdblink::tree::RootNode::operator=(const RootNode<OtherChildType>&)
|
||||
RootNode@endlink, that allow the source and destination to have different
|
||||
<TT>openvdb::tree::RootNode::operator=(const RootNode<OtherChildType>&)</TT>,
|
||||
that allow the source and destination to have different
|
||||
value types.
|
||||
- Modified @vdblink::tree::Tree::combine2() Tree::combine2@endlink to permit
|
||||
combination of trees with different value types.
|
||||
@@ -3619,7 +3779,7 @@ API changes:
|
||||
LeafManager::LeafRange::Iterator@endlink,
|
||||
@vdblink::util::BaseMaskIterator::operator=() BaseMaskIterator@endlink,
|
||||
@vdblink::util::NodeMask::operator=() NodeMask@endlink and
|
||||
@vdblink::util::RootNodeMask::operator=() RootNodeMask@endlink
|
||||
@c openvdb::util::RootNodeMask::operator=()
|
||||
now return references to the respective objects.
|
||||
- Removed a number of methods that were deprecated in version 2.0.0
|
||||
or earlier.
|
||||
@@ -4085,7 +4245,7 @@ API changes:
|
||||
- @vdblink::tree::LeafManager tree::LeafManager@endlink can now be
|
||||
instantiated with a @const tree, although buffer swapping with @const trees
|
||||
is disabled.
|
||||
- Added a @vdblink::Grid::signedFloodFill() Grid::signedFloodFill@endlink
|
||||
- Added an @c openvdb::Grid::signedFloodFill()
|
||||
overload that allows one to specify inside and outside values.
|
||||
- Fixed a bug in Grid::setBackground() so that now only the values of
|
||||
inactive voxels change.
|
||||
@@ -4095,9 +4255,9 @@ API changes:
|
||||
that relied on input and output grids having compatible tree topology.
|
||||
- @vdblink::math::Transform math::Transform@endlink now includes an
|
||||
@vdblink::math::Transform::isIdentity() isIdentity@endlink predicate
|
||||
and methods to @vdblink::math::Transform::preMult(const Mat4d&) pre-@endlink
|
||||
and @vdblink::math::Transform::postMult(const Mat4d&) postmultiply@endlink
|
||||
by a matrix.
|
||||
and methods <TT>openvdb::math::Transform::preMult(const Mat4d&)</TT> and
|
||||
<TT>openvdb::math::Transform::postMult(const Mat4d&)</TT>
|
||||
to pre- and postmultiply by a matrix.
|
||||
- Modified the @link NodeMasks.h node mask@endlink classes to permit
|
||||
octree-like tree configurations (i.e., with a branching factor of two)
|
||||
and to use 64-bit operations instead of 32-bit operations.
|
||||
@@ -4170,7 +4330,7 @@ API changes:
|
||||
faster for high-resolution meshes where the polygon/voxel size ratio
|
||||
is small.
|
||||
- Added @vdblink::createLevelSet() createLevelSet@endlink and
|
||||
@vdblink::createLevelSetSphere() createLevelSetSphere@endlink
|
||||
@vdblink::tools::createLevelSetSphere() createLevelSetSphere@endlink
|
||||
factory functions for level set grids.
|
||||
- @vdblink::tree::ValueAccessor tree::ValueAccessor@endlink is now faster
|
||||
for trees of height 2, 3 and 4 (the latter is the default), and it now
|
||||
@@ -4283,7 +4443,7 @@ API changes:
|
||||
- Added @vdblink::tools::compReplace() tools::compReplace@endlink,
|
||||
which copies the active values of one grid into another, and added
|
||||
a "Replace A With Active B" mode to the Combine SOP.
|
||||
- @vdblink::Grid::signedFloodFill() Grid::signedFloodFill@endlink
|
||||
- @c openvdb::Grid::signedFloodFill()
|
||||
no longer enters an infinite loop when filling an empty grid.
|
||||
- Fixed a bug in the particle to level set converter that sometimes
|
||||
produced level sets with holes, and fixed a bug in the SOP that
|
||||
@@ -4546,8 +4706,8 @@ API changes:
|
||||
math library and a method to return the
|
||||
@vdblink::math::Mat3::trace trace@endlink of a @c Mat3.
|
||||
- Fixed a bug in the
|
||||
@vdblink::tree::ValueAccessor::ValueAccessor(const ValueAccessor&)
|
||||
ValueAccessor@endlink copy constructor that caused the copy to reference
|
||||
<TT>openvdb::tree::ValueAccessor::ValueAccessor(const ValueAccessor&)</TT>
|
||||
copy constructor that caused the copy to reference
|
||||
the original.
|
||||
- Fixed a bug in @vdblink::tree::RootNode::setActiveState()
|
||||
RootNode::setActiveState@endlink that caused a crash
|
||||
@@ -4555,7 +4715,7 @@ API changes:
|
||||
- Added a @c Tree::pruneLevelSet method that is similar to but faster than
|
||||
Tree::pruneInactive() for level set grids.
|
||||
- Added fast leaf node voxel access
|
||||
@vdblink::tree::LeafNode::getValue(Index) const methods@endlink
|
||||
<TT>openvdb::tree::LeafNode::getValue(Index)</TT> methods
|
||||
that index by linear offset (as returned by
|
||||
@vdblink::tree::LeafNode::ValueOnIter::pos() ValueIter::pos@endlink)
|
||||
instead of by @ijk coordinates.
|
||||
@@ -4608,7 +4768,7 @@ API changes:
|
||||
just the active states of voxels from one grid with those of
|
||||
another grid of a possibly different type.
|
||||
- Fixed an incorrect scale factor in the Laplacian diffusion
|
||||
@vdblink::tools::Filter::laplacian() filter@endlink.
|
||||
@c openvdb::tools::Filter::laplacian() filter.
|
||||
- Fixed a bug in @vdblink::tree::Tree::merge() Tree::merge@endlink
|
||||
that could leave a tree with invalid value accessors.
|
||||
- Added @vdblink::tree::TreeValueIteratorBase::setActiveState()
|
||||
@@ -4643,7 +4803,7 @@ API changes:
|
||||
@par
|
||||
<B>Version 0.92.0</B> - <I>August 23 2012</I>
|
||||
- Added a Laplacian diffusion
|
||||
@vdblink::tools::Filter::laplacian() filter@endlink.
|
||||
@c openvdb::tools::Filter::laplacian() filter.
|
||||
- Fixed a bug in the initialization of the sparse contour tracer
|
||||
that caused mesh-to-volume conversion to fail in certain cases.
|
||||
- Fixed a bug in the curvature stencil that caused mean curvature
|
||||
@@ -4671,9 +4831,8 @@ API changes:
|
||||
method that replaces a grid’s tree with a new, empty tree of the
|
||||
correct type.
|
||||
- Fixed a crash in
|
||||
@vdblink::tree::Tree::setValueOff(const Coord& xyz, const ValueType& value)
|
||||
Tree::setValueOff@endlink when the new value was equal to the
|
||||
background value.
|
||||
<TT>openvdb::tree::Tree::setValueOff(const Coord& xyz, const ValueType& value)</TT>
|
||||
when the new value was equal to the background value.
|
||||
- Fixed bugs in Tree::prune() that could result in output tiles with
|
||||
incorrect active states.
|
||||
- Added @c librt to the link dependencies to address build failures
|
||||
|
||||
+44
-36
@@ -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 | Blosc, ZLib, Log4cplus, Imath::Imath (included), Boost::iostream, VCL (included)
|
||||
OpenVDB Print | Core Library dependencies | -
|
||||
OpenVDB LOD | Core Library dependencies | -
|
||||
OpenVDB Render | Core Library dependencies | OpenEXR, Imath::Imath, libpng
|
||||
@@ -45,41 +45,51 @@ OpenVDB Python | Core Library dependencies, Python, nanobind | -
|
||||
OpenVDB AX | Core Library dependencies, LLVM | Bison, Flex
|
||||
NanoVDB | - | Core Library, CUDA, TBB, Blosc, ZLib
|
||||
OpenVDB UnitTests | Core Library dependencies, GoogleTest, numpy* | -
|
||||
OpenVDB Documentation | Doxygen | -
|
||||
OpenVDB Documentation | Doxygen, Doxygen Awesome (included) | -
|
||||
|
||||
- @b * GLEW is only required for building the vdb_view binary on Windows.
|
||||
- @b * numpy only required for python binding unit tests
|
||||
- @b Note - Bison and Flex are only required for re-generation of the AX
|
||||
grammar which is not typically necessary for the majority of users.
|
||||
|
||||
Note that OpenVDB comes bundled with the following dependencies. Please see
|
||||
their respective license documentation in `ext/THIRD-PARTY.md` for license
|
||||
information.
|
||||
|
||||
- Doxygen Awesome
|
||||
- Imath::Imath
|
||||
- VCL
|
||||
|
||||
@subsection depDependencyTable Dependency Table
|
||||
|
||||
Package | Minimum | Recommended | Description | apt-get | Homebrew | Source
|
||||
-------------- | ------- | ----------- | ----------------------------------------------------------------- | ------- | -------- | ------
|
||||
CMake | 3.24 | Latest | Cross-platform family of tools designed to help build software | Y | Y | https://cmake.org
|
||||
GCC | 11.2.1 | 11.2.1 | C++ 17 Compiler: The GNU Compiler Collection | Y | Y | https://www.gnu.org/software/gcc
|
||||
Clang | 5.0 | Latest | C++ 17 Compiler: A C language family frontend for LLVM | Y | Y | https://clang.llvm.org
|
||||
Intel ICC | 19 | Latest | C++ 17 Compiler: Intels C++ Compiler | Y | Y | https://software.intel.com/en-us/c-compilers
|
||||
MSVC | 19.30 | 19.30 | C++ 17 Compiler: Microsoft Visual C++ Compiler | Y | Y | https://visualstudio.microsoft.com/vs
|
||||
Imath | 3.2 | >= 3.3 | Half precision floating points | Y | Y | http://www.openexr.com
|
||||
OpenEXR | 3.2 | >= 3.3 | EXR serialization support | Y | Y | http://www.openexr.com
|
||||
TBB | 2020.3 | >= 2021 | Threading Building Blocks - template library for task parallelism | Y | Y | https://www.threadingbuildingblocks.org
|
||||
ZLIB | 1.2.7 | Latest | Compression library for disk serialization compression | Y | Y | https://www.zlib.net
|
||||
Boost | 1.82 | >= 1.85 | Components: iostreams | Y | Y | https://www.boost.org
|
||||
LLVM | 15.0.0* | 18.0.0 | Target-independent code generation | Y | Y | https://llvm.org/
|
||||
Bison | 3.7.0 | 3.7.0 | General-purpose parser generator | Y | Y | https://www.gnu.org/software/gcc
|
||||
Flex | 2.6.4 | 2.6.4 | Fast lexical analyzer generator | Y | Y | https://github.com/westes/flex
|
||||
Python | 3.10 | 3.11 | The python interpreter and libraries | Y | Y | https://www.python.org
|
||||
nanobind** | 2.0.0 | >= 2.5.0 | C++/python bindings | Y | Y | https://nanobind.readthedocs.io
|
||||
GoogleTest | 1.10 | Latest | A unit testing framework module for C++ | Y | Y | https://github.com/google/googletest
|
||||
Blosc | 1.17.0* | Latest | Recommended dependency for improved disk compression | Y | Y | https://github.com/Blosc/c-blosc/releases
|
||||
Log4cplus | 1.1.2 | Latest | An optional dependency for improved OpenVDB Logging | Y | Y | https://github.com/log4cplus/log4cplus
|
||||
libpng | - | Latest | Library for manipulating PNG images | Y | Y | http://www.libpng.org/pub/png/libpng.html
|
||||
GLFW | 3.3 | Latest | Simple API for OpenGL development | Y | Y | https://www.glfw.org
|
||||
OpenGL | 3.2 | Latest | Environment for developing portable graphics applications | Y | Y | https://www.opengl.org
|
||||
GLEW | 1.0.0 | Latest | A cross-platform OpenGL extension loading library. | Y | Y | http://glew.sourceforge.net
|
||||
CUDA | - | Latest | Parallel computing platform for graphical processing units. | Y | N | https://developer.nvidia.com/cuda-downloads
|
||||
Doxygen | 1.8.8 | 1.8.11 | Documentation generation from C++ | Y | Y | http://www.doxygen.nl
|
||||
Package | Minimum | Recommended | Description | apt-get | Homebrew | Source
|
||||
--------------- | ------- | ----------- | ----------------------------------------------------------------- | ------- | -------- | ------
|
||||
CMake | 3.24 | Latest | Cross-platform family of tools designed to help build software | Y | Y | https://cmake.org
|
||||
GCC | 11.2.1 | 11.2.1 | C++ 17 Compiler: The GNU Compiler Collection | Y | Y | https://www.gnu.org/software/gcc
|
||||
Clang | 5.0 | Latest | C++ 17 Compiler: A C language family frontend for LLVM | Y | Y | https://clang.llvm.org
|
||||
Intel ICC | 19 | Latest | C++ 17 Compiler: Intels C++ Compiler | Y | Y | https://software.intel.com/en-us/c-compilers
|
||||
MSVC | 19.30 | 19.30 | C++ 17 Compiler: Microsoft Visual C++ Compiler | Y | Y | https://visualstudio.microsoft.com/vs
|
||||
Imath | 3.2 | >= 3.3 | Half precision floating points | Y | Y | http://www.openexr.com
|
||||
OpenEXR | 3.2 | >= 3.3 | EXR serialization support | Y | Y | http://www.openexr.com
|
||||
TBB | 2020.3 | >= 2021 | Threading Building Blocks - template library for task parallelism | Y | Y | https://www.threadingbuildingblocks.org
|
||||
ZLIB | 1.2.7 | Latest | Compression library for disk serialization compression | Y | Y | https://www.zlib.net
|
||||
Boost | 1.82 | >= 1.85 | Components: iostreams | Y | Y | https://www.boost.org
|
||||
VCL | 2.02.0 | >= 2.02.0 | x86 intrinsics and SIMD class wrappers and | Y | Y | https://github.com/vectorclass/version2
|
||||
LLVM | 15.0.0* | 18.0.0 | Target-independent code generation | Y | Y | https://llvm.org/
|
||||
Bison | 3.7.0 | 3.7.0 | General-purpose parser generator | Y | Y | https://www.gnu.org/software/gcc
|
||||
Flex | 2.6.4 | 2.6.4 | Fast lexical analyzer generator | Y | Y | https://github.com/westes/flex
|
||||
Python | 3.10 | 3.11 | The python interpreter and libraries | Y | Y | https://www.python.org
|
||||
nanobind** | 2.0.0 | >= 2.5.0 | C++/python bindings | Y | Y | https://nanobind.readthedocs.io
|
||||
GoogleTest | 1.10 | Latest | A unit testing framework module for C++ | Y | Y | https://github.com/google/googletest
|
||||
Blosc | 1.17.0* | Latest | Recommended dependency for improved disk compression | Y | Y | https://github.com/Blosc/c-blosc/releases
|
||||
Log4cplus | 1.1.2 | Latest | An optional dependency for improved OpenVDB Logging | Y | Y | https://github.com/log4cplus/log4cplus
|
||||
libpng | - | Latest | Library for manipulating PNG images | Y | Y | http://www.libpng.org/pub/png/libpng.html
|
||||
GLFW | 3.3 | Latest | Simple API for OpenGL development | Y | Y | https://www.glfw.org
|
||||
OpenGL | 3.2 | Latest | Environment for developing portable graphics applications | Y | Y | https://www.opengl.org
|
||||
GLEW | 1.0.0 | Latest | A cross-platform OpenGL extension loading library. | Y | Y | http://glew.sourceforge.net
|
||||
CUDA | - | Latest | Parallel computing platform for graphical processing units. | Y | N | https://developer.nvidia.com/cuda-downloads
|
||||
Doxygen | 1.8.8 | <= 1.14.0 | Documentation generation from C++ | Y | Y | http://www.doxygen.nl
|
||||
Doxygen Awesome | 2.0.0 | >= 2.0.0 | Documentation generation from C++ | Y | Y | https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
- @b * See the note in [known issues](@ref depKnownIssues) regarding supported blosc/llvm versions.
|
||||
- @b ** See the note in [known issues](@ref depKnownIssues) regarding supported nanobind build issues.
|
||||
@@ -100,15 +110,13 @@ Doxygen | 1.8.8 | 1.8.11 | Documentation generation from C++
|
||||
Additionally these versions of blosc may generally not be compatible with OpenVDB
|
||||
itself. Avoid using these versions and upgrade to the new minimum of 1.17.0 where
|
||||
possible.
|
||||
|
||||
- All versions from 1.11.0 through to, but excluding, 1.14.0.
|
||||
- Versions 1.16.0 and 1.16.1.
|
||||
|
||||
- All versions from 1.11.0 through to, but excluding, 1.14.0.
|
||||
- Versions 1.16.0 and 1.16.1.
|
||||
.
|
||||
For more information see:
|
||||
|
||||
- https://www.blosc.org/posts/new-forward-compat-policy/
|
||||
- https://github.com/Blosc/c-blosc/issues/260
|
||||
- https://github.com/Blosc/c-blosc/issues/261
|
||||
- https://www.blosc.org/posts/new-forward-compat-policy/
|
||||
- https://github.com/Blosc/c-blosc/issues/260
|
||||
- https://github.com/Blosc/c-blosc/issues/261
|
||||
|
||||
- @b nanobind: Nanobind requires a fast-hash-map implementation library called
|
||||
tsl-robin-map. If you're installing nanobind through homebrew, this library
|
||||
|
||||
+18
-29
@@ -22,44 +22,30 @@ primarily by
|
||||
- Mihai Aldén
|
||||
- David Hill
|
||||
|
||||
See the @subpage changes "Release Notes" for what’s new in this version
|
||||
of @b OpenVDB.
|
||||
|
||||
See the @subpage dependencies "Dependencies" page for @b OpenVDB requirements.
|
||||
|
||||
See @subpage build "Building OpenVDB" for information on building @b OpenVDB.
|
||||
|
||||
See the @subpage overview "Overview" for an introduction to the library.
|
||||
|
||||
See @subpage transformsAndMaps "Transforms and Maps" for more
|
||||
discussion of the transforms used in @b OpenVDB.
|
||||
|
||||
See the @subpage faq "FAQ" for frequently asked questions about @b OpenVDB.
|
||||
|
||||
See the @subpage codeExamples "Cookbook" to get started using @b OpenVDB.
|
||||
|
||||
See @subpage points "OpenVDB Points" to store point data using @b OpenVDB.
|
||||
|
||||
See @subpage python "Using OpenVDB in Python" to get started with the
|
||||
@b OpenVDB Python module.
|
||||
|
||||
@par Quick Links
|
||||
- @subpage changes "Release Notes"
|
||||
- @subpage dependencies "Dependencies"
|
||||
- @subpage build "Building OpenVDB"
|
||||
- @subpage overview "Overview"
|
||||
- @ref transformsAndMaps "Transforms and Maps"
|
||||
- @ref codeExamples "Cookbook"
|
||||
- @ref faq "FAQ"
|
||||
- @subpage python "OpenVDB Python"
|
||||
- @subpage points "OpenVDB Points"
|
||||
@if OPENVDB_AX
|
||||
See @subpage openvdbax "OpenVDB AX" for the AX documentation.
|
||||
- @subpage openvdbax "OpenVDB AX"
|
||||
@endif
|
||||
|
||||
@if OPENVDB_HOUDINI
|
||||
See the @subpage houdini "Houdini Cookbook" for help on implementing Houdini nodes.
|
||||
- @subpage houdini "Houdini Cookbook"
|
||||
@endif
|
||||
|
||||
@if NANOVDB
|
||||
See @subpage NanoVDB_MainPage "NanoVDB" for the NanoVDB documentation.
|
||||
- @subpage NanoVDB_MainPage "NanoVDB"
|
||||
@endif
|
||||
- <a href="../code_coverage/index.html">Code Coverage</a>
|
||||
|
||||
Contributors, please familiarize yourselves with our
|
||||
@subpage codingStyle "coding standards".
|
||||
|
||||
<a href="../code_coverage/index.html">Code Coverage</a>
|
||||
|
||||
|
||||
@page overview OpenVDB Overview
|
||||
|
||||
@@ -81,6 +67,9 @@ Contributors, please familiarize yourselves with our
|
||||
- @ref subsecNodeIter
|
||||
- @ref subsecValueAccessor
|
||||
- @ref subsecTraversal
|
||||
- @subpage transformsAndMaps "Transforms and Maps"
|
||||
|
||||
|
||||
<!-- - @ref secRegistry -->
|
||||
|
||||
|
||||
@@ -320,7 +309,7 @@ as level sets.
|
||||
(The lower-level @vdblink::tools::GridResampler GridResampler@endlink does not,
|
||||
but it is optimized for level set grids in that it transforms only voxels
|
||||
in the narrow band and relies on
|
||||
@vdblink::Grid::signedFloodFill() signed flood fill@endlink to reconstruct
|
||||
@vdblink::tools::signedFloodFill() signed flood fill@endlink to reconstruct
|
||||
the inside and outside regions.)
|
||||
Other tools whose behavior is affected by the grid class include the
|
||||
@vdblink::tools::divergence() divergence@endlink operator (which has an
|
||||
|
||||
+1
-1
@@ -196,7 +196,7 @@ For multithreaded insertion operations we typically assign a separate grid
|
||||
to each thread and then merge the grids as threads terminate.
|
||||
This technique works remarkably well: because OpenVDB
|
||||
is sparse and hierarchical, merging is very efficient.
|
||||
For more details, please consult the @subpage codeExamples and
|
||||
For more details, please consult the @ref codeExamples and
|
||||
Appendix B in the <i>Transactions on Graphics</i> paper.
|
||||
|
||||
@section sMaxRes Is OpenVDB unbounded?
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<!-- HTML header for doxygen 1.15.0-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="$langISO">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
|
||||
<meta name="generator" content="Doxygen $doxygenversion"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_ICON-->
|
||||
<link rel="icon" href="$relpath^$projecticon" type="image/x-icon" />
|
||||
<!--END PROJECT_ICON-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<script type="text/javascript">var page_layout=1;</script>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||
<!--BEGIN COPY_CLIPBOARD-->
|
||||
<script type="text/javascript" src="$relpath^clipboard.js"></script>
|
||||
<!--END COPY_CLIPBOARD-->
|
||||
$treeview
|
||||
$search
|
||||
$mathjax
|
||||
$darkmode
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||
$extrastylesheet
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
|
||||
<script type="text/javascript">
|
||||
DoxygenAwesomeDarkModeToggle.init()
|
||||
</script>
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
|
||||
<script type="text/javascript">
|
||||
DoxygenAwesomeFragmentCopyButton.init()
|
||||
</script>
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-paragraph-link.js"></script>
|
||||
<script type="text/javascript">
|
||||
DoxygenAwesomeParagraphLink.init()
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<div id="side-nav" class="ui-resizable side-nav-resizable"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
<!--END FULL_SIDEBAR-->
|
||||
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr id="projectrow">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"$logosize/></td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<td id="projectalign">
|
||||
<div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber"> $projectnumber</span><!--END PROJECT_NUMBER-->
|
||||
</div>
|
||||
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
|
||||
</td>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<td>
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<!--END PROJECT_BRIEF-->
|
||||
<!--END !PROJECT_NAME-->
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN !FULL_SIDEBAR-->
|
||||
<td>$searchbox</td>
|
||||
<!--END !FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</tr>
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<tr><td colspan="2">$searchbox</td></tr>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
+1
-1
@@ -26,7 +26,7 @@
|
||||
|
||||
* **Does NanoVDB depend on and require CUDA?**
|
||||
|
||||
No, NanoVDB is by design agnostic and works with most graphics APIs, e.g. OpenGL, OpenCL, OptiX, CUDA, HLSL, GLSL, DirectX, and even the CPU! The data structure itself (defined in [NanoVDB.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/NanoVDB.h) and [PNanoVDB.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/PNanoVDB.h)) only depends on C++11 or C99. However, some of the auxiliary math classes are optionally optimized with intrinsic CUDA calls (e.g. fma in Vec3) and some of the [Allocator](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/util/CudaDeviceBuffer.h) uses CUDA for explicit memory management. It should be straightforward to replace (or remove) these CUDA dependencies if you so desire.
|
||||
No, NanoVDB is by design agnostic and works with most graphics APIs, e.g. OpenGL, OpenCL, OptiX, CUDA, HLSL, GLSL, DirectX, and even the CPU! The data structure itself (defined in [NanoVDB.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/NanoVDB.h) and [PNanoVDB.h](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/PNanoVDB.h)) only depends on C++11 or C99. However, some of the auxiliary math classes are optionally optimized with intrinsic CUDA calls (e.g. fma in Vec3) and the [DeviceBuffer](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/cuda/DeviceBuffer.h) allocator uses CUDA for explicit memory management. It should be straightforward to replace (or remove) these CUDA dependencies if you so desire.
|
||||
|
||||
* **How does the data structure of NanoVDB compare to that of OpenVDB?**
|
||||
|
||||
|
||||
+25
-141
@@ -1,154 +1,38 @@
|
||||
# NanoVDB Hello World Examples {#NanoVDB_HelloWorld}
|
||||
|
||||
[The first example](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/examples/ex_openvdb_to_nanovdb_accessor/openvdb_to_nanovdb_accessor.cc) shows how to convert an OpenVDB level set sphere into a NanoVDB level set, use accessors to print out multiple values from both grids, and save the NanoVDB grid to file. Note that **this example depends on both OpenVDB and NanoVDB**.
|
||||
The example snippets below are pulled directly from compiled example source
|
||||
files via Doxygen's `\snippet` mechanism, so the code shown here is always
|
||||
in sync with what is built and tested in CI. Enable
|
||||
`-DNANOVDB_BUILD_EXAMPLES=ON` to build them locally.
|
||||
|
||||
```cpp
|
||||
#include <openvdb/tools/LevelSetSphere.h> // replace with your own dependencies for generating the OpenVDB grid
|
||||
#include <nanovdb/util/CreateNanoGrid.h> // converter from OpenVDB to NanoVDB (includes NanoVDB.h and GridManager.h)
|
||||
#include <nanovdb/util/IO.h>
|
||||
## Convert an OpenVDB grid to NanoVDB and use accessors
|
||||
|
||||
// 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.
|
||||
// Note, main depends on BOTH OpenVDB and NanoVDB.
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
// Create an OpenVDB grid (here a level set surface but replace this with your own code)
|
||||
auto srcGrid = openvdb::tools::createLevelSetSphere<openvdb::FloatGrid>(100.0f, openvdb::Vec3f(0.0f), 1.0f);
|
||||
[The first example](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/examples/ex_openvdb_to_nanovdb_accessor/openvdb_to_nanovdb_accessor.cc)
|
||||
shows how to convert an OpenVDB level set sphere into a NanoVDB level set, use
|
||||
accessors to print out multiple values from both grids, and save the NanoVDB
|
||||
grid to file. Note that **this example depends on both OpenVDB and NanoVDB**.
|
||||
|
||||
// Convert the OpenVDB grid, srcGrid, into a NanoVDB grid handle.
|
||||
auto handle = nanovdb::createNanoGrid(*srcGrid);
|
||||
\snippet nanovdb/nanovdb/examples/ex_openvdb_to_nanovdb_accessor/openvdb_to_nanovdb_accessor.cc openvdb_to_nanovdb_accessor
|
||||
|
||||
// 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>();
|
||||
## Read a NanoVDB grid from a file (host only)
|
||||
|
||||
if (!dstGrid)
|
||||
throw std::runtime_error("GridHandle does not contain a grid with value type float");
|
||||
[The second example](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor/read_nanovdb_sphere_accessor.cc)
|
||||
reads a NanoVDB grid from a file (the one saved in the previous example) and
|
||||
prints out multiple values. Note that **this example does not depend on
|
||||
OpenVDB (nor CUDA), only NanoVDB**.
|
||||
|
||||
// Get accessors for the two grids. Note that accessors only accelerate repeated access!
|
||||
auto dstAcc = dstGrid->getAccessor();
|
||||
auto srcAcc = srcGrid->getAccessor();
|
||||
\snippet nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor/read_nanovdb_sphere_accessor.cc read_nanovdb_sphere_accessor
|
||||
|
||||
// Access and print out a cross-section of the narrow-band level set from the two grids
|
||||
for (int i = 97; i < 104; ++i) {
|
||||
printf("(%3i,0,0) OpenVDB cpu: % -4.2f, NanoVDB cpu: % -4.2f\n", i, srcAcc.getValue(openvdb::Coord(i, 0, 0)), dstAcc.getValue(nanovdb::Coord(i, 0, 0)));
|
||||
}
|
||||
## Read a NanoVDB grid and access it on the CPU and GPU
|
||||
|
||||
nanovdb::io::writeGrid("data/sphere.nvdb", handle); // Write the NanoVDB grid to file and throw if writing fails
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "An exception occurred: \"" << e.what() << "\"" << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
[The third example](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor_cuda/read_nanovdb_sphere_accessor_cuda.cu)
|
||||
reads a NanoVDB grid from a file (the one saved in the first example) and
|
||||
prints out multiple values on both the CPU and GPU. Note that **this example
|
||||
does NOT depend on OpenVDB, only NanoVDB and CUDA**.
|
||||
|
||||
[The second example](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor/read_nanovdb_sphere_accessor.cc) reads a NanoVDB grid from a file (the one saved in the previous example) and prints out multiple values. Note that **this example does not depend on OpenVDB (nor CUDA), only NanoVDB**.
|
||||
\snippet nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor_cuda/read_nanovdb_sphere_accessor_cuda.cu read_nanovdb_sphere_accessor_cuda
|
||||
|
||||
```cpp
|
||||
#include <nanovdb/util/IO.h> // this is required to read (and write) NanoVDB files on the host
|
||||
NVCC requires the CUDA kernel to be defined in
|
||||
[a separate .cu file](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor_cuda/read_nanovdb_sphere_accessor_cuda_kernel.cu):
|
||||
|
||||
/// @brief Read a NanoVDB grid from a file and print out multiple values.
|
||||
///
|
||||
/// @note Note This example does NOT depend on OpenVDB (nor CUDA), only NanoVDB.
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
auto handle = nanovdb::io::readGrid("data/sphere.nvdb"); // reads first grid from file
|
||||
|
||||
auto* grid = handle.grid<float>(); // get a (raw) pointer to a NanoVDB grid of value type float
|
||||
|
||||
if (!grid)
|
||||
throw std::runtime_error("File did not contain a grid with value type float");
|
||||
|
||||
auto acc = grid->getAccessor(); // create an accessor for fast access to multiple values
|
||||
for (int i = 97; i < 104; ++i) {
|
||||
printf("(%3i,0,0) NanoVDB cpu: % -4.2f\n", i, acc.getValue(nanovdb::Coord(i, 0, 0)));
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "An exception occurred: \"" << e.what() << "\"" << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
[The third example](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor_cuda/read_nanovdb_sphere_accessor_cuda.cc) reads a NanoVDB grid from a file (the one saved in the first example) and prints out multiple values on both the CPU and GPU. Note that **this example does NOT depend on OpenVDB, only NanoVDB and CUDA**.
|
||||
|
||||
```cpp
|
||||
#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
|
||||
|
||||
extern "C" void launch_kernels(const nanovdb::NanoGrid<float>*,
|
||||
const nanovdb::NanoGrid<float>*,
|
||||
cudaStream_t stream);
|
||||
|
||||
/// @brief Read a NanoVDB grid from a file and print out multiple values on both the cpu and gpu.
|
||||
///
|
||||
/// @note Note This example does NOT depend on OpenVDB, only NanoVDB and CUDA.
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
// returns a GridHandle using CUDA for memory management.
|
||||
auto handle = nanovdb::io::readGrid<nanovdb::CudaDeviceBuffer>("data/sphere.nvdb");
|
||||
|
||||
cudaStream_t stream; // Create a CUDA stream to allow for asynchronous copy of pinned CUDA memory.
|
||||
cudaStreamCreate(&stream);
|
||||
|
||||
handle.deviceUpload(stream, false); // Copy the NanoVDB grid to the GPU asynchronously
|
||||
|
||||
auto* cpuGrid = handle.grid<float>(); // get a (raw) pointer to a NanoVDB grid of value type float on the CPU
|
||||
auto* deviceGrid = handle.deviceGrid<float>(); // get a (raw) pointer to a NanoVDB grid of value type float on the GPU
|
||||
|
||||
if (!deviceGrid || !cpuGrid)
|
||||
throw std::runtime_error("GridHandle did not contain a grid with value type float");
|
||||
|
||||
launch_kernels(deviceGrid, cpuGrid, stream); // Call a host method to print a grid values on both the CPU and GPU
|
||||
|
||||
cudaStreamDestroy(stream); // Destroy the CUDA stream
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "An exception occurred: \"" << e.what() << "\"" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
NVCC requires the CUDA kernel to be defined in [a separate .cu file](https://github.com/AcademySoftwareFoundation/openvdb/blob/master/nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor_cuda/read_nanovdb_sphere_accessor_cuda.cu):
|
||||
|
||||
```cpp
|
||||
#include <nanovdb/NanoVDB.h> // this defined the core tree data structure of NanoVDB accessible on both the host and device
|
||||
#include <stdio.h> // for printf
|
||||
|
||||
// This is called by the host only
|
||||
void cpu_kernel(const nanovdb::NanoGrid<float>* cpuGrid)
|
||||
{
|
||||
auto cpuAcc = cpuGrid->getAccessor();
|
||||
for (int i = 97; i < 104; ++i) {
|
||||
printf("(%3i,0,0) NanoVDB cpu: % -4.2f\n", i, cpuAcc.getValue(nanovdb::Coord(i, 0, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
// This is called by the device only
|
||||
__global__ void gpu_kernel(const nanovdb::NanoGrid<float>* deviceGrid)
|
||||
{
|
||||
if (threadIdx.x > 6)
|
||||
return;
|
||||
int i = 97 + threadIdx.x;
|
||||
auto gpuAcc = deviceGrid->getAccessor();
|
||||
printf("(%3i,0,0) NanoVDB gpu: % -4.2f\n", i, gpuAcc.getValue(nanovdb::Coord(i, 0, 0)));
|
||||
}
|
||||
|
||||
// This is called by the client code on the host
|
||||
extern "C" void launch_kernels(const nanovdb::NanoGrid<float>* deviceGrid,
|
||||
const nanovdb::NanoGrid<float>* cpuGrid,
|
||||
cudaStream_t stream)
|
||||
{
|
||||
// Launch the device kernel asynchronously
|
||||
gpu_kernel<<<1, 64, 0, stream>>>(deviceGrid);
|
||||
|
||||
// Launch the host "kernel" (synchronously)
|
||||
cpu_kernel(cpuGrid);
|
||||
}
|
||||
```
|
||||
\snippet nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor_cuda/read_nanovdb_sphere_accessor_cuda_kernel.cu read_nanovdb_sphere_accessor_cuda_kernel
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
* [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.
|
||||
|
||||
```bash
|
||||
foo@bar:~$ tree
|
||||
```
|
||||
$ tree
|
||||
.
|
||||
├── CMakeLists.txt
|
||||
├── cmd
|
||||
@@ -175,4 +175,5 @@ foo@bar:~$ tree
|
||||
├── SampleFromVoxels.h
|
||||
├── Stencils.h
|
||||
├── Timer.h
|
||||
└── Util.h
|
||||
└── Util.h
|
||||
```
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# TEACHME
|
||||
|
||||
This directory contains interactive lesson documents designed to be loaded by
|
||||
an LLM coding agent (Claude Code, Cursor, or similar) to teach a user NanoVDB
|
||||
interactively.
|
||||
|
||||
The lesson teaches how to *use* NanoVDB to read, build, transform, and render
|
||||
sparse volumetric data on CPU and GPU. It is a single self-contained markdown
|
||||
file the agent reads at the start of a session.
|
||||
|
||||
## Getting started
|
||||
|
||||
The easiest way to use this lesson is with [Claude Code](https://docs.anthropic.com/en/docs/claude-code)
|
||||
(CLI or IDE extension) from the root of an OpenVDB checkout. The agent can
|
||||
read the lesson files *and* the NanoVDB source code, so it can verify API
|
||||
details and help you debug exercises in real time.
|
||||
|
||||
**Example prompts:**
|
||||
|
||||
To learn how to use NanoVDB:
|
||||
|
||||
```
|
||||
Read doc/nanovdb/TEACHME/nanovdb_user_lesson.md and teach me how to use NanoVDB.
|
||||
```
|
||||
|
||||
You can also give the agent context about your background so it can tailor
|
||||
the lesson:
|
||||
|
||||
```
|
||||
Read doc/nanovdb/TEACHME/nanovdb_user_lesson.md and teach me NanoVDB. I have
|
||||
C++ and CUDA experience but I've never used OpenVDB or any sparse 3D data
|
||||
structure before.
|
||||
```
|
||||
|
||||
Keep the matching cheatsheet (`*_cheatsheet.md`) open in your editor while
|
||||
working through exercises — it's a quick reference for the APIs and
|
||||
invariants covered in the lesson.
|
||||
|
||||
## How it works
|
||||
|
||||
The lesson is a self-contained markdown file that serves as both a
|
||||
curriculum and an instructor prompt. The LLM acts as an interactive
|
||||
instructor: teaching concepts module by module, quizzing the student, and
|
||||
adapting to their responses.
|
||||
|
||||
The lesson includes:
|
||||
|
||||
- Teacher instructions (persona, pacing, scope)
|
||||
- Module-by-module curriculum with embedded concepts and code examples
|
||||
- Quiz questions and an answer key
|
||||
- Exercises with progressive difficulty
|
||||
- A capstone project
|
||||
- A reference table at the end
|
||||
|
||||
## Available lessons
|
||||
|
||||
| Lesson | Cheat sheet | Covers |
|
||||
|---|---|---|
|
||||
| [nanovdb_user_lesson.md](nanovdb_user_lesson.md) | [nanovdb_user_cheatsheet.md](nanovdb_user_cheatsheet.md) | Reading `.nvdb` files, `ReadAccessor`, `NodeManager`, math + sampling, HDDA ray-march, GPU kernels with `Grid`, GPU topology builders (`PointsToGrid`, `MeshToGrid`, `DilateGrid`, etc.), `IndexGrid` + `VoxelBlockManager`, OpenVDB ↔ NanoVDB conversion |
|
||||
|
||||
## Not covered
|
||||
|
||||
- The OpenVDB side (the dynamic, mutable tree used at film/sim time) — this
|
||||
lesson treats OpenVDB only at the conversion boundary.
|
||||
- Houdini / Maya integration — outside the scope.
|
||||
- Gaussian splatting and fVDB-specific topics — see the fVDB repo's own
|
||||
TEACHME at <https://github.com/openvdb/fvdb-core/tree/main/docs/TEACHME>.
|
||||
@@ -0,0 +1,6 @@
|
||||
# Build + editor + render artifacts (not source)
|
||||
build/
|
||||
compile_commands.json
|
||||
.clangd
|
||||
*.ppm
|
||||
sphere.png
|
||||
@@ -0,0 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(nanovdb_capstone LANGUAGES CXX CUDA)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CUDA_STANDARD 17)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # emit build/compile_commands.json
|
||||
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
|
||||
set(CMAKE_CUDA_ARCHITECTURES 89) # RTX 6000 Ada; override with -DCMAKE_CUDA_ARCHITECTURES=...
|
||||
endif()
|
||||
|
||||
# NanoVDB headers pull in CUDA lambdas / relaxed-constexpr paths.
|
||||
string(APPEND CMAKE_CUDA_FLAGS " --extended-lambda --expt-relaxed-constexpr")
|
||||
|
||||
# This file is at doc/nanovdb/TEACHME/capstone/CMakeLists.txt; the NanoVDB
|
||||
# include root (the dir containing the nanovdb/ header subdir) is the repo's
|
||||
# `nanovdb/` subdir — repo root is four dirs up.
|
||||
get_filename_component(_teachme "${CMAKE_CURRENT_SOURCE_DIR}" DIRECTORY) # TEACHME
|
||||
get_filename_component(_docnv "${_teachme}" DIRECTORY) # doc/nanovdb
|
||||
get_filename_component(_doc "${_docnv}" DIRECTORY) # doc
|
||||
get_filename_component(REPO_ROOT "${_doc}" DIRECTORY) # repo root
|
||||
set(NANOVDB_INCLUDE_ROOT "${REPO_ROOT}/nanovdb")
|
||||
message(STATUS "NanoVDB include root: ${NANOVDB_INCLUDE_ROOT}")
|
||||
|
||||
add_executable(raymarch raymarch.cu)
|
||||
target_include_directories(raymarch PRIVATE "${NANOVDB_INCLUDE_ROOT}")
|
||||
# Binary lands in the build dir (conventional). raymarch writes sphere.ppm to
|
||||
# the current working directory, so run it from wherever you want the output.
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Convert a binary P6 PPM to PNG using only the Python standard library
|
||||
(the container has no ImageMagick / PIL). Usage: python3 ppm2png.py in.ppm out.png"""
|
||||
import sys, struct, zlib, binascii
|
||||
|
||||
def main(src, dst):
|
||||
data = open(src, "rb").read()
|
||||
i = 0
|
||||
def tok(i):
|
||||
n = len(data)
|
||||
while i < n and data[i] in b" \t\n\r": i += 1
|
||||
s = i
|
||||
while i < n and data[i] not in b" \t\n\r": i += 1
|
||||
if s == i: raise ValueError("truncated or non-binary PPM")
|
||||
return data[s:i], i
|
||||
magic, i = tok(i)
|
||||
assert magic == b"P6", "not a binary PPM"
|
||||
w, i = tok(i); h, i = tok(i); mx, i = tok(i)
|
||||
i += 1 # single whitespace after maxval
|
||||
W, H = int(w), int(h)
|
||||
px = data[i:i + W * H * 3]
|
||||
|
||||
def chunk(typ, payload):
|
||||
return (struct.pack(">I", len(payload)) + typ + payload +
|
||||
struct.pack(">I", binascii.crc32(typ + payload) & 0xffffffff))
|
||||
|
||||
raw = bytearray()
|
||||
for y in range(H):
|
||||
raw.append(0) # filter: none
|
||||
raw += px[y * W * 3:(y + 1) * W * 3]
|
||||
png = (b"\x89PNG\r\n\x1a\n"
|
||||
+ chunk(b"IHDR", struct.pack(">IIBBBBB", W, H, 8, 2, 0, 0, 0))
|
||||
+ chunk(b"IDAT", zlib.compress(bytes(raw), 9))
|
||||
+ chunk(b"IEND", b""))
|
||||
open(dst, "wb").write(png)
|
||||
print(f"wrote {dst} ({W}x{H})")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1], sys.argv[2])
|
||||
@@ -0,0 +1,111 @@
|
||||
// Capstone scaffold: GPU level-set ray-march renderer.
|
||||
//
|
||||
// The host plumbing is DONE (build SDF, upload to device, launch, read back,
|
||||
// write PPM) and the light direction + ambient level are handed to the kernel
|
||||
// as parameters. Your job is the kernel body — the per-pixel ray-march + shade.
|
||||
// As shipped it compiles and runs but only writes a placeholder gradient.
|
||||
//
|
||||
// Build: cmake -S . -B build && cmake --build build
|
||||
// Run: ./build/raymarch [ambient] [lx ly lz] (writes sphere.ppm)
|
||||
// View: python3 ppm2png.py sphere.ppm sphere.png (then open sphere.png)
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/cuda/GridHandle.cuh>
|
||||
#include <nanovdb/math/Ray.h>
|
||||
#include <nanovdb/math/HDDA.h> // nanovdb::math::ZeroCrossing
|
||||
#include <nanovdb/math/Stencils.h> // nanovdb::math::GradStencil
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
using GridT = nanovdb::NanoGrid<float>;
|
||||
|
||||
__global__ void render(const GridT* grid, unsigned char* img, int W, int H,
|
||||
nanovdb::Vec3f lightDir, float ambient)
|
||||
{
|
||||
const int x = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const int y = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
if (x >= W || y >= H) return;
|
||||
const int pid = (y * W + x) * 3;
|
||||
|
||||
// ===================== YOUR WORK STARTS HERE =====================
|
||||
// Placeholder so it compiles & runs: a horizontal grey gradient.
|
||||
unsigned char shade = (unsigned char)(255.f * x / W);
|
||||
|
||||
// The aesthetic inputs are given to you as parameters: `lightDir` and
|
||||
// `ambient` (set in main, overridable on the command line). You write the
|
||||
// ray-march and the shading math. Signatures are in the cheat sheet.
|
||||
//
|
||||
// 1. Camera ray (WORLD space). Pinhole: an eye point and, per pixel, a
|
||||
// direction through the image plane. You decide the math — remember
|
||||
// aspect ratio and that image y grows downward. Wrap it in a Ray.
|
||||
//
|
||||
// 2. The SDF lives in INDEX space, so convert your world ray to index
|
||||
// space before you trace it. (Ray has a method for this.)
|
||||
//
|
||||
// 3. Find where the ray first crosses the surface (the SDF sign change).
|
||||
// Module 5 gave you a helper in math/HDDA.h that drives an HDDA and
|
||||
// reports the hit voxel, the value, and the ray parameter.
|
||||
//
|
||||
// 4. On a hit, the surface normal is the (normalized) SDF gradient at the
|
||||
// hit voxel. Module 5's gradient stencil computes it. (Watch its
|
||||
// template parameter.)
|
||||
//
|
||||
// 5. Shade: a Lambertian intensity from the normal and the provided
|
||||
// `lightDir`, lifted off the floor by `ambient`; write it as greyscale.
|
||||
// On a MISS, leave the background.
|
||||
// ====================== YOUR WORK ENDS HERE ======================
|
||||
|
||||
img[pid] = shade; img[pid + 1] = shade; img[pid + 2] = shade;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Shading parameters passed into the kernel. Defaults, overridable on the
|
||||
// command line: ./raymarch [ambient] [lx ly lz]
|
||||
float ambient = 0.15f;
|
||||
nanovdb::Vec3f lightDir(-0.577f, 0.577f, -0.577f); // (-1,1,-1) normalized
|
||||
if (argc >= 2) ambient = (float)std::atof(argv[1]);
|
||||
if (argc >= 5) lightDir = nanovdb::Vec3f((float)std::atof(argv[2]),
|
||||
(float)std::atof(argv[3]),
|
||||
(float)std::atof(argv[4]));
|
||||
lightDir.normalize();
|
||||
|
||||
// Build a level-set sphere SDF on the host (radius 100, voxel size 1).
|
||||
auto handle = nanovdb::tools::createLevelSetSphere<float>(
|
||||
/*radius=*/100.0, /*center=*/nanovdb::Vec3d(0.0),
|
||||
/*voxelSize=*/1.0, /*halfWidth=*/3.0);
|
||||
|
||||
// Move to the device. copy<DeviceBuffer>() fills the HOST side of the dual
|
||||
// buffer; deviceUpload() pushes it to the GPU — skip it and deviceGrid()
|
||||
// returns nullptr.
|
||||
auto devHandle = handle.copy<nanovdb::cuda::DeviceBuffer>();
|
||||
devHandle.deviceUpload();
|
||||
const GridT* dGrid = devHandle.deviceGrid<float>();
|
||||
if (!dGrid) { std::printf("no device grid\n"); return 1; }
|
||||
|
||||
const int W = 512, H = 512;
|
||||
unsigned char* dImg = nullptr;
|
||||
cudaMalloc(&dImg, size_t(W) * H * 3);
|
||||
const dim3 block(16, 16), gridDim((W + 15) / 16, (H + 15) / 16);
|
||||
render<<<gridDim, block>>>(dGrid, dImg, W, H, lightDir, ambient);
|
||||
cudaDeviceSynchronize();
|
||||
if (auto e = cudaGetLastError(); e != cudaSuccess) {
|
||||
std::printf("CUDA error: %s\n", cudaGetErrorString(e));
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> img(size_t(W) * H * 3);
|
||||
cudaMemcpy(img.data(), dImg, img.size(), cudaMemcpyDeviceToHost);
|
||||
cudaFree(dImg);
|
||||
|
||||
FILE* f = std::fopen("sphere.ppm", "wb");
|
||||
std::fprintf(f, "P6\n%d %d\n255\n", W, H);
|
||||
std::fwrite(img.data(), 1, img.size(), f);
|
||||
std::fclose(f);
|
||||
std::printf("wrote sphere.ppm (%dx%d)\n", W, H);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// Capstone: GPU level-set ray-march renderer.
|
||||
// Build an SDF sphere -> move to device -> one thread per pixel ->
|
||||
// ZeroCrossing to find the surface -> Lambertian shade via SDF gradient
|
||||
// normal -> write PPM.
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/cuda/GridHandle.cuh>
|
||||
#include <nanovdb/math/Ray.h>
|
||||
#include <nanovdb/math/HDDA.h> // ZeroCrossing lives here
|
||||
#include <nanovdb/math/Stencils.h> // GradStencil
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
using GridT = nanovdb::NanoGrid<float>;
|
||||
|
||||
__global__ void render(const GridT* grid, unsigned char* img, int W, int H,
|
||||
nanovdb::Vec3f lightDir, float ambient)
|
||||
{
|
||||
const int x = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const int y = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
if (x >= W || y >= H) return;
|
||||
const int pid = (y * W + x) * 3;
|
||||
|
||||
// --- pinhole camera in WORLD space ---
|
||||
const nanovdb::Vec3f eye(0.f, 0.f, -300.f);
|
||||
const float aspect = float(W) / float(H);
|
||||
const float tanHalfFov = tanf(0.5f * 45.f * 3.14159265f / 180.f);
|
||||
const float u = (2.f * ((x + 0.5f) / W) - 1.f) * tanHalfFov * aspect;
|
||||
const float v = (1.f - 2.f * ((y + 0.5f) / H)) * tanHalfFov; // flip y
|
||||
nanovdb::Vec3f dir(u, v, 1.f);
|
||||
dir.normalize();
|
||||
|
||||
nanovdb::math::Ray<float> wRay(eye, dir);
|
||||
auto iRay = wRay.worldToIndexF(*grid); // ray in index space
|
||||
|
||||
auto acc = grid->getAccessor();
|
||||
nanovdb::Coord ijk;
|
||||
float t = 0.f, val = 0.f;
|
||||
|
||||
unsigned char shade = 0; // background (black)
|
||||
if (nanovdb::math::ZeroCrossing(iRay, acc, ijk, val, t)) {
|
||||
nanovdb::math::GradStencil<GridT> stencil(*grid);
|
||||
stencil.moveTo(ijk);
|
||||
nanovdb::Vec3f n = stencil.gradient(); // SDF gradient = outward normal
|
||||
n.normalize();
|
||||
const float diff = fmaxf(0.f, n.dot(lightDir));
|
||||
const float I = fminf(1.f, ambient + (1.f - ambient) * diff);
|
||||
shade = (unsigned char)(255.f * I); // greyscale
|
||||
}
|
||||
img[pid] = shade; img[pid + 1] = shade; img[pid + 2] = shade;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Shading parameters (passed into the kernel). Defaults, overridable on the
|
||||
// command line: ./raymarch [ambient] [lx ly lz]
|
||||
float ambient = 0.15f;
|
||||
nanovdb::Vec3f lightDir(-0.577f, 0.577f, -0.577f); // (-1,1,-1) normalized
|
||||
if (argc >= 2) ambient = (float)std::atof(argv[1]);
|
||||
if (argc >= 5) lightDir = nanovdb::Vec3f((float)std::atof(argv[2]),
|
||||
(float)std::atof(argv[3]),
|
||||
(float)std::atof(argv[4]));
|
||||
lightDir.normalize();
|
||||
|
||||
// 1. Build a level-set sphere SDF on the host (radius 100, voxel size 1).
|
||||
auto handle = nanovdb::tools::createLevelSetSphere<float>(
|
||||
/*radius=*/100.0, /*center=*/nanovdb::Vec3d(0.0),
|
||||
/*voxelSize=*/1.0, /*halfWidth=*/3.0);
|
||||
|
||||
// 2. Move it to the device. copy<DeviceBuffer>() fills the HOST side of the
|
||||
// dual buffer; deviceUpload() pushes the bytes to the device. Only then
|
||||
// is deviceGrid() non-null.
|
||||
auto devHandle = handle.copy<nanovdb::cuda::DeviceBuffer>();
|
||||
devHandle.deviceUpload();
|
||||
const GridT* dGrid = devHandle.deviceGrid<float>();
|
||||
if (!dGrid) { std::printf("no device grid\n"); return 1; }
|
||||
|
||||
// 3. Render.
|
||||
const int W = 512, H = 512;
|
||||
unsigned char* dImg = nullptr;
|
||||
cudaMalloc(&dImg, size_t(W) * H * 3);
|
||||
const dim3 block(16, 16), grid((W + 15) / 16, (H + 15) / 16);
|
||||
render<<<grid, block>>>(dGrid, dImg, W, H, lightDir, ambient);
|
||||
cudaDeviceSynchronize();
|
||||
if (auto e = cudaGetLastError(); e != cudaSuccess) {
|
||||
std::printf("CUDA error: %s\n", cudaGetErrorString(e));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 4. Copy back and write PPM.
|
||||
std::vector<unsigned char> img(size_t(W) * H * 3);
|
||||
cudaMemcpy(img.data(), dImg, img.size(), cudaMemcpyDeviceToHost);
|
||||
cudaFree(dImg);
|
||||
|
||||
FILE* f = std::fopen("sphere.ppm", "wb");
|
||||
std::fprintf(f, "P6\n%d %d\n255\n", W, H);
|
||||
std::fwrite(img.data(), 1, img.size(), f);
|
||||
std::fclose(f);
|
||||
std::printf("wrote sphere.ppm (%dx%d)\n", W, H);
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,592 @@
|
||||
# NanoVDB User Cheat Sheet
|
||||
|
||||
A one-page reference for the APIs covered in `nanovdb_user_lesson.md`. Keep
|
||||
this open while working through exercises.
|
||||
|
||||
---
|
||||
|
||||
## In one paragraph
|
||||
|
||||
NanoVDB is a single-header, read-only, GPU-friendly sparse 3D data
|
||||
structure. The whole grid (one or more) lives in one contiguous,
|
||||
32-byte-aligned byte buffer. `cudaMemcpy` it to the device and it works
|
||||
as-is. Topology is *frozen* after construction; values can change, the set
|
||||
of active voxels cannot. Every primitive is `__hostdev__` (callable from
|
||||
both host and device with identical code).
|
||||
|
||||
---
|
||||
|
||||
## Includes & CMake
|
||||
|
||||
```cpp openvdb
|
||||
#include <nanovdb/NanoVDB.h> // grid, accessor, math
|
||||
#include <nanovdb/GridHandle.h> // owns the buffer
|
||||
#include <nanovdb/HostBuffer.h> // CPU allocator
|
||||
#include <nanovdb/NodeManager.h> // sequential iteration
|
||||
#include <nanovdb/io/IO.h> // file IO
|
||||
#include <nanovdb/math/HDDA.h> // ray-march
|
||||
#include <nanovdb/math/SampleFromVoxels.h> // sampling
|
||||
|
||||
// GPU
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/cuda/GridHandle.cuh>
|
||||
#include <nanovdb/cuda/NodeManager.cuh>
|
||||
|
||||
// Builders & operators (GPU)
|
||||
#include <nanovdb/tools/cuda/PointsToGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/MeshToGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/DilateGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/CoarsenGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/RefineGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/PruneGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/MergeGrids.cuh>
|
||||
#include <nanovdb/tools/cuda/SignedFloodFill.cuh>
|
||||
|
||||
// CPU builders & primitives
|
||||
#include <nanovdb/tools/GridBuilder.h>
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
|
||||
// IndexGrid / VBM
|
||||
#include <nanovdb/tools/VoxelBlockManager.h>
|
||||
#include <nanovdb/tools/cuda/VoxelBlockManager.cuh>
|
||||
|
||||
// Stats, validation, blind data
|
||||
#include <nanovdb/tools/GridStats.h>
|
||||
#include <nanovdb/tools/GridChecksum.h>
|
||||
#include <nanovdb/tools/GridValidator.h>
|
||||
#include <nanovdb/tools/cuda/AddBlindData.cuh>
|
||||
|
||||
// OpenVDB bridge (gated behind NANOVDB_USE_OPENVDB)
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/tools/NanoToOpenVDB.h>
|
||||
```
|
||||
|
||||
```cmake
|
||||
find_package(nanovdb CONFIG REQUIRED)
|
||||
target_link_libraries(my_app PRIVATE nanovdb::nanovdb)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build types (`NanoGrid<T>`)
|
||||
|
||||
| `T` | Stored per voxel |
|
||||
|-------------------|-------------------------------------------|
|
||||
| `float`, `double` | Scalar (SDF, density) |
|
||||
| `int32_t`, `int64_t` | Integer |
|
||||
| `Vec3f`, `Vec3d` | 3-vector (velocity, color) |
|
||||
| `bool` | Active state only |
|
||||
| `ValueIndex` | Sequential index over all voxels |
|
||||
| `ValueOnIndex` | Sequential index over **active** voxels |
|
||||
| `ValueMask` | Topology only (no per-voxel values) |
|
||||
| `Half` | 16-bit float |
|
||||
| `Fp4`/`Fp8`/`Fp16`/`FpN` | Quantized float (4/8/16 bits) |
|
||||
| `Point` | Point counts (for point grids) |
|
||||
|
||||
---
|
||||
|
||||
## File IO
|
||||
|
||||
```cpp
|
||||
namespace io = nanovdb::io;
|
||||
|
||||
auto h = io::readGrid("file.nvdb"); // first grid
|
||||
auto h2 = io::readGrid("file.nvdb", 2); // grid #2 (0-based)
|
||||
auto hN = io::readGrid("file.nvdb", "density"); // by name
|
||||
auto hAll = io::readGrid("file.nvdb", -1); // all → one handle
|
||||
auto v = io::readGrids("file.nvdb"); // vector<handle>, one per segment
|
||||
|
||||
io::writeGrid("out.nvdb", h); // single
|
||||
io::writeGrid("out.nvdb", h, io::Codec::BLOSC); // compressed
|
||||
io::writeGrids("out.nvdb", handles); // many
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Inspecting a grid
|
||||
|
||||
```cpp
|
||||
const auto* grid = handle.grid<float>(); // nullptr if T mismatch
|
||||
if (!grid) throw std::runtime_error("type mismatch");
|
||||
|
||||
grid->gridName(); // const char*
|
||||
grid->gridClass(); // GridClass enum (LevelSet, FogVolume, etc.)
|
||||
grid->gridType(); // GridType enum (Float, Vec3f, OnIndex, ...)
|
||||
grid->voxelSize(); // Vec3d
|
||||
grid->activeVoxelCount(); // uint64_t
|
||||
grid->indexBBox(); // CoordBBox
|
||||
grid->worldBBox(); // BBox<Vec3d>
|
||||
grid->tree().background(); // T
|
||||
grid->worldToIndex(p); // Vec3<T>, p is world-space
|
||||
grid->indexToWorld(p); // Vec3<T>, p is index-space (fractional)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Random access (ReadAccessor)
|
||||
|
||||
```cpp
|
||||
// One accessor per thread; not thread-safe; never copy between CPU & GPU
|
||||
auto acc = grid->getAccessor();
|
||||
float v = acc.getValue(nanovdb::Coord(1, 2, 3));
|
||||
bool on = acc.isActive(nanovdb::Coord(1, 2, 3));
|
||||
const auto* leaf = acc.getNode<nanovdb::NanoLeaf<float>>(); // cached leaf ptr, if needed
|
||||
|
||||
// probe* = value/state/leaf-existence in ONE descent (disambiguates the
|
||||
// two "got background" cases: inactive-vs-no-node)
|
||||
float pv;
|
||||
bool active = acc.probeValue(nanovdb::Coord(1,2,3), pv); // pv set; ret = state
|
||||
const nanovdb::NanoLeaf<float>* lf = acc.probeLeaf(nanovdb::Coord(1,2,3)); // nullptr if no leaf
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sequential access (NodeManager)
|
||||
|
||||
```cpp
|
||||
#include <nanovdb/NodeManager.h>
|
||||
|
||||
auto nmh = nanovdb::createNodeManager(*grid);
|
||||
const auto* mgr = nmh.mgr<float>();
|
||||
|
||||
auto leafN = mgr->leafCount();
|
||||
auto lowerN = mgr->lowerCount();
|
||||
auto upperN = mgr->upperCount();
|
||||
|
||||
for (uint64_t i = 0; i < leafN; ++i) {
|
||||
const auto& leaf = mgr->leaf(i);
|
||||
for (auto it = leaf.beginValueOn(); it; ++it) {
|
||||
float v = *it;
|
||||
auto c = it.getCoord();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
GPU. Pass the device-side `NodeManager*` to the kernel and index leaves by
|
||||
block, voxels within a leaf by thread:
|
||||
|
||||
```cpp no-compile
|
||||
#include <nanovdb/cuda/NodeManager.cuh>
|
||||
|
||||
auto dnmh = nanovdb::cuda::createNodeManager<float>(dGrid);
|
||||
const auto* dmgr = dnmh.template deviceMgr<float>(); // device pointer
|
||||
|
||||
// One block per leaf; 64 threads cover the 512 voxels in each leaf.
|
||||
perLeafKernel<<<dmgr->leafCount(), 64>>>(dmgr); // kernel defined just below
|
||||
```
|
||||
|
||||
The kernel:
|
||||
|
||||
```cpp
|
||||
__global__ void perLeafKernel(const nanovdb::NodeManager<float>* mgr)
|
||||
{
|
||||
using LeafT = nanovdb::NanoLeaf<float>;
|
||||
const uint32_t leafIdx = blockIdx.x;
|
||||
if (leafIdx >= mgr->leafCount()) return;
|
||||
|
||||
const LeafT& leaf = mgr->leaf(leafIdx);
|
||||
|
||||
// 512 voxels per leaf (LeafT::SIZE == 1<<3*LOG2DIM == 8^3).
|
||||
for (uint32_t n = threadIdx.x; n < LeafT::SIZE; n += blockDim.x) {
|
||||
if (!leaf.isActive(n)) continue;
|
||||
float v = leaf.getValue(n);
|
||||
nanovdb::Coord c = leaf.offsetToGlobalCoord(n);
|
||||
// ... do something with (v, c)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Same methods exist on `lower(i)` and `upper(i)` (internal nodes) — you'll
|
||||
mostly want `leaf(i)` since that's where actual voxel values live.
|
||||
|
||||
Indexed leaf-voxel API (callable from `__hostdev__` code):
|
||||
|
||||
| Call | Returns |
|
||||
|-------------------------------|-------------------------------------------|
|
||||
| `leaf.getValue(n)` | voxel value at offset `n` (0..511) |
|
||||
| `leaf.isActive(n)` | active state at offset `n` |
|
||||
| `leaf.offsetToGlobalCoord(n)` | the global `Coord` for offset `n` |
|
||||
| `leaf.origin()` | the leaf's `(8,8,8)`-aligned origin Coord |
|
||||
| `LeafT::SIZE` | `512`, voxels per leaf |
|
||||
|
||||
---
|
||||
|
||||
## Math primitives (all `__hostdev__`)
|
||||
|
||||
```cpp
|
||||
nanovdb::Coord c(1, 2, 3);
|
||||
auto c2 = nanovdb::Coord::Floor(nanovdb::Vec3f(1.7f, 2.1f, 3.9f)); // (1, 2, 3)
|
||||
|
||||
nanovdb::Vec3f v(1, 2, 3);
|
||||
auto d = v.dot(v); // 14
|
||||
auto n = v.length(); // sqrt(14)
|
||||
auto u = v.normalize();
|
||||
|
||||
nanovdb::math::BBox<nanovdb::Vec3f> b(nanovdb::Vec3f(0,0,0),
|
||||
nanovdb::Vec3f(10,10,10));
|
||||
b.expand(nanovdb::Vec3f(5,5,5));
|
||||
b.isInside(nanovdb::Vec3f(1,1,1));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sampling
|
||||
|
||||
```cpp
|
||||
#include <nanovdb/math/SampleFromVoxels.h>
|
||||
|
||||
auto acc = grid->getAccessor();
|
||||
auto sampler = nanovdb::math::createSampler<1>(acc); // 0=nearest, 1=trilinear, 2=triquadratic, 3=tricubic
|
||||
auto idxPt = grid->worldToIndex(worldPt);
|
||||
float v = sampler(idxPt);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## HDDA ray-march
|
||||
|
||||
```cpp
|
||||
#include <nanovdb/math/Ray.h>
|
||||
#include <nanovdb/math/HDDA.h>
|
||||
|
||||
auto acc = grid->getAccessor();
|
||||
nanovdb::math::Ray<float> wRay({0,0,-5}, {0,0,1});
|
||||
auto iRay = wRay.worldToIndexF(*grid);
|
||||
|
||||
// High-level: ZeroCrossing drives an HDDA and stops at the first sign change.
|
||||
nanovdb::Coord ijk;
|
||||
float v, t;
|
||||
if (nanovdb::math::ZeroCrossing(iRay, acc, ijk, v, t)) { /* hit at ijk */ }
|
||||
|
||||
// Low-level: HDDA ctor takes the ray AND the start dimension from getDim.
|
||||
nanovdb::Coord start(0, 0, 0);
|
||||
nanovdb::math::HDDA<decltype(iRay), nanovdb::Coord> hdda(iRay, acc.getDim(start, iRay));
|
||||
while (hdda.step()) {
|
||||
if (acc.isActive(hdda.voxel())) break; // first active voxel
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GPU: transfer + kernel
|
||||
|
||||
```cpp
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/cuda/GridHandle.cuh>
|
||||
|
||||
auto devH = hostH.copy<nanovdb::cuda::DeviceBuffer>();
|
||||
devH.deviceUpload(); // REQUIRED: push to device, else deviceGrid()==nullptr
|
||||
const auto* dGrid = devH.deviceGrid<float>();
|
||||
|
||||
__global__ void k(const nanovdb::NanoGrid<float>* g,
|
||||
const nanovdb::Coord* coords, float* out, int N) {
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid >= N) return;
|
||||
auto acc = g->getAccessor();
|
||||
out[tid] = acc.getValue(coords[tid]);
|
||||
}
|
||||
|
||||
k<<<(N+127)/128, 128>>>(dGrid, dCoords, dOut, N);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GPU builders
|
||||
|
||||
```cpp
|
||||
// Points → grid (builder-pattern class, not a free function)
|
||||
nanovdb::tools::cuda::PointsToGrid<nanovdb::Point> p2g(/*voxelSize=*/0.05);
|
||||
auto ptsH = p2g.getHandle(dPoints, nPts);
|
||||
const auto* ptsGrid = ptsH.deviceGrid<nanovdb::Point>();
|
||||
|
||||
// Mesh → OnIndexGrid (narrow-band). Verts are Vec3f, triangles Vec3i.
|
||||
const nanovdb::Vec3f* dVerts; size_t vertN;
|
||||
const nanovdb::Vec3i* dTris; size_t triN;
|
||||
nanovdb::tools::cuda::MeshToGrid<nanovdb::ValueOnIndex> m2g(
|
||||
dVerts, vertN, dTris, triN, nanovdb::Map(voxelSize));
|
||||
m2g.setNarrowBandWidth(3.f);
|
||||
auto meshH = m2g.getHandle();
|
||||
const auto* meshGrid = meshH.deviceGrid<nanovdb::ValueOnIndex>();
|
||||
|
||||
// MeshToGrid + per-active-voxel UDF sidecar
|
||||
auto [gridH, udfBuf] = m2g.getHandleAndUDF();
|
||||
```
|
||||
|
||||
## CPU builder
|
||||
|
||||
```cpp
|
||||
// Build a tree on the host, then bake into a packed NanoVDB grid.
|
||||
nanovdb::tools::build::Grid<float> g(/*background=*/0.0f,
|
||||
"my_grid", nanovdb::GridClass::FogVolume);
|
||||
g.setTransform(/*scale=*/0.1); // voxel size
|
||||
auto acc = g.getAccessor(); // not thread-safe
|
||||
acc.setValue(nanovdb::Coord(0, 0, 0), 1.0f);
|
||||
auto h = nanovdb::tools::createNanoGrid(g); // GridHandle<HostBuffer>
|
||||
```
|
||||
|
||||
## Primitive helpers
|
||||
|
||||
```cpp
|
||||
auto h = nanovdb::tools::createLevelSetSphere<float>(
|
||||
/*radius=*/10.0, /*center=*/{0,0,0}, /*voxelSize=*/0.1);
|
||||
// also: createLevelSetTorus, createLevelSetBox, createFogVolumeSphere, ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Topological operators (GPU)
|
||||
|
||||
```cpp
|
||||
// Builder classes (NOT free functions); ValueOnIndex grids only. Construct
|
||||
// from a device grid pointer, getHandle() returns a new GridHandle.
|
||||
using OnIdx = nanovdb::ValueOnIndex;
|
||||
auto* g = devH.deviceGrid<OnIdx>();
|
||||
|
||||
auto dilatedH = nanovdb::tools::cuda::DilateGrid<OnIdx>(g).getHandle();
|
||||
auto coarseH = nanovdb::tools::cuda::CoarsenGrid<OnIdx>(g).getHandle();
|
||||
auto fineH = nanovdb::tools::cuda::RefineGrid<OnIdx>(g).getHandle();
|
||||
const nanovdb::Mask<3>* leafMask = nullptr; // PruneGrid needs a per-leaf mask
|
||||
auto prunedH = nanovdb::tools::cuda::PruneGrid<OnIdx>(g, leafMask).getHandle();
|
||||
auto mergedH = nanovdb::tools::cuda::MergeGrids<OnIdx>(g, g).getHandle();
|
||||
|
||||
// signedFloodFill is the exception: a free function, in place, on a scalar
|
||||
// SDF grid (not OnIndex).
|
||||
nanovdb::tools::cuda::signedFloodFill(devH.deviceGrid<float>());
|
||||
```
|
||||
|
||||
CPU morphology equivalents live under `nanovdb/util/MorphologyHelpers.h`.
|
||||
|
||||
## Re-home sidecar data after a topology change (Injection.cuh)
|
||||
|
||||
A topology op renumbers the active-voxel indices, so the old sidecar no
|
||||
longer matches. Move values across, and find the new voxels:
|
||||
|
||||
```cpp
|
||||
#include <nanovdb/util/cuda/Injection.cuh>
|
||||
#include <nanovdb/util/cuda/DeviceGridTraits.cuh>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
using OnIdx = nanovdb::ValueOnIndex;
|
||||
const auto* srcGrid = devH.deviceGrid<OnIdx>(); // old grid
|
||||
const auto* dstGrid = devHandle.deviceGrid<OnIdx>(); // new (e.g. dilated) grid
|
||||
const float* oldSidecar = nullptr;
|
||||
float* newSidecar = nullptr; // size = new activeVoxelCount(+1)
|
||||
|
||||
// Copy overlapping values (non-overlap dst voxels left untouched):
|
||||
auto srcLeaves = nanovdb::util::cuda::DeviceGridTraits<OnIdx>::getTreeData(srcGrid).mNodeCount[0];
|
||||
nanovdb::util::cuda::operatorKernel<
|
||||
nanovdb::util::cuda::InjectGridDataFunctor<OnIdx, float>>
|
||||
<<<srcLeaves, 256>>>(srcGrid, dstGrid, oldSidecar, newSidecar);
|
||||
|
||||
// Per-leaf overlap mask (new voxels = dstMask & ~overlap):
|
||||
auto dstLeaves = nanovdb::util::cuda::DeviceGridTraits<OnIdx>::getTreeData(dstGrid).mNodeCount[0];
|
||||
auto maskBuf = nanovdb::cuda::DeviceBuffer::create(dstLeaves*sizeof(nanovdb::Mask<3>), nullptr, false);
|
||||
auto* overlap = static_cast<nanovdb::Mask<3>*>(maskBuf.deviceData());
|
||||
unsigned nt = 128, nb = (dstLeaves + nt - 1) / nt;
|
||||
nanovdb::util::cuda::lambdaKernel<<<nb, nt>>>(dstLeaves,
|
||||
nanovdb::util::cuda::InjectGridMaskFunctor<OnIdx>(), srcGrid, dstGrid, overlap);
|
||||
```
|
||||
|
||||
Simpler: pre-fill `newSidecar` with a sentinel; slots still holding it after
|
||||
the data inject are the new voxels.
|
||||
|
||||
---
|
||||
|
||||
## Stats, validation & integrity
|
||||
|
||||
Nodes cache bbox/min/max/avg per node. Mutating values in place leaves them
|
||||
**stale** — which silently breaks HDDA space-skipping and ray-tracing.
|
||||
Recompute after any manual edit.
|
||||
|
||||
```cpp
|
||||
#include <nanovdb/tools/GridStats.h> // CPU
|
||||
// #include <nanovdb/tools/cuda/GridStats.cuh> // GPU (device grid + stream)
|
||||
|
||||
nanovdb::NanoGrid<float>* mutableGrid = nullptr; // non-const: it gets written
|
||||
nanovdb::tools::updateGridStats(mutableGrid, nanovdb::tools::StatsMode::All);
|
||||
// StatsMode: Disable | BBox | MinMax | All(=Default)
|
||||
|
||||
// Extrema over an index-space box, computed directly (no cache):
|
||||
auto ex = nanovdb::tools::getExtrema(*grid, nanovdb::CoordBBox(nanovdb::Coord(-10), nanovdb::Coord(10)));
|
||||
float lo = ex.min(), hi = ex.max();
|
||||
```
|
||||
|
||||
Certify a grid after load / build / device transfer:
|
||||
|
||||
```cpp
|
||||
#include <nanovdb/tools/GridChecksum.h>
|
||||
#include <nanovdb/tools/GridValidator.h>
|
||||
|
||||
nanovdb::NanoGrid<float>* mutableGrid = nullptr; // non-const: edited grid
|
||||
|
||||
bool good = nanovdb::tools::validateChecksum(grid, nanovdb::CheckMode::Full);
|
||||
nanovdb::tools::updateChecksum(mutableGrid, nanovdb::CheckMode::Full); // after a legit edit
|
||||
|
||||
bool valid = nanovdb::tools::isValid(grid, nanovdb::CheckMode::Full, /*verbose=*/false);
|
||||
char err[(uint32_t)nanovdb::CheckMode::StrLen] = {}; // CheckMode is scoped → cast
|
||||
nanovdb::tools::checkGrid(grid, err, nanovdb::CheckMode::Full); // err="" if valid
|
||||
// CheckMode: Disable/Empty | Half/Partial/Default | Full
|
||||
// GPU variants: tools/cuda/GridChecksum.cuh, tools/cuda/GridValidator.cuh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## IndexGrid & VBM
|
||||
|
||||
```cpp no-compile
|
||||
// Build a ValueOnIndex grid from an OpenVDB grid
|
||||
auto h = nanovdb::tools::createNanoGrid<openvdb::FloatGrid,
|
||||
nanovdb::ValueOnIndex>(*ovGrid);
|
||||
|
||||
// Or from a mesh on the GPU (already returns OnIndexGrid)
|
||||
nanovdb::tools::cuda::MeshToGrid<nanovdb::ValueOnIndex> mesher(dV, vN, dT, tN, nanovdb::Map(vs));
|
||||
auto devH = mesher.getHandle();
|
||||
const auto* g = devH.deviceGrid<nanovdb::ValueOnIndex>();
|
||||
|
||||
// External attribute array — same length as active voxel count
|
||||
size_t N = g->activeVoxelCount();
|
||||
thrust::device_vector<float> attr(N);
|
||||
|
||||
// Read attribute at a coordinate
|
||||
auto acc = g->getAccessor();
|
||||
uint64_t idx = acc.getValue(c); // index into attr
|
||||
float a = attr[idx];
|
||||
|
||||
// VBM: SIMT-friendly block iteration (Log2BlockWidth >= 6, i.e. >=64/block)
|
||||
auto vbmH = nanovdb::tools::buildVoxelBlockManager<6>(g);
|
||||
// see tools/cuda/VoxelBlockManager.cuh for the GPU decode pattern
|
||||
```
|
||||
|
||||
ChannelAccessor — read the sidecar by attribute, not by index (one typed
|
||||
call instead of getValue(ijk) → array[idx]). Note: IndexT defaults to
|
||||
`ValueIndex`, so spell out `ValueOnIndex`:
|
||||
|
||||
```cpp
|
||||
__global__ void readChan(const nanovdb::NanoGrid<nanovdb::ValueOnIndex>* g,
|
||||
float* dValues, nanovdb::Coord c, float* out) {
|
||||
nanovdb::ChannelAccessor<float, nanovdb::ValueOnIndex> ca(*g, dValues);
|
||||
*out = ca.getValue(c); // attribute value (not the index)
|
||||
// ca(c) same; ca.getIndex(c) for the raw index; ca.probeValue(c, v) for state+value
|
||||
// ctor (*g, channelID) instead reads an internal blind-data channel
|
||||
}
|
||||
```
|
||||
|
||||
Bake a sidecar back into a value grid (round-trip partner of the split;
|
||||
needed by value-typed tools like `signedFloodFill`):
|
||||
|
||||
```cpp
|
||||
#include <nanovdb/tools/cuda/IndexToGrid.cuh>
|
||||
|
||||
const auto* idxGrid = devH.deviceGrid<nanovdb::ValueOnIndex>();
|
||||
const float* dValues = nullptr; // per-active-voxel sidecar
|
||||
auto floatH = nanovdb::tools::cuda::indexToGrid<float>(idxGrid, dValues);
|
||||
const auto* floatGrid = floatH.deviceGrid<float>(); // values now in-grid
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OpenVDB ↔ NanoVDB
|
||||
|
||||
```cpp openvdb
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/tools/NanoToOpenVDB.h>
|
||||
|
||||
// OpenVDB → NanoVDB (optionally with type change)
|
||||
auto h = nanovdb::tools::createNanoGrid<openvdb::FloatGrid, float>(*ovGrid);
|
||||
auto h_half = nanovdb::tools::createNanoGrid<openvdb::FloatGrid,
|
||||
nanovdb::Half>(*ovGrid);
|
||||
|
||||
// NanoVDB → OpenVDB
|
||||
auto ovGrid = nanovdb::tools::nanoToOpenVDB<float>(handle);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Blind data (attached arrays)
|
||||
|
||||
Read (host or device — `__hostdev__`):
|
||||
|
||||
```cpp
|
||||
uint32_t n = grid->blindDataCount();
|
||||
const nanovdb::GridBlindMetaData& meta = grid->blindMetaData(0);
|
||||
// meta.mValueCount, mValueSize, mDataType, mSemantic, mDataClass, mName
|
||||
|
||||
int id = grid->findBlindDataForSemantic(nanovdb::GridBlindDataSemantic::PointPosition);
|
||||
// or: int id = grid->findBlindData("name");
|
||||
const nanovdb::Vec3f* pos = grid->getBlindData<nanovdb::Vec3f>(id); // nullptr if T mismatch
|
||||
```
|
||||
|
||||
Attach on the GPU (returns a NEW handle with the array appended; fixes the
|
||||
checksum):
|
||||
|
||||
```cpp
|
||||
#include <nanovdb/tools/cuda/AddBlindData.cuh>
|
||||
|
||||
const float* dChannel = nullptr; uint64_t valueCount = 0;
|
||||
auto h = nanovdb::tools::cuda::addBlindData(
|
||||
dGrid, dChannel, valueCount,
|
||||
nanovdb::GridBlindDataClass::ChannelArray,
|
||||
nanovdb::GridBlindDataSemantic::Unknown, "my_channel");
|
||||
const auto* g = h.deviceGrid<float>();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common idioms
|
||||
|
||||
```cpp
|
||||
// Iterate active voxels in a grid (CPU, leaf-by-leaf)
|
||||
auto nmh = nanovdb::createNodeManager(*grid);
|
||||
auto* mgr = nmh.mgr<float>();
|
||||
for (uint64_t L = 0; L < mgr->leafCount(); ++L) {
|
||||
const auto& leaf = mgr->leaf(L);
|
||||
for (auto it = leaf.beginValueOn(); it; ++it) {
|
||||
// *it is the value, it.getCoord() is the ijk
|
||||
}
|
||||
}
|
||||
|
||||
// Sample a grid at a list of world-space points (pts is a std::vector<Vec3f>)
|
||||
auto sampler = nanovdb::math::createSampler<1>(acc);
|
||||
std::vector<float> sampled;
|
||||
for (const auto& w : pts)
|
||||
sampled.push_back(sampler(grid->worldToIndex(w)));
|
||||
|
||||
// Save a level-set sphere to disk
|
||||
auto h = nanovdb::tools::createLevelSetSphere<float>(10.0, {0,0,0}, 0.1);
|
||||
nanovdb::io::writeGrid("sphere.nvdb", h);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **Type mismatch returns nullptr.** Always check `handle.grid<T>()`.
|
||||
- **Accessors are not thread-safe.** One per thread/warp.
|
||||
- **Accessors don't cross CPU/GPU.** Build inside the kernel.
|
||||
- **Topology is frozen.** To "add a voxel" you must rebuild the grid.
|
||||
- **32-byte alignment.** Don't allocate the buffer yourself with
|
||||
`new char[N]` — use `HostBuffer` or `DeviceBuffer`, which guarantee
|
||||
alignment.
|
||||
- **No runtime `std::` in `__hostdev__` code.** Containers, iostreams,
|
||||
allocators, `std::sort` etc. won't work on device. For type traits and
|
||||
compile-time helpers, NanoVDB uses its own `nanovdb::util::is_same` /
|
||||
`util::enable_if` / `util::conditional` / `util::remove_const` (in
|
||||
`util/Util.h`) — these compile under C++11 across compilers and don't
|
||||
need `--expt-relaxed-constexpr`. Where NanoVDB needs more, it pulls from
|
||||
CCCL's `cuda::std::` (e.g. `cuda::std::numeric_limits` in `math/Math.h`).
|
||||
Plain `std::` compile-time facilities work with relaxed constexpr but
|
||||
NanoVDB's own headers avoid them for portability.
|
||||
- **HDDA is essential for sparse ray-march.** Without it, you're stepping
|
||||
one voxel at a time through empty space.
|
||||
|
||||
---
|
||||
|
||||
## Tools / binaries
|
||||
|
||||
| Tool | Purpose |
|
||||
|---------------------|-------------------------------------------|
|
||||
| `nanovdb_print` | Print metadata for grids in a `.nvdb` |
|
||||
| `nanovdb_convert` | OpenVDB ↔ NanoVDB on the CLI |
|
||||
| `nanovdb_validate` | ABI / format validation |
|
||||
|
||||
Run with `--help` for options.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
# Transient harness outputs
|
||||
gen/
|
||||
build/
|
||||
@@ -0,0 +1,87 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(teachme_tests LANGUAGES CXX CUDA)
|
||||
|
||||
# Compile-only gate: object libraries compile each generated translation unit
|
||||
# to an object file without linking. That is exactly what we want — catch API
|
||||
# errors at compile time; the fixture's extern stand-ins never need to resolve.
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CUDA_STANDARD 17)
|
||||
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
|
||||
set(CMAKE_CUDA_ARCHITECTURES 89) # RTX 6000 Ada; any arch suffices to compile
|
||||
endif()
|
||||
|
||||
# NanoVDB headers pull in CUDA lambdas / relaxed-constexpr code paths.
|
||||
# diag 177 = "function declared but never referenced": every wrapped block
|
||||
# becomes an unreferenced static function on purpose, so silence it.
|
||||
string(APPEND CMAKE_CUDA_FLAGS
|
||||
" --extended-lambda --expt-relaxed-constexpr --diag-suppress 177")
|
||||
|
||||
# NanoVDB include root: this file is doc/nanovdb/TEACHME/test/CMakeLists.txt, so
|
||||
# the repo root is four dirs up. Headers are at nanovdb/nanovdb/NanoVDB.h, so the
|
||||
# include root is the repo's `nanovdb/` subdir (a sibling of `doc/`).
|
||||
get_filename_component(_teachme_dir "${CMAKE_CURRENT_SOURCE_DIR}" DIRECTORY) # TEACHME
|
||||
get_filename_component(_docnv_dir "${_teachme_dir}" DIRECTORY) # doc/nanovdb
|
||||
get_filename_component(_doc_dir "${_docnv_dir}" DIRECTORY) # doc
|
||||
get_filename_component(REPO_ROOT "${_doc_dir}" DIRECTORY) # repo root
|
||||
set(NANOVDB_INCLUDE_ROOT "${REPO_ROOT}/nanovdb")
|
||||
set(OPENVDB_INCLUDE_ROOT "${REPO_ROOT}/openvdb")
|
||||
|
||||
message(STATUS "NanoVDB include root: ${NANOVDB_INCLUDE_ROOT}")
|
||||
|
||||
# Generated TUs + manifest live in ${GEN_DIR} (passed in, defaults to ./gen).
|
||||
if(NOT DEFINED GEN_DIR)
|
||||
set(GEN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/gen")
|
||||
endif()
|
||||
|
||||
set(_manifest "${GEN_DIR}/manifest.txt")
|
||||
if(NOT EXISTS "${_manifest}")
|
||||
message(FATAL_ERROR "No manifest at ${_manifest}. Run the extractor first "
|
||||
"(see run.sh).")
|
||||
endif()
|
||||
|
||||
file(STRINGS "${_manifest}" _lines)
|
||||
set(_core_srcs "")
|
||||
set(_ovdb_srcs "")
|
||||
foreach(_line IN LISTS _lines)
|
||||
if(_line STREQUAL "")
|
||||
continue()
|
||||
endif()
|
||||
separate_arguments(_parts UNIX_COMMAND "${_line}")
|
||||
list(GET _parts 0 _stem)
|
||||
list(GET _parts 1 _needs_ovdb)
|
||||
set(_src "${GEN_DIR}/${_stem}.cu")
|
||||
if(_needs_ovdb STREQUAL "1")
|
||||
list(APPEND _ovdb_srcs "${_src}")
|
||||
else()
|
||||
list(APPEND _core_srcs "${_src}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Core (NanoVDB-only) translation units.
|
||||
if(_core_srcs)
|
||||
add_library(teachme_blocks OBJECT ${_core_srcs})
|
||||
target_include_directories(teachme_blocks PRIVATE
|
||||
"${NANOVDB_INCLUDE_ROOT}" "${GEN_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
list(LENGTH _core_srcs _n_core)
|
||||
message(STATUS "teachme_blocks: ${_n_core} core TU(s)")
|
||||
endif()
|
||||
|
||||
# OpenVDB-dependent translation units — only if OpenVDB is available.
|
||||
if(_ovdb_srcs)
|
||||
find_package(OpenVDB QUIET)
|
||||
if(OpenVDB_FOUND)
|
||||
add_library(teachme_blocks_openvdb OBJECT ${_ovdb_srcs})
|
||||
target_compile_definitions(teachme_blocks_openvdb PRIVATE NANOVDB_USE_OPENVDB)
|
||||
target_include_directories(teachme_blocks_openvdb PRIVATE
|
||||
"${NANOVDB_INCLUDE_ROOT}" "${OPENVDB_INCLUDE_ROOT}"
|
||||
"${GEN_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
target_link_libraries(teachme_blocks_openvdb PRIVATE OpenVDB::openvdb)
|
||||
list(LENGTH _ovdb_srcs _n_ovdb)
|
||||
message(STATUS "teachme_blocks_openvdb: ${_n_ovdb} OpenVDB TU(s)")
|
||||
else()
|
||||
list(LENGTH _ovdb_srcs _n_ovdb)
|
||||
message(WARNING "OpenVDB not found — skipping ${_n_ovdb} OpenVDB TU(s). "
|
||||
"These blocks are reference-checked but not compiled.")
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,89 @@
|
||||
# TEACHME test harness
|
||||
|
||||
Keeps the code in the lesson honest. Two gates:
|
||||
|
||||
1. **Reference validation** (`teachme_tests.py check-refs`) — every
|
||||
`#include <...>` in a code block must resolve to a real header in the
|
||||
NanoVDB tree (or be explicitly exempt). Catches stale-path rot — e.g. a
|
||||
header that was renamed out from under the docs.
|
||||
|
||||
2. **Compilation** (`teachme_tests.py extract` + CMake) — code blocks tagged
|
||||
as compilable are extracted into `.cu` translation units and compiled
|
||||
(compile-only, `-c`) against the real NanoVDB headers. Catches API-shape
|
||||
drift — wrong namespace, a free function that's actually a class, a
|
||||
renamed method, a changed signature.
|
||||
|
||||
Run both with `./run.sh` from this directory.
|
||||
|
||||
## Why compile-only
|
||||
|
||||
The harness compiles (`nvcc -c`) but does not link or run. NanoVDB is
|
||||
header-only and the goal is to catch *API* errors (does this call name a
|
||||
real symbol with a compatible signature?), which all surface at compile
|
||||
time. Skipping the link step means a fixture header can declare the
|
||||
"given" symbols a fragment assumes (`grid`, `acc`, device pointers) as
|
||||
`extern` globals — unresolved at link, but fully type-checked at compile.
|
||||
|
||||
Everything compiles as CUDA (`.cu`) because even host-only examples pull
|
||||
in CUDA code paths (under `__CUDACC__`) when they include headers like
|
||||
`NodeManager.h` or `io/IO.h`.
|
||||
|
||||
## Block tagging
|
||||
|
||||
Code blocks are fenced with an info string: a language followed by
|
||||
whitespace-separated tags, e.g.
|
||||
|
||||
```cpp compile
|
||||
```cpp compile-global continuation
|
||||
```cpp skip-refs
|
||||
|
||||
**Opt-out model:** every `cpp` / `cu` block is compiled by default. A block
|
||||
is wrapped in a function body unless it opens a top-level definition
|
||||
(`__global__`, `template`, `struct`, `class`, `namespace`, `enum`, `using`,
|
||||
`typedef`), in which case it is auto-emitted at file scope. Tags only adjust
|
||||
the defaults:
|
||||
|
||||
| Tag | Meaning |
|
||||
|----------------|------------------------------------------------------------------------------------------|
|
||||
| *(none)* | Compiled. Auto: file scope if it opens a top-level definition, else function-wrapped. |
|
||||
| `no-compile` | Reference-check includes only; do **not** compile. For illustrative/partial fragments. |
|
||||
| `global` | Force file-scope emission (override the auto-detection). |
|
||||
| `main` | Block is already a complete program (its own `main`); compile verbatim. |
|
||||
| `continuation` | Append this block to the previous block's translation unit (shares its symbols). |
|
||||
| `openvdb` | Block needs OpenVDB; compiled only when OpenVDB is found. |
|
||||
| `skip-refs` | Do **not** validate includes in this block — for intentionally-stale counter-examples. |
|
||||
|
||||
A translation unit is a sequence of segments. A non-`continuation` block
|
||||
starts a new TU; each following `continuation` block adds a segment. At emit
|
||||
time, file-scope segments go to file scope (in order) and function segments
|
||||
each get their own wrapper function — so a kernel definition followed by a
|
||||
`continuation` launch line compose into one valid TU.
|
||||
|
||||
Every TU includes `fixture.cuh`, which pulls in the common NanoVDB headers
|
||||
(a deliberately broad "kitchen-sink" set, so terse fragments compile without
|
||||
restating includes) and declares the stand-in "given" symbols (`grid`,
|
||||
`acc`, device pointers, etc.) as `extern`. Including the heavy `tools/cuda/*`
|
||||
headers in every TU makes per-TU compilation slower; that's an accepted
|
||||
trade for letting fragments stay terse. If the suite grows large, switch to
|
||||
per-TU includes driven by each block's own `#include` lines.
|
||||
|
||||
## Coverage
|
||||
|
||||
`check-refs` reports how many code blocks are compile-tagged vs
|
||||
reference-only, so the fraction of the lesson that is actually
|
||||
compile-gated is visible (and ideally climbing).
|
||||
|
||||
## Exemptions
|
||||
|
||||
Includes that are intentionally not real are skipped by the validator:
|
||||
- ellipsis placeholders such as `#include <openvdb/...>`
|
||||
- any include inside a `skip-refs` block (e.g. the deprecated
|
||||
`nanovdb/util/cuda/CudaDeviceBuffer.h` shown as a "this path is stale"
|
||||
counter-example)
|
||||
|
||||
## Files
|
||||
|
||||
- `teachme_tests.py` — the extractor + reference validator
|
||||
- `fixture.cuh` — common headers + `extern` stand-in symbols
|
||||
- `CMakeLists.txt` — compiles the generated TUs
|
||||
- `run.sh` — driver: check-refs → extract → cmake configure+build → report
|
||||
@@ -0,0 +1,92 @@
|
||||
// fixture.cuh — common headers + stand-in "given" symbols for TEACHME
|
||||
// code-block compilation.
|
||||
//
|
||||
// Lesson fragments assume context: "you have a grid", "you have device
|
||||
// points", etc. The harness compiles each block (compile-only, never linked)
|
||||
// with this header in scope, so those assumed symbols need declarations with
|
||||
// the right *types* — enough for the compiler to type-check member calls and
|
||||
// catch API drift. They are declared `extern` (no definitions): a block that
|
||||
// declares its own local `grid`/`acc`/etc. simply shadows the extern, and
|
||||
// since we never link, the unresolved externs cost nothing.
|
||||
//
|
||||
// Grow this file as the tagging pass surfaces more assumed names. Keep the
|
||||
// types matching how the lesson uses each symbol.
|
||||
|
||||
#pragma once
|
||||
|
||||
// ---- common headers (header guards make re-includes in blocks harmless) ----
|
||||
// Core read path + IO + math + CUDA.
|
||||
#include <nanovdb/NanoVDB.h>
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/HostBuffer.h>
|
||||
#include <nanovdb/NodeManager.h>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#include <nanovdb/math/Ray.h>
|
||||
#include <nanovdb/math/HDDA.h>
|
||||
#include <nanovdb/math/SampleFromVoxels.h>
|
||||
#include <nanovdb/math/Stencils.h>
|
||||
#include <nanovdb/cuda/DeviceBuffer.h>
|
||||
#include <nanovdb/cuda/GridHandle.cuh>
|
||||
#include <nanovdb/cuda/NodeManager.cuh>
|
||||
// Commonly-assumed "tools" headers — ambient so terse fragments compile
|
||||
// without restating the include. (OpenVDB-conversion headers are NOT here;
|
||||
// blocks that need them are tagged `openvdb` and include them explicitly.)
|
||||
#include <nanovdb/tools/CreatePrimitives.h>
|
||||
#include <nanovdb/tools/CreateNanoGrid.h>
|
||||
#include <nanovdb/tools/GridBuilder.h>
|
||||
#include <nanovdb/tools/VoxelBlockManager.h>
|
||||
#include <nanovdb/tools/cuda/PointsToGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/MeshToGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/DilateGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/CoarsenGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/RefineGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/PruneGrid.cuh>
|
||||
#include <nanovdb/tools/cuda/MergeGrids.cuh>
|
||||
#include <nanovdb/tools/cuda/SignedFloodFill.cuh>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
// ---- stand-in "given" symbols (extern: declared, never defined) ----
|
||||
// Handles
|
||||
extern nanovdb::GridHandle<nanovdb::HostBuffer> handle;
|
||||
extern nanovdb::GridHandle<nanovdb::HostBuffer> hostH;
|
||||
extern nanovdb::GridHandle<nanovdb::cuda::DeviceBuffer> devH;
|
||||
extern nanovdb::GridHandle<nanovdb::cuda::DeviceBuffer> devHandle;
|
||||
extern std::vector<nanovdb::GridHandle<nanovdb::HostBuffer>> handles;
|
||||
|
||||
// Grids (host + device pointers into a float grid)
|
||||
extern const nanovdb::NanoGrid<float>* grid;
|
||||
extern const nanovdb::NanoGrid<float>* dGrid;
|
||||
|
||||
// A read accessor + sampler over the float grid (fragments often assume these
|
||||
// already exist from a prior step).
|
||||
extern nanovdb::DefaultReadAccessor<float> acc;
|
||||
|
||||
// Device point / coord / output buffers used by kernel launches.
|
||||
// dPoints is non-const: PointsToGrid<Point> writes input points into the
|
||||
// grid's blind data via getPoint<element_type>(...) = ..., which requires a
|
||||
// non-const element type.
|
||||
extern nanovdb::Vec3f* dPoints;
|
||||
extern std::size_t nPts;
|
||||
extern const nanovdb::Vec3f* dPts;
|
||||
extern const nanovdb::Vec3f* worldPts;
|
||||
extern const nanovdb::Coord* dCoords;
|
||||
extern const nanovdb::Coord* coords;
|
||||
extern float* dOut;
|
||||
extern float* out;
|
||||
extern int N;
|
||||
|
||||
// Host-side query containers + single query points
|
||||
extern std::vector<nanovdb::Vec3f> pts;
|
||||
extern std::vector<nanovdb::Coord> queries;
|
||||
extern nanovdb::Vec3f worldPt; // a single world-space query point
|
||||
extern nanovdb::Vec3f p; // generic world-space point
|
||||
|
||||
// Misc scalars
|
||||
extern double voxelSize;
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
# Drive the TEACHME test harness: reference-check -> extract -> compile.
|
||||
#
|
||||
# Usage: ./run.sh
|
||||
# Exits nonzero if any reference is dangling or any compile-tagged block
|
||||
# fails to compile.
|
||||
set -euo pipefail
|
||||
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TEACHME="$(dirname "$HERE")"
|
||||
GEN="$HERE/gen"
|
||||
BUILD="$HERE/build"
|
||||
|
||||
MD=(
|
||||
"$TEACHME/nanovdb_user_lesson.md"
|
||||
"$TEACHME/nanovdb_user_cheatsheet.md"
|
||||
"$TEACHME/README.md"
|
||||
)
|
||||
|
||||
echo "==> [1/3] reference check"
|
||||
python3 "$HERE/teachme_tests.py" check-refs "${MD[@]}"
|
||||
|
||||
echo "==> [2/3] extract compile-tagged blocks"
|
||||
rm -rf "$GEN"
|
||||
mkdir -p "$GEN"
|
||||
python3 "$HERE/teachme_tests.py" extract "${MD[@]}" --out "$GEN"
|
||||
|
||||
n_tu=$(find "$GEN" -name '*.cu' | wc -l | tr -d ' ')
|
||||
if [ "$n_tu" -eq 0 ]; then
|
||||
echo "==> [3/3] compile: no compile-tagged blocks yet — nothing to build"
|
||||
echo "PASS (reference check only)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "==> [3/3] compile $n_tu translation unit(s)"
|
||||
mkdir -p "$BUILD"
|
||||
cmake -S "$HERE" -B "$BUILD" -DGEN_DIR="$GEN" -DCMAKE_BUILD_TYPE=Release \
|
||||
> "$BUILD/configure.log" 2>&1 || { cat "$BUILD/configure.log"; exit 1; }
|
||||
|
||||
# Cap parallelism: each TU pulls in the kitchen-sink fixture (most of NanoVDB's
|
||||
# heavy headers), so nvcc is memory-hungry. Default to all cores locally; CI
|
||||
# sets TEACHME_JOBS lower so the parallel nvcc processes stay within the
|
||||
# runner's RAM (8 unbounded jobs OOM a 32 GB runner).
|
||||
jobs="${TEACHME_JOBS:-$(nproc)}"
|
||||
|
||||
# Keep going (--keep-going / -k) so every failing TU is reported, not just the
|
||||
# first. Capture cmake's status directly (don't pipe through tee, which would
|
||||
# mask it).
|
||||
set +e
|
||||
cmake --build "$BUILD" --parallel "$jobs" -- -k > "$BUILD/build.log" 2>&1
|
||||
status=$?
|
||||
set -e
|
||||
cat "$BUILD/build.log"
|
||||
if [ "$status" -eq 0 ]; then
|
||||
echo "PASS: all $n_tu translation unit(s) compiled"
|
||||
else
|
||||
echo "FAIL: one or more translation units did not compile (see above)"
|
||||
exit 1
|
||||
fi
|
||||
Executable
+319
@@ -0,0 +1,319 @@
|
||||
#!/usr/bin/env python3
|
||||
"""TEACHME lesson test harness: reference validation + block extraction.
|
||||
|
||||
Two subcommands:
|
||||
|
||||
check-refs <md...> Validate every #include in code blocks resolves
|
||||
against the NanoVDB include root. Reports
|
||||
compile-coverage. Exits nonzero on dangling refs.
|
||||
|
||||
extract <md...> --out <dir> Extract compile-tagged code blocks into .cu
|
||||
translation units under <dir>, ready for CMake.
|
||||
|
||||
See README.md for the block tagging convention.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Repo layout: this file is at doc/nanovdb/TEACHME/test/teachme_tests.py, so the
|
||||
# repo root is four levels up. The NanoVDB include root (the dir that contains
|
||||
# the `nanovdb/` header subdir) is the repo's `nanovdb/`; OpenVDB's is `openvdb/`.
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
REPO_ROOT = os.path.abspath(os.path.join(HERE, "..", "..", "..", ".."))
|
||||
NANOVDB_INCLUDE_ROOT = os.path.join(REPO_ROOT, "nanovdb")
|
||||
OPENVDB_INCLUDE_ROOT = os.path.join(REPO_ROOT, "openvdb")
|
||||
INCLUDE_ROOTS = [NANOVDB_INCLUDE_ROOT, OPENVDB_INCLUDE_ROOT]
|
||||
|
||||
FENCE_RE = re.compile(r"^```(\w+)?(.*)$")
|
||||
INCLUDE_RE = re.compile(r'^\s*#\s*include\s*<([^>]+)>')
|
||||
|
||||
# Inline-code spans in prose that look like header paths, e.g.
|
||||
# `nanovdb/tools/GridBuilder.h`. Used to catch stale "Read: ..." references.
|
||||
PROSE_PATH_RE = re.compile(r'`((?:nanovdb|openvdb)/[\w./]+\.(?:h|cuh|cc|cpp))`')
|
||||
# A line carrying this marker is exempt from prose-path checking (for
|
||||
# intentionally-stale counter-examples).
|
||||
STALE_OK = "teachme:stale-ok"
|
||||
|
||||
|
||||
def resolves(inc):
|
||||
"""True if an include path exists under any include root."""
|
||||
return any(os.path.isfile(os.path.join(r, inc)) for r in INCLUDE_ROOTS)
|
||||
|
||||
# Languages whose blocks we care about.
|
||||
CODE_LANGS = {"cpp", "cu", "c++"}
|
||||
|
||||
# Opt-OUT model: every cpp/cu block is compiled unless tagged `no-compile`.
|
||||
# A line that begins a file-scope definition forces file-scope emission even
|
||||
# without an explicit `global` tag.
|
||||
FILE_SCOPE_RE = re.compile(r'^\s*(template\b|struct\b|class\b|namespace\b|'
|
||||
r'__global__\b|enum\b|using\b|typedef\b)')
|
||||
|
||||
|
||||
class Block:
|
||||
__slots__ = ("path", "start_line", "lang", "tags", "lines")
|
||||
|
||||
def __init__(self, path, start_line, lang, tags):
|
||||
self.path = path
|
||||
self.start_line = start_line
|
||||
self.lang = lang
|
||||
self.tags = tags
|
||||
self.lines = []
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return "".join(self.lines)
|
||||
|
||||
|
||||
def parse_blocks(path):
|
||||
"""Yield Block objects for every fenced code block in a markdown file."""
|
||||
blocks = []
|
||||
with open(path, encoding="utf-8") as f:
|
||||
lines = f.readlines()
|
||||
i = 0
|
||||
in_block = None
|
||||
for lineno, line in enumerate(lines, 1):
|
||||
m = FENCE_RE.match(line)
|
||||
if m:
|
||||
if in_block is None:
|
||||
lang = (m.group(1) or "").lower()
|
||||
tags = set(m.group(2).split())
|
||||
in_block = Block(path, lineno, lang, tags)
|
||||
else:
|
||||
blocks.append(in_block)
|
||||
in_block = None
|
||||
continue
|
||||
if in_block is not None:
|
||||
in_block.lines.append(line)
|
||||
if in_block is not None:
|
||||
sys.stderr.write(f"{path}: unterminated code fence opened at "
|
||||
f"line {in_block.start_line}\n")
|
||||
return blocks
|
||||
|
||||
|
||||
def is_exempt_include(inc):
|
||||
"""Includes that are intentionally not real headers."""
|
||||
if "..." in inc: # ellipsis placeholder, e.g. openvdb/...
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def scan_prose_paths(path):
|
||||
"""Yield (lineno, token) for header-path references in prose inline code,
|
||||
skipping lines inside fenced code blocks and lines marked stale-ok."""
|
||||
with open(path, encoding="utf-8") as f:
|
||||
lines = f.readlines()
|
||||
in_fence = False
|
||||
for lineno, line in enumerate(lines, 1):
|
||||
if FENCE_RE.match(line) and line.lstrip().startswith("```"):
|
||||
in_fence = not in_fence
|
||||
continue
|
||||
if in_fence or STALE_OK in line:
|
||||
continue
|
||||
for tok in PROSE_PATH_RE.findall(line):
|
||||
yield lineno, tok
|
||||
|
||||
|
||||
def cmd_check_refs(args):
|
||||
code_blocks = 0
|
||||
compiled_blocks = 0
|
||||
dangling = [] # (path, lineno, inc, kind)
|
||||
for path in args.md:
|
||||
# 1) #include lines inside code blocks
|
||||
for b in parse_blocks(path):
|
||||
if b.lang not in CODE_LANGS:
|
||||
continue
|
||||
code_blocks += 1
|
||||
if "no-compile" not in b.tags:
|
||||
compiled_blocks += 1
|
||||
if "skip-refs" in b.tags:
|
||||
continue
|
||||
for off, line in enumerate(b.lines):
|
||||
im = INCLUDE_RE.match(line)
|
||||
if not im:
|
||||
continue
|
||||
inc = im.group(1).strip()
|
||||
if is_exempt_include(inc):
|
||||
continue
|
||||
if not resolves(inc):
|
||||
dangling.append((path, b.start_line + 1 + off, inc, "include"))
|
||||
# 2) header-path references in prose ("Read: `nanovdb/.../X.h`")
|
||||
for lineno, tok in scan_prose_paths(path):
|
||||
if not resolves(tok):
|
||||
dangling.append((path, lineno, tok, "prose"))
|
||||
|
||||
if dangling:
|
||||
sys.stderr.write("DANGLING REFERENCES:\n")
|
||||
for path, lineno, ref, kind in dangling:
|
||||
rel = os.path.relpath(path)
|
||||
sys.stderr.write(f" {rel}:{lineno}: [{kind}] {ref!r} does not "
|
||||
f"resolve under any include root\n")
|
||||
|
||||
pct = (100.0 * compiled_blocks / code_blocks) if code_blocks else 0.0
|
||||
print(f"code blocks: {code_blocks} compiled: {compiled_blocks} "
|
||||
f"({pct:.0f}%) no-compile: {code_blocks - compiled_blocks}")
|
||||
if dangling:
|
||||
print(f"FAIL: {len(dangling)} dangling reference(s)")
|
||||
return 1
|
||||
print("OK: all references resolve")
|
||||
return 0
|
||||
|
||||
|
||||
def split_block(block):
|
||||
"""Split one block's lines into ordered (kind, lines) sub-segments.
|
||||
|
||||
`kind` is 'global' (file scope) or 'fn' (function body). Explicit `global`
|
||||
/ `main` tags force the whole block. Otherwise we brace-walk: a top-level
|
||||
definition (matched by FILE_SCOPE_RE at brace depth 0, e.g. a __global__
|
||||
kernel) becomes a file-scope chunk spanning until its braces balance;
|
||||
everything else at depth 0 (launches, statements) becomes a function chunk.
|
||||
This lets a single block mix a kernel definition and its launch.
|
||||
"""
|
||||
if "global" in block.tags:
|
||||
return [("global", block.lines)]
|
||||
if "main" in block.tags:
|
||||
return [("main", block.lines)]
|
||||
|
||||
segs = []
|
||||
fn_buf = []
|
||||
lines = block.lines
|
||||
i, n = 0, len(lines)
|
||||
|
||||
def flush_fn():
|
||||
if fn_buf:
|
||||
segs.append(("fn", fn_buf[:]))
|
||||
fn_buf.clear()
|
||||
|
||||
while i < n:
|
||||
line = lines[i]
|
||||
stripped = line.lstrip()
|
||||
if stripped.startswith("#"): # preprocessor: neutral (hoisted later)
|
||||
fn_buf.append(line)
|
||||
i += 1
|
||||
continue
|
||||
if FILE_SCOPE_RE.match(line): # start of a top-level definition
|
||||
flush_fn()
|
||||
chunk = []
|
||||
depth = 0
|
||||
saw_brace = False
|
||||
while i < n:
|
||||
l = lines[i]
|
||||
chunk.append(l)
|
||||
i += 1
|
||||
depth += l.count("{") - l.count("}")
|
||||
if "{" in l:
|
||||
saw_brace = True
|
||||
if saw_brace and depth <= 0:
|
||||
break
|
||||
if not saw_brace and l.rstrip().endswith(";"):
|
||||
break # brace-less decl, e.g. `using X = ...;`
|
||||
segs.append(("global", chunk))
|
||||
continue
|
||||
fn_buf.append(line) # ordinary statement → function body
|
||||
i += 1
|
||||
flush_fn()
|
||||
return segs if segs else [("fn", lines)]
|
||||
|
||||
|
||||
def cmd_extract(args):
|
||||
os.makedirs(args.out, exist_ok=True)
|
||||
# A TU has a list of (kind, lines, line_no) sub-segments + an openvdb flag.
|
||||
tus = []
|
||||
current = None
|
||||
|
||||
for path in args.md:
|
||||
for b in parse_blocks(path):
|
||||
if b.lang not in CODE_LANGS:
|
||||
continue
|
||||
# Opt-out: compile everything except blocks tagged no-compile.
|
||||
if "no-compile" in b.tags:
|
||||
continue
|
||||
subsegs = [(k, lines, b.start_line) for (k, lines) in split_block(b)]
|
||||
cont = "continuation" in b.tags
|
||||
if cont and current is not None:
|
||||
current["segs"].extend(subsegs)
|
||||
if "openvdb" in b.tags:
|
||||
current["openvdb"] = True
|
||||
else:
|
||||
current = {
|
||||
"segs": list(subsegs),
|
||||
"openvdb": "openvdb" in b.tags,
|
||||
"src": os.path.basename(path),
|
||||
"line": b.start_line,
|
||||
}
|
||||
tus.append(current)
|
||||
|
||||
manifest = []
|
||||
for n, tu in enumerate(tus):
|
||||
stem = f"tu_{n:03d}_{os.path.splitext(tu['src'])[0]}_{tu['line']}"
|
||||
fname = os.path.join(args.out, stem + ".cu")
|
||||
# Hoist all preprocessor directives (#include/#define/#pragma) to file
|
||||
# scope, deduped in first-seen order, so function-wrapped segments
|
||||
# never carry an #include into block scope (illegal for headers that
|
||||
# define static __device__ functions).
|
||||
hoisted = []
|
||||
seen_hoist = set()
|
||||
seg_render = [] # (kind, body_lines, line)
|
||||
for kind, lines, line in tu["segs"]:
|
||||
body = []
|
||||
for ln in lines:
|
||||
if ln.lstrip().startswith("#"):
|
||||
if ln not in seen_hoist:
|
||||
seen_hoist.add(ln)
|
||||
hoisted.append(ln)
|
||||
else:
|
||||
body.append(ln)
|
||||
if body:
|
||||
seg_render.append((kind, body, line))
|
||||
|
||||
out = ['#include "fixture.cuh"\n']
|
||||
out += hoisted
|
||||
out.append("\n")
|
||||
fn_count = 0
|
||||
for kind, body, line in seg_render:
|
||||
out.append(f"// --- from {tu['src']}:{line} ---\n")
|
||||
if kind in ("global", "main"):
|
||||
out += body
|
||||
else: # fn
|
||||
out.append(f"static void _seg_{fn_count}() {{\n")
|
||||
out += body
|
||||
out.append("\n}\n")
|
||||
fn_count += 1
|
||||
out.append("\n")
|
||||
with open(fname, "w", encoding="utf-8") as f:
|
||||
f.writelines(out)
|
||||
manifest.append((stem, tu["openvdb"]))
|
||||
|
||||
# Emit a manifest CMake can read: <stem> <needs_openvdb 0|1>
|
||||
with open(os.path.join(args.out, "manifest.txt"), "w") as f:
|
||||
for stem, ovdb in manifest:
|
||||
f.write(f"{stem} {1 if ovdb else 0}\n")
|
||||
|
||||
n_ovdb = sum(1 for _, o in manifest if o)
|
||||
print(f"extracted {len(manifest)} translation unit(s) "
|
||||
f"({n_ovdb} need OpenVDB) into {args.out}")
|
||||
return 0
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
sub = ap.add_subparsers(dest="cmd", required=True)
|
||||
|
||||
p1 = sub.add_parser("check-refs", help="validate #include references")
|
||||
p1.add_argument("md", nargs="+", help="markdown files")
|
||||
p1.set_defaults(func=cmd_check_refs)
|
||||
|
||||
p2 = sub.add_parser("extract", help="extract compile-tagged blocks to .cu")
|
||||
p2.add_argument("md", nargs="+", help="markdown files")
|
||||
p2.add_argument("--out", required=True, help="output directory")
|
||||
p2.set_defaults(func=cmd_extract)
|
||||
|
||||
args = ap.parse_args()
|
||||
sys.exit(args.func(args))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -6,3 +6,7 @@ Welcome to the NanoVDB documentation page.
|
||||
* @subpage NanoVDB_FAQ
|
||||
* @subpage NanoVDB_SourceTree
|
||||
* @subpage NanoVDB_HelloWorld
|
||||
|
||||
For a hands-on introduction, point an LLM coding agent (Claude Code, Cursor, or
|
||||
similar) at the [TEACHME lessons](https://github.com/AcademySoftwareFoundation/openvdb/tree/master/doc/nanovdb/TEACHME)
|
||||
and ask it to teach you the NanoVDB API interactively.
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
html {
|
||||
/* override light-mode variables here */
|
||||
}
|
||||
|
||||
html.dark-mode {
|
||||
/* define dark-mode variable overrides here */
|
||||
--openvdb-group-header-override: white;
|
||||
}
|
||||
|
||||
/* Fix @name group sections in dark theme appearing strangely */
|
||||
div.groupHeader
|
||||
{
|
||||
box-shadow: none;
|
||||
color: var(--openvdb-group-header-override);
|
||||
}
|
||||
|
||||
/* Apply invert to all images in tables, regardless of nesting, required to
|
||||
properly handle latex formulas in dark theme (for some reason
|
||||
doxygen-awesome only applies this to the first level of nested tables via:
|
||||
html.dark-mode div.contents > table img)
|
||||
*/
|
||||
html.dark-mode div.contents table img
|
||||
{
|
||||
filter: brightness(89%) hue-rotate(180deg) invert();
|
||||
}
|
||||
@@ -21,7 +21,6 @@
|
||||
- @ref secPtIndexFilters
|
||||
- @ref secPtSpaceAndTrans
|
||||
|
||||
|
||||
@section secPtOverview Introduction
|
||||
@anchor openvdbPointsOverview
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
|
||||
@page python Using OpenVDB in Python
|
||||
@page python OpenVDB Python
|
||||
|
||||
This section describes the OpenVDB Python module and includes Python code
|
||||
snippets and some complete programs that illustrate how to perform common tasks.
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
See [LICENSE](LICENSE) for the main open source license of original
|
||||
code written for the OpenVDB project.
|
||||
|
||||
The remainder of this file reproduces the open source licensing details
|
||||
of other projects that have been imported, incorporated into, or derived
|
||||
into parts of OpenVDB.
|
||||
|
||||
In no particular order:
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
BSD 3-Clause License
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
* Imath, Copyright (c) Contributors to the Imath Project.
|
||||
https://github.com/AcademySoftwareFoundation/Imath
|
||||
|
||||
OpenVDB includes a copy of Half.h from the Imath project. This copy can
|
||||
be disabled in favour of using Half.h from the Imath project - but one
|
||||
must be provided either way.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
MIT License
|
||||
|
||||
* doxygen-awesome Copyright (c) 2021 - 2023 jothepro
|
||||
https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
OpenVDB uses doxygen-awesome for stylized html output with doxygen and
|
||||
comes bundled with copies of the css provided by doxygen-awesome. Usage
|
||||
of this css can be disabled.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
Apache 2.0
|
||||
|
||||
* VCL (c) Copyright 2012-2022 Agner Fog.
|
||||
https://github.com/vectorclass/version2
|
||||
|
||||
OpenVDB includes a copy of vectorclass for x86 SIMD intrinsic usage. Usage
|
||||
of VCL is disabled by default and requires the VCL headers to be shipped with
|
||||
OpenVDB installations. This behaviour and usage of VCL can be controlled
|
||||
during OpenVDB configuration. See the full LICENSE terms in:
|
||||
|
||||
vcl/openvdb/ext/vcl/LICENSE
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
Public domain
|
||||
|
||||
* The simplex noise implementation included in OpenVDB AX is Copyright (c)
|
||||
Arthur Tombs. It bears the following notice:
|
||||
|
||||
This is a derivative work based on OpenSimplex by Kurt Spencer:
|
||||
https://gist.github.com/KdotJPG/b1270127455a94ac5d19
|
||||
|
||||
Anyone is free to make use of this software in whatever way they want.
|
||||
Attribution is appreciated, but not required.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
If we have left anything out, it is unintentional. Please let us know.
|
||||
@@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/**
|
||||
|
||||
Doxygen Awesome
|
||||
https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
Copyright (c) 2021 - 2025 jothepro
|
||||
|
||||
*/
|
||||
|
||||
class DoxygenAwesomeDarkModeToggle extends HTMLElement {
|
||||
// SVG icons from https://fonts.google.com/icons
|
||||
// Licensed under the Apache 2.0 license:
|
||||
// https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
static lightModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FCBF00"><rect fill="none" height="24" width="24"/><circle cx="12" cy="12" opacity=".3" r="3"/><path d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>`
|
||||
static darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FE9700"><rect fill="none" height="24" width="24"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z" opacity=".3"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>`
|
||||
static title = "Toggle Light/Dark Mode"
|
||||
|
||||
static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode"
|
||||
static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode"
|
||||
|
||||
static _staticConstructor = function() {
|
||||
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference)
|
||||
// Update the color scheme when the browsers preference changes
|
||||
// without user interaction on the website.
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
||||
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
|
||||
})
|
||||
// Update the color scheme when the tab is made visible again.
|
||||
// It is possible that the appearance was changed in another tab
|
||||
// while this tab was in the background.
|
||||
document.addEventListener("visibilitychange", visibilityState => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
|
||||
}
|
||||
});
|
||||
}()
|
||||
|
||||
static init() {
|
||||
$(function() {
|
||||
$(document).ready(function() {
|
||||
const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
|
||||
toggleButton.title = DoxygenAwesomeDarkModeToggle.title
|
||||
toggleButton.updateIcon()
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
||||
toggleButton.updateIcon()
|
||||
})
|
||||
document.addEventListener("visibilitychange", visibilityState => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
toggleButton.updateIcon()
|
||||
}
|
||||
});
|
||||
|
||||
$(document).ready(function(){
|
||||
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
|
||||
})
|
||||
$(window).resize(function(){
|
||||
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.onclick=this.toggleDarkMode
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns `true` for dark-mode, `false` for light-mode system preference
|
||||
*/
|
||||
static get systemPreference() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns `true` for dark-mode, `false` for light-mode user preference
|
||||
*/
|
||||
static get userPreference() {
|
||||
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
|
||||
(DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
|
||||
}
|
||||
|
||||
static set userPreference(userPreference) {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference
|
||||
if(!userPreference) {
|
||||
if(DoxygenAwesomeDarkModeToggle.systemPreference) {
|
||||
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true)
|
||||
} else {
|
||||
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)
|
||||
}
|
||||
} else {
|
||||
if(!DoxygenAwesomeDarkModeToggle.systemPreference) {
|
||||
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true)
|
||||
} else {
|
||||
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)
|
||||
}
|
||||
}
|
||||
DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged()
|
||||
}
|
||||
|
||||
static enableDarkMode(enable) {
|
||||
if(enable) {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = true
|
||||
document.documentElement.classList.add("dark-mode")
|
||||
document.documentElement.classList.remove("light-mode")
|
||||
} else {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = false
|
||||
document.documentElement.classList.remove("dark-mode")
|
||||
document.documentElement.classList.add("light-mode")
|
||||
}
|
||||
}
|
||||
|
||||
static onSystemPreferenceChanged() {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference
|
||||
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
|
||||
}
|
||||
|
||||
static onUserPreferenceChanged() {
|
||||
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
|
||||
}
|
||||
|
||||
toggleDarkMode() {
|
||||
DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference
|
||||
this.updateIcon()
|
||||
}
|
||||
|
||||
updateIcon() {
|
||||
if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) {
|
||||
this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon
|
||||
} else {
|
||||
this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle);
|
||||
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/**
|
||||
|
||||
Doxygen Awesome
|
||||
https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
Copyright (c) 2022 - 2025 jothepro
|
||||
|
||||
*/
|
||||
|
||||
class DoxygenAwesomeFragmentCopyButton extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.onclick=this.copyContent
|
||||
}
|
||||
static title = "Copy to clipboard"
|
||||
static copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M23.04,10.322c0,-2.582 -2.096,-4.678 -4.678,-4.678l-6.918,-0c-2.582,-0 -4.678,2.096 -4.678,4.678c0,-0 0,8.04 0,8.04c0,2.582 2.096,4.678 4.678,4.678c0,-0 6.918,-0 6.918,-0c2.582,-0 4.678,-2.096 4.678,-4.678c0,-0 0,-8.04 0,-8.04Zm-2.438,-0l-0,8.04c-0,1.236 -1.004,2.24 -2.24,2.24l-6.918,-0c-1.236,-0 -2.239,-1.004 -2.239,-2.24l-0,-8.04c-0,-1.236 1.003,-2.24 2.239,-2.24c0,0 6.918,0 6.918,0c1.236,0 2.24,1.004 2.24,2.24Z"/><path d="M5.327,16.748c-0,0.358 -0.291,0.648 -0.649,0.648c0,0 0,0 0,0c-2.582,0 -4.678,-2.096 -4.678,-4.678c0,0 0,-8.04 0,-8.04c0,-2.582 2.096,-4.678 4.678,-4.678l6.918,0c2.168,0 3.994,1.478 4.523,3.481c0.038,0.149 0.005,0.306 -0.09,0.428c-0.094,0.121 -0.239,0.191 -0.392,0.191c-0.451,0.005 -1.057,0.005 -1.457,0.005c-0.238,0 -0.455,-0.14 -0.553,-0.357c-0.348,-0.773 -1.128,-1.31 -2.031,-1.31c-0,0 -6.918,0 -6.918,0c-1.236,0 -2.24,1.004 -2.24,2.24l0,8.04c0,1.236 1.004,2.24 2.24,2.24l0,-0c0.358,-0 0.649,0.29 0.649,0.648c-0,0.353 -0,0.789 -0,1.142Z" style="fill-opacity:0.6;"/></svg>`
|
||||
static successIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M8.084,16.111c-0.09,0.09 -0.212,0.141 -0.34,0.141c-0.127,-0 -0.249,-0.051 -0.339,-0.141c-0.746,-0.746 -2.538,-2.538 -3.525,-3.525c-0.375,-0.375 -0.983,-0.375 -1.357,0c-0.178,0.178 -0.369,0.369 -0.547,0.547c-0.375,0.375 -0.375,0.982 -0,1.357c1.135,1.135 3.422,3.422 4.75,4.751c0.27,0.27 0.637,0.421 1.018,0.421c0.382,0 0.749,-0.151 1.019,-0.421c2.731,-2.732 10.166,-10.167 12.454,-12.455c0.375,-0.375 0.375,-0.982 -0,-1.357c-0.178,-0.178 -0.369,-0.369 -0.547,-0.547c-0.375,-0.375 -0.982,-0.375 -1.357,0c-2.273,2.273 -9.567,9.567 -11.229,11.229Z"/></svg>`
|
||||
static successDuration = 980
|
||||
static init() {
|
||||
$(function() {
|
||||
$(document).ready(function() {
|
||||
if(navigator.clipboard) {
|
||||
const fragments = document.getElementsByClassName("fragment")
|
||||
for(const fragment of fragments) {
|
||||
const fragmentWrapper = document.createElement("div")
|
||||
fragmentWrapper.className = "doxygen-awesome-fragment-wrapper"
|
||||
const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button")
|
||||
fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
|
||||
fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title
|
||||
|
||||
fragment.parentNode.replaceChild(fragmentWrapper, fragment)
|
||||
fragmentWrapper.appendChild(fragment)
|
||||
fragmentWrapper.appendChild(fragmentCopyButton)
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
copyContent() {
|
||||
const content = this.previousSibling.cloneNode(true)
|
||||
// filter out line number from file listings
|
||||
content.querySelectorAll(".lineno, .ttc").forEach((node) => {
|
||||
node.remove()
|
||||
})
|
||||
let textContent = content.textContent
|
||||
// remove trailing newlines that appear in file listings
|
||||
let numberOfTrailingNewlines = 0
|
||||
while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') {
|
||||
numberOfTrailingNewlines++;
|
||||
}
|
||||
textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines)
|
||||
navigator.clipboard.writeText(textContent);
|
||||
this.classList.add("success")
|
||||
this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon
|
||||
window.setTimeout(() => {
|
||||
this.classList.remove("success")
|
||||
this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
|
||||
}, DoxygenAwesomeFragmentCopyButton.successDuration);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton)
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/**
|
||||
|
||||
Doxygen Awesome
|
||||
https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
Copyright (c) 2022 - 2025 jothepro
|
||||
|
||||
*/
|
||||
|
||||
class DoxygenAwesomeParagraphLink {
|
||||
// Icon from https://fonts.google.com/icons
|
||||
// Licensed under the Apache 2.0 license:
|
||||
// https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
static icon = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z"/></svg>`
|
||||
static title = "Permanent Link"
|
||||
static init() {
|
||||
$(function() {
|
||||
$(document).ready(function() {
|
||||
document.querySelectorAll(".contents a.anchor[id], .contents .groupheader > a[id]").forEach((node) => {
|
||||
let anchorlink = document.createElement("a")
|
||||
anchorlink.setAttribute("href", `#${node.getAttribute("id")}`)
|
||||
anchorlink.setAttribute("title", DoxygenAwesomeParagraphLink.title)
|
||||
anchorlink.classList.add("anchorlink")
|
||||
node.classList.add("anchor")
|
||||
anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon
|
||||
node.parentElement.appendChild(anchorlink)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/**
|
||||
|
||||
Doxygen Awesome
|
||||
https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
Copyright (c) 2021 - 2025 jothepro
|
||||
|
||||
*/
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
|
||||
#MSearchBox {
|
||||
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px);
|
||||
}
|
||||
|
||||
#MSearchField {
|
||||
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/**
|
||||
|
||||
Doxygen Awesome
|
||||
https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
Copyright (c) 2021 - 2025 jothepro
|
||||
|
||||
*/
|
||||
|
||||
html {
|
||||
/* side nav width. MUST be = `TREEVIEW_WIDTH`.
|
||||
* Make sure it is wide enough to contain the page title (logo + title + version)
|
||||
*/
|
||||
--side-nav-fixed-width: 335px;
|
||||
--menu-display: none;
|
||||
|
||||
--top-height: 120px;
|
||||
--toc-sticky-top: -25px;
|
||||
--toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px);
|
||||
}
|
||||
|
||||
#projectname {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
html {
|
||||
--searchbar-background: var(--page-background-color);
|
||||
}
|
||||
|
||||
#side-nav {
|
||||
min-width: var(--side-nav-fixed-width);
|
||||
max-width: var(--side-nav-fixed-width);
|
||||
top: var(--top-height);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#nav-tree, #side-nav {
|
||||
height: calc(100vh - var(--top-height)) !important;
|
||||
}
|
||||
|
||||
#top {
|
||||
display: block;
|
||||
border-bottom: none;
|
||||
height: var(--top-height);
|
||||
margin-bottom: calc(0px - var(--top-height));
|
||||
max-width: var(--side-nav-fixed-width);
|
||||
overflow: hidden;
|
||||
background: var(--side-nav-background);
|
||||
}
|
||||
|
||||
#main-nav {
|
||||
float: left;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.ui-resizable-handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-resizable-e {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
#nav-path {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
left: calc(var(--side-nav-fixed-width) + 1px);
|
||||
bottom: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#doc-content {
|
||||
height: calc(100vh - 31px) !important;
|
||||
padding-bottom: calc(3 * var(--spacing-large));
|
||||
padding-top: calc(var(--top-height) - 80px);
|
||||
box-sizing: border-box;
|
||||
margin-left: var(--side-nav-fixed-width) !important;
|
||||
}
|
||||
|
||||
#MSearchBox {
|
||||
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)));
|
||||
}
|
||||
|
||||
#MSearchField {
|
||||
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px);
|
||||
}
|
||||
|
||||
#MSearchResultsWindow {
|
||||
left: var(--spacing-medium) !important;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
#nav-sync {
|
||||
bottom: 4px;
|
||||
right: auto;
|
||||
left: 300px;
|
||||
width: 35px;
|
||||
top: auto !important;
|
||||
user-select: none;
|
||||
position: fixed
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@
|
||||
#ifndef OPENVDB_MATH_HALF_HAS_BEEN_INCLUDED
|
||||
#define OPENVDB_MATH_HALF_HAS_BEEN_INCLUDED
|
||||
|
||||
/// @file half.h
|
||||
/// @file Half.h
|
||||
/// The half type is a 16-bit floating number, compatible with the
|
||||
/// IEEE 754-2008 binary16 type.
|
||||
///
|
||||
@@ -21,6 +21,7 @@
|
||||
/// We assume that a float, f, is an IEEE 754 single-precision
|
||||
/// floating point number, whose bits are arranged as follows:
|
||||
///
|
||||
/// <pre>
|
||||
/// 31 (msb)
|
||||
/// |
|
||||
/// | 30 23
|
||||
@@ -30,28 +31,36 @@
|
||||
/// X XXXXXXXX XXXXXXXXXXXXXXXXXXXXXXX
|
||||
///
|
||||
/// s e m
|
||||
/// </pre>
|
||||
///
|
||||
/// S is the sign-bit, e is the exponent and m is the significand.
|
||||
///
|
||||
/// If e is between 1 and 254, f is a normalized number:
|
||||
///
|
||||
/// <pre>
|
||||
/// s e-127
|
||||
/// f = (-1) * 2 * 1.m
|
||||
/// </pre>
|
||||
///
|
||||
/// If e is 0, and m is not zero, f is a denormalized number:
|
||||
///
|
||||
/// <pre>
|
||||
/// s -126
|
||||
/// f = (-1) * 2 * 0.m
|
||||
/// </pre>
|
||||
///
|
||||
/// If e and m are both zero, f is zero:
|
||||
///
|
||||
/// <pre>
|
||||
/// f = 0.0
|
||||
/// </pre>
|
||||
///
|
||||
/// If e is 255, f is an "infinity" or "not a number" (NAN),
|
||||
/// depending on whether m is zero or not.
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// <pre>
|
||||
/// 0 00000000 00000000000000000000000 = 0.0
|
||||
/// 0 01111110 00000000000000000000000 = 0.5
|
||||
/// 0 01111111 00000000000000000000000 = 1.0
|
||||
@@ -62,11 +71,13 @@
|
||||
/// 1 11111111 00000000000000000000000 = -infinity
|
||||
/// 0 11111111 10000000000000000000000 = NAN
|
||||
/// 1 11111111 11111111111111111111111 = NAN
|
||||
/// </pre>
|
||||
///
|
||||
/// **Representation of a 16-bit half:**
|
||||
///
|
||||
/// Here is the bit-layout for a half number, h:
|
||||
///
|
||||
/// <pre>
|
||||
/// 15 (msb)
|
||||
/// |
|
||||
/// | 14 10
|
||||
@@ -76,28 +87,36 @@
|
||||
/// X XXXXX XXXXXXXXXX
|
||||
///
|
||||
/// s e m
|
||||
/// </pre>
|
||||
///
|
||||
/// S is the sign-bit, e is the exponent and m is the significand.
|
||||
///
|
||||
/// If e is between 1 and 30, h is a normalized number:
|
||||
///
|
||||
/// <pre>
|
||||
/// s e-15
|
||||
/// h = (-1) * 2 * 1.m
|
||||
/// </pre>
|
||||
///
|
||||
/// If e is 0, and m is not zero, h is a denormalized number:
|
||||
///
|
||||
/// <pre>
|
||||
/// S -14
|
||||
/// h = (-1) * 2 * 0.m
|
||||
/// </pre>
|
||||
///
|
||||
/// If e and m are both zero, h is zero:
|
||||
///
|
||||
/// <pre>
|
||||
/// h = 0.0
|
||||
/// </pre>
|
||||
///
|
||||
/// If e is 31, h is an "infinity" or "not a number" (NAN),
|
||||
/// depending on whether m is zero or not.
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// <pre>
|
||||
/// 0 00000 0000000000 = 0.0
|
||||
/// 0 01110 0000000000 = 0.5
|
||||
/// 0 01111 0000000000 = 1.0
|
||||
@@ -108,13 +127,14 @@
|
||||
/// 1 11111 0000000000 = -infinity
|
||||
/// 0 11111 1000000000 = NAN
|
||||
/// 1 11111 1111111111 = NAN
|
||||
/// </pre>
|
||||
///
|
||||
/// **Conversion via Lookup Table:**
|
||||
///
|
||||
/// Converting from half to float is performed by default using a
|
||||
/// lookup table. There are only 65,536 different half numbers; each
|
||||
/// of these numbers has been converted and stored in a table pointed
|
||||
/// to by the ``imath_half_to_float_table`` pointer.
|
||||
/// to by the `imath_half_to_float_table` pointer.
|
||||
///
|
||||
/// Prior to Imath v3.1, conversion from float to half was
|
||||
/// accomplished with the help of an exponent look table, but this is
|
||||
@@ -135,30 +155,25 @@
|
||||
/// 65,536-entry lookup table.
|
||||
///
|
||||
/// The lookup table symbol is included in the compilation even if
|
||||
/// ``IMATH_HALF_USE_LOOKUP_TABLE`` is false, because application code
|
||||
/// using the exported ``half.h`` may choose to enable the use of the table.
|
||||
/// `IMATH_HALF_USE_LOOKUP_TABLE` is false, because application code
|
||||
/// using the exported `half.h` may choose to enable the use of the table.
|
||||
///
|
||||
/// An implementation can eliminate the table from compilation by
|
||||
/// defining the ``IMATH_HALF_NO_LOOKUP_TABLE`` preprocessor symbol.
|
||||
/// Simply add:
|
||||
/// defining the `IMATH_HALF_NO_LOOKUP_TABLE` preprocessor symbol.
|
||||
/// Simply add `#define IMATH_HALF_NO_LOOKUP_TABLE` before including
|
||||
/// `half.h`, or define the symbol on the compile command line.
|
||||
///
|
||||
/// #define IMATH_HALF_NO_LOOKUP_TABLE
|
||||
///
|
||||
/// before including ``half.h``, or define the symbol on the compile
|
||||
/// command line.
|
||||
///
|
||||
/// Furthermore, an implementation wishing to receive ``FE_OVERFLOW``
|
||||
/// and ``FE_UNDERFLOW`` floating point exceptions when converting
|
||||
/// Furthermore, an implementation wishing to receive `FE_OVERFLOW`
|
||||
/// and `FE_UNDERFLOW` floating point exceptions when converting
|
||||
/// float to half by the bit-shift algorithm can define the
|
||||
/// preprocessor symbol ``IMATH_HALF_ENABLE_FP_EXCEPTIONS`` prior to
|
||||
/// including ``half.h``:
|
||||
///
|
||||
/// #define IMATH_HALF_ENABLE_FP_EXCEPTIONS
|
||||
/// preprocessor symbol `IMATH_HALF_ENABLE_FP_EXCEPTIONS` (that is,
|
||||
/// `#define IMATH_HALF_ENABLE_FP_EXCEPTIONS`) prior to including `half.h`.
|
||||
///
|
||||
/// **Conversion Performance Comparison:**
|
||||
///
|
||||
/// Testing on a Core i9, the timings are approximately:
|
||||
///
|
||||
/// <pre>
|
||||
/// half to float
|
||||
/// - table: 0.71 ns / call
|
||||
/// - no table: 1.06 ns / call
|
||||
@@ -168,6 +183,7 @@
|
||||
/// - original: 5.2 ns / call
|
||||
/// - no exp table + opt: 1.27 ns / call
|
||||
/// - f16c: 0.45 ns / call
|
||||
/// </pre>
|
||||
///
|
||||
/// **Note:** the timing above depends on the distribution of the
|
||||
/// floats in question.
|
||||
@@ -206,6 +222,12 @@ namespace internal {
|
||||
// Thus any attempt to link two versions of OpenVDB with different
|
||||
// namespaces will clash due to redefinition with a new type.
|
||||
// The default was not to use a lookup table.
|
||||
//
|
||||
/// @cond
|
||||
/// These internal half-float configuration and limit macros are excluded from
|
||||
/// the docs: the `half` type itself lives in the (undocumented) `internal`
|
||||
/// namespace, and documenting the `#define`s makes Doxygen auto-link the
|
||||
/// literal `#define` token (an unresolvable "link to 'define'" warning).
|
||||
#undef IMATH_HALF_USE_LOOKUP_TABLE
|
||||
#define IMATH_HALF_NO_LOOKUP_TABLE
|
||||
|
||||
@@ -227,7 +249,7 @@ namespace internal {
|
||||
#define VDB_HALF_MIN 6.10351562e-05f
|
||||
/// Largest positive half
|
||||
#define VDB_HALF_MAX 65504.0f
|
||||
/// Smallest positive e for which ``half(1.0 + e) != half(1.0)``
|
||||
/// Smallest positive e for which `half(1.0 + e) != half(1.0)`
|
||||
#define VDB_HALF_EPSILON 0.00097656f
|
||||
#else
|
||||
/// Smallest positive denormalized half
|
||||
@@ -238,7 +260,7 @@ namespace internal {
|
||||
#define VDB_HALF_MIN 6.10351562e-05f
|
||||
/// Largest positive half
|
||||
#define VDB_HALF_MAX 65504.0
|
||||
/// Smallest positive e for which ``half(1.0 + e) != half(1.0)``
|
||||
/// Smallest positive e for which `half(1.0 + e) != half(1.0)`
|
||||
#define VDB_HALF_EPSILON 0.00097656
|
||||
#endif
|
||||
|
||||
@@ -246,19 +268,19 @@ namespace internal {
|
||||
#define VDB_HALF_MANT_DIG 11
|
||||
/// Number of base 10 digits that can be represented without change:
|
||||
///
|
||||
/// ``floor( (HALF_MANT_DIG - 1) * log10(2) ) => 3.01... -> 3``
|
||||
/// `floor( (HALF_MANT_DIG - 1) * log10(2) ) => 3.01... -> 3`
|
||||
#define VDB_HALF_DIG 3
|
||||
/// Number of base-10 digits that are necessary to uniquely represent
|
||||
/// all distinct values:
|
||||
///
|
||||
/// ``ceil(HALF_MANT_DIG * log10(2) + 1) => 4.31... -> 5``
|
||||
/// `ceil(HALF_MANT_DIG * log10(2) + 1) => 4.31... -> 5`
|
||||
#define VDB_HALF_DECIMAL_DIG 5
|
||||
/// Base of the exponent
|
||||
#define VDB_HALF_RADIX 2
|
||||
/// Minimum negative integer such that ``HALF_RADIX`` raised to the power
|
||||
/// Minimum negative integer such that `HALF_RADIX` raised to the power
|
||||
/// of one less than that integer is a normalized half
|
||||
#define VDBB_HALF_DENORM_MIN_EXP -13
|
||||
/// Maximum positive integer such that ``HALF_RADIX`` raised to the power
|
||||
/// Maximum positive integer such that `HALF_RADIX` raised to the power
|
||||
/// of one less than that integer is a normalized half
|
||||
#define VDB_HALF_MAX_EXP 16
|
||||
/// Minimum positive integer such that 10 raised to that power is a
|
||||
@@ -267,6 +289,7 @@ namespace internal {
|
||||
/// Maximum positive integer such that 10 raised to that power is a
|
||||
/// normalized half
|
||||
#define VDB_HALF_MAX_10_EXP 4
|
||||
/// @endcond
|
||||
|
||||
/// a type for both C-only programs and C++ to use the same utilities
|
||||
typedef union imath_half_uif
|
||||
@@ -0,0 +1,191 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
|
||||
Copyright 2012-2019 Agner Fog.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,14 @@
|
||||
# version2
|
||||
Vector Class Library, latest version
|
||||
|
||||
This is a C++ class library for using the Single Instruction Multiple Data (SIMD) instructions to improve performance on modern microprocessors with the x86 or x86/64 instruction set on Windows, Linux, and Mac platforms. There are no plans to support ARM or other instruction sets.
|
||||
|
||||
[Latest release](https://github.com/vectorclass/version2/releases)
|
||||
|
||||
[Download manual](https://github.com/vectorclass/manual/raw/master/vcl_manual.pdf)
|
||||
|
||||
[Add-on packages for particular applications](https://github.com/vectorclass/add-on)
|
||||
|
||||
[Getting-started video.](https://www.youtube.com/watch?v=TKjYdLIMTrI) Video blogger Christopher Rose has made this nice video telling how to get started with the Vector Class Library.
|
||||
|
||||
**Help:** You may ask for programming help on [StackOverflow](https://stackoverflow.com) using the tag vector-class-library.
|
||||
@@ -0,0 +1,244 @@
|
||||
Change log for Vector class library
|
||||
-----------------------------------
|
||||
|
||||
2026-04-14 version 2.02.03
|
||||
* conversions Vec8h <-> Vec8f and Vec16h <-> Vec16f converted to templates in vectorfp16e.h
|
||||
|
||||
2023-07-04 version 2.02.02
|
||||
* remove various MS compiler warnings
|
||||
|
||||
2022-06-03 version 2.02.01
|
||||
* minor bug fixes and updates
|
||||
|
||||
2022-07-20 version 2.02.00
|
||||
* support half precision floating point vectors
|
||||
* new trigonometric functions sinpi, cospi, sincospi, tanpi
|
||||
* floating point modulo and remainder functions
|
||||
* functions extend_z and concatenate2
|
||||
* functions compress_saturated with one integer parameter
|
||||
* support for Intel compiler
|
||||
* fix bug for NAN inputs in operator > and >=
|
||||
* fix bug for underflow in pow function
|
||||
* fix unsafe optimization in store_partial function
|
||||
* new add-on package with container class and matrix class templates
|
||||
* major improvements in add-on package 'complex'
|
||||
* minor improvements in add-on packages 'random' and 'vector3d'
|
||||
|
||||
2021-08-18 version 2.01.04
|
||||
* fix bug in elementtype for Vec8uq
|
||||
* improved optimization of permute functions in MS compiler by using constexpr
|
||||
|
||||
2020-11-04 version 2.01.03
|
||||
* fix overflow in sin, cos, tan for large x
|
||||
* fix bug in is_nan for instruction sets prior to AVX
|
||||
* warning for MS compiler versions with poor support for AVX512
|
||||
|
||||
2020-04-11 version 2.01.02
|
||||
* only minor fixes
|
||||
|
||||
2020-02-25 version 2.01.01
|
||||
* added function store_nt
|
||||
* New dispatch_example1.cpp, dispatch_example2.cpp
|
||||
|
||||
2019-11-23 version 2.01.00
|
||||
* problem with performance of permute and blend functions fixed by avoiding
|
||||
unions in constexpr functions
|
||||
|
||||
2019-10-31 version 2.00.02
|
||||
* bug fix in permute function
|
||||
* is_nan function improved
|
||||
* templates constant4ui etc. improved
|
||||
|
||||
2019-08-02 version 2.00
|
||||
Derived from version 1.40
|
||||
* use C++17
|
||||
* use compact boolean vectors of all sizes if AVX512VL is enabled
|
||||
* permute and blend functions improved, using C++17 metaprogramming features
|
||||
* deprecated functions removed
|
||||
|
||||
2019-08-02 version 1.40
|
||||
* hosted on github
|
||||
* license changed to Apache 2.0.
|
||||
* added classes Vec64c, Vec64uc, Vec32s, Vec32us
|
||||
* test bench and scripts for automatic testing of VCL
|
||||
* new functions: maximum, minimum, to_float, to_double
|
||||
* conversion of bitfields to boolean vectors with load_bits. This replaces to_vec4ib etc.
|
||||
* shift_bytes_up/down functions changed to templates (old versions deprecated)
|
||||
* removed VECTORMATH define. vectormath_lib.h rewritten. svmlpatch.lib added.
|
||||
* many improvements and bug fixes
|
||||
* renamed functions: round_to_int and round_to_int64 functions renamed to roundi,
|
||||
* renamed functions: the type letter is removed from all permute and blend functions,
|
||||
e.g. permute4f renamed to permute4.
|
||||
These changes are made to facilitate generic template programming
|
||||
* renamed functions: to_Vec.. Replaced by load_bits member functions
|
||||
* deprecated functions: set_bit, get_bit
|
||||
* deprecated bit vector classes: Vec128b, Vec256b, Vec512b
|
||||
|
||||
2017-07-27 version 1.30
|
||||
* fixed bug in permute8f for a particular combination of indexes
|
||||
|
||||
2017-05-10 version 1.29
|
||||
* Reversed Apple Clang patch in version 1.28 because the problem has reoccurred in
|
||||
later versions of Clang
|
||||
|
||||
2017-05-02 version 1.28
|
||||
* Fixed problem with Apple Clang version 6.2 in vectorf128.h
|
||||
* Fixed return type for Vec8sb operator > (Vec8us, Vec8us)
|
||||
* cpuid function modified in instrset_detect.cpp
|
||||
|
||||
2017-02-19 version 1.27
|
||||
* fixed problem with scatter functions in MS Visual Studio
|
||||
|
||||
2016-12-21 version 1.26
|
||||
* added constant4ui template
|
||||
* fixed error for complexvec.h with clang
|
||||
* fixed error in vectormath_exp.h for MAX_VECTOR_SIZE < 512
|
||||
|
||||
2016-11-25 version 1.25
|
||||
* scatter functions
|
||||
* new functions to_float for unsigned integer vectors
|
||||
* instrset_detect function can detect AVX512VL, AVX512BW, AVX512DQ
|
||||
* functions hasF16C and hasAVX512ER for detecting instruction set extensions
|
||||
* fix bugs in horizontal_and and pow(0,0) for AVX512
|
||||
* functions improved for AVX512 and AVX512VL: pow, approx_recipr,
|
||||
approx_rsqrt
|
||||
* functions improved for AVX512DQ: 64 bit multiplication, to_double,
|
||||
32 and 64 bit rotate_left, round_to_int64, truncate_to_int64
|
||||
* functions improved for AVX512ER: approx_recipr, approx_rsqrt,
|
||||
exponential functions
|
||||
|
||||
2016-10-31 version 1.24
|
||||
* fix bug in Vec8uq constructor in vectori512e.h
|
||||
|
||||
2016-09-27 version 1.23
|
||||
* temporary fix of a problem in Clang version 3.9 inserted in vectorf128.h
|
||||
|
||||
2016-05-03 version 1.22
|
||||
* added optional namespace
|
||||
* fixed problem with decimal.h
|
||||
|
||||
2016-04-24 version 1.21
|
||||
* fix problems with XOP option in gcc
|
||||
* improved horizontal_and/or for sse2
|
||||
* improved Vec2q and Vec4q constructor on Microsoft Visual Studio 2015
|
||||
* removed warnings by gcc option -Wcast-qual
|
||||
|
||||
2015-12-04 version 1.20
|
||||
* round functions: suppress precision exception under SSE4.1 and higher
|
||||
* fix compiler problems with AVX512 multiplication in gcc version 5.1
|
||||
* fix compiler problems with pow function in Microsoft Visual Studio 2015
|
||||
|
||||
2015-11-14 version 1.19
|
||||
* fix various problems with Clang compiler
|
||||
|
||||
2015-09-25 version 1.18
|
||||
* fix compiler error for Vec8s divide_by_i(Vec8s const & x) under Clang compiler
|
||||
* fix error in Vec4d::size() in vectorf256e.h
|
||||
|
||||
2015-07-31 version 1.17
|
||||
* improved operator > for Vec4uq
|
||||
* more special cases in blend4q
|
||||
* nan_code functions made static inline
|
||||
* template parameter BTYPE renamed to BVTYPE in mathematical functions to avoid clash
|
||||
with macro named BTYPE in winnt.h
|
||||
* fixed bug in Vec4db constructor
|
||||
|
||||
2014-10-24 version 1.16
|
||||
* workaround for problem in Clang compiler extended to version 3.09 because not
|
||||
fixed yet by Clang (vectorf128.h line 134)
|
||||
* recognize problem with Apple version of Clang reporting wrong version number
|
||||
* remove various minor problems with Clang
|
||||
* function pow(vector, int) modified to strengthen type checking and avoid compiler warnings
|
||||
* manual discusses dynamic allocation of arrays of vectors
|
||||
* various minor changes
|
||||
|
||||
2014-10-17 version 1.15
|
||||
* added files ranvec1.h and ranvec1.cpp for random number generator
|
||||
* constructors to make boolean vectors from their elements
|
||||
* constructors and = operators to broadcast boolean scalar into boolean vectors
|
||||
* various lookup functions improved
|
||||
* operators &, |, ^, ~, etc. defined for various boolean vectors to avoid converson
|
||||
to integer vectors
|
||||
* nmul_add functions
|
||||
* mul_add etc. moved to main header files
|
||||
* explicit fused multiply-and-add used in math functions to improve performance
|
||||
on compilers that don't automatically insert FMA
|
||||
|
||||
2014-07-24 version 1.14
|
||||
* support for AVX-512f instruction set and 512-bit vectors:
|
||||
Vec16i, Vec16ui, Vec8q, Vec8uq, Vec16f, Vec8d, and corresponding boolean vectors
|
||||
* new define MAX_VECTOR_SIZE, valid values are 128, 256 and 512
|
||||
* added hyperbolic functions sinh, cosh, tanh, asinh, acosh, atanh
|
||||
* size() member function on all vector classes returns the number of elements
|
||||
* functions for conversion between boolean vectors and integer bitfields
|
||||
* extracting an element from a boolean vector now returns a bool, not an int
|
||||
* improved precision in exp2 and exp10 functions
|
||||
* various bug fixes
|
||||
|
||||
2014-05-11 version 1.13
|
||||
* pow function improved
|
||||
* mul_add, mul_sub, mul_sub_x functions
|
||||
* propagation of error codes through nan_code function
|
||||
* "denormal" renamed to "subnormal" everywhere, in accordance with IEEE 754-2008 standard
|
||||
|
||||
2014-04-20 version 1.12
|
||||
* inline implementation of mathematical functions added (vectormath_exp.h vectormath_trig.h
|
||||
vectormath_common.h)
|
||||
* vectormath.h renamed to vectormath_lib.h because a new alternative is added
|
||||
* gather functions with constant indexes
|
||||
* function sign_combine
|
||||
* function pow_const(vector, const int)
|
||||
* function pow_ratio(vector, const int, const int)
|
||||
* functions horizontal_find_first, horizontal_count
|
||||
* function recipr_sqrt removed
|
||||
* functions round_to_int64_limited, truncate_to_int64_limited, to_double_limited
|
||||
* function cubic_root renamed to cbrt
|
||||
* function atan(vector,vector) renamed to atan2
|
||||
* function if_mul
|
||||
* function Vec4i round_to_int(Vec2d)
|
||||
* operator & (float vector, boolean vector)
|
||||
* operator &= (int vector, int vector)
|
||||
* removed constructor Vec128b(int) and Vec256b(int) to avoid implicit conversion
|
||||
* removed signalling nan function
|
||||
* minor improvements in various blend and lookup functions
|
||||
|
||||
2014-03-01 version 1.11
|
||||
* fixed missing unsigned operators >>= in vectori256.h
|
||||
|
||||
2013-10-04 version 1.10
|
||||
* clear distinction between boolean vectors and integer vectors for the sake of
|
||||
compatibility with mask registers in forthcoming AVX512 instruction set
|
||||
* added function if_add
|
||||
* tentative support for clang version 3.3 with workaround for bugs
|
||||
* remove ambiguity for builtin m128i operator == in clang compiler.
|
||||
* problems in clang compiler, bug reports filed at clang
|
||||
(http://llvm.org/bugs/show_bug.cgi?id=17164, 17312)
|
||||
* instrset.h fixes problem with macros named min and max in MS windows.h
|
||||
* workaround problem in MS Visual Studio 11.0. Bug report 735861 and 804274
|
||||
* minor bug fixes
|
||||
|
||||
2013-03-31 version 1.03 beta
|
||||
* bug fix for Vec2d cos (Vec2d const & x), VECTORMATH = 1
|
||||
|
||||
2012-08-01 version 1.02 beta
|
||||
* added file vector3d.h for 3-dimensional vectors
|
||||
* added file complexvec.h for complex numbers and complex vectors
|
||||
* added file quaternion.h for quaternions
|
||||
* added function change_sign for floating point vectors
|
||||
* added operators +, -, *, / between floating point vectors and scalars to remove
|
||||
overloading ambiguity
|
||||
|
||||
2012-07-08 version 1.01 beta
|
||||
* added file decimal.h with Number <-> string conversion functions:
|
||||
bin2bcd, bin2ascii, bin2hex_ascii, ascii2bin
|
||||
* added andnot function for boolean vectors
|
||||
* added functions shift_bytes_up and shift_bytes_down
|
||||
* added operators for unsigned integer vector classes: >>=, &, &&, |, ||, ^, ~
|
||||
* inteldispatchpatch.cpp removed. Use asmlib instead (www.agner.org/optimize/#asmlib)
|
||||
* prefix ++ and -- operators now return a reference, postfix operators return a value
|
||||
* various improvements in permute and blend functions
|
||||
* minor improvement in abs function
|
||||
* added version number to VECTORCLASS_H
|
||||
|
||||
2012-05-30 version 1.00 beta
|
||||
* first public release at www.agner.org
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,828 @@
|
||||
/************************** vector_convert.h *******************************
|
||||
* Author: Agner Fog
|
||||
* Date created: 2014-07-23
|
||||
* Last modified: 2022-07-20
|
||||
* Version: 2.02.00
|
||||
* Project: vector class library
|
||||
* Description:
|
||||
* Header file for conversion between different vector classes with different
|
||||
* sizes. Also includes verious generic template functions.
|
||||
*
|
||||
* (c) Copyright 2012-2022 Agner Fog.
|
||||
* Apache License version 2.0 or later.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef VECTOR_CONVERT_H
|
||||
#define VECTOR_CONVERT_H
|
||||
|
||||
#ifndef VECTORCLASS_H
|
||||
#include "vectorclass.h"
|
||||
#endif
|
||||
|
||||
#if VECTORCLASS_H < 20200
|
||||
#error Incompatible versions of vector class library mixed
|
||||
#endif
|
||||
|
||||
#ifdef VCL_NAMESPACE
|
||||
namespace VCL_NAMESPACE {
|
||||
#endif
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Extend from 128 to 256 bit vectors
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#if INSTRSET >= 8 // AVX2. 256 bit integer vectors
|
||||
|
||||
// sign extend
|
||||
static inline Vec16s extend (Vec16c const a) {
|
||||
return _mm256_cvtepi8_epi16(a);
|
||||
}
|
||||
|
||||
// zero extend
|
||||
static inline Vec16us extend (Vec16uc const a) {
|
||||
return _mm256_cvtepu8_epi16(a);
|
||||
}
|
||||
|
||||
// sign extend
|
||||
static inline Vec8i extend (Vec8s const a) {
|
||||
return _mm256_cvtepi16_epi32(a);
|
||||
}
|
||||
|
||||
// zero extend
|
||||
static inline Vec8ui extend (Vec8us const a) {
|
||||
return _mm256_cvtepu16_epi32(a);
|
||||
}
|
||||
|
||||
// sign extend
|
||||
static inline Vec4q extend (Vec4i const a) {
|
||||
return _mm256_cvtepi32_epi64(a);
|
||||
}
|
||||
|
||||
// zero extend
|
||||
static inline Vec4uq extend (Vec4ui const a) {
|
||||
return _mm256_cvtepu32_epi64(a);
|
||||
}
|
||||
|
||||
|
||||
#else // no AVX2. 256 bit integer vectors are emulated
|
||||
|
||||
// sign extend and zero extend functions:
|
||||
static inline Vec16s extend (Vec16c const a) {
|
||||
return Vec16s(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
static inline Vec16us extend (Vec16uc const a) {
|
||||
return Vec16us(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
static inline Vec8i extend (Vec8s const a) {
|
||||
return Vec8i(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
static inline Vec8ui extend (Vec8us const a) {
|
||||
return Vec8ui(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
static inline Vec4q extend (Vec4i const a) {
|
||||
return Vec4q(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
static inline Vec4uq extend (Vec4ui const a) {
|
||||
return Vec4uq(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
#endif // AVX2
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Conversions between float and double
|
||||
*
|
||||
*****************************************************************************/
|
||||
#if INSTRSET >= 7 // AVX. 256 bit float vectors
|
||||
|
||||
// float to double
|
||||
static inline Vec4d to_double (Vec4f const a) {
|
||||
return _mm256_cvtps_pd(a);
|
||||
}
|
||||
|
||||
// double to float
|
||||
static inline Vec4f to_float (Vec4d const a) {
|
||||
return _mm256_cvtpd_ps(a);
|
||||
}
|
||||
|
||||
#else // no AVX2. 256 bit float vectors are emulated
|
||||
|
||||
// float to double
|
||||
static inline Vec4d to_double (Vec4f const a) {
|
||||
Vec2d lo = _mm_cvtps_pd(a);
|
||||
Vec2d hi = _mm_cvtps_pd(_mm_movehl_ps(a, a));
|
||||
return Vec4d(lo,hi);
|
||||
}
|
||||
|
||||
// double to float
|
||||
static inline Vec4f to_float (Vec4d const a) {
|
||||
Vec4f lo = _mm_cvtpd_ps(a.get_low());
|
||||
Vec4f hi = _mm_cvtpd_ps(a.get_high());
|
||||
return _mm_movelh_ps(lo, hi);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Reduce from 256 to 128 bit vectors
|
||||
*
|
||||
*****************************************************************************/
|
||||
#if INSTRSET >= 10 // AVX512VL
|
||||
|
||||
// compress functions. overflow wraps around
|
||||
static inline Vec16c compress (Vec16s const a) {
|
||||
return _mm256_cvtepi16_epi8(a);
|
||||
}
|
||||
|
||||
static inline Vec16uc compress (Vec16us const a) {
|
||||
return _mm256_cvtepi16_epi8(a);
|
||||
}
|
||||
|
||||
static inline Vec8s compress (Vec8i const a) {
|
||||
return _mm256_cvtepi32_epi16(a);
|
||||
}
|
||||
|
||||
static inline Vec8us compress (Vec8ui const a) {
|
||||
return _mm256_cvtepi32_epi16(a);
|
||||
}
|
||||
|
||||
static inline Vec4i compress (Vec4q const a) {
|
||||
return _mm256_cvtepi64_epi32(a);
|
||||
}
|
||||
|
||||
static inline Vec4ui compress (Vec4uq const a) {
|
||||
return _mm256_cvtepi64_epi32(a);
|
||||
}
|
||||
|
||||
// compress_saturated functions. overflow saturates
|
||||
static inline Vec16c compress_saturated (Vec16s const a) {
|
||||
return _mm256_cvtsepi16_epi8(a);
|
||||
}
|
||||
|
||||
static inline Vec16uc compress_saturated (Vec16us const a) {
|
||||
return _mm256_cvtusepi16_epi8(a);
|
||||
}
|
||||
|
||||
static inline Vec8s compress_saturated (Vec8i const a) {
|
||||
return _mm256_cvtsepi32_epi16(a);
|
||||
}
|
||||
|
||||
static inline Vec8us compress_saturated (Vec8ui const a) {
|
||||
return _mm256_cvtusepi32_epi16(a);
|
||||
}
|
||||
|
||||
static inline Vec4i compress_saturated (Vec4q const a) {
|
||||
return _mm256_cvtsepi64_epi32(a);
|
||||
}
|
||||
|
||||
static inline Vec4ui compress_saturated (Vec4uq const a) {
|
||||
return _mm256_cvtusepi64_epi32(a);
|
||||
}
|
||||
|
||||
|
||||
#else // no AVX512
|
||||
|
||||
// compress functions. overflow wraps around
|
||||
static inline Vec16c compress (Vec16s const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec16uc compress (Vec16us const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec8s compress (Vec8i const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec8us compress (Vec8ui const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec4i compress (Vec4q const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec4ui compress (Vec4uq const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
// compress_saturated functions. overflow saturates
|
||||
static inline Vec16c compress_saturated (Vec16s const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec16uc compress_saturated (Vec16us const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec8s compress_saturated (Vec8i const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec8us compress_saturated (Vec8ui const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec4i compress_saturated (Vec4q const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec4ui compress_saturated (Vec4uq const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
#endif // AVX512
|
||||
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Reduce from 512 to 256 bit vectors
|
||||
*
|
||||
*****************************************************************************/
|
||||
#if INSTRSET >= 10 // AVX512VL
|
||||
|
||||
// compress_saturated functions. overflow saturates
|
||||
static inline Vec32c compress_saturated (Vec32s const a) {
|
||||
return _mm512_cvtsepi16_epi8(a);
|
||||
}
|
||||
|
||||
static inline Vec32uc compress_saturated (Vec32us const a) {
|
||||
return _mm512_cvtusepi16_epi8(a);
|
||||
}
|
||||
|
||||
static inline Vec16s compress_saturated (Vec16i const a) {
|
||||
return _mm512_cvtsepi32_epi16(a);
|
||||
}
|
||||
|
||||
static inline Vec16us compress_saturated (Vec16ui const a) {
|
||||
return _mm512_cvtusepi32_epi16(a);
|
||||
}
|
||||
|
||||
static inline Vec8i compress_saturated (Vec8q const a) {
|
||||
return _mm512_cvtsepi64_epi32(a);
|
||||
}
|
||||
|
||||
static inline Vec8ui compress_saturated (Vec8uq const a) {
|
||||
return _mm512_cvtusepi64_epi32(a);
|
||||
}
|
||||
|
||||
#else // no AVX512
|
||||
|
||||
// compress_saturated functions. overflow saturates
|
||||
static inline Vec32c compress_saturated (Vec32s const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec32uc compress_saturated (Vec32us const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec16s compress_saturated (Vec16i const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec16us compress_saturated (Vec16ui const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec8i compress_saturated (Vec8q const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec8ui compress_saturated (Vec8uq const a) {
|
||||
return compress_saturated(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
#endif // AVX512
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Extend from 256 to 512 bit vectors
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#if INSTRSET >= 9 // AVX512. 512 bit integer vectors
|
||||
|
||||
// sign extend
|
||||
static inline Vec32s extend (Vec32c const a) {
|
||||
#if INSTRSET >= 10
|
||||
return _mm512_cvtepi8_epi16(a);
|
||||
#else
|
||||
return Vec32s(extend_low(a), extend_high(a));
|
||||
#endif
|
||||
}
|
||||
|
||||
// zero extend
|
||||
static inline Vec32us extend (Vec32uc const a) {
|
||||
#if INSTRSET >= 10
|
||||
return _mm512_cvtepu8_epi16(a);
|
||||
#else
|
||||
return Vec32us(extend_low(a), extend_high(a));
|
||||
#endif
|
||||
}
|
||||
|
||||
// sign extend
|
||||
static inline Vec16i extend (Vec16s const a) {
|
||||
return _mm512_cvtepi16_epi32(a);
|
||||
}
|
||||
|
||||
// zero extend
|
||||
static inline Vec16ui extend (Vec16us const a) {
|
||||
return _mm512_cvtepu16_epi32(a);
|
||||
}
|
||||
|
||||
// sign extend
|
||||
static inline Vec8q extend (Vec8i const a) {
|
||||
return _mm512_cvtepi32_epi64(a);
|
||||
}
|
||||
|
||||
// zero extend
|
||||
static inline Vec8uq extend (Vec8ui const a) {
|
||||
return _mm512_cvtepu32_epi64(a);
|
||||
}
|
||||
|
||||
#else // no AVX512. 512 bit vectors are emulated
|
||||
|
||||
|
||||
|
||||
// sign extend
|
||||
static inline Vec32s extend (Vec32c const a) {
|
||||
return Vec32s(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
// zero extend
|
||||
static inline Vec32us extend (Vec32uc const a) {
|
||||
return Vec32us(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
// sign extend
|
||||
static inline Vec16i extend (Vec16s const a) {
|
||||
return Vec16i(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
// zero extend
|
||||
static inline Vec16ui extend (Vec16us const a) {
|
||||
return Vec16ui(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
// sign extend
|
||||
static inline Vec8q extend (Vec8i const a) {
|
||||
return Vec8q(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
// zero extend
|
||||
static inline Vec8uq extend (Vec8ui const a) {
|
||||
return Vec8uq(extend_low(a), extend_high(a));
|
||||
}
|
||||
|
||||
#endif // AVX512
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Reduce from 512 to 256 bit vectors
|
||||
*
|
||||
*****************************************************************************/
|
||||
#if INSTRSET >= 9 // AVX512F
|
||||
|
||||
// compress functions. overflow wraps around
|
||||
static inline Vec32c compress (Vec32s const a) {
|
||||
#if INSTRSET >= 10 // AVVX512BW
|
||||
return _mm512_cvtepi16_epi8(a);
|
||||
#else
|
||||
return compress(a.get_low(), a.get_high());
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline Vec32uc compress (Vec32us const a) {
|
||||
return Vec32uc(compress(Vec32s(a)));
|
||||
}
|
||||
|
||||
static inline Vec16s compress (Vec16i const a) {
|
||||
return _mm512_cvtepi32_epi16(a);
|
||||
}
|
||||
|
||||
static inline Vec16us compress (Vec16ui const a) {
|
||||
return _mm512_cvtepi32_epi16(a);
|
||||
}
|
||||
|
||||
static inline Vec8i compress (Vec8q const a) {
|
||||
return _mm512_cvtepi64_epi32(a);
|
||||
}
|
||||
|
||||
static inline Vec8ui compress (Vec8uq const a) {
|
||||
return _mm512_cvtepi64_epi32(a);
|
||||
}
|
||||
|
||||
#else // no AVX512
|
||||
|
||||
// compress functions. overflow wraps around
|
||||
static inline Vec32c compress (Vec32s const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec32uc compress (Vec32us const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec16s compress (Vec16i const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec16us compress (Vec16ui const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec8i compress (Vec8q const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
static inline Vec8ui compress (Vec8uq const a) {
|
||||
return compress(a.get_low(), a.get_high());
|
||||
}
|
||||
|
||||
#endif // AVX512
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Conversions between float and double
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#if INSTRSET >= 9 // AVX512. 512 bit float vectors
|
||||
|
||||
// float to double
|
||||
static inline Vec8d to_double (Vec8f const a) {
|
||||
return _mm512_cvtps_pd(a);
|
||||
}
|
||||
|
||||
// double to float
|
||||
static inline Vec8f to_float (Vec8d const a) {
|
||||
return _mm512_cvtpd_ps(a);
|
||||
}
|
||||
|
||||
#else // no AVX512. 512 bit float vectors are emulated
|
||||
|
||||
// float to double
|
||||
static inline Vec8d to_double (Vec8f const a) {
|
||||
Vec4d lo = to_double(a.get_low());
|
||||
Vec4d hi = to_double(a.get_high());
|
||||
return Vec8d(lo,hi);
|
||||
}
|
||||
|
||||
// double to float
|
||||
static inline Vec8f to_float (Vec8d const a) {
|
||||
Vec4f lo = to_float(a.get_low());
|
||||
Vec4f hi = to_float(a.get_high());
|
||||
return Vec8f(lo, hi);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
// double to float
|
||||
static inline Vec4f to_float (Vec2d const a) {
|
||||
return _mm_cvtpd_ps(a);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Generic template functions
|
||||
*
|
||||
* These templates define functions for multiple vector types in one template
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
// concatenate two vectors into one vector of double size
|
||||
template <typename T> auto concatenate2(T const a, T const b) {
|
||||
static_assert(sizeof(T) * 8 < MAX_VECTOR_SIZE, "Maximum vector size exceeded");
|
||||
return decltype(extend_z(a))(a, b); // call constructor for double size vector type
|
||||
}
|
||||
|
||||
|
||||
// horizontal min/max of vector elements
|
||||
// implemented with universal template, works for all vector types:
|
||||
|
||||
template <typename T> auto horizontal_min(T const x) {
|
||||
if constexpr (T::elementtype() >= 15) {
|
||||
// T is a float or double vector
|
||||
if (horizontal_or(is_nan(x))) {
|
||||
// check for NAN because min does not guarantee NAN propagation
|
||||
return x[horizontal_find_first(is_nan(x))];
|
||||
}
|
||||
}
|
||||
return horizontal_min1(x);
|
||||
}
|
||||
|
||||
template <typename T> auto horizontal_min1(T const x) {
|
||||
if constexpr (T::elementtype() <= 3) { // boolean vector type
|
||||
return horizontal_and(x);
|
||||
}
|
||||
else if constexpr (sizeof(T) >= 32) {
|
||||
// split recursively into smaller vectors
|
||||
return horizontal_min1(min(x.get_low(), x.get_high()));
|
||||
}
|
||||
else if constexpr (T::size() == 2) {
|
||||
T a = permute2 <1, V_DC>(x); // high half
|
||||
T b = min(a, x);
|
||||
return b[0];
|
||||
}
|
||||
else if constexpr (T::size() == 4) {
|
||||
T a = permute4<2, 3, V_DC, V_DC>(x); // high half
|
||||
T b = min(a, x);
|
||||
a = permute4<1, V_DC, V_DC, V_DC>(b);
|
||||
b = min(a, b);
|
||||
return b[0];
|
||||
}
|
||||
else if constexpr (T::size() == 8) {
|
||||
T a = permute8<4, 5, 6, 7, V_DC, V_DC, V_DC, V_DC>(x); // high half
|
||||
T b = min(a, x);
|
||||
a = permute8<2, 3, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC>(b);
|
||||
b = min(a, b);
|
||||
a = permute8<1, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC>(b);
|
||||
b = min(a, b);
|
||||
return b[0];
|
||||
}
|
||||
else {
|
||||
static_assert(T::size() == 16); // no other size is allowed
|
||||
T a = permute16<8, 9, 10, 11, 12, 13, 14, 15, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC >(x); // high half
|
||||
T b = min(a, x);
|
||||
a = permute16<4, 5, 6, 7, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC>(b);
|
||||
b = min(a, b);
|
||||
a = permute16<2, 3, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC>(b);
|
||||
b = min(a, b);
|
||||
a = permute16<1, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC>(b);
|
||||
b = min(a, b);
|
||||
return b[0];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> auto horizontal_max(T const x) {
|
||||
if constexpr (T::elementtype() >= 15) {
|
||||
// T is a float or double vector
|
||||
if (horizontal_or(is_nan(x))) {
|
||||
// check for NAN because max does not guarantee NAN propagation
|
||||
return x[horizontal_find_first(is_nan(x))];
|
||||
}
|
||||
}
|
||||
return horizontal_max1(x);
|
||||
}
|
||||
|
||||
template <typename T> auto horizontal_max1(T const x) {
|
||||
if constexpr (T::elementtype() <= 3) { // boolean vector type
|
||||
return horizontal_or(x);
|
||||
}
|
||||
else if constexpr (sizeof(T) >= 32) {
|
||||
// split recursively into smaller vectors
|
||||
return horizontal_max1(max(x.get_low(), x.get_high()));
|
||||
}
|
||||
else if constexpr (T::size() == 2) {
|
||||
T a = permute2 <1, V_DC>(x); // high half
|
||||
T b = max(a, x);
|
||||
return b[0];
|
||||
}
|
||||
else if constexpr (T::size() == 4) {
|
||||
T a = permute4<2, 3, V_DC, V_DC>(x); // high half
|
||||
T b = max(a, x);
|
||||
a = permute4<1, V_DC, V_DC, V_DC>(b);
|
||||
b = max(a, b);
|
||||
return b[0];
|
||||
}
|
||||
else if constexpr (T::size() == 8) {
|
||||
T a = permute8<4, 5, 6, 7, V_DC, V_DC, V_DC, V_DC>(x); // high half
|
||||
T b = max(a, x);
|
||||
a = permute8<2, 3, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC>(b);
|
||||
b = max(a, b);
|
||||
a = permute8<1, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC>(b);
|
||||
b = max(a, b);
|
||||
return b[0];
|
||||
}
|
||||
else {
|
||||
static_assert(T::size() == 16); // no other size is allowed
|
||||
T a = permute16<8, 9, 10, 11, 12, 13, 14, 15, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC >(x); // high half
|
||||
T b = max(a, x);
|
||||
a = permute16<4, 5, 6, 7, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC>(b);
|
||||
b = max(a, b);
|
||||
a = permute16<2, 3, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC>(b);
|
||||
b = max(a, b);
|
||||
a = permute16<1, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC, V_DC>(b);
|
||||
b = max(a, b);
|
||||
return b[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Find first element that is true in a boolean vector
|
||||
template <typename V>
|
||||
static inline int horizontal_find_first(V const x) {
|
||||
static_assert(V::elementtype() == 2 || V::elementtype() == 3, "Boolean vector expected");
|
||||
auto bits = to_bits(x); // convert to bits
|
||||
if (bits == 0) return -1;
|
||||
if constexpr (V::size() < 32) {
|
||||
return bit_scan_forward((uint32_t)bits);
|
||||
}
|
||||
else {
|
||||
return bit_scan_forward(bits);
|
||||
}
|
||||
}
|
||||
|
||||
// Count the number of elements that are true in a boolean vector
|
||||
template <typename V>
|
||||
static inline int horizontal_count(V const x) {
|
||||
static_assert(V::elementtype() == 2 || V::elementtype() == 3, "Boolean vector expected");
|
||||
auto bits = to_bits(x); // convert to bits
|
||||
if constexpr (V::size() < 32) {
|
||||
return vml_popcnt((uint32_t)bits);
|
||||
}
|
||||
else {
|
||||
return (int)vml_popcnt(bits);
|
||||
}
|
||||
}
|
||||
|
||||
// maximum and minimum functions. This version is sure to propagate NANs,
|
||||
// conforming to the new IEEE-754 2019 standard
|
||||
template <typename V>
|
||||
static inline V maximum(V const a, V const b) {
|
||||
if constexpr (V::elementtype() < 15) {
|
||||
return max(a, b); // integer type
|
||||
}
|
||||
else { // float or double vector
|
||||
V y = select(is_nan(a), a, max(a, b));
|
||||
#ifdef SIGNED_ZERO // pedantic about signed zero
|
||||
y = select(a == b, a & b, y); // maximum(+0, -0) = +0
|
||||
#endif
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
static inline V minimum(V const a, V const b) {
|
||||
if constexpr (V::elementtype() < 15) {
|
||||
return min(a, b); // integer type
|
||||
}
|
||||
else { // float or double vector
|
||||
V y = select(is_nan(a), a, min(a, b));
|
||||
#ifdef SIGNED_ZERO // pedantic about signed zero
|
||||
y = select(a == b, a | b, y); // minimum(+0, -0) = -0
|
||||
#endif
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
// floating point remainder
|
||||
// -denominator/2 <= result < denominator/2
|
||||
template <typename V>
|
||||
static inline V fremainder(V const numerator, double const denominator) {
|
||||
// (Optimization notice: Calculation of 1/denominator and constants for extended precision reduction
|
||||
// may be optimized by a compiler moving loop-invariant code. This is intended)
|
||||
static_assert(V::elementtype() == 16 || V::elementtype() == 17, "wrong vector type"); // supports only float and double
|
||||
if (denominator > 0.) { // denominator must be positive
|
||||
if constexpr (V::elementtype() == 16) { // float
|
||||
#ifdef __FMA__
|
||||
float recipd = float(1.0 / denominator); // reciprocal denominator
|
||||
float fd = float(denominator); // denominator rounded to single precision
|
||||
float d2 = float(denominator - fd); // remaining bits for double precision
|
||||
V q = round(numerator * recipd); // divide and round
|
||||
V m = nmul_add(q, d2, nmul_add(q, fd, numerator));// double precision reduction
|
||||
#else // no FMA. Use extended precision reduction
|
||||
union {
|
||||
float f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.f = float(denominator);
|
||||
u.i &= 0xFFFFF000; // remove 12 least significant bits for extended precision reduction
|
||||
float d2 = denominator - u.f; // remaining bits
|
||||
float recipd = float(1.0 / denominator); // reciprocal
|
||||
V q = round(numerator * recipd); // divide and round
|
||||
V m = nmul_add(q, d2, nmul_add(q, u.f, numerator));// extended precision reduction
|
||||
#endif // FMA
|
||||
if (true) { // Check that result is within desired interval. This may be omitted if not essential:
|
||||
// This check may be needed in extreme cases of numerator > 1.E5 * denominator
|
||||
auto too_high = m >= float( denominator * 0.5);
|
||||
auto too_low = m < float(-denominator * 0.5);
|
||||
m = if_sub(too_high, m, float(denominator));
|
||||
m = if_add(too_low, m, float(denominator));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
else if constexpr (V::elementtype() == 17) { // double precision
|
||||
#ifdef __FMA__
|
||||
double recipd = 1.0 / denominator; // reciprocal
|
||||
V q = round(numerator * recipd); // divide and round
|
||||
V m = nmul_add(q, denominator, numerator); // nmul_add has extended precision
|
||||
#else // no FMA. Use extended precision reduction
|
||||
union {
|
||||
double f;
|
||||
uint64_t i;
|
||||
} u;
|
||||
u.f = denominator;
|
||||
u.i &= 0xFFFFFFFFFF000000; // remove 24 least significant bits for extended precision reduction
|
||||
double d2 = denominator - u.f; // remaining bits
|
||||
double recipd = 1.0 / denominator; // reciprocal
|
||||
V q = round(numerator * recipd); // divide and round
|
||||
V m = nmul_add(q, d2, nmul_add(q, u.f, numerator));// extended precision reduction
|
||||
#endif // FMA
|
||||
if (true) { // Check that result is within desired interval. This may be omitted if not essential:
|
||||
// This check is rarely needed except in extreme cases of numerator > 1.E14 * denominator
|
||||
auto too_high = m >= denominator * 0.5;
|
||||
auto too_low = m < -denominator * 0.5;
|
||||
m = if_sub(too_high, m, denominator);
|
||||
m = if_add(too_low, m, denominator);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return nan_vec<V>(1); // denominator is not positive
|
||||
}
|
||||
}
|
||||
|
||||
// floating point modulo
|
||||
// 0 <= result < denominator
|
||||
template <typename V>
|
||||
static inline V fmodulo(V const numerator, double const denominator) {
|
||||
// (Optimization notice: Calculation of 1/denominator and constants for extended precision reduction
|
||||
// may be optimized by a compiler moving loop-invariant code. This is intended)
|
||||
static_assert(V::elementtype() == 16 || V::elementtype() == 17, "wrong vector type"); // supports only float and double
|
||||
if (denominator > 0.) { // denominator must be positive
|
||||
if constexpr (V::elementtype() == 16) { // float
|
||||
#ifdef __FMA__
|
||||
float recipd = float(1.0 / denominator); // reciprocal denominator
|
||||
float fd = float(denominator); // denominator rounded to single precision
|
||||
float d2 = float(denominator - fd); // remaining bits for double precision
|
||||
V q = floor(numerator * recipd); // divide and floor
|
||||
V m = nmul_add(q, d2, nmul_add(q, fd, numerator));// double precision reduction
|
||||
#else // no FMA. Use extended precision reduction
|
||||
union {
|
||||
float f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.f = float(denominator);
|
||||
u.i &= 0xFFFFF000; // remove 12 least significant bits for extended precision reduction
|
||||
float d2 = denominator - u.f; // remaining bits
|
||||
float recipd = float(1.0 / denominator); // reciprocal
|
||||
V q = floor(numerator * recipd); // divide and floor
|
||||
V m = nmul_add(q, d2, nmul_add(q, u.f, numerator));// extended precision reduction
|
||||
#endif // FMA
|
||||
if (true) { // Check that result is within desired interval. This may be omitted if not essential:
|
||||
// This check may be needed in extreme cases of numerator > 1.E5 * denominator
|
||||
auto too_high = m >= float(denominator);
|
||||
auto too_low = m < 0.f;
|
||||
m = if_sub(too_high, m, float(denominator));
|
||||
m = if_add(too_low, m, float(denominator));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
else if constexpr (V::elementtype() == 17) { // double precision
|
||||
#ifdef __FMA__
|
||||
double recipd = 1.0 / denominator; // reciprocal
|
||||
V q = floor(numerator * recipd); // divide and floor
|
||||
V m = nmul_add(q, denominator, numerator); // nmul_add has extended precision
|
||||
#else // no FMA. Use extended precision reduction
|
||||
union {
|
||||
double f;
|
||||
uint64_t i;
|
||||
} u;
|
||||
u.f = denominator;
|
||||
u.i &= 0xFFFFFFFFFF000000; // remove 24 least significant bits for extended precision reduction
|
||||
double d2 = denominator - u.f; // remaining bits
|
||||
double recipd = 1.0 / denominator; // reciprocal
|
||||
V q = floor(numerator * recipd); // divide and floor
|
||||
V m = nmul_add(q, d2, nmul_add(q, u.f, numerator));// extended precision reduction
|
||||
#endif // FMA
|
||||
if (true) { // Check that result is within desired interval. This may be omitted if not essential:
|
||||
// This check is rarely needed except in extreme cases of numerator > 1.E14 * denominator
|
||||
auto too_high = m >= denominator;
|
||||
auto too_low = m < 0.;
|
||||
m = if_sub(too_high, m, denominator);
|
||||
m = if_add(too_low, m, denominator);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return nan_vec<V>(1); // denominator is not positive
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VCL_NAMESPACE
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // VECTOR_CONVERT_H
|
||||
@@ -0,0 +1,87 @@
|
||||
/**************************** vectorclass.h ********************************
|
||||
* Author: Agner Fog
|
||||
* Date created: 2012-05-30
|
||||
* Last modified: 2026-04-14
|
||||
* Version: 2.02.03
|
||||
* Project: vector class library
|
||||
* Home: https://github.com/vectorclass
|
||||
* Description:
|
||||
* Header file defining vector classes as interface to intrinsic functions
|
||||
* in x86 and x86-64 microprocessors with SSE2 and later instruction sets.
|
||||
*
|
||||
* Instructions:
|
||||
* Use Gnu, Clang, Microsoft, or Intel C++ compiler. Compile for the desired
|
||||
* instruction set, which must be at least SSE2. Specify the supported
|
||||
* instruction set by a command line define, e.g. __SSE4_1__ if the
|
||||
* compiler does not automatically do so.
|
||||
* For detailed instructions, see vcl_manual.pdf
|
||||
*
|
||||
* Each vector object is represented internally in the CPU as a vector
|
||||
* register with 128, 256 or 512 bits.
|
||||
*
|
||||
* This header file includes the appropriate header files depending on the
|
||||
* selected instruction set.
|
||||
*
|
||||
* (c) Copyright 2012-2026 Agner Fog.
|
||||
* Apache License version 2.0 or later.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef VECTORCLASS_H
|
||||
#define VECTORCLASS_H 20203
|
||||
|
||||
// Maximum vector size, bits. Allowed values are 128, 256, 512
|
||||
#ifndef MAX_VECTOR_SIZE
|
||||
#define MAX_VECTOR_SIZE 512
|
||||
#endif
|
||||
|
||||
// Determine instruction set, and define platform-dependent functions
|
||||
#include "instrset.h" // Select supported instruction set
|
||||
|
||||
#if INSTRSET < 2 // instruction set SSE2 is the minimum
|
||||
#error Please compile for the SSE2 instruction set or higher
|
||||
#else
|
||||
|
||||
// Select appropriate .h files depending on instruction set
|
||||
#include "vectori128.h" // 128-bit integer vectors
|
||||
#include "vectorf128.h" // 128-bit floating point vectors
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
#if INSTRSET >= 8
|
||||
#include "vectori256.h" // 256-bit integer vectors, requires AVX2 instruction set
|
||||
#else
|
||||
#include "vectori256e.h" // 256-bit integer vectors, emulated
|
||||
#endif // INSTRSET >= 8
|
||||
#if INSTRSET >= 7
|
||||
#include "vectorf256.h" // 256-bit floating point vectors, requires AVX instruction set
|
||||
#else
|
||||
#include "vectorf256e.h" // 256-bit floating point vectors, emulated
|
||||
#endif // INSTRSET >= 7
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
#if INSTRSET >= 9
|
||||
#include "vectori512.h" // 512-bit vectors of 32 and 64 bit integers, requires AVX512F instruction set
|
||||
#include "vectorf512.h" // 512-bit floating point vectors, requires AVX512F instruction set
|
||||
#else
|
||||
#include "vectori512e.h" // 512-bit integer vectors, emulated
|
||||
#include "vectorf512e.h" // 512-bit floating point vectors, emulated
|
||||
#endif // INSTRSET >= 9
|
||||
#if INSTRSET >= 10
|
||||
#include "vectori512s.h" // 512-bit vectors of 8 and 16 bit integers, requires AVX512BW instruction set
|
||||
#else
|
||||
#include "vectori512se.h" // 512-bit vectors of 8 and 16 bit integers, emulated
|
||||
#endif
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
#include "vector_convert.h" // conversion between different vector sizes, and common templates
|
||||
|
||||
#endif // INSTRSET >= 2
|
||||
|
||||
|
||||
#else // VECTORCLASS_H
|
||||
|
||||
#if VECTORCLASS_H < 20000
|
||||
#error Mixed versions of vector class library
|
||||
#endif
|
||||
|
||||
#endif // VECTORCLASS_H
|
||||
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
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
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,327 @@
|
||||
/*************************** vectormath_common.h ****************************
|
||||
* Author: Agner Fog
|
||||
* Date created: 2014-04-18
|
||||
* Last modified: 2022-07-20
|
||||
* Version: 2.02.00
|
||||
* Project: vector classes
|
||||
* Description:
|
||||
* Header file containing common code for inline version of mathematical functions.
|
||||
*
|
||||
* For detailed instructions, see VectorClass.pdf
|
||||
*
|
||||
* (c) Copyright 2014-2022 Agner Fog.
|
||||
* Apache License version 2.0 or later.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef VECTORMATH_COMMON_H
|
||||
#define VECTORMATH_COMMON_H 2
|
||||
|
||||
#ifdef VECTORMATH_LIB_H
|
||||
#error conflicting header files. More than one implementation of mathematical functions included
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#ifndef VECTORCLASS_H
|
||||
#include "vectorclass.h"
|
||||
#endif
|
||||
|
||||
#if VECTORCLASS_H < 20200
|
||||
#error Incompatible versions of vector class library mixed
|
||||
#endif
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Define NAN payload values
|
||||
******************************************************************************/
|
||||
#define NAN_LOG 0x101 // logarithm for x<0
|
||||
#define NAN_POW 0x102 // negative number raised to non-integer power
|
||||
#define NAN_HYP 0x104 // acosh for x<1 and atanh for abs(x)>1
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Define mathematical constants
|
||||
******************************************************************************/
|
||||
#define VM_PI 3.14159265358979323846 // pi
|
||||
#define VM_PI_2 1.57079632679489661923 // pi / 2
|
||||
#define VM_PI_4 0.785398163397448309616 // pi / 4
|
||||
#define VM_SQRT2 1.41421356237309504880 // sqrt(2)
|
||||
#define VM_LOG2E 1.44269504088896340736 // 1/log(2)
|
||||
#define VM_LOG10E 0.434294481903251827651 // 1/log(10)
|
||||
#define VM_LOG210 3.321928094887362347808 // log2(10)
|
||||
#define VM_LN2 0.693147180559945309417 // log(2)
|
||||
#define VM_LN10 2.30258509299404568402 // log(10)
|
||||
#define VM_SMALLEST_NORMAL 2.2250738585072014E-308 // smallest normal number, double
|
||||
#define VM_SMALLEST_NORMALF 1.17549435E-38f // smallest normal number, float
|
||||
|
||||
|
||||
#ifdef VCL_NAMESPACE
|
||||
namespace VCL_NAMESPACE {
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
templates for producing infinite and nan in desired vector type
|
||||
******************************************************************************/
|
||||
template <typename VTYPE>
|
||||
static inline VTYPE infinite_vec();
|
||||
|
||||
template <>
|
||||
inline Vec2d infinite_vec<Vec2d>() {
|
||||
return infinite2d();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Vec4f infinite_vec<Vec4f>() {
|
||||
return infinite4f();
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
|
||||
template <>
|
||||
inline Vec4d infinite_vec<Vec4d>() {
|
||||
return infinite4d();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Vec8f infinite_vec<Vec8f>() {
|
||||
return infinite8f();
|
||||
}
|
||||
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
|
||||
template <>
|
||||
inline Vec8d infinite_vec<Vec8d>() {
|
||||
return infinite8d();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Vec16f infinite_vec<Vec16f>() {
|
||||
return infinite16f();
|
||||
}
|
||||
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Detect NAN codes
|
||||
*
|
||||
* These functions return the code hidden in a NAN. The sign bit is ignored
|
||||
******************************************************************************/
|
||||
|
||||
static inline Vec4ui nan_code(Vec4f const x) {
|
||||
Vec4ui a = Vec4ui(reinterpret_i(x));
|
||||
Vec4ui const n = 0x007FFFFF;
|
||||
return select(Vec4ib(is_nan(x)), a & n, 0);
|
||||
}
|
||||
|
||||
// This function returns the code hidden in a NAN. The sign bit is ignored
|
||||
static inline Vec2uq nan_code(Vec2d const x) {
|
||||
Vec2uq a = Vec2uq(reinterpret_i(x));
|
||||
return select(Vec2qb(is_nan(x)), a << 12 >> (12+29), 0);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
|
||||
// This function returns the code hidden in a NAN. The sign bit is ignored
|
||||
static inline Vec8ui nan_code(Vec8f const x) {
|
||||
Vec8ui a = Vec8ui(reinterpret_i(x));
|
||||
Vec8ui const n = 0x007FFFFF;
|
||||
return select(Vec8ib(is_nan(x)), a & n, 0);
|
||||
}
|
||||
|
||||
// This function returns the code hidden in a NAN. The sign bit is ignored
|
||||
static inline Vec4uq nan_code(Vec4d const x) {
|
||||
Vec4uq a = Vec4uq(reinterpret_i(x));
|
||||
return select(Vec4qb(is_nan(x)), a << 12 >> (12+29), 0);
|
||||
}
|
||||
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
|
||||
// This function returns the code hidden in a NAN. The sign bit is ignored
|
||||
static inline Vec16ui nan_code(Vec16f const x) {
|
||||
Vec16ui a = Vec16ui(reinterpret_i(x));
|
||||
Vec16ui const n = 0x007FFFFF;
|
||||
return select(Vec16ib(is_nan(x)), a & n, 0);
|
||||
}
|
||||
|
||||
// This function returns the code hidden in a NAN. The sign bit is ignored
|
||||
static inline Vec8uq nan_code(Vec8d const x) {
|
||||
Vec8uq a = Vec8uq(reinterpret_i(x));
|
||||
return select(Vec8qb(is_nan(x)), a << 12 >> (12+29), 0);
|
||||
}
|
||||
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
templates for polynomials
|
||||
Using Estrin's scheme to make shorter dependency chains and use FMA, starting
|
||||
longest dependency chains first.
|
||||
******************************************************************************/
|
||||
|
||||
// template <typedef VECTYPE, typedef CTYPE>
|
||||
template <class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_2(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2) {
|
||||
// calculates polynomial c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
//return = x2 * c2 + (x * c1 + c0);
|
||||
return mul_add(x2, c2, mul_add(x, c1, c0));
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_3(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3) {
|
||||
// calculates polynomial c3*x^3 + c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
//return (c2 + c3*x)*x2 + (c1*x + c0);
|
||||
return mul_add(mul_add(c3, x, c2), x2, mul_add(c1, x, c0));
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_4(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3, CTYPE c4) {
|
||||
// calculates polynomial c4*x^4 + c3*x^3 + c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
//return (c2+c3*x)*x2 + ((c0+c1*x) + c4*x4);
|
||||
return mul_add(mul_add(c3, x, c2), x2, mul_add(c1, x, c0) + c4*x4);
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_4n(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3) {
|
||||
// calculates polynomial 1*x^4 + c3*x^3 + c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
//return (c2+c3*x)*x2 + ((c0+c1*x) + x4);
|
||||
return mul_add(mul_add(c3, x, c2), x2, mul_add(c1, x, c0) + x4);
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_5(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3, CTYPE c4, CTYPE c5) {
|
||||
// calculates polynomial c5*x^5 + c4*x^4 + c3*x^3 + c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
//return (c2+c3*x)*x2 + ((c4+c5*x)*x4 + (c0+c1*x));
|
||||
return mul_add(mul_add(c3, x, c2), x2, mul_add(mul_add(c5, x, c4), x4, mul_add(c1, x, c0)));
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_5n(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3, CTYPE c4) {
|
||||
// calculates polynomial 1*x^5 + c4*x^4 + c3*x^3 + c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
//return (c2+c3*x)*x2 + ((c4+x)*x4 + (c0+c1*x));
|
||||
return mul_add(mul_add(c3, x, c2), x2, mul_add(c4 + x, x4, mul_add(c1, x, c0)));
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_6(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3, CTYPE c4, CTYPE c5, CTYPE c6) {
|
||||
// calculates polynomial c6*x^6 + c5*x^5 + c4*x^4 + c3*x^3 + c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
//return (c4+c5*x+c6*x2)*x4 + ((c2+c3*x)*x2 + (c0+c1*x));
|
||||
return mul_add(mul_add(c6, x2, mul_add(c5, x, c4)), x4, mul_add(mul_add(c3, x, c2), x2, mul_add(c1, x, c0)));
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_6n(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3, CTYPE c4, CTYPE c5) {
|
||||
// calculates polynomial 1*x^6 + c5*x^5 + c4*x^4 + c3*x^3 + c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
//return (c4+c5*x+x2)*x4 + ((c2+c3*x)*x2 + (c0+c1*x));
|
||||
return mul_add(mul_add(c5, x, c4 + x2), x4, mul_add(mul_add(c3, x, c2), x2, mul_add(c1, x, c0)));
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_7(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3, CTYPE c4, CTYPE c5, CTYPE c6, CTYPE c7) {
|
||||
// calculates polynomial c7*x^7 + c6*x^6 + c5*x^5 + c4*x^4 + c3*x^3 + c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
//return ((c6+c7*x)*x2 + (c4+c5*x))*x4 + ((c2+c3*x)*x2 + (c0+c1*x));
|
||||
return mul_add(mul_add(mul_add(c7, x, c6), x2, mul_add(c5, x, c4)), x4, mul_add(mul_add(c3, x, c2), x2, mul_add(c1, x, c0)));
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_8(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3, CTYPE c4, CTYPE c5, CTYPE c6, CTYPE c7, CTYPE c8) {
|
||||
// calculates polynomial c8*x^8 + c7*x^7 + c6*x^6 + c5*x^5 + c4*x^4 + c3*x^3 + c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
VTYPE x8 = x4 * x4;
|
||||
//return ((c6+c7*x)*x2 + (c4+c5*x))*x4 + (c8*x8 + (c2+c3*x)*x2 + (c0+c1*x));
|
||||
return mul_add(mul_add(mul_add(c7, x, c6), x2, mul_add(c5, x, c4)), x4,
|
||||
mul_add(mul_add(c3, x, c2), x2, mul_add(c1, x, c0) + c8*x8));
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_9(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3, CTYPE c4, CTYPE c5, CTYPE c6, CTYPE c7, CTYPE c8, CTYPE c9) {
|
||||
// calculates polynomial c9*x^9 + c8*x^8 + c7*x^7 + c6*x^6 + c5*x^5 + c4*x^4 + c3*x^3 + c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
VTYPE x8 = x4 * x4;
|
||||
//return (((c6+c7*x)*x2 + (c4+c5*x))*x4 + (c8+c9*x)*x8) + ((c2+c3*x)*x2 + (c0+c1*x));
|
||||
return mul_add(mul_add(c9, x, c8), x8, mul_add(
|
||||
mul_add(mul_add(c7, x, c6), x2, mul_add(c5, x, c4)), x4,
|
||||
mul_add(mul_add(c3, x, c2), x2, mul_add(c1, x, c0))));
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_10(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3, CTYPE c4, CTYPE c5, CTYPE c6, CTYPE c7, CTYPE c8, CTYPE c9, CTYPE c10) {
|
||||
// calculates polynomial c10*x^10 + c9*x^9 + c8*x^8 + c7*x^7 + c6*x^6 + c5*x^5 + c4*x^4 + c3*x^3 + c2*x^2 + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
VTYPE x8 = x4 * x4;
|
||||
//return (((c6+c7*x)*x2 + (c4+c5*x))*x4 + (c8+c9*x+c10*x2)*x8) + ((c2+c3*x)*x2 + (c0+c1*x));
|
||||
return mul_add(mul_add(x2, c10, mul_add(c9, x, c8)), x8,
|
||||
mul_add(mul_add(mul_add(c7, x, c6), x2, mul_add(c5, x, c4)), x4,
|
||||
mul_add(mul_add(c3, x, c2), x2, mul_add(c1, x, c0))));
|
||||
}
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_13(VTYPE const x, CTYPE c0, CTYPE c1, CTYPE c2, CTYPE c3, CTYPE c4, CTYPE c5, CTYPE c6, CTYPE c7, CTYPE c8, CTYPE c9, CTYPE c10, CTYPE c11, CTYPE c12, CTYPE c13) {
|
||||
// calculates polynomial c13*x^13 + c12*x^12 + ... + c1*x + c0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
VTYPE x8 = x4 * x4;
|
||||
return mul_add(
|
||||
mul_add(
|
||||
mul_add(c13, x, c12), x4,
|
||||
mul_add(mul_add(c11, x, c10), x2, mul_add(c9, x, c8))), x8,
|
||||
mul_add(
|
||||
mul_add(mul_add(c7, x, c6), x2, mul_add(c5, x, c4)), x4,
|
||||
mul_add(mul_add(c3, x, c2), x2, mul_add(c1, x, c0))));
|
||||
}
|
||||
|
||||
|
||||
template<class VTYPE, class CTYPE>
|
||||
static inline VTYPE polynomial_13m(VTYPE const x, CTYPE c2, CTYPE c3, CTYPE c4, CTYPE c5, CTYPE c6, CTYPE c7, CTYPE c8, CTYPE c9, CTYPE c10, CTYPE c11, CTYPE c12, CTYPE c13) {
|
||||
// calculates polynomial c13*x^13 + c12*x^12 + ... + x + 0
|
||||
// VTYPE may be a vector type, CTYPE is a scalar type
|
||||
VTYPE x2 = x * x;
|
||||
VTYPE x4 = x2 * x2;
|
||||
VTYPE x8 = x4 * x4;
|
||||
// return ((c8+c9*x) + (c10+c11*x)*x2 + (c12+c13*x)*x4)*x8 + (((c6+c7*x)*x2 + (c4+c5*x))*x4 + ((c2+c3*x)*x2 + x));
|
||||
return mul_add(
|
||||
mul_add(mul_add(c13, x, c12), x4, mul_add(mul_add(c11, x, c10), x2, mul_add(c9, x, c8))), x8,
|
||||
mul_add(mul_add(mul_add(c7, x, c6), x2, mul_add(c5, x, c4)), x4, mul_add(mul_add(c3, x, c2), x2, x)));
|
||||
}
|
||||
|
||||
#ifdef VCL_NAMESPACE
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,717 @@
|
||||
/**************************** vectormath_hyp.h ******************************
|
||||
* Author: Agner Fog
|
||||
* Date created: 2014-07-09
|
||||
* Last modified: 2022-07-20
|
||||
* Version: 2.02.00
|
||||
* Project: vector class library
|
||||
* Description:
|
||||
* Header file containing inline vector functions of hyperbolic and inverse
|
||||
* hyperbolic functions:
|
||||
* sinh hyperbolic sine
|
||||
* cosh hyperbolic cosine
|
||||
* tanh hyperbolic tangent
|
||||
* asinh inverse hyperbolic sine
|
||||
* acosh inverse hyperbolic cosine
|
||||
* atanh inverse hyperbolic tangent
|
||||
*
|
||||
* Theory, methods and inspiration based partially on these sources:
|
||||
* > Moshier, Stephen Lloyd Baluk: Methods and programs for mathematical functions.
|
||||
* Ellis Horwood, 1989.
|
||||
* > VDT library developed on CERN by Danilo Piparo, Thomas Hauth and
|
||||
* Vincenzo Innocente, 2012, https://svnweb.cern.ch/trac/vdt
|
||||
* > Cephes math library by Stephen L. Moshier 1992,
|
||||
* http://www.netlib.org/cephes/
|
||||
*
|
||||
* For detailed instructions, see vectormath_common.h and vcl_manual.pdf
|
||||
*
|
||||
* (c) Copyright 2014-2022 Agner Fog.
|
||||
* Apache License version 2.0 or later.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef VECTORMATH_HYP_H
|
||||
#define VECTORMATH_HYP_H 202
|
||||
|
||||
#include "vectormath_exp.h"
|
||||
|
||||
#ifdef VCL_NAMESPACE
|
||||
namespace VCL_NAMESPACE {
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* Hyperbolic functions
|
||||
******************************************************************************/
|
||||
|
||||
// Template for sinh function, double precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE sinh_d(VTYPE const x0) {
|
||||
// The limit of abs(x) is 709.7, as defined by max_x in vectormath_exp.h for 0.5*exp(x).
|
||||
|
||||
// Coefficients
|
||||
const double p0 = -3.51754964808151394800E5;
|
||||
const double p1 = -1.15614435765005216044E4;
|
||||
const double p2 = -1.63725857525983828727E2;
|
||||
const double p3 = -7.89474443963537015605E-1;
|
||||
|
||||
const double q0 = -2.11052978884890840399E6;
|
||||
const double q1 = 3.61578279834431989373E4;
|
||||
const double q2 = -2.77711081420602794433E2;
|
||||
const double q3 = 1.0;
|
||||
|
||||
// data vectors
|
||||
VTYPE x, x2, y1, y2;
|
||||
|
||||
x = abs(x0);
|
||||
auto x_small = x <= 1.0; // use Pade approximation if abs(x) <= 1
|
||||
|
||||
if (horizontal_or(x_small)) {
|
||||
// At least one element needs small method
|
||||
x2 = x*x;
|
||||
y1 = polynomial_3(x2, p0, p1, p2, p3) / polynomial_3(x2, q0, q1, q2, q3);
|
||||
y1 = mul_add(y1, x*x2, x); // y1 = x + x2*(x*y1);
|
||||
}
|
||||
if (!horizontal_and(x_small)) {
|
||||
// At least one element needs big method
|
||||
y2 = exp_d<VTYPE, 0, 1>(x); // 0.5 * exp(x)
|
||||
y2 -= 0.25 / y2; // - 0.5 * exp(-x)
|
||||
}
|
||||
y1 = select(x_small, y1, y2); // choose method
|
||||
y1 = sign_combine(y1, x0); // get original sign
|
||||
// you can avoid the sign_combine by replacing x by x0 above, but at a loss of precision
|
||||
|
||||
return y1;
|
||||
}
|
||||
|
||||
// instances of sinh_d template
|
||||
static inline Vec2d sinh(Vec2d const x) {
|
||||
return sinh_d(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec4d sinh(Vec4d const x) {
|
||||
return sinh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec8d sinh(Vec8d const x) {
|
||||
return sinh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
// Template for sinh function, single precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE sinh_f(VTYPE const x0) {
|
||||
// The limit of abs(x) is 89.0, as defined by max_x in vectormath_exp.h for 0.5*exp(x).
|
||||
|
||||
// Coefficients
|
||||
const float r0 = 1.66667160211E-1f;
|
||||
const float r1 = 8.33028376239E-3f;
|
||||
const float r2 = 2.03721912945E-4f;
|
||||
|
||||
// data vectors
|
||||
VTYPE x, x2, y1, y2;
|
||||
|
||||
x = abs(x0);
|
||||
auto x_small = x <= 1.0f; // use polynomial approximation if abs(x) <= 1
|
||||
|
||||
if (horizontal_or(x_small)) {
|
||||
// At least one element needs small method
|
||||
x2 = x*x;
|
||||
y1 = polynomial_2(x2, r0, r1, r2);
|
||||
y1 = mul_add(y1, x2*x, x); // y1 = x + x2*(x*y1);
|
||||
}
|
||||
if (!horizontal_and(x_small)) {
|
||||
// At least one element needs big method
|
||||
y2 = exp_f<VTYPE, 0, 1>(x); // 0.5 * exp(x)
|
||||
y2 -= 0.25f / y2; // - 0.5 * exp(-x)
|
||||
}
|
||||
y1 = select(x_small, y1, y2); // choose method
|
||||
y1 = sign_combine(y1, x0); // get original sign
|
||||
// you can avoid the sign_combine by replacing x by x0 above, but at a loss of precision
|
||||
|
||||
return y1;
|
||||
}
|
||||
|
||||
// instances of sinh_f template
|
||||
static inline Vec4f sinh(Vec4f const x) {
|
||||
return sinh_f(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec8f sinh(Vec8f const x) {
|
||||
return sinh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec16f sinh(Vec16f const x) {
|
||||
return sinh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
// Template for cosh function, double precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE cosh_d(VTYPE const x0) {
|
||||
// The limit of abs(x) is 709.7, as defined by max_x in vectormath_exp.h for 0.5*exp(x).
|
||||
|
||||
// data vectors
|
||||
VTYPE x, y;
|
||||
x = abs(x0);
|
||||
y = exp_d<VTYPE, 0, 1>(x); // 0.5 * exp(x)
|
||||
y += 0.25 / y; // + 0.5 * exp(-x)
|
||||
return y;
|
||||
}
|
||||
|
||||
// instances of sinh_d template
|
||||
static inline Vec2d cosh(Vec2d const x) {
|
||||
return cosh_d(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec4d cosh(Vec4d const x) {
|
||||
return cosh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec8d cosh(Vec8d const x) {
|
||||
return cosh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
// Template for cosh function, single precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE cosh_f(VTYPE const x0) {
|
||||
// The limit of abs(x) is 89.0, as defined by max_x in vectormath_exp.h for 0.5*exp(x).
|
||||
|
||||
// data vectors
|
||||
VTYPE x, y;
|
||||
x = abs(x0);
|
||||
y = exp_f<VTYPE, 0, 1>(x); // 0.5 * exp(x)
|
||||
y += 0.25f / y; // + 0.5 * exp(-x)
|
||||
return y;
|
||||
}
|
||||
|
||||
// instances of sinh_d template
|
||||
static inline Vec4f cosh(Vec4f const x) {
|
||||
return cosh_f(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec8f cosh(Vec8f const x) {
|
||||
return cosh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec16f cosh(Vec16f const x) {
|
||||
return cosh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
// Template for tanh function, double precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE tanh_d(VTYPE const x0) {
|
||||
|
||||
// Coefficients
|
||||
const double p0 = -1.61468768441708447952E3;
|
||||
const double p1 = -9.92877231001918586564E1;
|
||||
const double p2 = -9.64399179425052238628E-1;
|
||||
|
||||
const double q0 = 4.84406305325125486048E3;
|
||||
const double q1 = 2.23548839060100448583E3;
|
||||
const double q2 = 1.12811678491632931402E2;
|
||||
const double q3 = 1.0;
|
||||
|
||||
// data vectors
|
||||
VTYPE x, x2, y1, y2;
|
||||
|
||||
x = abs(x0);
|
||||
auto x_small = x <= 0.625; // use Pade approximation if abs(x) <= 5/8
|
||||
|
||||
if (horizontal_or(x_small)) {
|
||||
// At least one element needs small method
|
||||
x2 = x*x;
|
||||
y1 = polynomial_2(x2, p0, p1, p2) / polynomial_3(x2, q0, q1, q2, q3);
|
||||
y1 = mul_add(y1, x2*x, x); // y1 = x + x2*(x*y1);
|
||||
}
|
||||
if (!horizontal_and(x_small)) {
|
||||
// At least one element needs big method
|
||||
y2 = exp(x+x); // exp(2*x)
|
||||
y2 = 1.0 - 2.0 / (y2 + 1.0); // tanh(x)
|
||||
}
|
||||
auto x_big = x > 350.;
|
||||
y1 = select(x_small, y1, y2); // choose method
|
||||
y1 = select(x_big, 1.0, y1); // avoid overflow
|
||||
y1 = sign_combine(y1, x0); // get original sign
|
||||
return y1;
|
||||
}
|
||||
|
||||
// instances of tanh_d template
|
||||
static inline Vec2d tanh(Vec2d const x) {
|
||||
return tanh_d(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec4d tanh(Vec4d const x) {
|
||||
return tanh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec8d tanh(Vec8d const x) {
|
||||
return tanh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
// Template for tanh function, single precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE tanh_f(VTYPE const x0) {
|
||||
// The limit of abs(x) is 89.0, as defined by max_x in vectormath_exp.h for 0.5*exp(x).
|
||||
|
||||
// Coefficients
|
||||
const float r0 = -3.33332819422E-1f;
|
||||
const float r1 = 1.33314422036E-1f;
|
||||
const float r2 = -5.37397155531E-2f;
|
||||
const float r3 = 2.06390887954E-2f;
|
||||
const float r4 = -5.70498872745E-3f;
|
||||
|
||||
// data vectors
|
||||
VTYPE x, x2, y1, y2;
|
||||
|
||||
x = abs(x0);
|
||||
auto x_small = x <= 0.625f; // use polynomial approximation if abs(x) <= 5/8
|
||||
|
||||
if (horizontal_or(x_small)) {
|
||||
// At least one element needs small method
|
||||
x2 = x*x;
|
||||
y1 = polynomial_4(x2, r0, r1, r2, r3, r4);
|
||||
y1 = mul_add(y1, x2*x, x); // y1 = x + (x2*x)*y1;
|
||||
}
|
||||
if (!horizontal_and(x_small)) {
|
||||
// At least one element needs big method
|
||||
y2 = exp(x+x); // exp(2*x)
|
||||
y2 = 1.0f - 2.0f / (y2 + 1.0f); // tanh(x)
|
||||
}
|
||||
auto x_big = x > 44.4f;
|
||||
y1 = select(x_small, y1, y2); // choose method
|
||||
y1 = select(x_big, 1.0f, y1); // avoid overflow
|
||||
y1 = sign_combine(y1, x0); // get original sign
|
||||
return y1;
|
||||
}
|
||||
|
||||
// instances of tanh_f template
|
||||
static inline Vec4f tanh(Vec4f const x) {
|
||||
return tanh_f(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec8f tanh(Vec8f const x) {
|
||||
return tanh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec16f tanh(Vec16f const x) {
|
||||
return tanh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Inverse hyperbolic functions
|
||||
******************************************************************************/
|
||||
|
||||
// Template for asinh function, double precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE asinh_d(VTYPE const x0) {
|
||||
|
||||
// Coefficients
|
||||
const double p0 = -5.56682227230859640450E0;
|
||||
const double p1 = -9.09030533308377316566E0;
|
||||
const double p2 = -4.37390226194356683570E0;
|
||||
const double p3 = -5.91750212056387121207E-1;
|
||||
const double p4 = -4.33231683752342103572E-3;
|
||||
|
||||
const double q0 = 3.34009336338516356383E1;
|
||||
const double q1 = 6.95722521337257608734E1;
|
||||
const double q2 = 4.86042483805291788324E1;
|
||||
const double q3 = 1.28757002067426453537E1;
|
||||
const double q4 = 1.0;
|
||||
|
||||
// data vectors
|
||||
VTYPE x, x2, y1, y2;
|
||||
|
||||
x2 = x0 * x0;
|
||||
x = abs(x0);
|
||||
auto x_small = x <= 0.533; // use Pade approximation if abs(x) <= 0.5
|
||||
// Both methods give the highest error close to 0.5.
|
||||
// This limit is adjusted for minimum error
|
||||
auto x_huge = x > 1.E20; // simple approximation, avoid overflow
|
||||
|
||||
if (horizontal_or(x_small)) {
|
||||
// At least one element needs small method
|
||||
y1 = polynomial_4(x2, p0, p1, p2, p3, p4) / polynomial_4(x2, q0, q1, q2, q3, q4);
|
||||
y1 = mul_add(y1, x2*x, x); // y1 = x + (x2*x)*y1;
|
||||
}
|
||||
if (!horizontal_and(x_small)) {
|
||||
// At least one element needs big method
|
||||
y2 = log(x + sqrt(x2 + 1.0));
|
||||
if (horizontal_or(x_huge)) {
|
||||
// At least one element needs huge method to avoid overflow
|
||||
y2 = select(x_huge, log(x) + VM_LN2, y2);
|
||||
}
|
||||
}
|
||||
y1 = select(x_small, y1, y2); // choose method
|
||||
y1 = sign_combine(y1, x0); // get original sign
|
||||
return y1;
|
||||
}
|
||||
|
||||
// instances of asinh_d template
|
||||
static inline Vec2d asinh(Vec2d const x) {
|
||||
return asinh_d(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec4d asinh(Vec4d const x) {
|
||||
return asinh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec8d asinh(Vec8d const x) {
|
||||
return asinh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
// Template for asinh function, single precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE asinh_f(VTYPE const x0) {
|
||||
|
||||
// Coefficients
|
||||
const float r0 = -1.6666288134E-1f;
|
||||
const float r1 = 7.4847586088E-2f;
|
||||
const float r2 = -4.2699340972E-2f;
|
||||
const float r3 = 2.0122003309E-2f;
|
||||
|
||||
// data vectors
|
||||
VTYPE x, x2, y1, y2;
|
||||
|
||||
x2 = x0 * x0;
|
||||
x = abs(x0);
|
||||
auto x_small = x <= 0.51f; // use polynomial approximation if abs(x) <= 0.5
|
||||
auto x_huge = x > 1.E10f; // simple approximation, avoid overflow
|
||||
|
||||
if (horizontal_or(x_small)) {
|
||||
// At least one element needs small method
|
||||
y1 = polynomial_3(x2, r0, r1, r2, r3);
|
||||
y1 = mul_add(y1, x2*x, x); // y1 = x + (x2*x)*y1;
|
||||
}
|
||||
if (!horizontal_and(x_small)) {
|
||||
// At least one element needs big method
|
||||
y2 = log(x + sqrt(x2 + 1.0f));
|
||||
if (horizontal_or(x_huge)) {
|
||||
// At least one element needs huge method to avoid overflow
|
||||
y2 = select(x_huge, log(x) + (float)VM_LN2, y2);
|
||||
}
|
||||
}
|
||||
y1 = select(x_small, y1, y2); // choose method
|
||||
y1 = sign_combine(y1, x0); // get original sign
|
||||
return y1;
|
||||
}
|
||||
|
||||
// instances of asinh_f template
|
||||
static inline Vec4f asinh(Vec4f const x) {
|
||||
return asinh_f(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec8f asinh(Vec8f const x) {
|
||||
return asinh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec16f asinh(Vec16f const x) {
|
||||
return asinh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
// Template for acosh function, double precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE acosh_d(VTYPE const x0) {
|
||||
|
||||
// Coefficients
|
||||
const double p0 = 1.10855947270161294369E5;
|
||||
const double p1 = 1.08102874834699867335E5;
|
||||
const double p2 = 3.43989375926195455866E4;
|
||||
const double p3 = 3.94726656571334401102E3;
|
||||
const double p4 = 1.18801130533544501356E2;
|
||||
|
||||
const double q0 = 7.83869920495893927727E4;
|
||||
const double q1 = 8.29725251988426222434E4;
|
||||
const double q2 = 2.97683430363289370382E4;
|
||||
const double q3 = 4.15352677227719831579E3;
|
||||
const double q4 = 1.86145380837903397292E2;
|
||||
const double q5 = 1.0;
|
||||
|
||||
// data vectors
|
||||
VTYPE x1, y1, y2;
|
||||
|
||||
x1 = x0 - 1.0;
|
||||
auto undef = x0 < 1.0; // result is NAN
|
||||
auto x_small = x1 < 0.49; // use Pade approximation if abs(x-1) < 0.5
|
||||
auto x_huge = x1 > 1.E20; // simple approximation, avoid overflow
|
||||
|
||||
if (horizontal_or(x_small)) {
|
||||
// At least one element needs small method
|
||||
y1 = sqrt(x1) * (polynomial_4(x1, p0, p1, p2, p3, p4) / polynomial_5(x1, q0, q1, q2, q3, q4, q5));
|
||||
// x < 1 generates NAN
|
||||
y1 = select(undef, nan_vec<VTYPE>(NAN_HYP), y1);
|
||||
}
|
||||
if (!horizontal_and(x_small)) {
|
||||
// At least one element needs big method
|
||||
y2 = log(x0 + sqrt(mul_sub(x0,x0,1.0)));
|
||||
if (horizontal_or(x_huge)) {
|
||||
// At least one element needs huge method to avoid overflow
|
||||
y2 = select(x_huge, log(x0) + VM_LN2, y2);
|
||||
}
|
||||
}
|
||||
y1 = select(x_small, y1, y2); // choose method
|
||||
return y1;
|
||||
}
|
||||
|
||||
// instances of acosh_d template
|
||||
static inline Vec2d acosh(Vec2d const x) {
|
||||
return acosh_d(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec4d acosh(Vec4d const x) {
|
||||
return acosh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec8d acosh(Vec8d const x) {
|
||||
return acosh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
// Template for acosh function, single precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE acosh_f(VTYPE const x0) {
|
||||
|
||||
// Coefficients
|
||||
const float r0 = 1.4142135263E0f;
|
||||
const float r1 = -1.1784741703E-1f;
|
||||
const float r2 = 2.6454905019E-2f;
|
||||
const float r3 = -7.5272886713E-3f;
|
||||
const float r4 = 1.7596881071E-3f;
|
||||
|
||||
// data vectors
|
||||
VTYPE x1, y1, y2;
|
||||
|
||||
x1 = x0 - 1.0f;
|
||||
auto undef = x0 < 1.0f; // result is NAN
|
||||
auto x_small = x1 < 0.49f; // use Pade approximation if abs(x-1) < 0.5
|
||||
auto x_huge = x1 > 1.E10f; // simple approximation, avoid overflow
|
||||
|
||||
if (horizontal_or(x_small)) {
|
||||
// At least one element needs small method
|
||||
y1 = sqrt(x1) * polynomial_4(x1, r0, r1, r2, r3, r4);
|
||||
// x < 1 generates NAN
|
||||
y1 = select(undef, nan_vec<VTYPE>(NAN_HYP), y1);
|
||||
}
|
||||
if (!horizontal_and(x_small)) {
|
||||
// At least one element needs big method
|
||||
y2 = log(x0 + sqrt(mul_sub(x0,x0,1.0)));
|
||||
if (horizontal_or(x_huge)) {
|
||||
// At least one element needs huge method to avoid overflow
|
||||
y2 = select(x_huge, log(x0) + (float)VM_LN2, y2);
|
||||
}
|
||||
}
|
||||
y1 = select(x_small, y1, y2); // choose method
|
||||
return y1;
|
||||
}
|
||||
|
||||
// instances of acosh_f template
|
||||
static inline Vec4f acosh(Vec4f const x) {
|
||||
return acosh_f(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec8f acosh(Vec8f const x) {
|
||||
return acosh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec16f acosh(Vec16f const x) {
|
||||
return acosh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
// Template for atanh function, double precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE atanh_d(VTYPE const x0) {
|
||||
|
||||
// Coefficients
|
||||
const double p0 = -3.09092539379866942570E1;
|
||||
const double p1 = 6.54566728676544377376E1;
|
||||
const double p2 = -4.61252884198732692637E1;
|
||||
const double p3 = 1.20426861384072379242E1;
|
||||
const double p4 = -8.54074331929669305196E-1;
|
||||
|
||||
const double q0 = -9.27277618139601130017E1;
|
||||
const double q1 = 2.52006675691344555838E2;
|
||||
const double q2 = -2.49839401325893582852E2;
|
||||
const double q3 = 1.08938092147140262656E2;
|
||||
const double q4 = -1.95638849376911654834E1;
|
||||
const double q5 = 1.0;
|
||||
|
||||
// data vectors
|
||||
VTYPE x, x2, y1, y2, y3;
|
||||
|
||||
x = abs(x0);
|
||||
auto x_small = x < 0.5; // use Pade approximation if abs(x) < 0.5
|
||||
|
||||
if (horizontal_or(x_small)) {
|
||||
// At least one element needs small method
|
||||
x2 = x * x;
|
||||
y1 = polynomial_4(x2, p0, p1, p2, p3, p4) / polynomial_5(x2, q0, q1, q2, q3, q4, q5);
|
||||
y1 = mul_add(y1, x2*x, x);
|
||||
}
|
||||
if (!horizontal_and(x_small)) {
|
||||
// At least one element needs big method
|
||||
y2 = log((1.0+x)/(1.0-x)) * 0.5;
|
||||
// check if out of range
|
||||
y3 = select(x == 1.0, infinite_vec<VTYPE>(), nan_vec<VTYPE>(NAN_HYP));
|
||||
y2 = select(x >= 1.0, y3, y2);
|
||||
}
|
||||
y1 = select(x_small, y1, y2); // choose method
|
||||
y1 = sign_combine(y1, x0); // get original sign
|
||||
return y1;
|
||||
}
|
||||
|
||||
// instances of atanh_d template
|
||||
static inline Vec2d atanh(Vec2d const x) {
|
||||
return atanh_d(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec4d atanh(Vec4d const x) {
|
||||
return atanh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec8d atanh(Vec8d const x) {
|
||||
return atanh_d(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
|
||||
// Template for atanh function, single precision
|
||||
// This function does not produce denormals
|
||||
// Template parameters:
|
||||
// VTYPE: double vector type
|
||||
template<typename VTYPE>
|
||||
static inline VTYPE atanh_f(VTYPE const x0) {
|
||||
|
||||
// Coefficients
|
||||
const float r0 = 3.33337300303E-1f;
|
||||
const float r1 = 1.99782164500E-1f;
|
||||
const float r2 = 1.46691431730E-1f;
|
||||
const float r3 = 8.24370301058E-2f;
|
||||
const float r4 = 1.81740078349E-1f;
|
||||
|
||||
// data vectors
|
||||
VTYPE x, x2, y1, y2, y3;
|
||||
|
||||
x = abs(x0);
|
||||
auto x_small = x < 0.5f; // use polynomial approximation if abs(x) < 0.5
|
||||
|
||||
if (horizontal_or(x_small)) {
|
||||
// At least one element needs small method
|
||||
x2 = x * x;
|
||||
y1 = polynomial_4(x2, r0, r1, r2, r3, r4);
|
||||
y1 = mul_add(y1, x2*x, x);
|
||||
}
|
||||
if (!horizontal_and(x_small)) {
|
||||
// At least one element needs big method
|
||||
y2 = log((1.0f+x)/(1.0f-x)) * 0.5f;
|
||||
// check if out of range
|
||||
y3 = select(x == 1.0f, infinite_vec<VTYPE>(), nan_vec<VTYPE>(NAN_HYP));
|
||||
y2 = select(x >= 1.0f, y3, y2);
|
||||
}
|
||||
y1 = select(x_small, y1, y2); // choose method
|
||||
y1 = sign_combine(y1, x0); // get original sign
|
||||
return y1;
|
||||
}
|
||||
|
||||
// instances of atanh_f template
|
||||
static inline Vec4f atanh(Vec4f const x) {
|
||||
return atanh_f(x);
|
||||
}
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 256
|
||||
static inline Vec8f atanh(Vec8f const x) {
|
||||
return atanh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 256
|
||||
|
||||
#if MAX_VECTOR_SIZE >= 512
|
||||
static inline Vec16f atanh(Vec16f const x) {
|
||||
return atanh_f(x);
|
||||
}
|
||||
#endif // MAX_VECTOR_SIZE >= 512
|
||||
|
||||
#ifdef VCL_NAMESPACE
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,8 @@ option(NANOVDB_BUILD_PYTHON_MODULE "Build the nanovdb Python module" OFF)
|
||||
option(NANOVDB_USE_INTRINSICS "Build with hardware intrinsics support" OFF)
|
||||
option(NANOVDB_USE_CUDA "Build with CUDA support" OFF)
|
||||
option(NANOVDB_CUDA_KEEP_PTX "Keep CUDA PTX" OFF)
|
||||
option(NANOVDB_CUDA_WERROR "Treat all NVCC diagnostics as errors" ON)
|
||||
option(NANOVDB_USE_OLD_ACCESSOR "Use the old (buggy) ReadAccessor instead of the new fixed one" OFF)
|
||||
|
||||
option(NANOVDB_USE_OPENVDB "Build with OpenVDB support" OFF)
|
||||
option(NANOVDB_USE_BLOSC "Build with BLOSC support" ${USE_BLOSC})
|
||||
@@ -95,6 +97,21 @@ if(NANOVDB_USE_CUDA)
|
||||
|
||||
set(CMAKE_CUDA_FLAGS "${NANOVDB_CUDA_EXTENDED_LAMBDA} -use_fast_math -lineinfo ${CMAKE_CUDA_FLAGS}")
|
||||
|
||||
if(NANOVDB_CUDA_WERROR OR OPENVDB_CXX_STRICT)
|
||||
# Device-side analog of the host -Werror configured in OpenVDBCXX.cmake:
|
||||
# every nvcc front-end diagnostic becomes an error. NANOVDB_CUDA_WERROR
|
||||
# enables it independently of the host-side strict flags.
|
||||
set(CMAKE_CUDA_FLAGS "--Werror=all-warnings ${CMAKE_CUDA_FLAGS}")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# NVCC analog of the /wd4251 and /wd4275 suppressions in OpenVDBCXX.cmake:
|
||||
# it's not possible to use STL types in DLL interfaces in a portable and
|
||||
# reliable way, so the dll-interface diagnostics (#1394 and #1388) fire on
|
||||
# every include of the OpenVDB core headers from a CUDA source.
|
||||
set(CMAKE_CUDA_FLAGS "--diag-suppress=1388,1394 ${CMAKE_CUDA_FLAGS}")
|
||||
endif()
|
||||
|
||||
# workaround for win32 bug when nvcc "--keep" is used.
|
||||
if(WIN32)
|
||||
if(NANOVDB_CUDA_KEEP_PTX)
|
||||
@@ -304,6 +321,10 @@ if(NANOVDB_USE_INTRINSICS)
|
||||
target_compile_definitions(nanovdb INTERFACE -DNANOVDB_USE_INTRINSICS)
|
||||
endif()
|
||||
|
||||
if(NANOVDB_USE_OLD_ACCESSOR)
|
||||
target_compile_definitions(nanovdb INTERFACE -DNANOVDB_USE_OLD_ACCESSOR)
|
||||
endif()
|
||||
|
||||
if(NANOVDB_USE_OPENVDB)
|
||||
if(NOT OPENVDB_BUILD_CORE)
|
||||
target_link_libraries(nanovdb INTERFACE OpenVDB::openvdb)
|
||||
|
||||
+269
-29
@@ -5,14 +5,15 @@
|
||||
// Simple C-wrapper for the nanovdb structure
|
||||
// Meant for systems where you lack a C++ compiler.
|
||||
//
|
||||
|
||||
/// @warning This header file will soon be deprecated in favor of PNanoVDB.h
|
||||
|
||||
#ifndef __CNANOVDB__
|
||||
#define __CNANOVDB__
|
||||
|
||||
#define CNANOVDB_DATA_ALIGNMENT 32
|
||||
#define CNANOVDB_ALIGNMENT_PADDING(x, n) (-(x) & ((n)-1))
|
||||
|
||||
// We compute alignment, but if it evaluates to 0
|
||||
// it will be invalid C99, allowing pedantic compilers to
|
||||
// crash and claim they remain standards conforming.
|
||||
//#define CNANOVDB_DATA_ALIGNMENT 32
|
||||
//#define CNANOVDB_ALIGNMENT_PADDING(x, n) (-(x) & ((n)-1))
|
||||
|
||||
#define USE_SINGLE_ROOT_KEY
|
||||
|
||||
@@ -53,6 +54,21 @@ enum cnanovdb_GridType
|
||||
cnanovdb_GridType_End = 10
|
||||
};
|
||||
|
||||
// #define versions of the grid type to allow pre-processor
|
||||
// tests.
|
||||
#define CNANOVDB_GRIDTYPE_UNKNOWN 0
|
||||
#define CNANOVDB_GRIDTYPE_FLOAT 1
|
||||
#define CNANOVDB_GRIDTYPE_DOUBLE 2
|
||||
#define CNANOVDB_GRIDTYPE_INT16 3
|
||||
#define CNANOVDB_GRIDTYPE_INT32 4
|
||||
#define CNANOVDB_GRIDTYPE_INT64 5
|
||||
#define CNANOVDB_GRIDTYPE_VEC3F 6
|
||||
#define CNANOVDB_GRIDTYPE_VEC3D 7
|
||||
#define CNANOVDB_GRIDTYPE_MASK 8
|
||||
#define CNANOVDB_GRIDTYPE_FP16 9
|
||||
#define CNANOVDB_GRIDTYPE_END 10
|
||||
|
||||
|
||||
#define ROOT_LEVEL 3
|
||||
|
||||
#define DEFINEMASK_int(LOG2DIM, SIZE) \
|
||||
@@ -88,7 +104,7 @@ typedef struct
|
||||
double mInvMatD[9]; // r,c = 3*r + c
|
||||
double mVecD[3];
|
||||
double mTaperD;
|
||||
} cnanovdb_map;
|
||||
} cnanovdb_map; // Size 264
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -204,12 +220,13 @@ typedef struct
|
||||
{
|
||||
int64_t mByteOffset; // byte offset to the blind data, relative to the GridData.
|
||||
uint64_t mElementCount; // number of elements, e.g. point count
|
||||
uint32_t mFlags; // flags
|
||||
uint32_t mValueSize; // byte size of each value, etc.
|
||||
uint32_t mSemantic; // semantic meaning of the data.
|
||||
uint32_t mDataClass; // 4 bytes
|
||||
uint32_t mDataType; // 4 bytes
|
||||
char mName[256];
|
||||
uint8_t _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(int64_t)+sizeof(uint64_t)+2*sizeof(uint32_t)+2*sizeof(uint32_t)+256*sizeof(char), CNANOVDB_DATA_ALIGNMENT)];
|
||||
// 2*8+4*4+256 : aligned, so don't include.
|
||||
// uint8_t _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(int64_t)+sizeof(uint64_t)+2*sizeof(uint32_t)+2*sizeof(uint32_t)+256*sizeof(char), CNANOVDB_DATA_ALIGNMENT)];
|
||||
} cnanovdb_gridblindmetadata;
|
||||
|
||||
typedef struct
|
||||
@@ -227,9 +244,12 @@ typedef struct
|
||||
double mVoxelSize[3]; // 24B. size of a voxel in world units
|
||||
uint32_t mGridClass; // 4B.
|
||||
uint32_t mGridType; // 4B.
|
||||
uint64_t mBlindMetadataOffset; // 8B. offset of GridBlindMetaData structures.
|
||||
int32_t mBlindMetadataCount; // 4B. count of GridBlindMetaData structures.
|
||||
uint32_t _reserved[CNANOVDB_ALIGNMENT_PADDING(8 + 8 + 4 + 4 + 4 + 4 + 8 + 256 + 24 + 24 + sizeof(cnanovdb_map) + 24 + 4 + 4 + 8 + 4, CNANOVDB_DATA_ALIGNMENT) / 4];
|
||||
int64_t mBlindMetadataOffset; // 8B. offset of GridBlindMetaData structures.
|
||||
uint32_t mBlindMetadataCount; // 4B. count of GridBlindMetaData structures.
|
||||
|
||||
// Total 652: misaligned by 20.
|
||||
// uint32_t _reserved[CNANOVDB_ALIGNMENT_PADDING(8 + 8 + 4 + 4 + 4 + 4 + 8 + 256 + 24 + 24 + sizeof(cnanovdb_map) + 24 + 4 + 4 + 8 + 4, CNANOVDB_DATA_ALIGNMENT) / 4];
|
||||
uint32_t _reserved[5];
|
||||
} cnanovdb_griddata;
|
||||
|
||||
static void
|
||||
@@ -268,7 +288,8 @@ typedef struct
|
||||
uint32_t mNodeCount[ROOT_LEVEL];
|
||||
uint32_t mTileCount[ROOT_LEVEL];
|
||||
uint64_t mVoxelCount;
|
||||
uint8_t _reserved[CNANOVDB_ALIGNMENT_PADDING(4*sizeof(uint64_t)+(3+3)*sizeof(uint32_t)+sizeof(uint64_t), CNANOVDB_DATA_ALIGNMENT)];
|
||||
// Total 64, so aligned, so do not include:
|
||||
// uint8_t _reserved[CNANOVDB_ALIGNMENT_PADDING(4*sizeof(uint64_t)+(3+3)*sizeof(uint32_t)+sizeof(uint64_t), CNANOVDB_DATA_ALIGNMENT)];
|
||||
} cnanovdb_treedata;
|
||||
|
||||
static const CNANOVDB_GLOBAL cnanovdb_treedata *
|
||||
@@ -301,6 +322,17 @@ cnanovdb_readaccessor_insert(cnanovdb_readaccessor *RESTRICT acc, int childlevel
|
||||
acc->mKey.mVec[2] = ijk->mVec[2];
|
||||
}
|
||||
|
||||
|
||||
// The padding of nodes varies on the signature, as the value type may
|
||||
// result in aligned or misaligned...
|
||||
#define LEAFDATA_PAD_F
|
||||
#define LEAFDATA_PAD_F3 uint32_t _reserved[4];
|
||||
#define LEAFDATA_PAD_I
|
||||
|
||||
#define NODEDATA_PAD_F uint8_t _reserved[16];
|
||||
#define NODEDATA_PAD_F3
|
||||
#define NODEDATA_PAD_I uint8_t _reserved[16];
|
||||
|
||||
#define CREATE_LEAF_NODE_int(LEVEL, LOG2DIM, CHILDTOTAL, TOTAL, MASK, VALUETYPE, STATSTYPE, SUFFIX) \
|
||||
typedef struct \
|
||||
{ \
|
||||
@@ -312,7 +344,8 @@ typedef struct \
|
||||
VALUETYPE mMaximum; \
|
||||
STATSTYPE mAverage; \
|
||||
STATSTYPE mStdDevi; \
|
||||
uint32_t _reserved[ CNANOVDB_ALIGNMENT_PADDING(sizeof(cnanovdb_mask##LOG2DIM)+2*sizeof(VALUETYPE)+2*sizeof(STATSTYPE)+sizeof(cnanovdb_coord)+sizeof(uint8_t[3])+sizeof(uint8_t), CNANOVDB_DATA_ALIGNMENT)/4]; \
|
||||
LEAFDATA_PAD_##SUFFIX \
|
||||
/* uint32_t _reserved[ CNANOVDB_ALIGNMENT_PADDING(sizeof(cnanovdb_mask##LOG2DIM)+2*sizeof(VALUETYPE)+2*sizeof(STATSTYPE)+sizeof(cnanovdb_coord)+sizeof(uint8_t[3])+sizeof(uint8_t), CNANOVDB_DATA_ALIGNMENT)/4]; */ \
|
||||
VALUETYPE mVoxels[1u << (3*LOG2DIM)]; \
|
||||
} cnanovdb_node##LEVEL##SUFFIX; \
|
||||
\
|
||||
@@ -340,6 +373,23 @@ cnanovdb_node##LEVEL##SUFFIX##_getValueAndCache(const CNANOVDB_GLOBAL cnanovdb_n
|
||||
} \
|
||||
\
|
||||
static bool \
|
||||
cnanovdb_node##LEVEL##SUFFIX##_setValue(CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk, VALUETYPE value) \
|
||||
{ \
|
||||
uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
|
||||
node->mVoxels[n] = value; \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
static bool \
|
||||
cnanovdb_node##LEVEL##SUFFIX##_setValueAndCache(CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk, VALUETYPE value,cnanovdb_readaccessor *RESTRICT /* DO NOT REMOVE: Required for C99 compliance */ acc) \
|
||||
{ \
|
||||
(void)(acc); \
|
||||
uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
|
||||
node->mVoxels[n] = value; \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
static bool \
|
||||
cnanovdb_node##LEVEL##SUFFIX##_isActive(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk) \
|
||||
{ \
|
||||
uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
|
||||
@@ -379,7 +429,7 @@ typedef struct \
|
||||
cnanovdb_mask##LOG2DIM mValueMask, mChildMask; \
|
||||
VALUETYPE mMinimum, mMaximum; \
|
||||
STATSTYPE mAverage, mStdDevi; \
|
||||
uint8_t _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(cnanovdb_mask##LOG2DIM)+sizeof(VALUETYPE)*2+sizeof(STATSTYPE)*2+sizeof(cnanovdb_coord)*2+sizeof(int32_t)+sizeof(uint32_t), CNANOVDB_DATA_ALIGNMENT)]; \
|
||||
NODEDATA_PAD_##SUFFIX \
|
||||
cnanovdb_tileentry##SUFFIX mTable[1u << (3*LOG2DIM)]; \
|
||||
} cnanovdb_node##LEVEL##SUFFIX; \
|
||||
\
|
||||
@@ -398,6 +448,13 @@ cnanovdb_node##LEVEL##SUFFIX##_getChild(const CNANOVDB_GLOBAL cnanovdb_node##LEV
|
||||
return childnode; \
|
||||
} \
|
||||
\
|
||||
static CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX * \
|
||||
cnanovdb_node##LEVEL##SUFFIX##_getChildNC(CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, uint32_t n) \
|
||||
{ \
|
||||
CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX *childnode = (CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX *)( ((CNANOVDB_GLOBAL uint8_t *)node) + node->mTable[n].child); \
|
||||
return childnode; \
|
||||
} \
|
||||
\
|
||||
static VALUETYPE \
|
||||
cnanovdb_node##LEVEL##SUFFIX##_getValue(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk) \
|
||||
{ \
|
||||
@@ -424,6 +481,31 @@ cnanovdb_node##LEVEL##SUFFIX##_getValueAndCache(const CNANOVDB_GLOBAL cnanovdb_n
|
||||
} \
|
||||
\
|
||||
static bool \
|
||||
cnanovdb_node##LEVEL##SUFFIX##_setValue(CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk, VALUETYPE value) \
|
||||
{ \
|
||||
uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
|
||||
if (cnanovdb_mask##LOG2DIM##_isOn(&node->mChildMask, n)) \
|
||||
{ \
|
||||
CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX *child = cnanovdb_node##LEVEL##SUFFIX##_getChildNC(node, n); \
|
||||
return cnanovdb_node##CHILDLEVEL##SUFFIX##_setValue(child, ijk, value); \
|
||||
} \
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
static bool \
|
||||
cnanovdb_node##LEVEL##SUFFIX##_setValueAndCache(CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk, VALUETYPE value, cnanovdb_readaccessor *RESTRICT acc) \
|
||||
{ \
|
||||
uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
|
||||
if (cnanovdb_mask##LOG2DIM##_isOn(&node->mChildMask, n)) \
|
||||
{ \
|
||||
CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX *child = cnanovdb_node##LEVEL##SUFFIX##_getChildNC(node, n); \
|
||||
cnanovdb_readaccessor_insert(acc, CHILDLEVEL, child, ijk); \
|
||||
return cnanovdb_node##CHILDLEVEL##SUFFIX##_setValueAndCache(child, ijk, value, acc); \
|
||||
} \
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
static bool \
|
||||
cnanovdb_node##LEVEL##SUFFIX##_isActive(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk) \
|
||||
{ \
|
||||
uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
|
||||
@@ -462,10 +544,33 @@ CREATE_INTERNAL_NODE_int(CHILDLEVEL, LEVEL, LOG2DIM, (TOTAL-LOG2DIM), TOTAL, ((1
|
||||
|
||||
|
||||
#ifdef USE_SINGLE_ROOT_KEY
|
||||
|
||||
// Whether this is aligned varies with the signature...
|
||||
#define ROOTDATATILE_PAD_F uint8_t _reserved[8];
|
||||
#define ROOTDATATILE_PAD_I uint8_t _reserved[8];
|
||||
#define ROOTDATATILE_PAD_F3
|
||||
|
||||
#define DEFINE_KEY(KEY) \
|
||||
uint64_t KEY;
|
||||
#define KEYSIZE sizeof(uint64_t)
|
||||
|
||||
#ifdef CNANOVDB_FULL_ROOT_SEARCH
|
||||
#define KEYSEARCH(SUFFIX) \
|
||||
uint64_t key; \
|
||||
const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *result = 0; \
|
||||
key = cnanovdb_coord_to_key(ijk); \
|
||||
\
|
||||
(void)high; \
|
||||
/* Re-read tablesize for compiler bug on intel windows driver */ \
|
||||
for (int idx = low; idx < rootdata->mTableSize; idx++) \
|
||||
{ \
|
||||
const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *tile = tiles + idx; \
|
||||
if (tile->key == key) \
|
||||
result = tile; \
|
||||
} \
|
||||
return result; \
|
||||
/**/
|
||||
#else
|
||||
#define KEYSEARCH(SUFFIX) \
|
||||
uint64_t key; \
|
||||
key = cnanovdb_coord_to_key(ijk); \
|
||||
@@ -477,7 +582,14 @@ CREATE_INTERNAL_NODE_int(CHILDLEVEL, LEVEL, LOG2DIM, (TOTAL-LOG2DIM), TOTAL, ((1
|
||||
return tile; \
|
||||
} \
|
||||
/**/
|
||||
#endif
|
||||
#else
|
||||
|
||||
// Whether this is aligned varies with the signature...
|
||||
#define ROOTDATATILE_PAD_F
|
||||
#define ROOTDATATILE_PAD_I
|
||||
#define ROOTDATATILE_PAD_F3 uint8_t _reserved[24];
|
||||
|
||||
#define DEFINE_KEY(KEY) \
|
||||
cnanovdb_coord KEY;
|
||||
#define KEYSIZE sizeof(cnanovdb_coord)
|
||||
@@ -504,6 +616,9 @@ CREATE_INTERNAL_NODE_int(CHILDLEVEL, LEVEL, LOG2DIM, (TOTAL-LOG2DIM), TOTAL, ((1
|
||||
/**/
|
||||
#endif
|
||||
|
||||
#define ROOTDATA_PAD_F uint32_t reserved[4];
|
||||
#define ROOTDATA_PAD_F3 uint32_t reserved[6];
|
||||
#define ROOTDATA_PAD_I uint32_t reserved[4];
|
||||
|
||||
#define CREATE_ROOTDATA(VALUETYPE, STATSTYPE, SUFFIX) \
|
||||
typedef struct \
|
||||
@@ -512,7 +627,11 @@ typedef struct \
|
||||
int64_t child; \
|
||||
uint32_t state; \
|
||||
VALUETYPE value; \
|
||||
uint8_t _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(KEYSIZE)+sizeof(VALUETYPE)+sizeof(int64_t)+sizeof(uint32_t), CNANOVDB_DATA_ALIGNMENT)]; \
|
||||
/* Key is either 8 or 3*4, coord will round to 4*4 due to alignment */ \
|
||||
/* of child, so 8+8 or 4*4+8. state+value is either 4+4 or 4+3*4 */ \
|
||||
/* 8+8+4+4:pad, 8+8+4*4:align, 4*4+8+2*4:align, 4*4+8+4*4:pad */ \
|
||||
ROOTDATATILE_PAD_##SUFFIX \
|
||||
/* uint8_t _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(KEYSIZE)+sizeof(VALUETYPE)+sizeof(int64_t)+sizeof(uint32_t), CNANOVDB_DATA_ALIGNMENT)]; */ \
|
||||
} cnanovdb_rootdata_tile##SUFFIX; \
|
||||
\
|
||||
typedef struct \
|
||||
@@ -522,7 +641,10 @@ typedef struct \
|
||||
VALUETYPE mBackground; \
|
||||
VALUETYPE mMinimum, mMaximum; \
|
||||
STATSTYPE mAverage, mStdDevi; \
|
||||
uint32_t _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(cnanovdb_coord)*2+sizeof(uint32_t)+sizeof(VALUETYPE)*3+sizeof(STATSTYPE)*2, CNANOVDB_DATA_ALIGNMENT)/4]; \
|
||||
/* 2*3*4 + 4 + 3*4 + 2*4: 12*4, unaligned */ \
|
||||
/* 2*3*4 + 4 + 3*3*4 + 2*4: 18*4, unaligned */ \
|
||||
ROOTDATA_PAD_##SUFFIX \
|
||||
/* uint32_t _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(cnanovdb_coord)*2+sizeof(uint32_t)+sizeof(VALUETYPE)*3+sizeof(STATSTYPE)*2, CNANOVDB_DATA_ALIGNMENT)/4]; */ \
|
||||
} cnanovdb_rootdata##SUFFIX; \
|
||||
\
|
||||
static const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX * \
|
||||
@@ -540,6 +662,13 @@ cnanovdb_rootdata##SUFFIX##_getTile(const CNANOVDB_GLOBAL cnanovdb_rootdata##SUF
|
||||
\
|
||||
static const CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX * \
|
||||
cnanovdb_rootdata##SUFFIX##_getChild(const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *RESTRICT tile) \
|
||||
{ \
|
||||
CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *basenode = (CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *) (((CNANOVDB_GLOBAL uint8_t *) rootdata) + tile->child); \
|
||||
return basenode; \
|
||||
} \
|
||||
\
|
||||
static CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX * \
|
||||
cnanovdb_rootdata##SUFFIX##_getChildNC(CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *RESTRICT tile) \
|
||||
{ \
|
||||
CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *basenode = (CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *) (((CNANOVDB_GLOBAL uint8_t *) rootdata) + tile->child); \
|
||||
return basenode; \
|
||||
@@ -555,6 +684,13 @@ cnanovdb_rootdata##SUFFIX##_findTile(const CNANOVDB_GLOBAL cnanovdb_rootdata##SU
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
static CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX * \
|
||||
cnanovdb_rootdata##SUFFIX##_findTileNC(CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, const cnanovdb_coord *RESTRICT ijk) \
|
||||
{ \
|
||||
return (CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *) \
|
||||
cnanovdb_rootdata##SUFFIX##_findTile(rootdata, ijk); \
|
||||
} \
|
||||
\
|
||||
static VALUETYPE \
|
||||
cnanovdb_rootdata##SUFFIX##_getValue(const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, const cnanovdb_coord *RESTRICT ijk) \
|
||||
{ \
|
||||
@@ -577,6 +713,30 @@ cnanovdb_rootdata##SUFFIX##_getValueAndCache(const CNANOVDB_GLOBAL cnanovdb_root
|
||||
const CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *child = cnanovdb_rootdata##SUFFIX##_getChild(rootdata, tile); \
|
||||
cnanovdb_readaccessor_insert(acc, 2, child, ijk); \
|
||||
return cnanovdb_node2##SUFFIX##_getValueAndCache( child, ijk, acc ); \
|
||||
} \
|
||||
\
|
||||
static bool \
|
||||
cnanovdb_rootdata##SUFFIX##_setValue(CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, const cnanovdb_coord *RESTRICT ijk, VALUETYPE value) \
|
||||
{ \
|
||||
CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *tile = cnanovdb_rootdata##SUFFIX##_findTileNC(rootdata, ijk); \
|
||||
if (!tile) \
|
||||
return false; \
|
||||
if (tile->child == 0) \
|
||||
return false; \
|
||||
return cnanovdb_node2##SUFFIX##_setValue( cnanovdb_rootdata##SUFFIX##_getChildNC(rootdata, tile), ijk, value ); \
|
||||
} \
|
||||
\
|
||||
static bool \
|
||||
cnanovdb_rootdata##SUFFIX##_setValueAndCache(CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, const cnanovdb_coord *RESTRICT ijk, VALUETYPE value, cnanovdb_readaccessor *RESTRICT acc) \
|
||||
{ \
|
||||
CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *tile = cnanovdb_rootdata##SUFFIX##_findTileNC(rootdata, ijk); \
|
||||
if (!tile) \
|
||||
return false; \
|
||||
if (tile->child == 0) \
|
||||
return false; \
|
||||
CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *child = cnanovdb_rootdata##SUFFIX##_getChildNC(rootdata, tile); \
|
||||
cnanovdb_readaccessor_insert(acc, 2, child, ijk); \
|
||||
return cnanovdb_node2##SUFFIX##_setValueAndCache( child, ijk, value, acc );\
|
||||
} \
|
||||
\
|
||||
static bool \
|
||||
@@ -605,16 +765,19 @@ cnanovdb_rootdata##SUFFIX##_isActiveAndCache(const CNANOVDB_GLOBAL cnanovdb_root
|
||||
/**/
|
||||
|
||||
|
||||
inline void
|
||||
static inline void
|
||||
cnanovdb_readaccessor_init(cnanovdb_readaccessor *RESTRICT acc,
|
||||
const CNANOVDB_GLOBAL void /*cnanovdb_rootdata* */ *RESTRICT rootdata)
|
||||
{
|
||||
acc->mKey.mVec[0] = 0;
|
||||
acc->mKey.mVec[1] = 0;
|
||||
acc->mKey.mVec[2] = 0;
|
||||
acc->mNode[0] = acc->mNode[1] = acc->mNode[2] = 0;
|
||||
acc->mNode[3] = rootdata;
|
||||
}
|
||||
|
||||
#define DEFINE_ISCACHED(LEVEL, MASK) \
|
||||
inline bool \
|
||||
static inline bool \
|
||||
cnanovdb_readaccessor_isCached##LEVEL(cnanovdb_readaccessor *RESTRICT acc, int32_t dirty) \
|
||||
{ \
|
||||
if (!acc->mNode[LEVEL]) \
|
||||
@@ -632,7 +795,7 @@ DEFINE_ISCACHED(0, ((1u << 3) - 1u) )
|
||||
DEFINE_ISCACHED(1, ((1u << 7) - 1u) )
|
||||
DEFINE_ISCACHED(2, ((1u << 12) - 1u) )
|
||||
|
||||
inline int32_t
|
||||
static inline int32_t
|
||||
cnanovdb_readaccessor_computeDirty(const cnanovdb_readaccessor *RESTRICT acc, const cnanovdb_coord *RESTRICT ijk)
|
||||
{
|
||||
return (ijk->mVec[0] ^ acc->mKey.mVec[0]) |
|
||||
@@ -641,35 +804,102 @@ cnanovdb_readaccessor_computeDirty(const cnanovdb_readaccessor *RESTRICT acc, co
|
||||
}
|
||||
|
||||
#define CREATE_ACCESSOR(VALUETYPE, SUFFIX) \
|
||||
inline VALUETYPE \
|
||||
static inline VALUETYPE \
|
||||
cnanovdb_readaccessor_getValue##SUFFIX(cnanovdb_readaccessor *RESTRICT acc, const cnanovdb_coord *RESTRICT ijk) \
|
||||
{ \
|
||||
int32_t dirty = cnanovdb_readaccessor_computeDirty(acc, ijk); \
|
||||
\
|
||||
if (cnanovdb_readaccessor_isCached0(acc, dirty)) \
|
||||
return cnanovdb_node0##SUFFIX##_getValue( ((CNANOVDB_GLOBAL cnanovdb_node0##SUFFIX *) acc->mNode[0]), ijk); \
|
||||
return cnanovdb_node0##SUFFIX##_getValue( ((const CNANOVDB_GLOBAL cnanovdb_node0##SUFFIX *) acc->mNode[0]), ijk); \
|
||||
if (cnanovdb_readaccessor_isCached1(acc, dirty)) \
|
||||
return cnanovdb_node1##SUFFIX##_getValueAndCache( ((CNANOVDB_GLOBAL cnanovdb_node1##SUFFIX *) acc->mNode[1]), ijk, acc); \
|
||||
return cnanovdb_node1##SUFFIX##_getValueAndCache( ((const CNANOVDB_GLOBAL cnanovdb_node1##SUFFIX *) acc->mNode[1]), ijk, acc); \
|
||||
if (cnanovdb_readaccessor_isCached2(acc, dirty)) \
|
||||
return cnanovdb_node2##SUFFIX##_getValueAndCache( ((CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *) acc->mNode[2]), ijk, acc); \
|
||||
return cnanovdb_node2##SUFFIX##_getValueAndCache( ((const CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *) acc->mNode[2]), ijk, acc); \
|
||||
\
|
||||
return cnanovdb_rootdata##SUFFIX##_getValueAndCache( ((CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *)acc->mNode[3]), ijk, acc); \
|
||||
return cnanovdb_rootdata##SUFFIX##_getValueAndCache( ((const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *)acc->mNode[3]), ijk, acc); \
|
||||
} \
|
||||
\
|
||||
inline bool \
|
||||
static inline VALUETYPE \
|
||||
cnanovdb_readaccessor_getIJKValue##SUFFIX(cnanovdb_readaccessor *RESTRICT acc, int i, int j, int k) \
|
||||
{ \
|
||||
cnanovdb_coord coord; \
|
||||
coord.mVec[0] = i; coord.mVec[1] = j; coord.mVec[2] = k; \
|
||||
return cnanovdb_readaccessor_getValue##SUFFIX(acc, &coord); \
|
||||
} \
|
||||
\
|
||||
static inline CNANOVDB_GLOBAL VALUETYPE * \
|
||||
cnanovdb_readaccessor_getLeaf##SUFFIX(cnanovdb_readaccessor *RESTRICT acc, const cnanovdb_coord *RESTRICT ijk) \
|
||||
{ \
|
||||
int32_t dirty = cnanovdb_readaccessor_computeDirty(acc, ijk); \
|
||||
\
|
||||
if (cnanovdb_readaccessor_isCached0(acc, dirty)) \
|
||||
return ((CNANOVDB_GLOBAL cnanovdb_node0##SUFFIX *) acc->mNode[0])->mVoxels; \
|
||||
else if (cnanovdb_readaccessor_isCached1(acc, dirty)) \
|
||||
cnanovdb_node1##SUFFIX##_getValueAndCache( ((const CNANOVDB_GLOBAL cnanovdb_node1##SUFFIX *) acc->mNode[1]), ijk, acc); \
|
||||
else if (cnanovdb_readaccessor_isCached2(acc, dirty)) \
|
||||
cnanovdb_node2##SUFFIX##_getValueAndCache( ((const CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *) acc->mNode[2]), ijk, acc); \
|
||||
else \
|
||||
cnanovdb_rootdata##SUFFIX##_getValueAndCache( ((const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *)acc->mNode[3]), ijk, acc); \
|
||||
if (acc->mNode[0]) \
|
||||
return ((CNANOVDB_GLOBAL cnanovdb_node0##SUFFIX *) acc->mNode[0])->mVoxels; \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
static inline CNANOVDB_GLOBAL VALUETYPE * \
|
||||
cnanovdb_readaccessor_getIJKLeaf##SUFFIX(cnanovdb_readaccessor *RESTRICT acc, int i, int j, int k) \
|
||||
{ \
|
||||
cnanovdb_coord coord; \
|
||||
coord.mVec[0] = i; coord.mVec[1] = j; coord.mVec[2] = k; \
|
||||
return cnanovdb_readaccessor_getLeaf##SUFFIX(acc, &coord); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
cnanovdb_readaccessor_isActive##SUFFIX(cnanovdb_readaccessor *RESTRICT acc, const cnanovdb_coord *RESTRICT ijk) \
|
||||
{ \
|
||||
int32_t dirty = cnanovdb_readaccessor_computeDirty(acc, ijk); \
|
||||
\
|
||||
if (cnanovdb_readaccessor_isCached0(acc, dirty)) \
|
||||
return cnanovdb_node0##SUFFIX##_isActive( ((CNANOVDB_GLOBAL cnanovdb_node0##SUFFIX *) acc->mNode[0]), ijk); \
|
||||
return cnanovdb_node0##SUFFIX##_isActive( ((const CNANOVDB_GLOBAL cnanovdb_node0##SUFFIX *) acc->mNode[0]), ijk); \
|
||||
if (cnanovdb_readaccessor_isCached1(acc, dirty)) \
|
||||
return cnanovdb_node1##SUFFIX##_isActiveAndCache( ((CNANOVDB_GLOBAL cnanovdb_node1##SUFFIX *) acc->mNode[1]), ijk, acc); \
|
||||
return cnanovdb_node1##SUFFIX##_isActiveAndCache( ((const CNANOVDB_GLOBAL cnanovdb_node1##SUFFIX *) acc->mNode[1]), ijk, acc); \
|
||||
if (cnanovdb_readaccessor_isCached2(acc, dirty)) \
|
||||
return cnanovdb_node2##SUFFIX##_isActiveAndCache( ((CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *) acc->mNode[2]), ijk, acc); \
|
||||
return cnanovdb_node2##SUFFIX##_isActiveAndCache( ((const CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *) acc->mNode[2]), ijk, acc); \
|
||||
\
|
||||
return cnanovdb_rootdata##SUFFIX##_isActiveAndCache( ((CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *)acc->mNode[3]), ijk, acc); \
|
||||
return cnanovdb_rootdata##SUFFIX##_isActiveAndCache( ((const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *)acc->mNode[3]), ijk, acc); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
cnanovdb_readaccessor_isIJKActive##SUFFIX(cnanovdb_readaccessor *RESTRICT acc, int i, int j, int k) \
|
||||
{ \
|
||||
cnanovdb_coord coord; \
|
||||
coord.mVec[0] = i; coord.mVec[1] = j; coord.mVec[2] = k; \
|
||||
return cnanovdb_readaccessor_isActive##SUFFIX(acc, &coord); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
cnanovdb_readaccessor_setValue##SUFFIX(cnanovdb_readaccessor *RESTRICT acc, const cnanovdb_coord *RESTRICT ijk, VALUETYPE value) \
|
||||
{ \
|
||||
int32_t dirty = cnanovdb_readaccessor_computeDirty(acc, ijk); \
|
||||
\
|
||||
if (cnanovdb_readaccessor_isCached0(acc, dirty)) \
|
||||
return cnanovdb_node0##SUFFIX##_setValue( ((CNANOVDB_GLOBAL cnanovdb_node0##SUFFIX *) acc->mNode[0]), ijk, value); \
|
||||
if (cnanovdb_readaccessor_isCached1(acc, dirty)) \
|
||||
return cnanovdb_node1##SUFFIX##_setValueAndCache( ((CNANOVDB_GLOBAL cnanovdb_node1##SUFFIX *) acc->mNode[1]), ijk, value, acc); \
|
||||
if (cnanovdb_readaccessor_isCached2(acc, dirty)) \
|
||||
return cnanovdb_node2##SUFFIX##_setValueAndCache( ((CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *) acc->mNode[2]), ijk, value, acc); \
|
||||
\
|
||||
return cnanovdb_rootdata##SUFFIX##_setValueAndCache( ((CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *)acc->mNode[3]), ijk, value, acc); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
cnanovdb_readaccessor_setIJKValue##SUFFIX(cnanovdb_readaccessor *RESTRICT acc, int i, int j, int k, VALUETYPE value) \
|
||||
{ \
|
||||
cnanovdb_coord coord; \
|
||||
coord.mVec[0] = i; coord.mVec[1] = j; coord.mVec[2] = k; \
|
||||
return cnanovdb_readaccessor_setValue##SUFFIX(acc, &coord, value); \
|
||||
} \
|
||||
\
|
||||
\
|
||||
/**/
|
||||
|
||||
|
||||
@@ -684,6 +914,7 @@ CREATE_ACCESSOR(VALUETYPE, SUFFIX) \
|
||||
|
||||
CREATE_GRIDTYPE(float, float, F)
|
||||
CREATE_GRIDTYPE(cnanovdb_Vec3F, float, F3)
|
||||
CREATE_GRIDTYPE(int, int, I)
|
||||
|
||||
static int
|
||||
cnanovdb_griddata_valid(const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT grid)
|
||||
@@ -715,4 +946,13 @@ cnanovdb_griddata_validF3(const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT grid
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cnanovdb_griddata_validI(const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT grid)
|
||||
{
|
||||
if (!cnanovdb_griddata_valid(grid))
|
||||
return 0;
|
||||
if (grid->mGridType != cnanovdb_GridType_Int32)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
+214
-57
@@ -8,8 +8,9 @@
|
||||
|
||||
\date January 8, 2020
|
||||
|
||||
\brief Defines GridHandle, which manages a host, and possibly a device,
|
||||
memory buffer containing one or more NanoVDB grids.
|
||||
\brief Defines GridHandle, which manages a memory buffer containing one
|
||||
or more NanoVDB grids: host-resident, dual host/device, or a
|
||||
single-space device-only buffer.
|
||||
*/
|
||||
|
||||
#ifndef NANOVDB_GRID_HANDLE_H_HAS_BEEN_INCLUDED
|
||||
@@ -30,18 +31,63 @@ namespace nanovdb {
|
||||
|
||||
struct GridHandleMetaData {uint64_t offset, size; GridType gridType;};
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// @brief Allocates @c bytes of host-readable storage for a GridHandle:
|
||||
/// through @c BufferT::create for buffers that provide it, and through
|
||||
/// @c pool's resource for a host-accessible single-space buffer (e.g. a
|
||||
/// pinned-resource cuda::Buffer). The braced third argument selects the
|
||||
/// uninitialized-storage constructor without naming its tag type, so
|
||||
/// this header stays CUDA-free.
|
||||
template<typename BufferT>
|
||||
inline BufferT createHostStorage(uint64_t bytes, const BufferT& pool)
|
||||
{
|
||||
if constexpr (BufferHasHostSingle<BufferT>::value && BufferHasStream<BufferT>::value) {
|
||||
return BufferT(pool.stream(), pool.resource(), bytes, {});// stream-ordered resource: allocate on the pool's retained stream
|
||||
} else if constexpr (BufferHasHostSingle<BufferT>::value) {
|
||||
return BufferT(pool.resource(), bytes, {});
|
||||
} else {
|
||||
return BufferT::create(bytes, &pool);
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace detail
|
||||
|
||||
/// @brief This class serves to manage a buffer containing one or more NanoVDB Grids.
|
||||
///
|
||||
/// @note It is important to note that this class does NOT depend on OpenVDB.
|
||||
template<typename BufferT = HostBuffer>
|
||||
class GridHandle
|
||||
{
|
||||
static_assert(!(BufferTraits<BufferT>::hasDeviceDual && BufferHasDeviceSingle<BufferT>::value),
|
||||
"a buffer cannot be both dual-space and single-space");
|
||||
static_assert(!(BufferHasDeviceSingle<BufferT>::value || BufferHasHostSingle<BufferT>::value)
|
||||
|| BufferHasByteElements<BufferT>::value,
|
||||
"GridHandle requires byte-addressed single-space storage, e.g. cuda::Buffer<std::byte, R>");
|
||||
|
||||
std::vector<GridHandleMetaData> mMetaData;
|
||||
BufferT mBuffer;
|
||||
|
||||
template <typename T>
|
||||
static T* no_const(const T* ptr) { return const_cast<T*>(ptr); }
|
||||
|
||||
/// @brief Shared lookup behind grid() and deviceGrid(): the @a n'th grid
|
||||
/// within @a base, or nullptr when @a base is null, @a n is out of
|
||||
/// range, or the value type does not match the grid.
|
||||
template<typename ValueT>
|
||||
const NanoGrid<ValueT>* gridAt(const void* base, uint32_t n) const
|
||||
{
|
||||
if (base == nullptr || n >= mMetaData.size() || mMetaData[n].gridType != toGridType<ValueT>()) return nullptr;
|
||||
return util::PtrAdd<NanoGrid<ValueT>>(base, mMetaData[n].offset);
|
||||
}
|
||||
|
||||
/// @brief Adopts a buffer whose metadata is already known, so a deep copy
|
||||
/// does not re-parse and a non-default-constructible buffer (e.g.
|
||||
/// over a ResourceRef) never needs default construction.
|
||||
GridHandle(BufferT&& buffer, std::vector<GridHandleMetaData> meta)
|
||||
: mMetaData(std::move(meta))
|
||||
, mBuffer(std::move(buffer)) {}
|
||||
|
||||
public:
|
||||
using BufferType = BufferT;
|
||||
|
||||
@@ -55,7 +101,15 @@ public:
|
||||
/// @brief Move constructor from a host buffer
|
||||
/// @param buffer buffer containing one or more NanoGrids that will be moved into this GridHandle
|
||||
/// @throw Will throw and error with the buffer does not contain a valid NanoGrid!
|
||||
template<typename T = BufferT, typename util::disable_if<BufferTraits<T>::hasDeviceDual, int>::type = 0>
|
||||
template<typename T = BufferT, typename util::disable_if<BufferTraits<T>::hasDeviceDual || BufferHasDeviceSingle<T>::value, int>::type = 0>
|
||||
GridHandle(T&& buffer);
|
||||
|
||||
/// @brief Move constructor from a single-space device buffer: the grid
|
||||
/// metadata is read through the device on the buffer's stream.
|
||||
/// @param buffer buffer containing one or more NanoGrids that will be moved into this GridHandle
|
||||
/// @throw Will throw an error if the buffer does not contain a valid NanoGrid
|
||||
/// @note The implementation of this template specialization is in nanovdb/cuda/GridHandle.cuh since it requires CUDA
|
||||
template<typename T = BufferT, typename util::enable_if<BufferHasDeviceSingle<T>::value, int>::type = 0, typename = void>
|
||||
GridHandle(T&& buffer);
|
||||
|
||||
/// @brief Constructs an empty GridHandle
|
||||
@@ -65,14 +119,14 @@ public:
|
||||
GridHandle(const GridHandle&) = delete;
|
||||
|
||||
/// @brief Move copy-constructor
|
||||
GridHandle(GridHandle&& other) noexcept {
|
||||
mBuffer = std::move(other.mBuffer);
|
||||
mMetaData = std::move(other.mMetaData);
|
||||
}
|
||||
GridHandle(GridHandle&& other) noexcept
|
||||
: mMetaData(std::move(other.mMetaData))
|
||||
, mBuffer(std::move(other.mBuffer)) {}
|
||||
|
||||
/// @brief clear this GridHandle to an empty handle
|
||||
void reset() {
|
||||
mBuffer.clear();
|
||||
if constexpr (BufferHasDestroy<BufferT>::value) mBuffer.destroy();
|
||||
else mBuffer.clear();
|
||||
mMetaData.clear();
|
||||
}
|
||||
|
||||
@@ -87,11 +141,25 @@ public:
|
||||
}
|
||||
|
||||
/// @brief Performs a deep copy of the GridHandle, possibly templated on a different buffer type
|
||||
/// @tparam OtherBufferT Buffer type of the deep copy
|
||||
/// @param buffer optional buffer used for allocation
|
||||
/// @tparam OtherBufferT Buffer type of the deep copy. A single-space device
|
||||
/// handle only copies to its own buffer type (device-to-device,
|
||||
/// ordered on the source's retained stream), so call it as
|
||||
/// copy<BufferT>() -- the default OtherBufferT does not compile.
|
||||
/// @param buffer optional buffer used for allocation; a compile error for
|
||||
/// single-space handles, which allocate through the source buffer's
|
||||
/// resource (use the no-argument overload)
|
||||
/// @return A new handle of the specified buffer type that contains a deep copy of the current handle
|
||||
template <typename OtherBufferT = HostBuffer>
|
||||
GridHandle<OtherBufferT> copy(const OtherBufferT& buffer = OtherBufferT()) const;
|
||||
GridHandle<OtherBufferT> copy(const OtherBufferT& buffer) const;
|
||||
|
||||
/// @brief Deep copy without a pool argument. The single-space path
|
||||
/// allocates through the source buffer's resource and never
|
||||
/// constructs a pool buffer, so it works for buffers that are not
|
||||
/// default-constructible, e.g. over a ResourceRef. The host path
|
||||
/// default-constructs the pool argument, so it requires a
|
||||
/// default-constructible OtherBufferT.
|
||||
template <typename OtherBufferT = HostBuffer>
|
||||
GridHandle<OtherBufferT> copy() const;
|
||||
|
||||
/// @brief Return a reference to the buffer
|
||||
BufferT& buffer() { return mBuffer; }
|
||||
@@ -99,13 +167,15 @@ public:
|
||||
/// @brief Return a const reference to the buffer
|
||||
const BufferT& buffer() const { return mBuffer; }
|
||||
|
||||
/// @brief Returns a non-const pointer to the data.
|
||||
//@{
|
||||
/// @brief Returns a pointer to the host data; not available for a
|
||||
/// single-space device buffer, which has no host-readable bytes.
|
||||
/// @warning Note that the return pointer can be NULL if the GridHandle was not initialized
|
||||
template<typename U = BufferT, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type = 0>
|
||||
void* data() { return mBuffer.data(); }
|
||||
|
||||
/// @brief Returns a const pointer to the data.
|
||||
/// @warning Note that the return pointer can be NULL if the GridHandle was not initialized
|
||||
template<typename U = BufferT, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type = 0>
|
||||
const void* data() const { return mBuffer.data(); }
|
||||
//@}
|
||||
|
||||
template<typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, const void*>::type
|
||||
@@ -120,6 +190,16 @@ public:
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, void*>::type
|
||||
deviceData(int device) { return mBuffer.deviceData(device); }
|
||||
|
||||
//@{
|
||||
/// @brief For a single-space buffer the device data is the buffer itself.
|
||||
template<typename U = BufferT>
|
||||
typename util::enable_if<BufferHasDeviceSingle<U>::value, const void*>::type
|
||||
deviceData() const { return mBuffer.data(); }
|
||||
template<typename U = BufferT>
|
||||
typename util::enable_if<BufferHasDeviceSingle<U>::value, void*>::type
|
||||
deviceData() { return mBuffer.data(); }
|
||||
//@}
|
||||
|
||||
//@{
|
||||
/// @brief Returns the size in bytes of the raw memory buffer managed by this GridHandle.
|
||||
[[deprecated("Use GridHandle::bufferSize instead.")]] uint64_t size() const { return mBuffer.size(); }
|
||||
@@ -140,7 +220,7 @@ public:
|
||||
/// @param n Index of the (host) grid pointer to be returned
|
||||
/// @warning Note that the return pointer can be NULL if the GridHandle no host grid, @a n is invalid
|
||||
/// or if the template parameter does not match the specified grid!
|
||||
template<typename ValueT>
|
||||
template<typename ValueT, typename U = BufferT, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type = 0>
|
||||
const NanoGrid<ValueT>* grid(uint32_t n = 0) const;
|
||||
|
||||
/// @brief Returns a host pointer to the @a n'th NanoVDB grid encoded in this GridHandle.
|
||||
@@ -148,7 +228,7 @@ public:
|
||||
/// @param n Index of the (host) grid pointer to be returned
|
||||
/// @warning Note that the return pointer can be NULL if the GridHandle no host grid, @a n is invalid
|
||||
/// or if the template parameter does not match the specified grid!
|
||||
template<typename ValueT>
|
||||
template<typename ValueT, typename U = BufferT, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type = 0>
|
||||
NanoGrid<ValueT>* grid(uint32_t n = 0) {return const_cast<NanoGrid<ValueT>*>(static_cast<const GridHandle*>(this)->template grid<ValueT>(n));}
|
||||
|
||||
/// @brief Return a const pointer to the @a n'th grid encoded in this GridHandle on the device, e.g. GPU
|
||||
@@ -163,13 +243,24 @@ public:
|
||||
/// @brief Return a const pointer to the @a n'th grid encoded in this GridHandle on the device, e.g. GPU
|
||||
/// @tparam ValueT Value type of the grid point to be returned
|
||||
/// @param n Index of the grid pointer to be returned
|
||||
/// @param verbose if non-zero error messages will be printed in case something failed
|
||||
/// @warning Note that the return pointer can be NULL if the GridHandle was not initialized, @a n is invalid,
|
||||
/// or if the template parameter does not match the specified grid.
|
||||
template<typename ValueT, typename U = BufferT>
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, NanoGrid<ValueT>*>::type
|
||||
deviceGrid(uint32_t n=0){return const_cast<NanoGrid<ValueT>*>(static_cast<const GridHandle*>(this)->template deviceGrid<ValueT>(n));}
|
||||
|
||||
//@{
|
||||
/// @brief Return a pointer to the @a n'th grid of a single-space device buffer
|
||||
/// @warning Note that the return pointer can be NULL if the GridHandle is empty, @a n is invalid,
|
||||
/// or if the template parameter does not match the specified grid.
|
||||
template<typename ValueT, typename U = BufferT>
|
||||
typename util::enable_if<BufferHasDeviceSingle<U>::value, const NanoGrid<ValueT>*>::type
|
||||
deviceGrid(uint32_t n=0) const { return this->template gridAt<ValueT>(mBuffer.data(), n); }
|
||||
template<typename ValueT, typename U = BufferT>
|
||||
typename util::enable_if<BufferHasDeviceSingle<U>::value, NanoGrid<ValueT>*>::type
|
||||
deviceGrid(uint32_t n=0){return const_cast<NanoGrid<ValueT>*>(static_cast<const GridHandle*>(this)->template deviceGrid<ValueT>(n));}
|
||||
//@}
|
||||
|
||||
/// @brief Upload the grid to the device, e.g. from CPU to GPU
|
||||
/// @note This method is only available if the buffer supports devices
|
||||
template<typename U = BufferT>
|
||||
@@ -231,17 +322,21 @@ public:
|
||||
/// @brief Access to the GridData of the n'th grid in the current handle
|
||||
/// @param n zero-based ID of the grid
|
||||
/// @return Const pointer to the n'th GridData in the current handle
|
||||
template<typename U = BufferT, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type = 0>
|
||||
const GridData* gridData(uint32_t n = 0) const;
|
||||
|
||||
/// @brief Returns a const point to the @a n'th grid meta data
|
||||
/// @param n zero-based ID of the grid
|
||||
/// @warning Note that the return pointer can be NULL if the GridHandle was not initialized
|
||||
template<typename U = BufferT, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type = 0>
|
||||
const GridMetaData* gridMetaData(uint32_t n = 0) const;
|
||||
|
||||
/// @brief Write a specific grid in this buffer to an output stream
|
||||
/// @param os output stream that the buffer will be written to
|
||||
/// @param n zero-based index of the grid to be written to stream
|
||||
void write(std::ostream& os, uint32_t n) const {
|
||||
static_assert(!BufferHasDeviceSingle<BufferT>::value,
|
||||
"GridHandle::write requires host-accessible grids: cuda::copyTo a host-readable handle first");
|
||||
if (const GridData* data = this->gridData(n)) {
|
||||
os.write((const char*)data, data->mGridSize);
|
||||
} else {
|
||||
@@ -252,6 +347,9 @@ public:
|
||||
/// @brief Write the entire grid buffer to an output stream
|
||||
/// @param os output stream that the buffer will be written to
|
||||
void write(std::ostream& os) const {
|
||||
static_assert(!BufferHasDeviceSingle<BufferT>::value,
|
||||
"GridHandle::write requires host-accessible grids: cuda::copyTo a host-readable handle first");
|
||||
|
||||
for (uint32_t n=0; n<this->gridCount(); ++n) this->write(os, n);
|
||||
}
|
||||
|
||||
@@ -293,7 +391,7 @@ public:
|
||||
void read(std::istream& is, const std::string &gridName, const BufferT& pool = BufferT());
|
||||
|
||||
/// @brief Read a raw grid buffer from a file
|
||||
/// @param filename string name of the input file containing a raw grid buffer
|
||||
/// @param fileName string name of the input file containing a raw grid buffer
|
||||
/// @param pool optional pool from which to allocate the new grid buffer
|
||||
void read(const std::string &fileName, const BufferT& pool = BufferT()) {
|
||||
std::ifstream is(fileName, std::ios::in | std::ios::binary);
|
||||
@@ -302,7 +400,7 @@ public:
|
||||
}
|
||||
|
||||
/// @brief Read a specific grid from a file containing a raw grid buffer
|
||||
/// @param filename string name of the input file containing a raw grid buffer
|
||||
/// @param fileName string name of the input file containing a raw grid buffer
|
||||
/// @param n zero-based index of the grid to be read
|
||||
/// @param pool optional pool from which to allocate the new grid buffer
|
||||
/// @throw Will throw a std::ios_base::failure if the file does not exist and a
|
||||
@@ -314,7 +412,7 @@ public:
|
||||
}
|
||||
|
||||
/// @brief Read a specific grid from a file containing a raw grid buffer
|
||||
/// @param filename string name of the input file containing a raw grid buffer
|
||||
/// @param fileName string name of the input file containing a raw grid buffer
|
||||
/// @param gridName string name of the grid to be read
|
||||
/// @param pool optional pool from which to allocate the new grid buffer
|
||||
/// @throw Will throw a std::ios_base::failure if the file does not exist and a
|
||||
@@ -329,6 +427,7 @@ public:
|
||||
// --------------------------> Implementation of private methods in GridHandle <------------------------------------
|
||||
|
||||
template<typename BufferT>
|
||||
template<typename U, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type>
|
||||
inline const GridData* GridHandle<BufferT>::gridData(uint32_t n) const
|
||||
{
|
||||
const void *data = this->data();
|
||||
@@ -337,6 +436,7 @@ inline const GridData* GridHandle<BufferT>::gridData(uint32_t n) const
|
||||
}// const GridData* GridHandle<BufferT>::gridData(uint32_t n) const
|
||||
|
||||
template<typename BufferT>
|
||||
template<typename U, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type>
|
||||
inline const GridMetaData* GridHandle<BufferT>::gridMetaData(uint32_t n) const
|
||||
{
|
||||
const auto *data = this->data();
|
||||
@@ -356,11 +456,11 @@ inline __hostdev__ void cpyGridHandleMeta(const GridData *data, GridHandleMetaDa
|
||||
|
||||
// template specialization of move constructor from a host buffer
|
||||
template<typename BufferT>
|
||||
template<typename T, typename util::disable_if<BufferTraits<T>::hasDeviceDual, int>::type>
|
||||
template<typename T, typename util::disable_if<BufferTraits<T>::hasDeviceDual || BufferHasDeviceSingle<T>::value, int>::type>
|
||||
GridHandle<BufferT>::GridHandle(T&& buffer)
|
||||
: mBuffer(std::move(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);
|
||||
@@ -372,19 +472,38 @@ template<typename BufferT>
|
||||
template <typename OtherBufferT>
|
||||
inline GridHandle<OtherBufferT> GridHandle<BufferT>::copy(const OtherBufferT& other) const
|
||||
{
|
||||
if (mBuffer.isEmpty()) return GridHandle<OtherBufferT>();// return an empty handle
|
||||
auto buffer = OtherBufferT::create(mBuffer.size(), &other);
|
||||
static_assert(!(BufferHasDeviceSingle<BufferT>::value || BufferHasDeviceSingle<OtherBufferT>::value),
|
||||
"GridHandle::copy(pool) cannot honor a pool argument for a single-space device buffer, "
|
||||
"whose copy allocates through the source buffer's resource: use the no-argument copy()");
|
||||
if (mBuffer.size() == 0) return GridHandle<OtherBufferT>();// return an empty handle
|
||||
auto buffer = detail::createHostStorage<OtherBufferT>(mBuffer.size(), other);
|
||||
std::memcpy(buffer.data(), mBuffer.data(), mBuffer.size());// deep copy of buffer
|
||||
return GridHandle<OtherBufferT>(std::move(buffer));
|
||||
}// GridHandle<OtherBufferT> GridHandle<BufferT>::copy(const OtherBufferT& other) const
|
||||
|
||||
template<typename BufferT>
|
||||
template<typename ValueT>
|
||||
template <typename OtherBufferT>
|
||||
inline GridHandle<OtherBufferT> GridHandle<BufferT>::copy() const
|
||||
{
|
||||
if constexpr (BufferHasDeviceSingle<BufferT>::value || BufferHasDeviceSingle<OtherBufferT>::value) {
|
||||
static_assert(util::is_same<OtherBufferT, BufferT>::value && BufferHasDeviceSingle<BufferT>::value,
|
||||
"GridHandle::copy is same-space only: a single-space device handle copies to its own "
|
||||
"buffer type; use cuda::copyTo (cuda/GridHandle.cuh) to move grids across address spaces");
|
||||
// Device-to-device deep copy; for a stream-ordered resource it is
|
||||
// ordered on the source's retained stream, so synchronize that stream
|
||||
// before reading the result. Metadata is host-resident, so the copy
|
||||
// adopts it directly with no device re-parse.
|
||||
return GridHandle(mBuffer.copy(), mMetaData);
|
||||
} else {
|
||||
return this->copy(OtherBufferT());
|
||||
}
|
||||
}// GridHandle<OtherBufferT> GridHandle<BufferT>::copy() const
|
||||
|
||||
template<typename BufferT>
|
||||
template<typename ValueT, typename U, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type>
|
||||
inline const NanoGrid<ValueT>* GridHandle<BufferT>::grid(uint32_t n) const
|
||||
{
|
||||
const void *data = mBuffer.data();
|
||||
if (data == nullptr || n >= mMetaData.size() || mMetaData[n].gridType != toGridType<ValueT>()) return nullptr;
|
||||
return util::PtrAdd<NanoGrid<ValueT>>(data, mMetaData[n].offset);
|
||||
return this->template gridAt<ValueT>(mBuffer.data(), n);
|
||||
}// const NanoGrid<ValueT>* GridHandle<BufferT>::grid(uint32_t n) const
|
||||
|
||||
template<typename BufferT>
|
||||
@@ -392,14 +511,15 @@ template<typename ValueT, typename U>
|
||||
inline typename util::enable_if<BufferTraits<U>::hasDeviceDual, const NanoGrid<ValueT>*>::type
|
||||
GridHandle<BufferT>::deviceGrid(uint32_t n) const
|
||||
{
|
||||
const void *data = mBuffer.deviceData();
|
||||
if (data == nullptr || n >= mMetaData.size() || mMetaData[n].gridType != toGridType<ValueT>()) return nullptr;
|
||||
return util::PtrAdd<NanoGrid<ValueT>>(data, mMetaData[n].offset);
|
||||
return this->template gridAt<ValueT>(mBuffer.deviceData(), n);
|
||||
}// GridHandle<BufferT>::deviceGrid(uint32_t n) cons
|
||||
|
||||
template<typename BufferT>
|
||||
void GridHandle<BufferT>::read(std::istream& is, const BufferT& pool)
|
||||
{
|
||||
static_assert(!BufferHasDeviceSingle<BufferT>::value,
|
||||
"GridHandle::read requires a host-accessible buffer: read into a host-readable handle, then cuda::copyTo");
|
||||
const std::streampos start = is.tellg();// remember where the raw buffer begins
|
||||
GridData data;
|
||||
is.read((char*)&data, sizeof(GridData));
|
||||
if (data.isValid()) {
|
||||
@@ -409,8 +529,8 @@ void GridHandle<BufferT>::read(std::istream& is, const BufferT& pool)
|
||||
is.read((char*)&data, sizeof(GridData));
|
||||
sum += data.mGridSize;
|
||||
}
|
||||
auto buffer = BufferT::create(size + sum, &pool);
|
||||
is.seekg(-int64_t(sum + sizeof(GridData)), std::ios::cur);// rewind to start
|
||||
auto buffer = detail::createHostStorage(size + sum, pool);
|
||||
is.seekg(start);// rewind to the start of the raw buffer
|
||||
is.read((char*)(buffer.data()), buffer.size());
|
||||
*this = GridHandle(std::move(buffer));
|
||||
} else {
|
||||
@@ -422,6 +542,8 @@ void GridHandle<BufferT>::read(std::istream& is, const BufferT& pool)
|
||||
template<typename BufferT>
|
||||
void GridHandle<BufferT>::read(std::istream& is, uint32_t n, const BufferT& pool)
|
||||
{
|
||||
static_assert(!BufferHasDeviceSingle<BufferT>::value,
|
||||
"GridHandle::read requires a host-accessible buffer: read into a host-readable handle, then cuda::copyTo");
|
||||
GridData data;
|
||||
is.read((char*)&data, sizeof(GridData));
|
||||
if (data.isValid()) {
|
||||
@@ -430,7 +552,7 @@ void GridHandle<BufferT>::read(std::istream& is, uint32_t n, const BufferT& pool
|
||||
is.seekg(data.mGridSize - sizeof(GridData), std::ios::cur);// skip grid
|
||||
is.read((char*)&data, sizeof(GridData));
|
||||
}
|
||||
auto buffer = BufferT::create(data.mGridSize, &pool);
|
||||
auto buffer = detail::createHostStorage(data.mGridSize, pool);
|
||||
is.seekg(-sizeof(GridData), std::ios::cur);// rewind
|
||||
is.read((char*)(buffer.data()), data.mGridSize);
|
||||
tools::updateGridCount((GridData*)buffer.data(), 0u, 1u);
|
||||
@@ -444,6 +566,8 @@ void GridHandle<BufferT>::read(std::istream& is, uint32_t n, const BufferT& pool
|
||||
template<typename BufferT>
|
||||
void GridHandle<BufferT>::read(std::istream& is, const std::string &gridName, const BufferT& pool)
|
||||
{
|
||||
static_assert(!BufferHasDeviceSingle<BufferT>::value,
|
||||
"GridHandle::read requires a host-accessible buffer: read into a host-readable handle, then cuda::copyTo");
|
||||
static const std::streamsize byteSize = sizeof(GridData);
|
||||
GridData data;
|
||||
is.read((char*)&data, byteSize);
|
||||
@@ -456,7 +580,7 @@ void GridHandle<BufferT>::read(std::istream& is, const std::string &gridName, co
|
||||
is.seekg(-byteSize, std::ios::cur);// rewind
|
||||
}
|
||||
if (n>data.mGridCount) throw std::runtime_error("No raw grid named \""+gridName+"\"");
|
||||
auto buffer = BufferT::create(data.mGridSize, &pool);
|
||||
auto buffer = detail::createHostStorage(data.mGridSize, pool);
|
||||
is.read((char*)(buffer.data()), data.mGridSize);
|
||||
tools::updateGridCount((GridData*)buffer.data(), 0u, 1u);
|
||||
*this = GridHandle(std::move(buffer));
|
||||
@@ -470,12 +594,16 @@ void GridHandle<BufferT>::read(std::istream& is, const std::string &gridName, co
|
||||
/// @brief Split all grids in a single GridHandle into a vector of multiple GridHandles each with a single grid
|
||||
/// @tparam BufferT Type of the input and output grid buffers
|
||||
/// @param handle GridHandle with grids that will be slip into individual GridHandles
|
||||
/// @param pool optional pool used for allocation of output GridHandle
|
||||
/// @param other optional pool used for allocation of output GridHandle
|
||||
/// @return Vector of GridHandles each containing a single grid
|
||||
template<typename BufferT, template <class, class...> class VectorT = std::vector>
|
||||
inline VectorT<GridHandle<BufferT>>
|
||||
splitGrids(const GridHandle<BufferT> &handle, const BufferT* other = nullptr)
|
||||
{
|
||||
static_assert(!BufferHasDeviceSingle<BufferT>::value,
|
||||
"splitGrids requires a buffer type providing create(): cuda::copyTo a HostBuffer handle first");
|
||||
static_assert(!BufferHasHostSingle<BufferT>::value,
|
||||
"splitGrids requires a buffer type providing create(): copy the handle to a HostBuffer first");
|
||||
using HandleT = GridHandle<BufferT>;
|
||||
const void *ptr = handle.data();
|
||||
if (ptr == nullptr) return VectorT<HandleT>();
|
||||
@@ -493,6 +621,51 @@ splitGrids(const GridHandle<BufferT> &handle, const BufferT* other = nullptr)
|
||||
return handles;
|
||||
}// splitGrids
|
||||
|
||||
/// @brief Combines (or merges) multiple GridHandles into a single GridHandle containing all grids
|
||||
/// @tparam BufferT Type of the input and output grid buffers
|
||||
/// @param handles array of non-owning pointers to the GridHandles to be combined
|
||||
/// @param count number of pointers in @a handles
|
||||
/// @param pool optional pool used for allocation of output GridHandle
|
||||
/// @return single GridHandle containing all input grids
|
||||
/// @note This overload borrows the input handles, so it can be used when the
|
||||
/// handles are owned elsewhere (e.g. by a language binding) and can be
|
||||
/// neither moved from nor cheaply copied.
|
||||
template<typename BufferT>
|
||||
inline GridHandle<BufferT>
|
||||
mergeGrids(const GridHandle<BufferT>* const* handles, size_t count, const BufferT* pool = nullptr)
|
||||
{
|
||||
static_assert(!BufferHasDeviceSingle<BufferT>::value,
|
||||
"mergeGrids requires a buffer type providing create(): cuda::copyTo HostBuffer handles first");
|
||||
static_assert(!BufferHasHostSingle<BufferT>::value,
|
||||
"mergeGrids requires a buffer type providing create(): copy the handles to HostBuffer first");
|
||||
uint64_t size = 0u;
|
||||
uint32_t counter = 0u, gridCount = 0u;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
gridCount += handles[i]->gridCount();
|
||||
for (uint32_t n=0; n<handles[i]->gridCount(); ++n) size += handles[i]->gridSize(n);
|
||||
}
|
||||
// Return an empty handle when there is nothing to merge: BufferT::create(0)
|
||||
// can produce a non-null zero-byte region, from which the GridHandle
|
||||
// constructor would attempt to read a GridData header.
|
||||
if (gridCount == 0u) return GridHandle<BufferT>();
|
||||
auto buffer = BufferT::create(size, pool);
|
||||
void *dst = buffer.data();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
const GridHandle<BufferT> &h = *handles[i];
|
||||
for (uint32_t n=0; n<h.gridCount(); ++n) {
|
||||
// gridData(n) applies the per-grid offset, so padded (non-contiguous)
|
||||
// source buffers are copied correctly.
|
||||
const GridData *src = h.gridData(n);
|
||||
NANOVDB_ASSERT(src && src->isValid());
|
||||
const uint64_t gridSize = h.gridSize(n);
|
||||
std::memcpy(dst, src, gridSize);
|
||||
tools::updateGridCount(reinterpret_cast<GridData*>(dst), counter++, gridCount);
|
||||
dst = util::PtrAdd(dst, gridSize);
|
||||
}
|
||||
}
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
}// mergeGrids
|
||||
|
||||
/// @brief Combines (or merges) multiple GridHandles into a single GridHandle containing all grids
|
||||
/// @tparam BufferT Type of the input and output grid buffers
|
||||
/// @param handles Vector of GridHandles to be combined
|
||||
@@ -502,26 +675,10 @@ template<typename BufferT, template <class, class...> class VectorT>
|
||||
inline GridHandle<BufferT>
|
||||
mergeGrids(const VectorT<GridHandle<BufferT>> &handles, const BufferT* pool = nullptr)
|
||||
{
|
||||
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, pool);
|
||||
void *dst = buffer.data();
|
||||
for (auto &h : handles) {
|
||||
const void *src = h.data();
|
||||
for (uint32_t n=0; n<h.gridCount(); ++n) {
|
||||
std::memcpy(dst, src, h.gridSize(n));
|
||||
GridData *data = reinterpret_cast<GridData*>(dst);
|
||||
NANOVDB_ASSERT(data->isValid());
|
||||
tools::updateGridCount(data, counter++, gridCount);
|
||||
dst = util::PtrAdd(dst, data->mGridSize);
|
||||
src = util::PtrAdd(src, data->mGridSize);
|
||||
}
|
||||
}
|
||||
return GridHandle<BufferT>(std::move(buffer));
|
||||
std::vector<const GridHandle<BufferT>*> ptrs;
|
||||
ptrs.reserve(handles.size());
|
||||
for (auto &h : handles) ptrs.push_back(&h);
|
||||
return mergeGrids<BufferT>(ptrs.data(), ptrs.size(), pool);
|
||||
}// mergeGrids
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
#include <cassert>// for assert
|
||||
#include <sstream>// for std::stringstream
|
||||
#include <cstring>// for memcpy
|
||||
#include <type_traits>// for std::void_t
|
||||
|
||||
#define checkPtr(ptr, msg) \
|
||||
{ \
|
||||
@@ -98,9 +99,57 @@ namespace nanovdb {
|
||||
template<typename BufferT>
|
||||
struct BufferTraits
|
||||
{
|
||||
static constexpr bool hasDeviceDual = false;
|
||||
static constexpr bool hasDeviceDual = false;
|
||||
static constexpr bool hasDeviceSingle = false;
|
||||
};
|
||||
|
||||
/// @brief Detects whether @c BufferTraits<BufferT> defines @c hasDeviceSingle,
|
||||
/// i.e. whether the buffer manages a single device-resident allocation.
|
||||
/// @details Defaults to false when the trait member is absent, so pre-existing
|
||||
/// BufferTraits specializations (in or out of tree) that only define
|
||||
/// hasDeviceDual keep compiling unchanged.
|
||||
template<typename BufferT, typename = void>
|
||||
struct BufferHasDeviceSingle { static constexpr bool value = false; };
|
||||
template<typename BufferT>
|
||||
struct BufferHasDeviceSingle<BufferT, std::void_t<decltype(BufferTraits<BufferT>::hasDeviceSingle)>>
|
||||
{ static constexpr bool value = BufferTraits<BufferT>::hasDeviceSingle; };
|
||||
|
||||
/// @brief Companion detection for BufferTraits<...>::hasHostSingle: a
|
||||
/// single-space buffer whose storage is host-accessible (e.g. a
|
||||
/// pinned-resource cuda::Buffer).
|
||||
template<typename BufferT, typename = void>
|
||||
struct BufferHasHostSingle { static constexpr bool value = false; };
|
||||
template<typename BufferT>
|
||||
struct BufferHasHostSingle<BufferT, std::void_t<decltype(BufferTraits<BufferT>::hasHostSingle)>>
|
||||
{ static constexpr bool value = BufferTraits<BufferT>::hasHostSingle; };
|
||||
|
||||
/// @brief Detects whether a buffer exposes a retained stream (a stream()
|
||||
/// member), i.e. whether its resource is stream-ordered. Used to pick
|
||||
/// the buffer's stream-taking constructor without naming CUDA types.
|
||||
template<typename BufferT, typename = void>
|
||||
struct BufferHasStream { static constexpr bool value = false; };
|
||||
template<typename BufferT>
|
||||
struct BufferHasStream<BufferT, std::void_t<decltype(std::declval<const BufferT&>().stream())>>
|
||||
{ static constexpr bool value = true; };
|
||||
|
||||
/// @brief Detects whether a buffer's elements are byte-sized. Buffers that
|
||||
/// expose no ElementType (e.g. HostBuffer) address raw bytes by
|
||||
/// definition, so the primary defaults to true.
|
||||
template<typename BufferT, typename = void>
|
||||
struct BufferHasByteElements { static constexpr bool value = true; };
|
||||
template<typename BufferT>
|
||||
struct BufferHasByteElements<BufferT, std::void_t<typename BufferT::ElementType>>
|
||||
{ static constexpr bool value = sizeof(typename BufferT::ElementType) == 1; };
|
||||
|
||||
/// @brief Detects whether a buffer provides destroy(), the cuda::Buffer
|
||||
/// spelling for releasing its storage. Handle reset() dispatches to it
|
||||
/// when present and falls back to the legacy clear() otherwise.
|
||||
template<typename BufferT, typename = void>
|
||||
struct BufferHasDestroy { static constexpr bool value = false; };
|
||||
template<typename BufferT>
|
||||
struct BufferHasDestroy<BufferT, std::void_t<decltype(std::declval<BufferT&>().destroy())>>
|
||||
{ static constexpr bool value = true; };
|
||||
|
||||
// ----------------------------> HostBuffer <--------------------------------------
|
||||
|
||||
/// @brief This is a buffer that contains a shared or private pool
|
||||
|
||||
+253
-21
@@ -145,7 +145,7 @@
|
||||
|
||||
#define NANOVDB_MAJOR_VERSION_NUMBER 32 // reflects changes to the ABI and hence also the file format
|
||||
#define NANOVDB_MINOR_VERSION_NUMBER 9 // reflects changes to the API but not ABI
|
||||
#define NANOVDB_PATCH_VERSION_NUMBER 0 // reflects changes that does not affect the ABI or API
|
||||
#define NANOVDB_PATCH_VERSION_NUMBER 2 // reflects changes that do not affect the ABI or API
|
||||
|
||||
#define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
|
||||
|
||||
@@ -158,6 +158,11 @@
|
||||
// Use this to switch between std::ofstream or FILE implementations
|
||||
//#define NANOVDB_USE_IOSTREAMS
|
||||
|
||||
// Define NANOVDB_USE_OLD_ACCESSOR before including this header to temporarily
|
||||
// restore legacy ReadAccessor behavior where value lookups fall back to root
|
||||
// after a leaf-cache miss.
|
||||
|
||||
// Comment out to use (slower) branched version of LeafData<FpN,...>::getValue
|
||||
#define NANOVDB_FPN_BRANCHLESS
|
||||
|
||||
#if !defined(NANOVDB_ALIGN)
|
||||
@@ -290,7 +295,8 @@ enum class GridClass : uint32_t { Unknown = 0,
|
||||
VoxelVolume = 7, // volume of geometric cubes, e.g. colors cubes in Minecraft
|
||||
IndexGrid = 8, // grid whose values are offsets, e.g. into an external array
|
||||
TensorGrid = 9, // Index grid for indexing learnable tensor features
|
||||
End = 10,// total number of types in this enum (excluding StrLen since it's not a type)
|
||||
VoxelBVH = 10, // grid where each voxel points to list of primitive ids
|
||||
End = 11,// total number of types in this enum (excluding StrLen since it's not a type)
|
||||
StrLen = End + 7};// this entry is used to determine the minimum size of c-string
|
||||
|
||||
|
||||
@@ -310,6 +316,7 @@ __hostdev__ inline char* toStr(char *dst, GridClass gridClass)
|
||||
case GridClass::VoxelVolume: return util::strcpy(dst, "VOX");
|
||||
case GridClass::IndexGrid: return util::strcpy(dst, "INDEX");
|
||||
case GridClass::TensorGrid: return util::strcpy(dst, "TENSOR");
|
||||
case GridClass::VoxelBVH: return util::strcpy(dst, "VOXBVH");
|
||||
default: return util::strcpy(dst, "END");
|
||||
}
|
||||
}
|
||||
@@ -421,7 +428,17 @@ enum class GridBlindDataSemantic : uint32_t { Unknown = 0,
|
||||
LevelSet = 10, // narrow band level set, e.g. SDF
|
||||
FogVolume = 11, // fog volume, e.g. density
|
||||
Staggered = 12, // staggered MAC grid, e.g. velocity
|
||||
End = 13 };
|
||||
PointOpacity = 13, // opacity associated with point
|
||||
PointQuat = 14, // quaternion rotation, wxyz convention
|
||||
PointScale = 15, // vec3 scale
|
||||
PointSH0 = 16, // spherical harmonics, DC component
|
||||
PointSHN = 17, // spherical haromnics, AC components
|
||||
LineId = 18, // integer ID of line
|
||||
TriangleId = 19, // integer ID of triangle
|
||||
GaussianId = 20, // integer ID of Gaussian
|
||||
Range = 21, // begin/end pair of indices
|
||||
VoxelBVH = 22, // voxelbvh 64-bit voxel value
|
||||
End = 23 };
|
||||
|
||||
/// @brief Maps from GridBlindDataSemantic to GridClass
|
||||
/// @note Useful when converting an IndexGrid with blind data of type T into a Grid<T>
|
||||
@@ -450,6 +467,8 @@ __hostdev__ inline GridClass toGridClass(GridBlindDataSemantic semantics,
|
||||
return GridClass::FogVolume;
|
||||
case GridBlindDataSemantic::Staggered:
|
||||
return GridClass::Staggered;
|
||||
case GridBlindDataSemantic::VoxelBVH:
|
||||
return GridClass::VoxelBVH;
|
||||
default:
|
||||
return defaultClass;
|
||||
}
|
||||
@@ -472,6 +491,8 @@ __hostdev__ inline GridBlindDataSemantic toSemantic(GridClass gridClass,
|
||||
return GridBlindDataSemantic::FogVolume;
|
||||
case GridClass::Staggered:
|
||||
return GridBlindDataSemantic::Staggered;
|
||||
case GridClass::VoxelBVH:
|
||||
return GridBlindDataSemantic::VoxelBVH;
|
||||
default:
|
||||
return defaultSemantic;
|
||||
}
|
||||
@@ -1242,19 +1263,21 @@ public:
|
||||
/// @brief Set the specified bit off.
|
||||
__hostdev__ void setOff(uint32_t n) { mWords[n >> 6] &= ~(uint64_t(1) << (n & 63)); }
|
||||
|
||||
#if defined(__CUDACC__) // the following functions only run on the GPU!
|
||||
__device__ inline void setOnAtomic(uint32_t n)
|
||||
__hostdev__ inline void setOnAtomic(uint32_t n)
|
||||
{
|
||||
atomicOr(reinterpret_cast<unsigned long long int*>(this) + (n >> 6), 1ull << (n & 63));
|
||||
util::atomicOr(mWords + (n >> 6), uint64_t(1) << (n & 63));
|
||||
}
|
||||
__device__ inline void setOffAtomic(uint32_t n)
|
||||
__hostdev__ inline void setOffAtomic(uint32_t n)
|
||||
{
|
||||
atomicAnd(reinterpret_cast<unsigned long long int*>(this) + (n >> 6), ~(1ull << (n & 63)));
|
||||
util::atomicAnd(mWords + (n >> 6), ~(uint64_t(1) << (n & 63)));
|
||||
}
|
||||
__device__ inline void setAtomic(uint32_t n, bool on)
|
||||
__hostdev__ inline void setAtomic(uint32_t n, bool on)
|
||||
{
|
||||
on ? this->setOnAtomic(n) : this->setOffAtomic(n);
|
||||
}
|
||||
#if defined(__CUDACC__)
|
||||
// TODO: setWordAtomic is currently disabled. Before re-enabling, audit for
|
||||
// possible migration to __hostdev__ using util::atomicOr/util::atomicAnd.
|
||||
/*
|
||||
template<typename WordT>
|
||||
__device__ inline void setWordAtomic(WordT w, uint32_t n)
|
||||
@@ -1501,7 +1524,7 @@ struct Map
|
||||
/// e.g. inverse scale and inverse rotation WITHOUT translation.
|
||||
/// @note Typically this operation is used for scale and rotation from world -> index mapping
|
||||
/// @tparam Vec3T Template type of the 3D vector to be mapped
|
||||
/// @param ijk 3D vector to be mapped - typically floating point index coordinates
|
||||
/// @param xyz 3D vector to be mapped - typically floating point index coordinates
|
||||
/// @return linear inverse 3x3 mapping of the input vector i.e. xyz x mat^-1
|
||||
template<typename Vec3T>
|
||||
__hostdev__ Vec3T applyInverseJacobian(const Vec3T& xyz) const { return math::matMult(mInvMatD, xyz); }
|
||||
@@ -1510,7 +1533,7 @@ struct Map
|
||||
/// e.g. inverse scale and inverse rotation WITHOUT translation.
|
||||
/// @note Typically this operation is used for scale and rotation from world -> index mapping
|
||||
/// @tparam Vec3T Template type of the 3D vector to be mapped
|
||||
/// @param ijk 3D vector to be mapped - typically floating point index coordinates
|
||||
/// @param xyz 3D vector to be mapped - typically floating point index coordinates
|
||||
/// @return linear inverse 3x3 mapping of the input vector i.e. xyz x mat^-1
|
||||
template<typename Vec3T>
|
||||
__hostdev__ Vec3T applyInverseJacobianF(const Vec3T& xyz) const { return math::matMult(mInvMatF, xyz); }
|
||||
@@ -1519,7 +1542,7 @@ struct Map
|
||||
/// e.g. inverse scale and inverse rotation WITHOUT translation.
|
||||
/// @note Typically this operation is used for scale and rotation from world -> index mapping
|
||||
/// @tparam Vec3T Template type of the 3D vector to be mapped
|
||||
/// @param ijk 3D vector to be mapped - typically floating point index coordinates
|
||||
/// @param xyz 3D vector to be mapped - typically floating point index coordinates
|
||||
/// @return linear inverse 3x3 mapping of the input vector i.e. xyz x mat^-1
|
||||
template<typename Vec3T>
|
||||
__hostdev__ Vec3T applyIJT(const Vec3T& xyz) const { return math::matMultT(mInvMatD, xyz); }
|
||||
@@ -1785,6 +1808,42 @@ struct ProbeValue;
|
||||
template<typename BuildT>
|
||||
struct GetNodeInfo;
|
||||
|
||||
/// @brief Result of a fused ReadAccessor::getDimAndActive traversal: the
|
||||
/// getDim(ijk, ray) step size and the policy-dependent active state of @a ijk.
|
||||
///
|
||||
/// The low 31 bits store @c dim and bit 31 stores @c active, saving one live
|
||||
/// return register versus {uint32_t, bool}. The default FloatTree's upper-node
|
||||
/// maximum is 4096. The constructor supports `return {dim, active}`; dim() and
|
||||
/// active() decode the packed value.
|
||||
struct DimAndActive
|
||||
{
|
||||
uint32_t packed{0u};
|
||||
|
||||
__hostdev__ DimAndActive() : packed(0u) {}
|
||||
__hostdev__ DimAndActive(uint32_t d, bool a)
|
||||
: packed((d & 0x7FFFFFFFu) | (uint32_t(a) << 31))
|
||||
{
|
||||
NANOVDB_ASSERT(d < (uint32_t(1) << 31)); // dim must fit in 31 bits (bit 31 = active)
|
||||
}
|
||||
|
||||
__hostdev__ uint32_t dim() const { return packed & 0x7FFFFFFFu; }
|
||||
__hostdev__ bool active() const { return (packed >> 31) != 0u; }
|
||||
};
|
||||
|
||||
/// @brief Policy that exactly matches separate getDim and isActive calls. A
|
||||
/// skip flag still controls @c dim but cannot stop the active-state descent:
|
||||
/// GridStats flags value range versus render delta, not activeness. This costs
|
||||
/// a full descent through skipped InternalNodes; use it when @c active is read
|
||||
/// unconditionally, such as by HDDA iterators observing upper-level activeness.
|
||||
struct ActiveExact {};
|
||||
|
||||
/// @brief Policy that preserves getDim's skip-flag fast path. If an
|
||||
/// InternalNode short-circuits, @c active is unspecified and must be gated on
|
||||
/// @c dim. Otherwise the leaf-level active read is fused into the descent. Use
|
||||
/// for logic such as `if (hdda.dim() > 1 || !active) continue;`, where the
|
||||
/// short-circuit ensures @c active is read only after a leaf-level descent.
|
||||
struct ActiveOnLeafOnly {};
|
||||
|
||||
// ----------------------------> CheckMode <----------------------------------
|
||||
|
||||
/// @brief List of different modes for computing for a checksum
|
||||
@@ -2260,6 +2319,7 @@ public:
|
||||
__hostdev__ bool isPointIndex() const { return DataType::mGridClass == GridClass::PointIndex; }
|
||||
__hostdev__ bool isGridIndex() const { return DataType::mGridClass == GridClass::IndexGrid; }
|
||||
__hostdev__ bool isPointData() const { return DataType::mGridClass == GridClass::PointData; }
|
||||
__hostdev__ bool isVoxelBVH() const { return DataType::mGridClass == GridClass::VoxelBVH; }
|
||||
__hostdev__ bool isMask() const { return DataType::mGridClass == GridClass::Topology; }
|
||||
__hostdev__ bool isUnknown() const { return DataType::mGridClass == GridClass::Unknown; }
|
||||
__hostdev__ bool hasMinMax() const { return DataType::mFlags.isMaskOn(GridFlags::HasMinMax); }
|
||||
@@ -3112,6 +3172,23 @@ private:
|
||||
return ChildNodeType::dim(); // background
|
||||
}
|
||||
|
||||
/// @brief Returns dim and active state in one traversal, sharing cache and
|
||||
/// terminal tile/mask reads. Root has no skip flag, so @c Policy affects
|
||||
/// only the child descent; see ActiveExact and ActiveOnLeafOnly.
|
||||
template<typename Policy = ActiveExact, typename RayT, typename AccT>
|
||||
__hostdev__ DimAndActive getDimAndActiveAndCache(const CoordType& ijk, const RayT& ray, const AccT& acc) const
|
||||
{
|
||||
if (const Tile* tile = this->probeTile(ijk)) {
|
||||
if (tile->isChild()) {
|
||||
const auto* child = this->getChild(tile);
|
||||
acc.insert(ijk, child);
|
||||
return child->template getDimAndActiveAndCache<Policy>(ijk, ray, acc);
|
||||
}
|
||||
return {uint32_t(1) << ChildT::TOTAL, tile->state > 0u}; // tile value
|
||||
}
|
||||
return {ChildNodeType::dim(), false}; // background is never active
|
||||
}
|
||||
|
||||
template<typename OpT, typename AccT, typename... ArgsT>
|
||||
__hostdev__ typename OpT::Type getAndCache(const CoordType& ijk, const AccT& acc, ArgsT&&... args) const
|
||||
{
|
||||
@@ -3608,6 +3685,39 @@ private:
|
||||
return ChildNodeType::dim(); // tile value
|
||||
}
|
||||
|
||||
/// @brief Returns dim and active state in one traversal. On a skip flag,
|
||||
/// ActiveExact resolves the true active state but returns this node's dim;
|
||||
/// ActiveOnLeafOnly returns {dim, false} immediately. Without a skip flag
|
||||
/// both policies are identical. See GridStats::setFlag.
|
||||
template<typename Policy = ActiveExact, typename RayT, typename AccT>
|
||||
__hostdev__ DimAndActive getDimAndActiveAndCache(const CoordType& ijk, const RayT& ray, const AccT& acc) const
|
||||
{
|
||||
//if (!ray.intersects( this->bbox() )) return {1<<TOTAL, ...};
|
||||
// Separate paths keep `skip` out of ActiveOnLeafOnly's recursion,
|
||||
// saving ~2 registers/thread and avoiding an occupancy cliff.
|
||||
if constexpr (util::is_same<Policy, ActiveOnLeafOnly>::value) {
|
||||
if (DataType::mFlags & uint32_t(1u))
|
||||
return {this->dim(), false}; // active unspecified per policy
|
||||
const uint32_t n = CoordToOffset(ijk);
|
||||
if (DataType::mChildMask.isOn(n)) {
|
||||
const ChildT* child = this->getChild(n);
|
||||
acc.insert(ijk, child);
|
||||
return child->template getDimAndActiveAndCache<Policy>(ijk, ray, acc);
|
||||
}
|
||||
return {ChildNodeType::dim(), DataType::mValueMask.isOn(n)};
|
||||
} else {
|
||||
const bool skip = (DataType::mFlags & uint32_t(1u));
|
||||
const uint32_t n = CoordToOffset(ijk);
|
||||
if (DataType::mChildMask.isOn(n)) {
|
||||
const ChildT* child = this->getChild(n);
|
||||
acc.insert(ijk, child);
|
||||
const auto r = child->template getDimAndActiveAndCache<Policy>(ijk, ray, acc);
|
||||
return {skip ? this->dim() : r.dim(), r.active()};
|
||||
}
|
||||
return {skip ? this->dim() : ChildNodeType::dim(), DataType::mValueMask.isOn(n)};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename OpT, typename AccT, typename... ArgsT>
|
||||
__hostdev__ typename OpT::Type getAndCache(const CoordType& ijk, const AccT& acc, ArgsT&&... args) const
|
||||
{
|
||||
@@ -4548,6 +4658,21 @@ private:
|
||||
return ChildNodeType::dim();
|
||||
}
|
||||
|
||||
/// @brief Returns dim and active state in one traversal. @c Policy is
|
||||
/// accepted for recursion but does not change leaf behavior: the skip flag
|
||||
/// controls dim while active always comes from mValueMask. Branching here
|
||||
/// raised parallelForKernel from 64 to 66 registers/thread and reduced
|
||||
/// occupancy from four to three blocks/SM, negating the descent savings;
|
||||
/// leaf skip flags are rare because upper-level flags usually short-circuit.
|
||||
template<typename Policy = ActiveExact, typename RayT, typename AccT>
|
||||
__hostdev__ DimAndActive getDimAndActiveAndCache(const CoordT& ijk, const RayT& /*ray*/, const AccT&) const
|
||||
{
|
||||
//if (!ray.intersects( this->bbox() )) return {1 << LOG2DIM, ...};
|
||||
const uint32_t n = CoordToOffset(ijk);
|
||||
const uint32_t dimOut = (DataType::mFlags & uint8_t(1u)) ? this->dim() : ChildNodeType::dim();
|
||||
return {dimOut, DataType::mValueMask.isOn(n)};
|
||||
}
|
||||
|
||||
template<typename OpT, typename AccT, typename... ArgsT>
|
||||
__hostdev__ auto
|
||||
//__hostdev__ decltype(OpT::get(util::declval<const LeafNode&>(), util::declval<uint32_t>(), util::declval<ArgsT>()...))
|
||||
@@ -4717,7 +4842,7 @@ using OnIndexGrid = Grid<OnIndexTree>;
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
/// @brief Use this function, which depends a pointer to GridData, to call
|
||||
/// @brief Use this function, which depends on a pointer to GridData, to call
|
||||
/// other functions that depend on a NanoGrid of a known ValueType.
|
||||
/// @details This function allows for generic programming by converting GridData
|
||||
/// to a NanoGrid of the type encoded in GridData::mGridType.
|
||||
@@ -4829,7 +4954,7 @@ public:
|
||||
}
|
||||
|
||||
/// @brief Reset this access to its initial state, i.e. with an empty cache
|
||||
/// @node Noop since this template specialization has no cache
|
||||
/// @note Noop since this template specialization has no cache
|
||||
__hostdev__ void clear() {}
|
||||
|
||||
__hostdev__ const RootT& root() const { return *mRoot; }
|
||||
@@ -4854,6 +4979,15 @@ public:
|
||||
{
|
||||
return mRoot->getDimAndCache(ijk, ray, *this);
|
||||
}
|
||||
/// @brief Fused getDim + isActive: one tree walk returns both the HDDA
|
||||
/// step size and the active state of @a ijk, shaving the second descent
|
||||
/// / cache-check / leaf ValueMask lookup. See @c ActiveExact /
|
||||
/// @c ActiveOnLeafOnly for the @c Policy contract.
|
||||
template<typename Policy = ActiveExact, typename RayT>
|
||||
__hostdev__ DimAndActive getDimAndActive(const CoordType& ijk, const RayT& ray) const
|
||||
{
|
||||
return mRoot->template getDimAndActiveAndCache<Policy>(ijk, ray, *this);
|
||||
}
|
||||
template<typename OpT, typename... ArgsT>
|
||||
__hostdev__ auto get(const CoordType& ijk, ArgsT&&... args) const
|
||||
{
|
||||
@@ -4969,6 +5103,17 @@ public:
|
||||
return mRoot->getDimAndCache(ijk, ray, *this);
|
||||
}
|
||||
|
||||
/// @brief Fused getDim + isActive: one tree walk returns both the HDDA
|
||||
/// step size and the active state of @a ijk, shaving the second descent
|
||||
/// / cache-check / leaf ValueMask lookup. See @c ActiveExact /
|
||||
/// @c ActiveOnLeafOnly for the @c Policy contract.
|
||||
template<typename Policy = ActiveExact, typename RayT>
|
||||
__hostdev__ DimAndActive getDimAndActive(const CoordType& ijk, const RayT& ray) const
|
||||
{
|
||||
if (this->isCached(ijk)) return mNode->template getDimAndActiveAndCache<Policy>(ijk, ray, *this);
|
||||
return mRoot->template getDimAndActiveAndCache<Policy>(ijk, ray, *this);
|
||||
}
|
||||
|
||||
template<typename OpT, typename... ArgsT>
|
||||
__hostdev__ typename OpT::Type get(const CoordType& ijk, ArgsT&&... args) const
|
||||
{
|
||||
@@ -5151,6 +5296,26 @@ public:
|
||||
return mRoot->getDimAndCache(ijk, ray, *this);
|
||||
}
|
||||
|
||||
/// @brief Fused getDim + isActive: one tree walk returns both the HDDA
|
||||
/// step size and the active state of @a ijk, shaving the second descent
|
||||
/// / cache-check / leaf ValueMask lookup. See @c ActiveExact /
|
||||
/// @c ActiveOnLeafOnly for the @c Policy contract.
|
||||
template<typename Policy = ActiveExact, typename RayT>
|
||||
__hostdev__ DimAndActive getDimAndActive(const CoordType& ijk, const RayT& ray) const
|
||||
{
|
||||
#ifdef NANOVDB_USE_SINGLE_ACCESSOR_KEY
|
||||
const CoordValueType dirty = this->computeDirty(ijk);
|
||||
#else
|
||||
auto&& dirty = ijk;
|
||||
#endif
|
||||
if (this->isCached1(dirty)) {
|
||||
return mNode1->template getDimAndActiveAndCache<Policy>(ijk, ray, *this);
|
||||
} else if (this->isCached2(dirty)) {
|
||||
return mNode2->template getDimAndActiveAndCache<Policy>(ijk, ray, *this);
|
||||
}
|
||||
return mRoot->template getDimAndActiveAndCache<Policy>(ijk, ray, *this);
|
||||
}
|
||||
|
||||
template<typename OpT, typename... ArgsT>
|
||||
__hostdev__ typename OpT::Type get(const CoordType& ijk, ArgsT&&... args) const
|
||||
{
|
||||
@@ -5161,7 +5326,11 @@ public:
|
||||
#endif
|
||||
if constexpr(OpT::LEVEL <= LEVEL0) {
|
||||
if (this->isCached1(dirty)) return mNode1->template getAndCache<OpT>(ijk, *this, args...);
|
||||
} else if constexpr(OpT::LEVEL <= LEVEL1) {
|
||||
}
|
||||
#ifdef NANOVDB_USE_OLD_ACCESSOR
|
||||
else
|
||||
#endif
|
||||
if constexpr(OpT::LEVEL <= LEVEL1) {
|
||||
if (this->isCached2(dirty)) return mNode2->template getAndCache<OpT>(ijk, *this, args...);
|
||||
}
|
||||
return mRoot->template getAndCache<OpT>(ijk, *this, args...);
|
||||
@@ -5177,7 +5346,11 @@ public:
|
||||
#endif
|
||||
if constexpr(OpT::LEVEL <= LEVEL0) {
|
||||
if (this->isCached1(dirty)) return const_cast<Node1T*>(mNode1)->template setAndCache<OpT>(ijk, *this, args...);
|
||||
} else if constexpr(OpT::LEVEL <= LEVEL1) {
|
||||
}
|
||||
#ifdef NANOVDB_USE_OLD_ACCESSOR
|
||||
else
|
||||
#endif
|
||||
if constexpr(OpT::LEVEL <= LEVEL1) {
|
||||
if (this->isCached2(dirty)) return const_cast<Node2T*>(mNode2)->template setAndCache<OpT>(ijk, *this, args...);
|
||||
}
|
||||
return const_cast<RootT*>(mRoot)->template setAndCache<OpT>(ijk, *this, args...);
|
||||
@@ -5335,7 +5508,7 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
__hostdev__ ValueType getValue(const CoordType& ijk) const {return this->template get<GetValue<BuildT>>(ijk);}
|
||||
__hostdev__ ValueType getValue(const CoordType& ijk) const {return this->template get<GetValue<BuildT>>(ijk);}
|
||||
__hostdev__ ValueType getValue(int i, int j, int k) const { return this->template get<GetValue<BuildT>>(CoordType(i, j, k)); }
|
||||
__hostdev__ ValueType operator()(const CoordType& ijk) const { return this->template get<GetValue<BuildT>>(ijk); }
|
||||
__hostdev__ ValueType operator()(int i, int j, int k) const { return this->template get<GetValue<BuildT>>(CoordType(i, j, k)); }
|
||||
@@ -5354,9 +5527,17 @@ public:
|
||||
#endif
|
||||
if constexpr(OpT::LEVEL <=0) {
|
||||
if (this->isCached<LeafT>(dirty)) return ((const LeafT*)mNode[0])->template getAndCache<OpT>(ijk, *this, args...);
|
||||
} else if constexpr(OpT::LEVEL <= 1) {
|
||||
}
|
||||
#ifdef NANOVDB_USE_OLD_ACCESSOR
|
||||
else
|
||||
#endif
|
||||
if constexpr(OpT::LEVEL <= 1) {
|
||||
if (this->isCached<NodeT1>(dirty)) return ((const NodeT1*)mNode[1])->template getAndCache<OpT>(ijk, *this, args...);
|
||||
} else if constexpr(OpT::LEVEL <= 2) {
|
||||
}
|
||||
#ifdef NANOVDB_USE_OLD_ACCESSOR
|
||||
else
|
||||
#endif
|
||||
if constexpr(OpT::LEVEL <= 2) {
|
||||
if (this->isCached<NodeT2>(dirty)) return ((const NodeT2*)mNode[2])->template getAndCache<OpT>(ijk, *this, args...);
|
||||
}
|
||||
return mRoot->template getAndCache<OpT>(ijk, *this, args...);
|
||||
@@ -5372,9 +5553,17 @@ public:
|
||||
#endif
|
||||
if constexpr(OpT::LEVEL <= 0) {
|
||||
if (this->isCached<LeafT>(dirty)) return ((LeafT*)mNode[0])->template setAndCache<OpT>(ijk, *this, args...);
|
||||
} else if constexpr(OpT::LEVEL <= 1) {
|
||||
}
|
||||
#ifdef NANOVDB_USE_OLD_ACCESSOR
|
||||
else
|
||||
#endif
|
||||
if constexpr(OpT::LEVEL <= 1) {
|
||||
if (this->isCached<NodeT1>(dirty)) return ((NodeT1*)mNode[1])->template setAndCache<OpT>(ijk, *this, args...);
|
||||
} else if constexpr(OpT::LEVEL <= 2) {
|
||||
}
|
||||
#ifdef NANOVDB_USE_OLD_ACCESSOR
|
||||
else
|
||||
#endif
|
||||
if constexpr(OpT::LEVEL <= 2) {
|
||||
if (this->isCached<NodeT2>(dirty)) return ((NodeT2*)mNode[2])->template setAndCache<OpT>(ijk, *this, args...);
|
||||
}
|
||||
return ((RootT*)mRoot)->template setAndCache<OpT>(ijk, *this, args...);
|
||||
@@ -5398,6 +5587,28 @@ public:
|
||||
return mRoot->getDimAndCache(ijk, ray, *this);
|
||||
}
|
||||
|
||||
/// @brief Fused getDim + isActive: one tree walk returns both the HDDA
|
||||
/// step size and the active state of @a ijk, shaving the second descent
|
||||
/// / cache-check / leaf ValueMask lookup. See @c ActiveExact /
|
||||
/// @c ActiveOnLeafOnly for the @c Policy contract.
|
||||
template<typename Policy = ActiveExact, typename RayT>
|
||||
__hostdev__ DimAndActive getDimAndActive(const CoordType& ijk, const RayT& ray) const
|
||||
{
|
||||
#ifdef NANOVDB_USE_SINGLE_ACCESSOR_KEY
|
||||
const CoordValueType dirty = this->computeDirty(ijk);
|
||||
#else
|
||||
auto&& dirty = ijk;
|
||||
#endif
|
||||
if (this->isCached<LeafT>(dirty)) {
|
||||
return ((LeafT*)mNode[0])->template getDimAndActiveAndCache<Policy>(ijk, ray, *this);
|
||||
} else if (this->isCached<NodeT1>(dirty)) {
|
||||
return ((NodeT1*)mNode[1])->template getDimAndActiveAndCache<Policy>(ijk, ray, *this);
|
||||
} else if (this->isCached<NodeT2>(dirty)) {
|
||||
return ((NodeT2*)mNode[2])->template getDimAndActiveAndCache<Policy>(ijk, ray, *this);
|
||||
}
|
||||
return mRoot->template getDimAndActiveAndCache<Policy>(ijk, ray, *this);
|
||||
}
|
||||
|
||||
private:
|
||||
/// @brief Allow nodes to insert themselves into the cache.
|
||||
template<typename>
|
||||
@@ -5511,6 +5722,7 @@ public:
|
||||
__hostdev__ bool isPointIndex() const { return mGridData.mGridClass == GridClass::PointIndex; }
|
||||
__hostdev__ bool isGridIndex() const { return mGridData.mGridClass == GridClass::IndexGrid; }
|
||||
__hostdev__ bool isPointData() const { return mGridData.mGridClass == GridClass::PointData; }
|
||||
__hostdev__ bool isVoxelBVH() const { return mGridData.mGridClass == GridClass::VoxelBVH; }
|
||||
__hostdev__ bool isMask() const { return mGridData.mGridClass == GridClass::Topology; }
|
||||
__hostdev__ bool isUnknown() const { return mGridData.mGridClass == GridClass::Unknown; }
|
||||
__hostdev__ bool hasMinMax() const { return mGridData.mFlags.isMaskOn(GridFlags::HasMinMax); }
|
||||
@@ -5760,6 +5972,26 @@ public:
|
||||
|
||||
}; // ChannelAccessor
|
||||
|
||||
/// @brief Generic Accessor type that maps to either a ReadAccessor or ChannelAccessor
|
||||
/// @tparam BuildT Build type, e.g. float or ValueOnIndex
|
||||
/// @tparam ValueT Value type, e.g. float or Vec3f
|
||||
template <typename BuildT, typename ValueT>
|
||||
using AccType = typename util::conditional<BuildTraits<BuildT>::is_index,
|
||||
ChannelAccessor<ValueT, BuildT>, DefaultReadAccessor<BuildT>>::type;
|
||||
|
||||
/// @brief Generic template functions that return an Accessor to either an index grid or a regular grid
|
||||
template <typename GridT, typename ValueT>
|
||||
inline __hostdev__ auto getAccessor(const GridT &grid, ValueT *sideCar = nullptr)
|
||||
{
|
||||
using BuildT = typename GridT::BuildType;
|
||||
if constexpr(BuildTraits<BuildT>::is_index) {
|
||||
return sideCar ? ChannelAccessor<ValueT, BuildT>(grid, sideCar) : ChannelAccessor<ValueT, BuildT>(grid);
|
||||
} else {
|
||||
static_assert(util::is_same<ValueT, typename GridT::ValueType>::value, "wrong ValueT for regular GridT");
|
||||
return DefaultReadAccessor<BuildT>(grid);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// This MiniGridHandle class is only included as a stand-alone example. Note that aligned_alloc is a C++17 feature!
|
||||
// Normally we recommend using GridHandle defined in util/GridHandle.h but this minimal implementation could be an
|
||||
|
||||
@@ -53,10 +53,14 @@ struct NodeManagerData
|
||||
template<typename BufferT>
|
||||
class NodeManagerHandle
|
||||
{
|
||||
static_assert(!(BufferHasDeviceSingle<BufferT>::value || BufferHasHostSingle<BufferT>::value)
|
||||
|| BufferHasByteElements<BufferT>::value,
|
||||
"NodeManagerHandle requires byte-addressed single-space storage, e.g. cuda::Buffer<std::byte, R>");
|
||||
|
||||
GridType mGridType{GridType::Unknown};
|
||||
BufferT mBuffer;
|
||||
|
||||
template<typename BuildT>
|
||||
template<typename BuildT, typename U = BufferT, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type = 0>
|
||||
const NodeManager<BuildT>* getMgr() const {
|
||||
return mGridType == toGridType<BuildT>() ? (const NodeManager<BuildT>*)mBuffer.data() : nullptr;
|
||||
}
|
||||
@@ -72,7 +76,9 @@ class NodeManagerHandle
|
||||
|
||||
public:
|
||||
/// @brief Move constructor from a buffer
|
||||
NodeManagerHandle(GridType gridType, BufferT&& buffer) : mGridType(gridType) { mBuffer = std::move(buffer); }
|
||||
NodeManagerHandle(GridType gridType, BufferT&& buffer)
|
||||
: mGridType(gridType)
|
||||
, mBuffer(std::move(buffer)) {}// member-initialized, so a non-default-constructible buffer works
|
||||
/// @brief Empty ctor
|
||||
NodeManagerHandle() = default;
|
||||
/// @brief Disallow copy-construction
|
||||
@@ -87,15 +93,19 @@ public:
|
||||
return *this;
|
||||
}
|
||||
/// @brief Move copy-constructor
|
||||
NodeManagerHandle(NodeManagerHandle&& other) noexcept {
|
||||
mGridType = other.mGridType;
|
||||
mBuffer = std::move(other.mBuffer);
|
||||
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 Release the buffer's storage
|
||||
void reset() {
|
||||
if constexpr (BufferHasDestroy<BufferT>::value) mBuffer.destroy();
|
||||
else mBuffer.clear();
|
||||
}
|
||||
|
||||
/// @brief Return a reference to the buffer
|
||||
BufferT& buffer() { return mBuffer; }
|
||||
@@ -103,15 +113,15 @@ public:
|
||||
/// @brief Return a const reference to the buffer
|
||||
const BufferT& buffer() const { return mBuffer; }
|
||||
|
||||
/// @brief Returns a non-const pointer to the data.
|
||||
///
|
||||
//@{
|
||||
/// @brief Returns a pointer to the host data; not available for a
|
||||
/// single-space device buffer, which has no host-readable bytes.
|
||||
/// @warning Note that the return pointer can be NULL if the NodeManagerHandle was not initialized
|
||||
template<typename U = BufferT, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type = 0>
|
||||
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
|
||||
template<typename U = BufferT, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type = 0>
|
||||
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(); }
|
||||
@@ -142,6 +152,19 @@ public:
|
||||
typename util::enable_if<BufferTraits<U>::hasDeviceDual, NodeManager<BuildT>*>::type
|
||||
deviceMgr() { return no_const(this->template getDeviceMgr<BuildT>()); }
|
||||
|
||||
//@{
|
||||
/// @brief Return a pointer to the NodeManager of a single-space device buffer
|
||||
/// @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<BufferHasDeviceSingle<U>::value, const NodeManager<BuildT>*>::type
|
||||
deviceMgr() const {
|
||||
return mGridType == toGridType<BuildT>() ? (const NodeManager<BuildT>*)mBuffer.data() : nullptr;
|
||||
}
|
||||
template<typename BuildT, typename U = BufferT>
|
||||
typename util::enable_if<BufferHasDeviceSingle<U>::value, NodeManager<BuildT>*>::type
|
||||
deviceMgr() { return no_const(static_cast<const NodeManagerHandle*>(this)->template deviceMgr<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
|
||||
|
||||
+31
-17
@@ -1088,7 +1088,7 @@ PNANOVDB_FORCE_INLINE void pnanovdb_write_vec3(pnanovdb_buf_t buf, pnanovdb_addr
|
||||
|
||||
#define PNANOVDB_MAJOR_VERSION_NUMBER 32// reflects changes to the ABI
|
||||
#define PNANOVDB_MINOR_VERSION_NUMBER 9// reflects changes to the API but not ABI
|
||||
#define PNANOVDB_PATCH_VERSION_NUMBER 0// reflects bug-fixes with no ABI or API changes
|
||||
#define PNANOVDB_PATCH_VERSION_NUMBER 2// reflects bug-fixes with no ABI or API changes
|
||||
|
||||
#define PNANOVDB_GRID_TYPE_UNKNOWN 0
|
||||
#define PNANOVDB_GRID_TYPE_FLOAT 1
|
||||
@@ -1130,8 +1130,9 @@ PNANOVDB_FORCE_INLINE void pnanovdb_write_vec3(pnanovdb_buf_t buf, pnanovdb_addr
|
||||
#define PNANOVDB_GRID_CLASS_TOPOLOGY 6 // grid with active states only (no values)
|
||||
#define PNANOVDB_GRID_CLASS_VOXEL_VOLUME 7 // volume of geometric cubes, e.g. minecraft
|
||||
#define PNANOVDB_GRID_CLASS_INDEX_GRID 8 // grid whose values are offsets, e.g. into an external array
|
||||
#define PNANOVDB_GRID_CLASS_TENSOR_GRID 9 // grid which can have extra metadata and features
|
||||
#define PNANOVDB_GRID_CLASS_END 10
|
||||
#define PNANOVDB_GRID_CLASS_TENSOR_GRID 9 // grid which can have extra metadata and features
|
||||
#define PNANOVDB_GRID_CLASS_VOXEL_BVH 10 // grid where each voxel points to list of primitive ids
|
||||
#define PNANOVDB_GRID_CLASS_END 11
|
||||
|
||||
#define PNANOVDB_GRID_FLAGS_HAS_LONG_GRID_NAME (1 << 0)
|
||||
#define PNANOVDB_GRID_FLAGS_HAS_BBOX (1 << 1)
|
||||
@@ -1412,23 +1413,36 @@ PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_version_get_patch(pnanovdb_uint
|
||||
}
|
||||
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_UNKNOWN 0
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_INDEX_ARRAY 1
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_ATTRIBUTE_ARRAY 2
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_GRID_NAME 3
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_CHANNEL_ARRAY 4
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_INDEX_ARRAY 1 // indices typically used for mapping into other arrays
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_ATTRIBUTE_ARRAY 2 // attributes typically associated with points
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_GRID_NAME 3 // grid names of length longer than 256 characters
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_CHANNEL_ARRAY 4 // channel of values typically used by index grids
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_CLASS_END 5
|
||||
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_UNKNOWN 0
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_POSITION 1
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_COLOR 2
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_NORMAL 3
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_RADIUS 4
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_VELOCITY 5
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_ID 6
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_WORLD_COORDS 7
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_GRID_COORDS 8
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_VOXEL_COORDS 9
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_END 10
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_POSITION 1 // 3D coordinates in an unknown space
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_COLOR 2 // color associated with point
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_NORMAL 3 // normal associated with point
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_RADIUS 4 // radius of point
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_VELOCITY 5 // velocity associated with point
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_ID 6 // integer ID of point
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_WORLD_COORDS 7 // 3D coordinates in world space, e.g. (0.056, 0.8, 1,8)
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_GRID_COORDS 8 // 3D coordinates in grid space, e.g. (1.2, 4.0, 5.7), aka index-space
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_VOXEL_COORDS 9 // 3D coordinates in voxel space, e.g. (0.2, 0.0, 0.7)
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_LEVEL_SET 10 // narrow band level set, e.g. SDF
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_FOG_VOLUME 11 // fog volume, e.g. density
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_STAGGERED 12 // staggered MAC grid, e.g. velocity
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_OPACITY 13 // opacity associated with point
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_QUAT 14 // quaternion rotation, wxyz convention
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_SCALE 15 // vec3 scale
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_SH0 16 // spherical harmonics, DC component
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_POINT_SHN 17 // spherical haromnics, AC components
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_LINE_ID 18 // integer ID of line
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_TRIANGLE_ID 19 // integer ID of triangle
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_GAUSSIAN_ID 20 // integer ID of Gaussian
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_RANGE 21 // begin/end pair of indices
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_VOXEL_BVH 22 // voxelbvh 64-bit voxel value
|
||||
#define PNANOVDB_GRIDBLINDMETADATA_SEMANTIC_END 23
|
||||
|
||||
struct pnanovdb_gridblindmetadata_t
|
||||
{
|
||||
|
||||
@@ -7,6 +7,8 @@ A lightweight GPU friendly version of VDB initially targeting rendering applicat
|
||||
* [Frequently asked questions](../../doc/nanovdb/FAQ.md)
|
||||
* [Source tree](../../doc/nanovdb/SourceTree.md)
|
||||
* [Examples](../../doc/nanovdb/HelloWorld.md)
|
||||
* [TEACHME — learn NanoVDB by prompting an LLM coding agent](../../doc/nanovdb/TEACHME/README.md)
|
||||
* [Python examples](python/examples/)
|
||||
|
||||
### Copyright Contributors to the OpenVDB Project
|
||||
### SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@@ -0,0 +1,465 @@
|
||||
// Copyright Contributors to the OpenVDB Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
/// @file nanovdb/cuda/Buffer.h
|
||||
///
|
||||
/// @brief Typed containers for CUDA memory: the owning, resource-aware,
|
||||
/// stream-ordered cuda::Buffer and the non-owning cuda::BufferView.
|
||||
|
||||
#ifndef NANOVDB_CUDA_BUFFER_H_HAS_BEEN_INCLUDED
|
||||
#define NANOVDB_CUDA_BUFFER_H_HAS_BEEN_INCLUDED
|
||||
|
||||
#include <cuda_runtime_api.h>
|
||||
#include <nanovdb/cuda/DeviceResource.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace cuda {
|
||||
|
||||
/// @brief Tag type selecting the Buffer constructors that skip element
|
||||
/// initialization, leaving the contents indeterminate.
|
||||
struct NoInit {};
|
||||
inline constexpr NoInit noInit{};
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// @brief Conditional stream storage for Buffer. The async specialization
|
||||
/// retains the stream of the most recent allocation; the synchronous
|
||||
/// specialization is an empty base, so a Buffer over a synchronous
|
||||
/// resource carries no stream state and exposes no stream API.
|
||||
template<bool IsAsync>
|
||||
struct StreamHolder {};
|
||||
|
||||
template<>
|
||||
struct StreamHolder<true> { cudaStream_t mStream = 0; };
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// @brief Owning, typed container of @c T elements allocated from a memory
|
||||
/// resource @c R held by value.
|
||||
/// @tparam T element type; sizes are expressed in elements, not bytes
|
||||
/// @tparam R memory resource, either stream-ordered (AsyncResource concept,
|
||||
/// see is_async_resource) or synchronous (Resource concept, see
|
||||
/// is_resource). When @c R provides both interfaces the stream-ordered
|
||||
/// one is used.
|
||||
/// @details With a stream-ordered resource the Buffer retains the stream of
|
||||
/// the most recent allocation (or the one supplied via set_stream)
|
||||
/// and orders its deallocation on that stream. Buffer is move-only.
|
||||
template<typename T, typename R = DeviceResource>
|
||||
class Buffer : private detail::StreamHolder<is_async_resource<R>::value>
|
||||
{
|
||||
static_assert(is_async_resource<R>::value || is_resource<R>::value,
|
||||
"Buffer requires R to model the AsyncResource or the Resource concept");
|
||||
static_assert(std::is_trivially_copyable<T>::value,
|
||||
"Buffer requires a trivially copyable T: elements are copied bytewise and never constructed or destroyed");
|
||||
|
||||
static constexpr bool IsAsync = is_async_resource<R>::value;
|
||||
|
||||
public:
|
||||
/// @brief Element and resource types, for generic code that rebinds one
|
||||
/// or constructs sibling buffers over the same resource.
|
||||
using ElementType = T;
|
||||
using ResourceType = R;
|
||||
|
||||
/// @brief Alias for a sibling buffer over the same resource with a
|
||||
/// different element type.
|
||||
template<typename U>
|
||||
using rebind = Buffer<U, R>;
|
||||
|
||||
private:
|
||||
R mResource;
|
||||
T* mData = nullptr;
|
||||
size_t mSize = 0; // element count
|
||||
|
||||
public:
|
||||
/// @brief Default c-tor of an empty buffer; performs no allocation.
|
||||
Buffer() = default;
|
||||
|
||||
/// @brief C-tor allocating @c count uninitialized elements, stream-ordered
|
||||
/// on @c stream. Parameter order follows cuda::buffer:
|
||||
/// (stream, resource, count, no_init).
|
||||
/// @param stream cuda stream the allocation is ordered on
|
||||
/// @param resource resource instance the buffer takes ownership of
|
||||
/// @param count number of elements
|
||||
template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
|
||||
explicit Buffer(cudaStream_t stream, R resource, size_t count, NoInit)
|
||||
: detail::StreamHolder<true>{stream}
|
||||
, mResource(std::move(resource))
|
||||
{
|
||||
this->allocate(count, stream);
|
||||
}
|
||||
|
||||
/// @brief Convenience c-tor using a default-constructed resource.
|
||||
/// @param stream cuda stream the allocation is ordered on
|
||||
/// @param count number of elements
|
||||
/// @note There is deliberately no count c-tor without NoInit: implicit
|
||||
/// initialization of freshly allocated memory costs a hidden fill
|
||||
/// pass that the dominant allocate-then-overwrite pattern wastes,
|
||||
/// so initialization is always explicit (matching cuda::buffer,
|
||||
/// whose count c-tor likewise requires cuda::no_init).
|
||||
template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
|
||||
Buffer(cudaStream_t stream, size_t count, NoInit) : Buffer(stream, R(), count, noInit) {}
|
||||
|
||||
/// @brief C-tor allocating @c count uninitialized elements from a
|
||||
/// synchronous resource: the stream-less analog of
|
||||
/// (stream, resource, count, no_init).
|
||||
/// @param resource resource instance the buffer takes ownership of
|
||||
/// @param count number of elements
|
||||
template<typename S = R, std::enable_if_t<!is_async_resource<S>::value && is_resource<S>::value, int> = 0>
|
||||
explicit Buffer(R resource, size_t count, NoInit)
|
||||
: mResource(std::move(resource))
|
||||
{
|
||||
this->allocate(count, cudaStream_t{0});
|
||||
}
|
||||
|
||||
/// @brief Convenience c-tor using a default-constructed resource.
|
||||
/// @param count number of elements
|
||||
template<typename S = R, std::enable_if_t<!is_async_resource<S>::value && is_resource<S>::value, int> = 0>
|
||||
Buffer(size_t count, NoInit) : Buffer(R(), count, noInit) {}
|
||||
|
||||
/// @brief Explicitly disallow copy construction and assignment operation
|
||||
Buffer(const Buffer&) = delete;
|
||||
Buffer& operator=(const Buffer&) = delete;
|
||||
|
||||
/// @brief Move c-tor; steals the allocation (and retained stream, if any)
|
||||
/// and leaves @c other empty.
|
||||
Buffer(Buffer&& other) noexcept
|
||||
: detail::StreamHolder<IsAsync>(other)
|
||||
, mResource(std::move(other.mResource))
|
||||
, mData(other.mData)
|
||||
, mSize(other.mSize)
|
||||
{
|
||||
other.mData = nullptr;
|
||||
other.mSize = 0;
|
||||
}
|
||||
|
||||
/// @brief Move assignment; frees the current allocation first, then steals
|
||||
/// from @c other and leaves it empty. Self-move is a no-op.
|
||||
Buffer& operator=(Buffer&& other) noexcept
|
||||
{
|
||||
if (this != &other) {
|
||||
this->destroy();
|
||||
static_cast<detail::StreamHolder<IsAsync>&>(*this) = other;
|
||||
mResource = std::move(other.mResource);
|
||||
mData = other.mData;
|
||||
mSize = other.mSize;
|
||||
other.mData = nullptr;
|
||||
other.mSize = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief Returns a deep copy of this buffer, allocated from a copy of the
|
||||
/// resource; the allocation and element copy are ordered on @c stream,
|
||||
/// which becomes the copy's retained stream.
|
||||
/// @param stream cuda stream the allocation and element copy are ordered on
|
||||
template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
|
||||
Buffer copy(cudaStream_t stream) const
|
||||
{
|
||||
Buffer out(stream, mResource, mSize, noInit);
|
||||
if (mData) cudaCheck(cudaMemcpyAsync(out.mData, mData, this->size_bytes(), cudaMemcpyDefault, stream));
|
||||
return out;
|
||||
}
|
||||
|
||||
/// @brief Returns a deep copy of this buffer ordered on the retained
|
||||
/// stream, i.e. copy(this->stream()).
|
||||
template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
|
||||
Buffer copy() const { return this->copy(this->stream()); }
|
||||
|
||||
/// @brief Returns a deep copy of this buffer, allocated from a copy of the
|
||||
/// synchronous resource.
|
||||
template<typename S = R, std::enable_if_t<!is_async_resource<S>::value && is_resource<S>::value, int> = 0>
|
||||
Buffer copy() const
|
||||
{
|
||||
Buffer out(mResource, mSize, noInit);
|
||||
if (mData) cudaCheck(cudaMemcpy(out.mData, mData, this->size_bytes(), cudaMemcpyDefault));
|
||||
return out;
|
||||
}
|
||||
|
||||
/// @brief D-tor. A stream-ordered resource frees on the retained stream;
|
||||
/// a synchronous resource frees immediately.
|
||||
~Buffer() { this->destroy(); }
|
||||
|
||||
/// @brief Returns the retained stream, i.e. the stream the buffer's memory
|
||||
/// will be freed on.
|
||||
template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
|
||||
cudaStream_t stream() const { return this->mStream; }
|
||||
|
||||
/// @brief Replaces the retained stream without synchronizing; subsequent
|
||||
/// deallocation (and destruction) is ordered on @c stream instead.
|
||||
/// @param stream cuda stream subsequent deallocation is ordered on
|
||||
/// @warning The caller is responsible for ordering @c stream after any
|
||||
/// in-flight work that uses the buffer's memory. This deliberately
|
||||
/// does not synchronize, matching cuda::buffer's set_stream, which
|
||||
/// avoids implicit synchronization in fundamental primitives.
|
||||
template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
|
||||
void set_stream(cudaStream_t stream) { this->mStream = stream; }
|
||||
|
||||
/// @brief Resizes the buffer to @c count elements, preserving the leading
|
||||
/// min(old, new) elements. Every operation — the new allocation, the
|
||||
/// prefix copy, and the free of the old block — is ordered on
|
||||
/// @c stream, which becomes the retained stream: the prefix copy is
|
||||
/// the old block's last use, so that is the stream its free must be
|
||||
/// ordered on.
|
||||
/// @param count number of elements
|
||||
/// @param stream cuda stream the reallocation is ordered on
|
||||
/// @warning The caller is responsible for ordering @c stream after any
|
||||
/// in-flight work on the previously retained stream that uses the
|
||||
/// buffer's memory.
|
||||
template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
|
||||
void resize(size_t count, cudaStream_t stream)
|
||||
{
|
||||
if (count != mSize) {
|
||||
// No member is mutated until every throwing operation has
|
||||
// succeeded, so a failed resize leaves the buffer untouched --
|
||||
// including its retained stream.
|
||||
T* newData = count ? static_cast<T*>(mResource.allocate_async(checkedBytes(count), R::DEFAULT_ALIGNMENT, stream))
|
||||
: nullptr;
|
||||
if (newData && mData) {
|
||||
const size_t prefix = count < mSize ? count : mSize;
|
||||
try {
|
||||
cudaCheck(cudaMemcpyAsync(newData, mData, prefix * sizeof(T), cudaMemcpyDefault, stream));
|
||||
}
|
||||
catch (...) {
|
||||
mResource.deallocate_async(newData, checkedBytes(count), R::DEFAULT_ALIGNMENT, stream);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
this->mStream = stream;
|
||||
this->deallocate(mData, mSize); // ordered on stream: after the prefix copy
|
||||
mData = newData;
|
||||
mSize = count;
|
||||
}
|
||||
else {
|
||||
this->mStream = stream; // no reallocation: set_stream semantics
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Resizes the buffer to @c count elements through the synchronous
|
||||
/// resource, preserving the leading min(old, new) elements.
|
||||
/// @param count number of elements
|
||||
template<typename S = R, std::enable_if_t<!is_async_resource<S>::value && is_resource<S>::value, int> = 0>
|
||||
void resize(size_t count)
|
||||
{
|
||||
if (count == mSize) return;
|
||||
// No member is mutated until every throwing operation has succeeded,
|
||||
// so a failed resize leaves the buffer untouched.
|
||||
T* newData = count ? static_cast<T*>(mResource.allocate(checkedBytes(count), R::DEFAULT_ALIGNMENT))
|
||||
: nullptr;
|
||||
if (newData && mData) {
|
||||
const size_t prefix = count < mSize ? count : mSize;
|
||||
try {
|
||||
cudaCheck(cudaMemcpy(newData, mData, prefix * sizeof(T), cudaMemcpyDefault));
|
||||
}
|
||||
catch (...) {
|
||||
mResource.deallocate(newData, checkedBytes(count), R::DEFAULT_ALIGNMENT);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
this->deallocate(mData, mSize);
|
||||
mData = newData;
|
||||
mSize = count;
|
||||
}
|
||||
|
||||
/// @brief Returns a pointer to the elements, or nullptr if empty.
|
||||
T* data() { return mData; }
|
||||
const T* data() const { return mData; }
|
||||
|
||||
/// @brief Returns a copy of the resource; for a ResourceRef this refers
|
||||
/// to the same underlying instance.
|
||||
/// @note Requires R to be copy-constructible (the cuda::mr convention:
|
||||
/// resources are cheap handles). A resource that owns its pool by
|
||||
/// value hands the caller an independent copy of that pool.
|
||||
R resource() const { return mResource; }
|
||||
|
||||
/// @brief Returns the number of elements.
|
||||
size_t size() const { return mSize; }
|
||||
|
||||
/// @brief Returns the size of the buffer's allocation in bytes.
|
||||
size_t size_bytes() const { return mSize * sizeof(T); }
|
||||
|
||||
/// @brief Returns true if this buffer manages no memory.
|
||||
bool empty() const { return mSize == 0; }
|
||||
|
||||
/// @brief Frees the buffer memory (if any) and resets to the empty state.
|
||||
/// A stream-ordered resource frees on the retained stream.
|
||||
/// @note Spelled destroy to match cuda::buffer. This is the name to use.
|
||||
void destroy()
|
||||
{
|
||||
this->deallocate(mData, mSize);
|
||||
mData = nullptr;
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
/// @brief Frees the buffer memory (if any) and resets to the empty state.
|
||||
/// @deprecated Use destroy(): the handles now dispatch to it directly.
|
||||
[[deprecated("Use cuda::Buffer::destroy instead")]]
|
||||
void clear() { this->destroy(); }
|
||||
|
||||
/// @brief Frees the buffer memory (if any) on @c stream and resets to the
|
||||
/// empty state. @c stream becomes the retained stream.
|
||||
/// @param stream cuda stream the deallocation is ordered on
|
||||
/// @warning The caller is responsible for ordering @c stream after any
|
||||
/// in-flight work that uses the buffer's memory.
|
||||
template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
|
||||
void destroy(cudaStream_t stream)
|
||||
{
|
||||
this->mStream = stream;
|
||||
this->destroy();
|
||||
}
|
||||
|
||||
/// @brief Exchanges the contents of this buffer with @c other. Neither
|
||||
/// buffer allocates, frees, or copies element data.
|
||||
/// @param other buffer to exchange contents with
|
||||
void swap(Buffer& other) noexcept
|
||||
{
|
||||
auto& lhs = static_cast<detail::StreamHolder<IsAsync>&>(*this);
|
||||
auto& rhs = static_cast<detail::StreamHolder<IsAsync>&>(other);
|
||||
std::swap(lhs, rhs);
|
||||
std::swap(mResource, other.mResource);
|
||||
std::swap(mData, other.mData);
|
||||
std::swap(mSize, other.mSize);
|
||||
}
|
||||
|
||||
private:
|
||||
/// @brief Returns @c count * sizeof(T), throwing std::runtime_error if the
|
||||
/// byte size would overflow size_t instead of silently wrapping into
|
||||
/// a tiny allocation.
|
||||
/// @param count number of elements
|
||||
static size_t checkedBytes(size_t count)
|
||||
{
|
||||
if (count > std::numeric_limits<size_t>::max() / sizeof(T))
|
||||
throw std::runtime_error("nanovdb::cuda::Buffer: element count overflows the byte size");
|
||||
return count * sizeof(T);
|
||||
}
|
||||
|
||||
/// @brief Allocates @c count elements through the resource and records the
|
||||
/// new extent. A zero count allocates nothing.
|
||||
/// @param count number of elements
|
||||
/// @param stream cuda stream the allocation is ordered on; used by the
|
||||
/// stream-ordered form and ignored by the synchronous one
|
||||
void allocate(size_t count, cudaStream_t stream)
|
||||
{
|
||||
if (count) {
|
||||
if constexpr (IsAsync)
|
||||
mData = static_cast<T*>(mResource.allocate_async(checkedBytes(count), R::DEFAULT_ALIGNMENT, stream));
|
||||
else
|
||||
mData = static_cast<T*>(mResource.allocate(checkedBytes(count), R::DEFAULT_ALIGNMENT));
|
||||
}
|
||||
mSize = count;
|
||||
}
|
||||
|
||||
/// @brief Frees @c count elements at @c p through the resource; the
|
||||
/// stream-ordered form frees on the retained stream. Null is a no-op.
|
||||
/// @param p pointer to the elements to free
|
||||
/// @param count number of elements
|
||||
void deallocate(T* p, size_t count)
|
||||
{
|
||||
if (!p) return;
|
||||
if constexpr (IsAsync)
|
||||
mResource.deallocate_async(p, count * sizeof(T), R::DEFAULT_ALIGNMENT, this->mStream);
|
||||
else
|
||||
mResource.deallocate(p, count * sizeof(T), R::DEFAULT_ALIGNMENT);
|
||||
}
|
||||
}; // Buffer<T, R> class
|
||||
|
||||
/// @brief Non-owning, trivially copyable view of a contiguous range of @c T
|
||||
/// elements, with span semantics.
|
||||
/// @tparam T element type; spell constness in the element type
|
||||
/// (e.g. BufferView<const std::byte> is the read-only form), since
|
||||
/// const on the view itself is shallow.
|
||||
template<typename T>
|
||||
class BufferView
|
||||
{
|
||||
T* mData = nullptr;
|
||||
size_t mSize = 0; // element count
|
||||
|
||||
public:
|
||||
/// @brief Default c-tor of an empty view.
|
||||
BufferView() = default;
|
||||
|
||||
/// @brief C-tor viewing a contiguous range; the caller guarantees the
|
||||
/// underlying storage outlives every use of the view.
|
||||
/// @param data pointer to the first element
|
||||
/// @param count number of elements
|
||||
/// @throw std::runtime_error if @c data is null while @c count is non-zero.
|
||||
BufferView(T* data, size_t count) : mData(data), mSize(count)
|
||||
{
|
||||
if (data == nullptr && count != 0)
|
||||
throw std::runtime_error("BufferView: null data with a non-zero element count");
|
||||
}
|
||||
|
||||
/// @brief Returns a pointer to the viewed elements, or nullptr if empty.
|
||||
T* data() const { return mData; }
|
||||
|
||||
/// @brief Returns the number of viewed elements.
|
||||
size_t size() const { return mSize; }
|
||||
|
||||
/// @brief Returns the size of the viewed range in bytes.
|
||||
size_t size_bytes() const { return mSize * sizeof(T); }
|
||||
|
||||
/// @brief Returns true if this view references no elements.
|
||||
bool empty() const { return mSize == 0; }
|
||||
|
||||
/// @brief Detaches the view (nulls the pointer and zeroes the size)
|
||||
/// without touching the underlying storage -- the view is
|
||||
/// non-owning, so "destroying" it releases nothing. This is the
|
||||
/// one deliberate deviation from std::span, required by the buffer
|
||||
/// static interface the handles consume through reset().
|
||||
void destroy()
|
||||
{
|
||||
mData = nullptr;
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
/// @brief Detaches the view.
|
||||
/// @deprecated Use destroy(): the handles now dispatch to it directly.
|
||||
[[deprecated("Use cuda::BufferView::destroy instead")]]
|
||||
void clear() { this->destroy(); }
|
||||
}; // BufferView<T> class
|
||||
|
||||
} // namespace cuda
|
||||
|
||||
// Primary template defined in HostBuffer.h; declared here so this header
|
||||
// stays self-contained without pulling in the host-buffer machinery.
|
||||
template<typename BufferT>
|
||||
struct BufferTraits;
|
||||
|
||||
/// @brief GridHandle support for the single-space cuda::Buffer: the buffer
|
||||
/// owns exactly one allocation, resident on the device, so the handle
|
||||
/// parses metadata through a device read and exposes only the device
|
||||
/// accessors. Requires byte-addressed storage.
|
||||
/// @note This trait doubles as the definition of the single-space
|
||||
/// device-buffer concept: a buffer whose BufferTraits specialization
|
||||
/// sets hasDeviceSingle guarantees ElementType and ResourceType
|
||||
/// typedefs, data(), size() and size_bytes() (byte-addressed elements,
|
||||
/// enforced by the consumer), resource(), copy(), destroy(), and stream()
|
||||
/// when the resource is stream-ordered. Any consumer of hasDeviceSingle
|
||||
/// may rely on exactly this interface and nothing more; in particular,
|
||||
/// scratch allocates through resource() as a cuda::Buffer, so a
|
||||
/// conforming buffer never needs to be constructible by a consumer.
|
||||
template<typename T, typename R>
|
||||
struct BufferTraits<cuda::Buffer<T, R>>
|
||||
{
|
||||
static constexpr bool hasDeviceDual = false;
|
||||
// Device-resident storage; the byte-addressed requirement is enforced by
|
||||
// the single-space GridHandle constructor, so trait queries stay
|
||||
// answerable for any element type.
|
||||
static constexpr bool hasDeviceSingle = !cuda::is_host_accessible_resource<R>::value;
|
||||
// A buffer over a host-accessible resource (e.g. PinnedResource) is
|
||||
// host-readable single-space storage: GridHandle parses its metadata on
|
||||
// the host, exposes the host accessors, and allocates reads and copies
|
||||
// through the buffer's resource.
|
||||
static constexpr bool hasHostSingle = cuda::is_host_accessible_resource<R>::value;
|
||||
};
|
||||
|
||||
} // namespace nanovdb
|
||||
|
||||
#endif // end of NANOVDB_CUDA_BUFFER_H_HAS_BEEN_INCLUDED
|
||||
@@ -38,6 +38,7 @@ 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
|
||||
int mDeviceCount, mManaged;// if mManaged is non-zero this class is responsible for allocating and freeing memory buffers. Otherwise this is assumed to be handled externally
|
||||
cudaEvent_t *mEvents = nullptr;// per-device event marking the last use of each managed device buffer (parallel to mGpuData, length mDeviceCount). Every use waits on this event before issuing work and re-records it afterwards, so the single event transitively covers EVERY stream the buffer has been used on. Frees then wait on it, which orders them after all outstanding work: freeing on the default stream alone is only safe for blocking streams, and freeing on the last-used stream alone is only safe when just one stream was used.
|
||||
|
||||
/// @brief Initialize buffer
|
||||
/// @param size byte size of buffer to be initialized
|
||||
@@ -46,6 +47,33 @@ class DeviceBuffer
|
||||
/// @warning size is expected to be non-zero. Use clear() clear buffer!
|
||||
void init(uint64_t size, int device, cudaStream_t stream);
|
||||
|
||||
/// @brief Free every managed device allocation, each ordered after all tracked uses of the
|
||||
/// buffer, and destroy the tracking events.
|
||||
/// @param stream Stream the frees are issued on for allocations owned by the CURRENT device.
|
||||
/// A stream belongs to a single device, so it cannot carry frees for other devices'
|
||||
/// memory pools; allocations on other devices are freed on their own device's default
|
||||
/// stream, after switching to that device.
|
||||
/// @note Destroying an event with a pending wait is safe: CUDA releases it once the device
|
||||
/// has completed it.
|
||||
void freeDeviceBuffers(cudaStream_t stream)
|
||||
{
|
||||
int current = 0;
|
||||
cudaCheck(cudaGetDevice(¤t));
|
||||
for (int i = 0; i < mDeviceCount; ++i) {
|
||||
if (mGpuData[i]) {
|
||||
const cudaStream_t freeStream = (i == current) ? stream : cudaStream_t{0};
|
||||
if (i != current) cudaCheck(cudaSetDevice(i));
|
||||
this->orderAfterPriorUses(i, freeStream);
|
||||
cudaCheck(util::cuda::freeAsync(mGpuData[i], freeStream));
|
||||
if (i != current) cudaCheck(cudaSetDevice(current));
|
||||
}
|
||||
if (mEvents && mEvents[i]) {
|
||||
cudaCheck(cudaEventDestroy(mEvents[i]));
|
||||
mEvents[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
using PtrT = std::shared_ptr<DeviceBuffer>;
|
||||
@@ -122,8 +150,10 @@ public:
|
||||
, mGpuData(other.mGpuData)
|
||||
, mDeviceCount(other.mDeviceCount)
|
||||
, mManaged(other.mManaged)
|
||||
, mEvents(other.mEvents)
|
||||
{
|
||||
other.mCpuData = other.mGpuData = nullptr;
|
||||
other.mEvents = nullptr;
|
||||
other.mSize = other.mDeviceCount = other.mManaged = 0;
|
||||
}
|
||||
|
||||
@@ -142,6 +172,8 @@ public:
|
||||
}
|
||||
|
||||
/// @brief Destructor frees memory on both the host and device
|
||||
/// @note Each managed device free waits on that device's tracking event first, so it is
|
||||
/// ordered after every stream the buffer was used on, not just the most recent one.
|
||||
~DeviceBuffer() { this->clear(); };
|
||||
|
||||
/// @brief Static factory method that return an instance of this buffer
|
||||
@@ -226,8 +258,59 @@ public:
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// @brief Order work subsequently issued on @a stream after every prior use of this
|
||||
/// device buffer, whichever stream those uses were issued on. The consume-side
|
||||
/// companion of recordUse: an external consumer (e.g. a zero-copy array-interface
|
||||
/// export) calls this with its own stream before reading, so it cannot observe a
|
||||
/// partially-written buffer after asynchronous uploads or recorded kernels.
|
||||
/// @param device Device whose buffer is about to be read
|
||||
/// @param stream Stream the consumer's work will be issued on
|
||||
void orderAfterPriorUses(int device, cudaStream_t stream) const
|
||||
{
|
||||
if (mEvents && mEvents[device]) cudaCheck(cudaStreamWaitEvent(stream, mEvents[device], 0));
|
||||
}
|
||||
|
||||
/// @brief Record that this buffer's device data was just used on @a stream, so that the
|
||||
/// buffer's device frees (destructor, move-assignment, clear) are ordered after that
|
||||
/// work. Uses issued through deviceUpload/deviceDownload are recorded automatically;
|
||||
/// callers that enqueue their own kernels or copies against the raw pointer returned
|
||||
/// by deviceData() should call this afterwards. Without it, such work is only safe if
|
||||
/// it is on a blocking stream (which the free, issued on the default stream, waits on
|
||||
/// implicitly) or if the caller synchronizes before the buffer is cleared/destroyed.
|
||||
/// @param device Device whose buffer was used
|
||||
/// @param stream Stream the work was issued on
|
||||
/// @note Recording chains across streams: @a stream is first ordered after the previously
|
||||
/// recorded use (if any) so the single per-device event transitively covers every
|
||||
/// recorded use, not just the last one. Without this, concurrent uses on streams A
|
||||
/// then B would leave only B's event, and the device free could run while A's work
|
||||
/// is still in flight. The side effect is that work subsequently issued on @a stream
|
||||
/// also waits on the previously recorded use -- acceptable for a shared buffer, where
|
||||
/// later-recorded consumers observing earlier writes is the expected ordering. Note
|
||||
/// this also serializes CONCURRENT READERS that record uses (the single event cannot
|
||||
/// distinguish read-read from write-read); if that ever matters in a profile, the
|
||||
/// upgrade path is a read/write-separated or per-record event scheme, not a revert.
|
||||
void recordUse(int device, cudaStream_t stream)
|
||||
{
|
||||
if (!mEvents) return;
|
||||
if (mEvents[device] == nullptr) {// events are per-device, so create it on the right one
|
||||
int current = 0;
|
||||
cudaCheck(cudaGetDevice(¤t));
|
||||
if (current != device) cudaCheck(cudaSetDevice(device));
|
||||
cudaCheck(cudaEventCreateWithFlags(&mEvents[device], cudaEventDisableTiming));
|
||||
if (current != device) cudaCheck(cudaSetDevice(current));
|
||||
} else {
|
||||
// Re-recording MOVES the event; chain first so the new capture also covers the
|
||||
// prior recorded use (waiting on a never-recorded or completed event is a no-op).
|
||||
cudaCheck(cudaStreamWaitEvent(stream, mEvents[device], 0));
|
||||
}
|
||||
cudaCheck(cudaEventRecord(mEvents[device], stream));
|
||||
}
|
||||
|
||||
/// @brief Retuns a raw pointer to the specified device/GPU buffer managed by this allocator.
|
||||
/// @warning Note that the pointer can be NULL!
|
||||
/// @note Work enqueued against this raw pointer is invisible to the buffer's lifetime
|
||||
/// tracking: on a non-blocking stream, call recordUse afterwards (or synchronize
|
||||
/// before the buffer is cleared/destroyed) so the device free is ordered after it.
|
||||
void* deviceData(int device) const {
|
||||
NANOVDB_ASSERT(device >= 0 && device < mDeviceCount);
|
||||
return mGpuData[device];
|
||||
@@ -301,6 +384,10 @@ public:
|
||||
/// @}
|
||||
|
||||
/// @brief De-allocate all memory managed by this allocator and set all pointers to NULL
|
||||
/// @param stream Stream the device frees are issued on. The frees are additionally ordered
|
||||
/// after every stream the buffer was used on (via the per-device tracking event), so
|
||||
/// @a stream selects where the free is enqueued, not what it is ordered against - any
|
||||
/// stream is safe to pass here regardless of where the buffer was used.
|
||||
void clear(cudaStream_t stream = 0);
|
||||
void clear(void* stream){this->clear(cudaStream_t(stream));}
|
||||
|
||||
@@ -310,18 +397,22 @@ public:
|
||||
|
||||
inline DeviceBuffer& DeviceBuffer::operator=(DeviceBuffer&& other) noexcept
|
||||
{
|
||||
if (mManaged) {// first free all the managed data buffers
|
||||
if (this == &other) return *this;// self-move would free our buffers and then read them back
|
||||
if (mManaged) {// first free all the managed data buffers, ordered after every use of each
|
||||
cudaCheck(cudaFreeHost(mCpuData));
|
||||
for (int i=0; i<mDeviceCount; ++i) cudaCheck(util::cuda::freeAsync(mGpuData[i], 0));
|
||||
this->freeDeviceBuffers(cudaStream_t{0});
|
||||
}
|
||||
delete [] mGpuData;
|
||||
delete [] mEvents;
|
||||
mSize = other.mSize;
|
||||
mCpuData = other.mCpuData;
|
||||
mGpuData = other.mGpuData;
|
||||
mDeviceCount = other.mDeviceCount;
|
||||
mManaged = other.mManaged;
|
||||
mEvents = other.mEvents;
|
||||
other.mCpuData = nullptr;
|
||||
other.mGpuData = nullptr;
|
||||
other.mEvents = nullptr;
|
||||
other.mSize = 0;
|
||||
other.mDeviceCount = 0;
|
||||
other.mManaged = 0;
|
||||
@@ -333,6 +424,7 @@ inline void DeviceBuffer::init(uint64_t size, int device, cudaStream_t stream)
|
||||
if (size==0) return;
|
||||
cudaCheck(cudaGetDeviceCount(&mDeviceCount));
|
||||
mGpuData = new void*[mDeviceCount]();// NULL initialization
|
||||
mEvents = new cudaEvent_t[mDeviceCount]();// NULL initialization; created lazily on first use
|
||||
NANOVDB_ASSERT(device >= cudaCpuDeviceId && device < mDeviceCount);
|
||||
if (device == cudaCpuDeviceId) {
|
||||
cudaCheck(cudaMallocHost((void**)&mCpuData, size)); // un-managed pinned memory on the host (can be slow to access!). Always 32B aligned
|
||||
@@ -340,6 +432,7 @@ inline void DeviceBuffer::init(uint64_t size, int device, cudaStream_t stream)
|
||||
} else {
|
||||
cudaCheck(util::cuda::mallocAsync(mGpuData+device, size, stream)); // un-managed memory on the device, always 32B aligned!
|
||||
checkPtr(mGpuData[device], "cuda::DeviceBuffer::init: failed to allocate device buffer");
|
||||
this->recordUse(device, stream);// the free must be ordered after this allocation
|
||||
}
|
||||
mSize = size;
|
||||
mManaged = 1;// i.e. this instance is responsible for allocating and delete memory
|
||||
@@ -354,7 +447,11 @@ inline void DeviceBuffer::deviceUpload(int device, cudaStream_t stream, bool syn
|
||||
cudaCheck(util::cuda::mallocAsync(mGpuData+device, mSize, stream)); // un-managed memory on the device, always 32B aligned!
|
||||
}
|
||||
checkPtr(mGpuData[device], "uninitialized gpu destination data");
|
||||
// Order this transfer after any use of the buffer on another stream, then mark it as the
|
||||
// latest use, so the tracking event keeps covering every stream the buffer has seen.
|
||||
this->orderAfterPriorUses(device, stream);
|
||||
cudaCheck(cudaMemcpyAsync(mGpuData[device], mCpuData, mSize, cudaMemcpyHostToDevice, stream));
|
||||
this->recordUse(device, stream);
|
||||
if (sync) cudaCheck(cudaStreamSynchronize(stream));
|
||||
} // DeviceBuffer::deviceUpload
|
||||
|
||||
@@ -374,7 +471,9 @@ inline void DeviceBuffer::deviceDownload(int device, cudaStream_t stream, bool s
|
||||
cudaCheck(cudaMallocHost((void**)&mCpuData, mSize)); // un-managed pinned memory on the host (can be slow to access!). Always 32B aligned
|
||||
}
|
||||
checkPtr(mCpuData, "uninitialized cpu destination data");
|
||||
this->orderAfterPriorUses(device, stream);
|
||||
cudaCheck(cudaMemcpyAsync(mCpuData, mGpuData[device], mSize, cudaMemcpyDeviceToHost, stream));
|
||||
this->recordUse(device, stream);
|
||||
if (sync) cudaCheck(cudaStreamSynchronize(stream));
|
||||
} // DeviceBuffer::deviceDownload
|
||||
|
||||
@@ -387,13 +486,15 @@ inline void DeviceBuffer::deviceDownload(void* stream, bool sync)
|
||||
|
||||
inline void DeviceBuffer::clear(cudaStream_t stream)
|
||||
{
|
||||
if (mManaged) {// free all the managed data buffers
|
||||
if (mManaged) {// free all the managed data buffers, ordered after every use of each
|
||||
cudaCheck(cudaFreeHost(mCpuData));
|
||||
for (int i=0; i<mDeviceCount; ++i) cudaCheck(util::cuda::freeAsync(mGpuData[i], stream));
|
||||
this->freeDeviceBuffers(stream);
|
||||
}
|
||||
delete [] mGpuData;
|
||||
delete [] mEvents;
|
||||
mCpuData = nullptr;
|
||||
mGpuData = nullptr;
|
||||
mEvents = nullptr;
|
||||
mSize = 0;
|
||||
mDeviceCount = 0;
|
||||
mManaged = 0;
|
||||
|
||||
@@ -7,27 +7,334 @@
|
||||
#include <cuda_runtime_api.h>
|
||||
#include <nanovdb/util/cuda/Util.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace nanovdb {
|
||||
|
||||
namespace cuda {
|
||||
|
||||
/// @brief Default stream-ordered device memory resource. Allocations are made
|
||||
/// with cudaMallocAsync and freed with cudaFreeAsync via the
|
||||
/// util::cuda wrappers.
|
||||
/// @note Models the AsyncResource concept, which refines the synchronous
|
||||
/// Resource concept (as in CCCL's cuda::mr): the instance methods
|
||||
/// provide both the stream-ordered allocate_async / deallocate_async
|
||||
/// and the synchronous allocate / deallocate. The type is stateless,
|
||||
/// so a default-constructed instance adds no overhead. The static
|
||||
/// allocateAsync / deallocateAsync methods are deprecated.
|
||||
class DeviceResource
|
||||
{
|
||||
public:
|
||||
// cudaMalloc aligns memory to 256 bytes by default
|
||||
static constexpr size_t DEFAULT_ALIGNMENT = 256;
|
||||
|
||||
static void* allocateAsync(size_t bytes, size_t, cudaStream_t stream) {
|
||||
/// @brief Stream-ordered allocation.
|
||||
/// @param bytes number of bytes to allocate
|
||||
/// @param stream cuda stream the allocation is ordered on
|
||||
/// @note the alignment parameter is unnamed: cudaMallocAsync always
|
||||
/// 256B-aligns
|
||||
void* allocate_async(size_t bytes, size_t, cudaStream_t stream) {
|
||||
void* p = nullptr;
|
||||
cudaCheck(util::cuda::mallocAsync(&p, bytes, stream));
|
||||
return p;
|
||||
}
|
||||
|
||||
static void deallocateAsync(void *p, size_t, size_t, cudaStream_t stream) {
|
||||
/// @brief Stream-ordered deallocation.
|
||||
/// @param p pointer previously returned by allocate_async
|
||||
/// @param stream cuda stream the deallocation is ordered on
|
||||
void deallocate_async(void* p, size_t, size_t, cudaStream_t stream) {
|
||||
cudaCheck(util::cuda::freeAsync(p, stream));
|
||||
}
|
||||
|
||||
/// @brief Synchronous allocation; the returned memory is immediately
|
||||
/// valid on every stream.
|
||||
/// @param bytes number of bytes to allocate
|
||||
/// @param alignment requested alignment
|
||||
void* allocate(size_t bytes, size_t alignment) {
|
||||
void* p = this->allocate_async(bytes, alignment, cudaStream_t(0));
|
||||
cudaCheck(cudaStreamSynchronize(cudaStream_t(0)));
|
||||
return p;
|
||||
}
|
||||
|
||||
/// @brief Synchronous deallocation; the caller guarantees that device
|
||||
/// work touching the memory has completed.
|
||||
/// @param p pointer previously returned by allocate or allocate_async
|
||||
void deallocate(void* p, size_t bytes, size_t alignment) {
|
||||
this->deallocate_async(p, bytes, alignment, cudaStream_t(0));
|
||||
}
|
||||
|
||||
[[deprecated("use the instance method allocate_async")]]
|
||||
static void* allocateAsync(size_t bytes, size_t alignment, cudaStream_t stream) {
|
||||
return DeviceResource().allocate_async(bytes, alignment, stream);
|
||||
}
|
||||
|
||||
[[deprecated("use the instance method deallocate_async")]]
|
||||
static void deallocateAsync(void *p, size_t bytes, size_t alignment, cudaStream_t stream) {
|
||||
DeviceResource().deallocate_async(p, bytes, alignment, stream);
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Returns a program-lifetime, address-stable reference to a default
|
||||
/// instance of resource @c R.
|
||||
/// @details The instance is a function-local static, so it outlives every
|
||||
/// caller and is safe to bind through a default function/constructor
|
||||
/// argument. @c R must be default-constructible.
|
||||
template <class R>
|
||||
inline R& default_resource()
|
||||
{
|
||||
static R sResource;
|
||||
return sResource;
|
||||
}
|
||||
|
||||
/// @brief Detection trait: @c is_async_resource<R>::value is true iff @c R
|
||||
/// models the stream-ordered AsyncResource concept, i.e. exposes
|
||||
/// allocate_async(size_t, size_t, cudaStream_t) and
|
||||
/// deallocate_async(void*, size_t, size_t, cudaStream_t).
|
||||
/// @details Use it to dispatch between a stream-ordered resource and a
|
||||
/// synchronous one (which exposes allocate/deallocate without a
|
||||
/// stream argument):
|
||||
/// @code
|
||||
/// template<typename R>
|
||||
/// void* allocate(R& resource, size_t bytes, size_t alignment, cudaStream_t stream)
|
||||
/// {
|
||||
/// if constexpr (nanovdb::cuda::is_async_resource<R>::value)
|
||||
/// return resource.allocate_async(bytes, alignment, stream); // stream-ordered
|
||||
/// else
|
||||
/// return resource.allocate(bytes, alignment); // synchronous
|
||||
/// }
|
||||
/// @endcode
|
||||
/// @note AsyncResource refines the synchronous Resource concept, matching
|
||||
/// CCCL's cuda::mr: an async resource must also provide the
|
||||
/// synchronous allocate / deallocate, so is_async_resource<R> implies
|
||||
/// is_resource<R>. A synchronous-only resource (e.g. PinnedResource)
|
||||
/// models just is_resource. The synchronous methods of a stream-ordered
|
||||
/// resource are typically thin delegates (allocate_async on the null
|
||||
/// stream followed by a stream synchronize).
|
||||
template <class R, class = void>
|
||||
struct is_async_resource : std::false_type {};
|
||||
|
||||
template <class R>
|
||||
struct is_async_resource<R, std::void_t<
|
||||
decltype(std::declval<R&>().allocate_async(size_t{0}, size_t{0}, cudaStream_t{0})),
|
||||
decltype(std::declval<R&>().deallocate_async(std::declval<void*>(), size_t{0}, size_t{0}, cudaStream_t{0})),
|
||||
decltype(std::declval<R&>().allocate(size_t{0}, size_t{0})),
|
||||
decltype(std::declval<R&>().deallocate(std::declval<void*>(), size_t{0}, size_t{0}))>>
|
||||
: std::true_type {};
|
||||
|
||||
/// @brief Detection trait: @c is_host_accessible_resource<R>::value is true
|
||||
/// iff @c R declares `static constexpr bool HOST_ACCESSIBLE = true`,
|
||||
/// i.e. its allocations are mapped into the host address space (e.g.
|
||||
/// PinnedResource). Defaults to false: allocations are device-resident.
|
||||
/// @note Unlike the void_t detections above, this checks the member's VALUE:
|
||||
/// a resource declaring HOST_ACCESSIBLE = false stays device-resident.
|
||||
template<typename R, typename = void>
|
||||
struct is_host_accessible_resource : std::false_type {};
|
||||
template<typename R>
|
||||
struct is_host_accessible_resource<R, typename std::enable_if<bool(R::HOST_ACCESSIBLE)>::type> : std::true_type {};
|
||||
|
||||
/// @brief Detection trait: @c is_resource<R>::value is true iff @c R models
|
||||
/// the synchronous Resource concept, i.e. exposes
|
||||
/// allocate(size_t, size_t) and deallocate(void*, size_t, size_t).
|
||||
/// @details Use it to constrain code paths that need a resource without a
|
||||
/// stream argument, e.g. host-side allocations:
|
||||
/// @code
|
||||
/// template<typename R>
|
||||
/// void* allocate(R& resource, size_t bytes, size_t alignment)
|
||||
/// {
|
||||
/// static_assert(nanovdb::cuda::is_resource<R>::value, "R must be a synchronous resource");
|
||||
/// return resource.allocate(bytes, alignment);
|
||||
/// }
|
||||
/// @endcode
|
||||
template <class R, class = void>
|
||||
struct is_resource : std::false_type {};
|
||||
|
||||
template <class R>
|
||||
struct is_resource<R, std::void_t<
|
||||
decltype(std::declval<R&>().allocate(size_t{0}, size_t{0})),
|
||||
decltype(std::declval<R&>().deallocate(std::declval<void*>(), size_t{0}, size_t{0}))>>
|
||||
: std::true_type {};
|
||||
|
||||
/// @brief CRTP base supplying the synchronous half of the resource concept in
|
||||
/// terms of the stream-ordered half, so a custom stream-ordered resource
|
||||
/// only has to write allocate_async and deallocate_async.
|
||||
/// @tparam Derived the resource deriving from this base
|
||||
/// @details A stream-ordered resource must also model the synchronous concept
|
||||
/// (is_async_resource implies is_resource), which means writing four
|
||||
/// methods where two would do. The synchronous pair is not a bare
|
||||
/// delegate: memory from allocate must be usable immediately on any
|
||||
/// stream, so the null-stream allocation has to be synchronized before
|
||||
/// it is returned. Omitting that synchronization yields memory that
|
||||
/// satisfies the concept but is not actually synchronous -- a race
|
||||
/// rather than a compile error -- so it lives here rather than being
|
||||
/// rewritten per resource.
|
||||
/// @code
|
||||
/// struct MyResource : nanovdb::cuda::SyncFromAsync<MyResource> {
|
||||
/// static constexpr size_t DEFAULT_ALIGNMENT = 256;
|
||||
/// void* allocate_async(size_t bytes, size_t alignment, cudaStream_t stream);
|
||||
/// void deallocate_async(void* p, size_t bytes, size_t alignment, cudaStream_t stream);
|
||||
/// };
|
||||
/// @endcode
|
||||
template <class Derived>
|
||||
struct SyncFromAsync
|
||||
{
|
||||
/// @brief Allocates @c bytes usable on any stream when this returns.
|
||||
/// @param bytes number of bytes to allocate
|
||||
/// @param alignment requested alignment
|
||||
/// @note Every call synchronizes the null stream; on hot paths prefer the
|
||||
/// stream-ordered pair.
|
||||
void* allocate(size_t bytes, size_t alignment)
|
||||
{
|
||||
void* p = static_cast<Derived&>(*this).allocate_async(bytes, alignment, cudaStream_t{0});
|
||||
cudaCheck(cudaStreamSynchronize(cudaStream_t{0}));
|
||||
return p;
|
||||
}
|
||||
|
||||
/// @brief Frees @c p on the null stream.
|
||||
/// @param p pointer previously returned by allocate
|
||||
/// @param bytes size passed to the matching allocate
|
||||
/// @param alignment alignment passed to the matching allocate
|
||||
/// @note No synchronization here: the synchronous concept's contract is
|
||||
/// that the memory is already quiescent when deallocate is called.
|
||||
void deallocate(void* p, size_t bytes, size_t alignment)
|
||||
{
|
||||
static_cast<Derived&>(*this).deallocate_async(p, bytes, alignment, cudaStream_t{0});
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Synchronous device memory resource backed by cudaMalloc/cudaFree.
|
||||
/// Models only the Resource concept: it never touches stream-ordered
|
||||
/// allocation, so it works on devices without memory-pool support
|
||||
/// (cudaDevAttrMemoryPoolsSupported == 0), where DeviceResource's
|
||||
/// cudaMallocAsync path fails by design. Pair with AsyncFromSync to
|
||||
/// drive the stream-ordered builders on such a device.
|
||||
class MallocResource
|
||||
{
|
||||
public:
|
||||
// cudaMalloc aligns memory to 256 bytes by default
|
||||
static constexpr size_t DEFAULT_ALIGNMENT = 256;
|
||||
|
||||
/// @brief Allocates @c bytes with cudaMalloc; valid on every stream when
|
||||
/// this returns. A zero request returns nullptr.
|
||||
void* allocate(size_t bytes, size_t)
|
||||
{
|
||||
if (bytes == 0) return nullptr;
|
||||
void* p = nullptr;
|
||||
cudaCheck(cudaMalloc(&p, bytes));
|
||||
return p;
|
||||
}
|
||||
|
||||
/// @brief Frees @c p with cudaFree; the caller guarantees that device work
|
||||
/// touching the memory has completed.
|
||||
void deallocate(void* p, size_t, size_t) { cudaCheck(cudaFree(p)); }
|
||||
};// MallocResource
|
||||
|
||||
/// @brief Wrapper presenting a synchronous resource as a stream-ordered one,
|
||||
/// so it can drive components that require the AsyncResource concept
|
||||
/// (TempPool and the GPU builders).
|
||||
/// @tparam R the wrapped synchronous resource, held by value; wrap a
|
||||
/// ResourceRef<R> to borrow a stateful instance instead.
|
||||
/// @details The mirror of SyncFromAsync, and the analog of cuda::mr's
|
||||
/// synchronous_resource_adapter. allocate_async forwards to
|
||||
/// R::allocate, whose memory is immediately valid on every stream --
|
||||
/// a stronger guarantee than stream-ordering requires.
|
||||
/// deallocate_async synchronizes @c stream before R::deallocate,
|
||||
/// establishing the quiescence the synchronous contract demands.
|
||||
/// @warning Every deallocation synchronizes its stream, so expect
|
||||
/// serialization relative to a genuinely stream-ordered resource.
|
||||
/// That is the unavoidable cost of a synchronous backend under a
|
||||
/// stream-ordered algorithm; this wrapper exists so the cost is
|
||||
/// explicit and chosen by the caller -- e.g. on a device without
|
||||
/// memory-pool support -- rather than silently substituted.
|
||||
template<class R>
|
||||
struct AsyncFromSync
|
||||
{
|
||||
static_assert(is_resource<R>::value,
|
||||
"AsyncFromSync requires R to model the synchronous Resource concept");
|
||||
|
||||
static constexpr size_t DEFAULT_ALIGNMENT = R::DEFAULT_ALIGNMENT;
|
||||
|
||||
/// @brief The adapter is host-accessible iff the adapted resource is.
|
||||
static constexpr bool HOST_ACCESSIBLE = is_host_accessible_resource<R>::value;
|
||||
|
||||
R resource;
|
||||
|
||||
/// @brief Allocates through the synchronous resource; the result is valid
|
||||
/// on every stream, hence trivially valid on @c stream.
|
||||
void* allocate_async(size_t bytes, size_t alignment, cudaStream_t) { return resource.allocate(bytes, alignment); }
|
||||
|
||||
/// @brief Synchronizes @c stream, then frees through the synchronous
|
||||
/// resource -- the synchronize makes the quiescence contract hold.
|
||||
/// Null is a no-op and skips the synchronize.
|
||||
void deallocate_async(void* p, size_t bytes, size_t alignment, cudaStream_t stream)
|
||||
{
|
||||
if (p == nullptr) return;
|
||||
cudaCheck(cudaStreamSynchronize(stream));
|
||||
resource.deallocate(p, bytes, alignment);
|
||||
}
|
||||
|
||||
/// @brief Synchronous pair, forwarding to the wrapped resource.
|
||||
void* allocate(size_t bytes, size_t alignment) { return resource.allocate(bytes, alignment); }
|
||||
void deallocate(void* p, size_t bytes, size_t alignment) { resource.deallocate(p, bytes, alignment); }
|
||||
};// AsyncFromSync<R>
|
||||
|
||||
/// @brief Non-owning reference to a memory resource that is itself a resource:
|
||||
/// copying the ref shares the underlying resource rather than copying it.
|
||||
/// @tparam R the referenced resource type
|
||||
/// @details Types that hold their resource by value -- cuda::Buffer, matching
|
||||
/// cuda::buffer -- select their ownership semantics by what is placed
|
||||
/// in that slot: a concrete resource is owned as a copy, while a
|
||||
/// ResourceRef borrows. This is the same division cuda::mr draws
|
||||
/// between any_resource (owning) and resource_ref (borrowing), and the
|
||||
/// same shape as std::pmr::polymorphic_allocator over memory_resource*.
|
||||
/// Use it when a resource is stateful or long-lived and a container
|
||||
/// must allocate through *that* instance rather than a copy of it.
|
||||
/// @warning The referenced resource must outlive every use of this ref and of
|
||||
/// all copies of it, including any container holding one.
|
||||
template <class R>
|
||||
struct ResourceRef
|
||||
{
|
||||
static_assert(is_async_resource<R>::value || is_resource<R>::value,
|
||||
"ResourceRef requires R to model the AsyncResource or the Resource concept");
|
||||
|
||||
static constexpr size_t DEFAULT_ALIGNMENT = R::DEFAULT_ALIGNMENT;
|
||||
|
||||
/// @brief A reference is host-accessible iff the referenced resource is.
|
||||
static constexpr bool HOST_ACCESSIBLE = is_host_accessible_resource<R>::value;
|
||||
|
||||
/// @brief Constructs a ref borrowing @c resource.
|
||||
/// @param resource resource to allocate from; must outlive this ref
|
||||
ResourceRef(R& resource) : mResource(&resource) {}
|
||||
|
||||
/// @{
|
||||
/// @brief Stream-ordered pair, present only when @c R models AsyncResource,
|
||||
/// so a ref over a synchronous resource does not misreport its tier.
|
||||
template<class S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
|
||||
void* allocate_async(size_t bytes, size_t alignment, cudaStream_t stream)
|
||||
{
|
||||
return mResource->allocate_async(bytes, alignment, stream);
|
||||
}
|
||||
template<class S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
|
||||
void deallocate_async(void* p, size_t bytes, size_t alignment, cudaStream_t stream)
|
||||
{
|
||||
mResource->deallocate_async(p, bytes, alignment, stream);
|
||||
}
|
||||
/// @}
|
||||
|
||||
/// @brief Synchronous pair, forwarding to the referenced resource.
|
||||
void* allocate(size_t bytes, size_t alignment) { return mResource->allocate(bytes, alignment); }
|
||||
void deallocate(void* p, size_t bytes, size_t alignment) { mResource->deallocate(p, bytes, alignment); }
|
||||
|
||||
/// @brief Two refs compare equal iff they reference the same resource, i.e.
|
||||
/// memory allocated through one may be deallocated through the other.
|
||||
friend bool operator==(ResourceRef lhs, ResourceRef rhs) { return lhs.mResource == rhs.mResource; }
|
||||
friend bool operator!=(ResourceRef lhs, ResourceRef rhs) { return lhs.mResource != rhs.mResource; }
|
||||
|
||||
private:
|
||||
R* mResource;
|
||||
};// ResourceRef<R>
|
||||
|
||||
}
|
||||
|
||||
} // namespace nanovdb::cuda
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user