From 07885c13ed2b14725b6be3899f9eef7771cdb576 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 16 Sep 2026 20:55:38 +0200 Subject: [PATCH 1/7] [geom] privatize bvh headers that are only used by cxx files --- README/ReleaseNotes/v642/index.md | 1 + geom/geom/CMakeLists.txt | 1 + geom/geom/{inc => res}/bvh/v2/CMakeLists.txt | 0 geom/geom/{inc => res}/bvh/v2/README.md | 0 geom/geom/{inc => res}/bvh/v2/bbox.h | 0 geom/geom/{inc => res}/bvh/v2/binned_sah_builder.h | 0 geom/geom/{inc => res}/bvh/v2/bvh.h | 0 geom/geom/{inc => res}/bvh/v2/default_builder.h | 0 geom/geom/{inc => res}/bvh/v2/executor.h | 0 geom/geom/{inc => res}/bvh/v2/index.h | 0 geom/geom/{inc => res}/bvh/v2/mini_tree_builder.h | 0 geom/geom/{inc => res}/bvh/v2/node.h | 0 geom/geom/{inc => res}/bvh/v2/platform.h | 0 geom/geom/{inc => res}/bvh/v2/ray.h | 0 geom/geom/{inc => res}/bvh/v2/reinsertion_optimizer.h | 0 geom/geom/{inc => res}/bvh/v2/split_heuristic.h | 0 geom/geom/{inc => res}/bvh/v2/stack.h | 0 geom/geom/{inc => res}/bvh/v2/stream.h | 0 geom/geom/{inc => res}/bvh/v2/sweep_sah_builder.h | 0 geom/geom/{inc => res}/bvh/v2/thread_pool.h | 0 geom/geom/{inc => res}/bvh/v2/top_down_sah_builder.h | 0 geom/geom/{inc => res}/bvh/v2/utils.h | 0 geom/geom/{inc => res}/bvh/v2/vec.h | 0 geom/geom/{inc => res}/bvh2_extra_kernels.h | 0 geom/geom/{inc => res}/bvh2_third_party.h | 0 25 files changed, 2 insertions(+) rename geom/geom/{inc => res}/bvh/v2/CMakeLists.txt (100%) rename geom/geom/{inc => res}/bvh/v2/README.md (100%) rename geom/geom/{inc => res}/bvh/v2/bbox.h (100%) rename geom/geom/{inc => res}/bvh/v2/binned_sah_builder.h (100%) rename geom/geom/{inc => res}/bvh/v2/bvh.h (100%) rename geom/geom/{inc => res}/bvh/v2/default_builder.h (100%) rename geom/geom/{inc => res}/bvh/v2/executor.h (100%) rename geom/geom/{inc => res}/bvh/v2/index.h (100%) rename geom/geom/{inc => res}/bvh/v2/mini_tree_builder.h (100%) rename geom/geom/{inc => res}/bvh/v2/node.h (100%) rename geom/geom/{inc => res}/bvh/v2/platform.h (100%) rename geom/geom/{inc => res}/bvh/v2/ray.h (100%) rename geom/geom/{inc => res}/bvh/v2/reinsertion_optimizer.h (100%) rename geom/geom/{inc => res}/bvh/v2/split_heuristic.h (100%) rename geom/geom/{inc => res}/bvh/v2/stack.h (100%) rename geom/geom/{inc => res}/bvh/v2/stream.h (100%) rename geom/geom/{inc => res}/bvh/v2/sweep_sah_builder.h (100%) rename geom/geom/{inc => res}/bvh/v2/thread_pool.h (100%) rename geom/geom/{inc => res}/bvh/v2/top_down_sah_builder.h (100%) rename geom/geom/{inc => res}/bvh/v2/utils.h (100%) rename geom/geom/{inc => res}/bvh/v2/vec.h (100%) rename geom/geom/{inc => res}/bvh2_extra_kernels.h (100%) rename geom/geom/{inc => res}/bvh2_third_party.h (100%) diff --git a/README/ReleaseNotes/v642/index.md b/README/ReleaseNotes/v642/index.md index 7b21ef2a0075a..8ed39b1b7d139 100644 --- a/README/ReleaseNotes/v642/index.md +++ b/README/ReleaseNotes/v642/index.md @@ -80,6 +80,7 @@ The `TMVA_SOFIE_GNN` tutorials have been migrated to this workflow and produce i * The header `Rpair.h` is deprecated and will be removed after ROOT 6.44, use `` instead. * The header `Hparam.h` and `Hoption.h` are now deprecated and will be removed in ROOT 6.44, use instead `THistPainter`, `TGraph2DPainter` public interfaces. * The headers `Htypes.h` and `Gtypes.h` that were deprecated in ROOT 6.20 will now emit warnings and will be fully removed in ROOT 6.44. Use instead `Rtypes.h`. +* The headers of the `bvh` library in `Geom` are no longer public in ROOT 6.42. * The header `GLConstants.h` is no longer part of ROOT installed headers. * The header `Bswapcpy.h` is deprecated and will be removed from the public interface after ROOT 6.44, it's an implementation detail for Unix i386 GNUC used by TBufferFile. * The header `VectorizedTMath.h` is deprecated and will be removed in ROOT 6.44. Use instead `TMathVectorized.h`. diff --git a/geom/geom/CMakeLists.txt b/geom/geom/CMakeLists.txt index 71d76e74fb8a1..7bc5afd055eb6 100644 --- a/geom/geom/CMakeLists.txt +++ b/geom/geom/CMakeLists.txt @@ -130,6 +130,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(Geom Imt ) +target_include_directories(Geom PRIVATE res) # GCC has bugs with -O3 or -Ofast that break Geom if(CMAKE_COMPILER_IS_GNUCXX) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5 OR CMAKE_SIZEOF_VOID_P LESS 8) diff --git a/geom/geom/inc/bvh/v2/CMakeLists.txt b/geom/geom/res/bvh/v2/CMakeLists.txt similarity index 100% rename from geom/geom/inc/bvh/v2/CMakeLists.txt rename to geom/geom/res/bvh/v2/CMakeLists.txt diff --git a/geom/geom/inc/bvh/v2/README.md b/geom/geom/res/bvh/v2/README.md similarity index 100% rename from geom/geom/inc/bvh/v2/README.md rename to geom/geom/res/bvh/v2/README.md diff --git a/geom/geom/inc/bvh/v2/bbox.h b/geom/geom/res/bvh/v2/bbox.h similarity index 100% rename from geom/geom/inc/bvh/v2/bbox.h rename to geom/geom/res/bvh/v2/bbox.h diff --git a/geom/geom/inc/bvh/v2/binned_sah_builder.h b/geom/geom/res/bvh/v2/binned_sah_builder.h similarity index 100% rename from geom/geom/inc/bvh/v2/binned_sah_builder.h rename to geom/geom/res/bvh/v2/binned_sah_builder.h diff --git a/geom/geom/inc/bvh/v2/bvh.h b/geom/geom/res/bvh/v2/bvh.h similarity index 100% rename from geom/geom/inc/bvh/v2/bvh.h rename to geom/geom/res/bvh/v2/bvh.h diff --git a/geom/geom/inc/bvh/v2/default_builder.h b/geom/geom/res/bvh/v2/default_builder.h similarity index 100% rename from geom/geom/inc/bvh/v2/default_builder.h rename to geom/geom/res/bvh/v2/default_builder.h diff --git a/geom/geom/inc/bvh/v2/executor.h b/geom/geom/res/bvh/v2/executor.h similarity index 100% rename from geom/geom/inc/bvh/v2/executor.h rename to geom/geom/res/bvh/v2/executor.h diff --git a/geom/geom/inc/bvh/v2/index.h b/geom/geom/res/bvh/v2/index.h similarity index 100% rename from geom/geom/inc/bvh/v2/index.h rename to geom/geom/res/bvh/v2/index.h diff --git a/geom/geom/inc/bvh/v2/mini_tree_builder.h b/geom/geom/res/bvh/v2/mini_tree_builder.h similarity index 100% rename from geom/geom/inc/bvh/v2/mini_tree_builder.h rename to geom/geom/res/bvh/v2/mini_tree_builder.h diff --git a/geom/geom/inc/bvh/v2/node.h b/geom/geom/res/bvh/v2/node.h similarity index 100% rename from geom/geom/inc/bvh/v2/node.h rename to geom/geom/res/bvh/v2/node.h diff --git a/geom/geom/inc/bvh/v2/platform.h b/geom/geom/res/bvh/v2/platform.h similarity index 100% rename from geom/geom/inc/bvh/v2/platform.h rename to geom/geom/res/bvh/v2/platform.h diff --git a/geom/geom/inc/bvh/v2/ray.h b/geom/geom/res/bvh/v2/ray.h similarity index 100% rename from geom/geom/inc/bvh/v2/ray.h rename to geom/geom/res/bvh/v2/ray.h diff --git a/geom/geom/inc/bvh/v2/reinsertion_optimizer.h b/geom/geom/res/bvh/v2/reinsertion_optimizer.h similarity index 100% rename from geom/geom/inc/bvh/v2/reinsertion_optimizer.h rename to geom/geom/res/bvh/v2/reinsertion_optimizer.h diff --git a/geom/geom/inc/bvh/v2/split_heuristic.h b/geom/geom/res/bvh/v2/split_heuristic.h similarity index 100% rename from geom/geom/inc/bvh/v2/split_heuristic.h rename to geom/geom/res/bvh/v2/split_heuristic.h diff --git a/geom/geom/inc/bvh/v2/stack.h b/geom/geom/res/bvh/v2/stack.h similarity index 100% rename from geom/geom/inc/bvh/v2/stack.h rename to geom/geom/res/bvh/v2/stack.h diff --git a/geom/geom/inc/bvh/v2/stream.h b/geom/geom/res/bvh/v2/stream.h similarity index 100% rename from geom/geom/inc/bvh/v2/stream.h rename to geom/geom/res/bvh/v2/stream.h diff --git a/geom/geom/inc/bvh/v2/sweep_sah_builder.h b/geom/geom/res/bvh/v2/sweep_sah_builder.h similarity index 100% rename from geom/geom/inc/bvh/v2/sweep_sah_builder.h rename to geom/geom/res/bvh/v2/sweep_sah_builder.h diff --git a/geom/geom/inc/bvh/v2/thread_pool.h b/geom/geom/res/bvh/v2/thread_pool.h similarity index 100% rename from geom/geom/inc/bvh/v2/thread_pool.h rename to geom/geom/res/bvh/v2/thread_pool.h diff --git a/geom/geom/inc/bvh/v2/top_down_sah_builder.h b/geom/geom/res/bvh/v2/top_down_sah_builder.h similarity index 100% rename from geom/geom/inc/bvh/v2/top_down_sah_builder.h rename to geom/geom/res/bvh/v2/top_down_sah_builder.h diff --git a/geom/geom/inc/bvh/v2/utils.h b/geom/geom/res/bvh/v2/utils.h similarity index 100% rename from geom/geom/inc/bvh/v2/utils.h rename to geom/geom/res/bvh/v2/utils.h diff --git a/geom/geom/inc/bvh/v2/vec.h b/geom/geom/res/bvh/v2/vec.h similarity index 100% rename from geom/geom/inc/bvh/v2/vec.h rename to geom/geom/res/bvh/v2/vec.h diff --git a/geom/geom/inc/bvh2_extra_kernels.h b/geom/geom/res/bvh2_extra_kernels.h similarity index 100% rename from geom/geom/inc/bvh2_extra_kernels.h rename to geom/geom/res/bvh2_extra_kernels.h diff --git a/geom/geom/inc/bvh2_third_party.h b/geom/geom/res/bvh2_third_party.h similarity index 100% rename from geom/geom/inc/bvh2_third_party.h rename to geom/geom/res/bvh2_third_party.h From 642d24c79c9da2f83639f2548b7d6c7bd852c57d Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 16 Sep 2026 23:32:24 +0200 Subject: [PATCH 2/7] [bvh] mv to builtins folder It was only very minorly modified for ROOT --- {geom/geom/res => builtins}/bvh/v2/CMakeLists.txt | 0 {geom/geom/res => builtins}/bvh/v2/README.md | 0 {geom/geom/res => builtins}/bvh/v2/bbox.h | 0 {geom/geom/res => builtins}/bvh/v2/binned_sah_builder.h | 0 {geom/geom/res => builtins}/bvh/v2/bvh.h | 0 {geom/geom/res => builtins}/bvh/v2/default_builder.h | 0 {geom/geom/res => builtins}/bvh/v2/executor.h | 0 {geom/geom/res => builtins}/bvh/v2/index.h | 0 {geom/geom/res => builtins}/bvh/v2/mini_tree_builder.h | 0 {geom/geom/res => builtins}/bvh/v2/node.h | 0 {geom/geom/res => builtins}/bvh/v2/platform.h | 0 {geom/geom/res => builtins}/bvh/v2/ray.h | 0 {geom/geom/res => builtins}/bvh/v2/reinsertion_optimizer.h | 0 {geom/geom/res => builtins}/bvh/v2/split_heuristic.h | 0 {geom/geom/res => builtins}/bvh/v2/stack.h | 0 {geom/geom/res => builtins}/bvh/v2/stream.h | 0 {geom/geom/res => builtins}/bvh/v2/sweep_sah_builder.h | 0 {geom/geom/res => builtins}/bvh/v2/thread_pool.h | 0 {geom/geom/res => builtins}/bvh/v2/top_down_sah_builder.h | 0 {geom/geom/res => builtins}/bvh/v2/utils.h | 0 {geom/geom/res => builtins}/bvh/v2/vec.h | 0 geom/geom/CMakeLists.txt | 4 +++- 22 files changed, 3 insertions(+), 1 deletion(-) rename {geom/geom/res => builtins}/bvh/v2/CMakeLists.txt (100%) rename {geom/geom/res => builtins}/bvh/v2/README.md (100%) rename {geom/geom/res => builtins}/bvh/v2/bbox.h (100%) rename {geom/geom/res => builtins}/bvh/v2/binned_sah_builder.h (100%) rename {geom/geom/res => builtins}/bvh/v2/bvh.h (100%) rename {geom/geom/res => builtins}/bvh/v2/default_builder.h (100%) rename {geom/geom/res => builtins}/bvh/v2/executor.h (100%) rename {geom/geom/res => builtins}/bvh/v2/index.h (100%) rename {geom/geom/res => builtins}/bvh/v2/mini_tree_builder.h (100%) rename {geom/geom/res => builtins}/bvh/v2/node.h (100%) rename {geom/geom/res => builtins}/bvh/v2/platform.h (100%) rename {geom/geom/res => builtins}/bvh/v2/ray.h (100%) rename {geom/geom/res => builtins}/bvh/v2/reinsertion_optimizer.h (100%) rename {geom/geom/res => builtins}/bvh/v2/split_heuristic.h (100%) rename {geom/geom/res => builtins}/bvh/v2/stack.h (100%) rename {geom/geom/res => builtins}/bvh/v2/stream.h (100%) rename {geom/geom/res => builtins}/bvh/v2/sweep_sah_builder.h (100%) rename {geom/geom/res => builtins}/bvh/v2/thread_pool.h (100%) rename {geom/geom/res => builtins}/bvh/v2/top_down_sah_builder.h (100%) rename {geom/geom/res => builtins}/bvh/v2/utils.h (100%) rename {geom/geom/res => builtins}/bvh/v2/vec.h (100%) diff --git a/geom/geom/res/bvh/v2/CMakeLists.txt b/builtins/bvh/v2/CMakeLists.txt similarity index 100% rename from geom/geom/res/bvh/v2/CMakeLists.txt rename to builtins/bvh/v2/CMakeLists.txt diff --git a/geom/geom/res/bvh/v2/README.md b/builtins/bvh/v2/README.md similarity index 100% rename from geom/geom/res/bvh/v2/README.md rename to builtins/bvh/v2/README.md diff --git a/geom/geom/res/bvh/v2/bbox.h b/builtins/bvh/v2/bbox.h similarity index 100% rename from geom/geom/res/bvh/v2/bbox.h rename to builtins/bvh/v2/bbox.h diff --git a/geom/geom/res/bvh/v2/binned_sah_builder.h b/builtins/bvh/v2/binned_sah_builder.h similarity index 100% rename from geom/geom/res/bvh/v2/binned_sah_builder.h rename to builtins/bvh/v2/binned_sah_builder.h diff --git a/geom/geom/res/bvh/v2/bvh.h b/builtins/bvh/v2/bvh.h similarity index 100% rename from geom/geom/res/bvh/v2/bvh.h rename to builtins/bvh/v2/bvh.h diff --git a/geom/geom/res/bvh/v2/default_builder.h b/builtins/bvh/v2/default_builder.h similarity index 100% rename from geom/geom/res/bvh/v2/default_builder.h rename to builtins/bvh/v2/default_builder.h diff --git a/geom/geom/res/bvh/v2/executor.h b/builtins/bvh/v2/executor.h similarity index 100% rename from geom/geom/res/bvh/v2/executor.h rename to builtins/bvh/v2/executor.h diff --git a/geom/geom/res/bvh/v2/index.h b/builtins/bvh/v2/index.h similarity index 100% rename from geom/geom/res/bvh/v2/index.h rename to builtins/bvh/v2/index.h diff --git a/geom/geom/res/bvh/v2/mini_tree_builder.h b/builtins/bvh/v2/mini_tree_builder.h similarity index 100% rename from geom/geom/res/bvh/v2/mini_tree_builder.h rename to builtins/bvh/v2/mini_tree_builder.h diff --git a/geom/geom/res/bvh/v2/node.h b/builtins/bvh/v2/node.h similarity index 100% rename from geom/geom/res/bvh/v2/node.h rename to builtins/bvh/v2/node.h diff --git a/geom/geom/res/bvh/v2/platform.h b/builtins/bvh/v2/platform.h similarity index 100% rename from geom/geom/res/bvh/v2/platform.h rename to builtins/bvh/v2/platform.h diff --git a/geom/geom/res/bvh/v2/ray.h b/builtins/bvh/v2/ray.h similarity index 100% rename from geom/geom/res/bvh/v2/ray.h rename to builtins/bvh/v2/ray.h diff --git a/geom/geom/res/bvh/v2/reinsertion_optimizer.h b/builtins/bvh/v2/reinsertion_optimizer.h similarity index 100% rename from geom/geom/res/bvh/v2/reinsertion_optimizer.h rename to builtins/bvh/v2/reinsertion_optimizer.h diff --git a/geom/geom/res/bvh/v2/split_heuristic.h b/builtins/bvh/v2/split_heuristic.h similarity index 100% rename from geom/geom/res/bvh/v2/split_heuristic.h rename to builtins/bvh/v2/split_heuristic.h diff --git a/geom/geom/res/bvh/v2/stack.h b/builtins/bvh/v2/stack.h similarity index 100% rename from geom/geom/res/bvh/v2/stack.h rename to builtins/bvh/v2/stack.h diff --git a/geom/geom/res/bvh/v2/stream.h b/builtins/bvh/v2/stream.h similarity index 100% rename from geom/geom/res/bvh/v2/stream.h rename to builtins/bvh/v2/stream.h diff --git a/geom/geom/res/bvh/v2/sweep_sah_builder.h b/builtins/bvh/v2/sweep_sah_builder.h similarity index 100% rename from geom/geom/res/bvh/v2/sweep_sah_builder.h rename to builtins/bvh/v2/sweep_sah_builder.h diff --git a/geom/geom/res/bvh/v2/thread_pool.h b/builtins/bvh/v2/thread_pool.h similarity index 100% rename from geom/geom/res/bvh/v2/thread_pool.h rename to builtins/bvh/v2/thread_pool.h diff --git a/geom/geom/res/bvh/v2/top_down_sah_builder.h b/builtins/bvh/v2/top_down_sah_builder.h similarity index 100% rename from geom/geom/res/bvh/v2/top_down_sah_builder.h rename to builtins/bvh/v2/top_down_sah_builder.h diff --git a/geom/geom/res/bvh/v2/utils.h b/builtins/bvh/v2/utils.h similarity index 100% rename from geom/geom/res/bvh/v2/utils.h rename to builtins/bvh/v2/utils.h diff --git a/geom/geom/res/bvh/v2/vec.h b/builtins/bvh/v2/vec.h similarity index 100% rename from geom/geom/res/bvh/v2/vec.h rename to builtins/bvh/v2/vec.h diff --git a/geom/geom/CMakeLists.txt b/geom/geom/CMakeLists.txt index 7bc5afd055eb6..a405c1a2f36ae 100644 --- a/geom/geom/CMakeLists.txt +++ b/geom/geom/CMakeLists.txt @@ -130,7 +130,9 @@ ROOT_STANDARD_LIBRARY_PACKAGE(Geom Imt ) -target_include_directories(Geom PRIVATE res) +target_include_directories(Geom PRIVATE res +../../builtins/ # for bvh hard-coded builtin, forked from https://github.com/madmann91/bvh/commit/66e445b92f68801a6dd8ef943fe3038976ecb4ff +) # GCC has bugs with -O3 or -Ofast that break Geom if(CMAKE_COMPILER_IS_GNUCXX) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5 OR CMAKE_SIZEOF_VOID_P LESS 8) From 3c98f1c35c0389831dfa3ffe581757eb60a604f3 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 16 Sep 2026 23:34:39 +0200 Subject: [PATCH 3/7] [bvh] add license file --- builtins/bvh/LICENSE.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 builtins/bvh/LICENSE.txt diff --git a/builtins/bvh/LICENSE.txt b/builtins/bvh/LICENSE.txt new file mode 100644 index 0000000000000..8fe2274ab8872 --- /dev/null +++ b/builtins/bvh/LICENSE.txt @@ -0,0 +1,16 @@ +Copyright 2022 Arsène Pérard-Gayot + +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. From 78299645794e7221c16766bc3f334be30cadb6bd Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 16 Sep 2026 23:36:38 +0200 Subject: [PATCH 4/7] [cmake] comment bvh builtin roadmap --- geom/geom/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/geom/geom/CMakeLists.txt b/geom/geom/CMakeLists.txt index a405c1a2f36ae..8d186eb24b20d 100644 --- a/geom/geom/CMakeLists.txt +++ b/geom/geom/CMakeLists.txt @@ -132,6 +132,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(Geom target_include_directories(Geom PRIVATE res ../../builtins/ # for bvh hard-coded builtin, forked from https://github.com/madmann91/bvh/commit/66e445b92f68801a6dd8ef943fe3038976ecb4ff +# TODO: move this to a proper isolated builtin with find_package, and resync with upstream version, add ROOT builtin_bvh option ) # GCC has bugs with -O3 or -Ofast that break Geom if(CMAKE_COMPILER_IS_GNUCXX) From 0c9c493b897d6892251fdfe3c0285d492b716ac6 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 23 Sep 2026 15:25:58 +0200 Subject: [PATCH 5/7] [bvh] mv to src subfolder to keep structure --- builtins/bvh/{ => src/bvh}/v2/CMakeLists.txt | 0 builtins/bvh/{ => src/bvh}/v2/README.md | 0 builtins/bvh/{ => src/bvh}/v2/bbox.h | 0 builtins/bvh/{ => src/bvh}/v2/binned_sah_builder.h | 0 builtins/bvh/{ => src/bvh}/v2/bvh.h | 0 builtins/bvh/{ => src/bvh}/v2/default_builder.h | 0 builtins/bvh/{ => src/bvh}/v2/executor.h | 0 builtins/bvh/{ => src/bvh}/v2/index.h | 0 builtins/bvh/{ => src/bvh}/v2/mini_tree_builder.h | 0 builtins/bvh/{ => src/bvh}/v2/node.h | 0 builtins/bvh/{ => src/bvh}/v2/platform.h | 0 builtins/bvh/{ => src/bvh}/v2/ray.h | 0 builtins/bvh/{ => src/bvh}/v2/reinsertion_optimizer.h | 0 builtins/bvh/{ => src/bvh}/v2/split_heuristic.h | 0 builtins/bvh/{ => src/bvh}/v2/stack.h | 0 builtins/bvh/{ => src/bvh}/v2/stream.h | 0 builtins/bvh/{ => src/bvh}/v2/sweep_sah_builder.h | 0 builtins/bvh/{ => src/bvh}/v2/thread_pool.h | 0 builtins/bvh/{ => src/bvh}/v2/top_down_sah_builder.h | 0 builtins/bvh/{ => src/bvh}/v2/utils.h | 0 builtins/bvh/{ => src/bvh}/v2/vec.h | 0 geom/geom/CMakeLists.txt | 4 ++-- 22 files changed, 2 insertions(+), 2 deletions(-) rename builtins/bvh/{ => src/bvh}/v2/CMakeLists.txt (100%) rename builtins/bvh/{ => src/bvh}/v2/README.md (100%) rename builtins/bvh/{ => src/bvh}/v2/bbox.h (100%) rename builtins/bvh/{ => src/bvh}/v2/binned_sah_builder.h (100%) rename builtins/bvh/{ => src/bvh}/v2/bvh.h (100%) rename builtins/bvh/{ => src/bvh}/v2/default_builder.h (100%) rename builtins/bvh/{ => src/bvh}/v2/executor.h (100%) rename builtins/bvh/{ => src/bvh}/v2/index.h (100%) rename builtins/bvh/{ => src/bvh}/v2/mini_tree_builder.h (100%) rename builtins/bvh/{ => src/bvh}/v2/node.h (100%) rename builtins/bvh/{ => src/bvh}/v2/platform.h (100%) rename builtins/bvh/{ => src/bvh}/v2/ray.h (100%) rename builtins/bvh/{ => src/bvh}/v2/reinsertion_optimizer.h (100%) rename builtins/bvh/{ => src/bvh}/v2/split_heuristic.h (100%) rename builtins/bvh/{ => src/bvh}/v2/stack.h (100%) rename builtins/bvh/{ => src/bvh}/v2/stream.h (100%) rename builtins/bvh/{ => src/bvh}/v2/sweep_sah_builder.h (100%) rename builtins/bvh/{ => src/bvh}/v2/thread_pool.h (100%) rename builtins/bvh/{ => src/bvh}/v2/top_down_sah_builder.h (100%) rename builtins/bvh/{ => src/bvh}/v2/utils.h (100%) rename builtins/bvh/{ => src/bvh}/v2/vec.h (100%) diff --git a/builtins/bvh/v2/CMakeLists.txt b/builtins/bvh/src/bvh/v2/CMakeLists.txt similarity index 100% rename from builtins/bvh/v2/CMakeLists.txt rename to builtins/bvh/src/bvh/v2/CMakeLists.txt diff --git a/builtins/bvh/v2/README.md b/builtins/bvh/src/bvh/v2/README.md similarity index 100% rename from builtins/bvh/v2/README.md rename to builtins/bvh/src/bvh/v2/README.md diff --git a/builtins/bvh/v2/bbox.h b/builtins/bvh/src/bvh/v2/bbox.h similarity index 100% rename from builtins/bvh/v2/bbox.h rename to builtins/bvh/src/bvh/v2/bbox.h diff --git a/builtins/bvh/v2/binned_sah_builder.h b/builtins/bvh/src/bvh/v2/binned_sah_builder.h similarity index 100% rename from builtins/bvh/v2/binned_sah_builder.h rename to builtins/bvh/src/bvh/v2/binned_sah_builder.h diff --git a/builtins/bvh/v2/bvh.h b/builtins/bvh/src/bvh/v2/bvh.h similarity index 100% rename from builtins/bvh/v2/bvh.h rename to builtins/bvh/src/bvh/v2/bvh.h diff --git a/builtins/bvh/v2/default_builder.h b/builtins/bvh/src/bvh/v2/default_builder.h similarity index 100% rename from builtins/bvh/v2/default_builder.h rename to builtins/bvh/src/bvh/v2/default_builder.h diff --git a/builtins/bvh/v2/executor.h b/builtins/bvh/src/bvh/v2/executor.h similarity index 100% rename from builtins/bvh/v2/executor.h rename to builtins/bvh/src/bvh/v2/executor.h diff --git a/builtins/bvh/v2/index.h b/builtins/bvh/src/bvh/v2/index.h similarity index 100% rename from builtins/bvh/v2/index.h rename to builtins/bvh/src/bvh/v2/index.h diff --git a/builtins/bvh/v2/mini_tree_builder.h b/builtins/bvh/src/bvh/v2/mini_tree_builder.h similarity index 100% rename from builtins/bvh/v2/mini_tree_builder.h rename to builtins/bvh/src/bvh/v2/mini_tree_builder.h diff --git a/builtins/bvh/v2/node.h b/builtins/bvh/src/bvh/v2/node.h similarity index 100% rename from builtins/bvh/v2/node.h rename to builtins/bvh/src/bvh/v2/node.h diff --git a/builtins/bvh/v2/platform.h b/builtins/bvh/src/bvh/v2/platform.h similarity index 100% rename from builtins/bvh/v2/platform.h rename to builtins/bvh/src/bvh/v2/platform.h diff --git a/builtins/bvh/v2/ray.h b/builtins/bvh/src/bvh/v2/ray.h similarity index 100% rename from builtins/bvh/v2/ray.h rename to builtins/bvh/src/bvh/v2/ray.h diff --git a/builtins/bvh/v2/reinsertion_optimizer.h b/builtins/bvh/src/bvh/v2/reinsertion_optimizer.h similarity index 100% rename from builtins/bvh/v2/reinsertion_optimizer.h rename to builtins/bvh/src/bvh/v2/reinsertion_optimizer.h diff --git a/builtins/bvh/v2/split_heuristic.h b/builtins/bvh/src/bvh/v2/split_heuristic.h similarity index 100% rename from builtins/bvh/v2/split_heuristic.h rename to builtins/bvh/src/bvh/v2/split_heuristic.h diff --git a/builtins/bvh/v2/stack.h b/builtins/bvh/src/bvh/v2/stack.h similarity index 100% rename from builtins/bvh/v2/stack.h rename to builtins/bvh/src/bvh/v2/stack.h diff --git a/builtins/bvh/v2/stream.h b/builtins/bvh/src/bvh/v2/stream.h similarity index 100% rename from builtins/bvh/v2/stream.h rename to builtins/bvh/src/bvh/v2/stream.h diff --git a/builtins/bvh/v2/sweep_sah_builder.h b/builtins/bvh/src/bvh/v2/sweep_sah_builder.h similarity index 100% rename from builtins/bvh/v2/sweep_sah_builder.h rename to builtins/bvh/src/bvh/v2/sweep_sah_builder.h diff --git a/builtins/bvh/v2/thread_pool.h b/builtins/bvh/src/bvh/v2/thread_pool.h similarity index 100% rename from builtins/bvh/v2/thread_pool.h rename to builtins/bvh/src/bvh/v2/thread_pool.h diff --git a/builtins/bvh/v2/top_down_sah_builder.h b/builtins/bvh/src/bvh/v2/top_down_sah_builder.h similarity index 100% rename from builtins/bvh/v2/top_down_sah_builder.h rename to builtins/bvh/src/bvh/v2/top_down_sah_builder.h diff --git a/builtins/bvh/v2/utils.h b/builtins/bvh/src/bvh/v2/utils.h similarity index 100% rename from builtins/bvh/v2/utils.h rename to builtins/bvh/src/bvh/v2/utils.h diff --git a/builtins/bvh/v2/vec.h b/builtins/bvh/src/bvh/v2/vec.h similarity index 100% rename from builtins/bvh/v2/vec.h rename to builtins/bvh/src/bvh/v2/vec.h diff --git a/geom/geom/CMakeLists.txt b/geom/geom/CMakeLists.txt index 8d186eb24b20d..ae7ca776821a1 100644 --- a/geom/geom/CMakeLists.txt +++ b/geom/geom/CMakeLists.txt @@ -131,8 +131,8 @@ ROOT_STANDARD_LIBRARY_PACKAGE(Geom ) target_include_directories(Geom PRIVATE res -../../builtins/ # for bvh hard-coded builtin, forked from https://github.com/madmann91/bvh/commit/66e445b92f68801a6dd8ef943fe3038976ecb4ff -# TODO: move this to a proper isolated builtin with find_package, and resync with upstream version, add ROOT builtin_bvh option +../../builtins/bvh/src # for bvh hard-coded builtin, forked from https://github.com/madmann91/bvh/commit/66e445b92f68801a6dd8ef943fe3038976ecb4ff +# TODO: move this to a proper isolated builtin with find_package, and add ROOT builtin_bvh option ) # GCC has bugs with -O3 or -Ofast that break Geom if(CMAKE_COMPILER_IS_GNUCXX) From 5503b869cb0db661262399638a8e87763e2dded2 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 23 Sep 2026 15:56:53 +0200 Subject: [PATCH 6/7] [bvh] resync with upstream commit b715167 --- builtins/bvh/CMakeLists.txt | 21 ++ builtins/bvh/README.md | 83 ++++ builtins/bvh/cmake/Install.cmake | 44 +++ builtins/bvh/cmake/bvh-config.cmake.in | 5 + builtins/bvh/src/bvh/v2/CMakeLists.txt | 6 - builtins/bvh/src/bvh/v2/binned_sah_builder.h | 2 +- builtins/bvh/src/bvh/v2/bvh.h | 50 ++- builtins/bvh/src/bvh/v2/c_api/CMakeLists.txt | 19 + builtins/bvh/src/bvh/v2/c_api/bvh.cpp | 28 ++ builtins/bvh/src/bvh/v2/c_api/bvh.h | 301 +++++++++++++++ builtins/bvh/src/bvh/v2/c_api/bvh_impl.h | 357 ++++++++++++++++++ builtins/bvh/src/bvh/v2/default_builder.h | 4 +- builtins/bvh/src/bvh/v2/executor.h | 22 +- builtins/bvh/src/bvh/v2/index.h | 14 +- builtins/bvh/src/bvh/v2/mini_tree_builder.h | 7 +- builtins/bvh/src/bvh/v2/node.h | 16 +- .../bvh/src/bvh/v2/reinsertion_optimizer.h | 2 +- builtins/bvh/src/bvh/v2/sphere.h | 54 +++ builtins/bvh/src/bvh/v2/stack.h | 4 - builtins/bvh/src/bvh/v2/stream.h | 5 +- builtins/bvh/src/bvh/v2/sweep_sah_builder.h | 2 +- builtins/bvh/src/bvh/v2/thread_pool.h | 91 ++++- .../bvh/src/bvh/v2/top_down_sah_builder.h | 7 +- builtins/bvh/src/bvh/v2/tri.h | 78 ++++ builtins/bvh/src/bvh/v2/vec.h | 5 +- 25 files changed, 1128 insertions(+), 99 deletions(-) create mode 100644 builtins/bvh/CMakeLists.txt create mode 100644 builtins/bvh/README.md create mode 100644 builtins/bvh/cmake/Install.cmake create mode 100644 builtins/bvh/cmake/bvh-config.cmake.in create mode 100644 builtins/bvh/src/bvh/v2/c_api/CMakeLists.txt create mode 100644 builtins/bvh/src/bvh/v2/c_api/bvh.cpp create mode 100644 builtins/bvh/src/bvh/v2/c_api/bvh.h create mode 100644 builtins/bvh/src/bvh/v2/c_api/bvh_impl.h create mode 100644 builtins/bvh/src/bvh/v2/sphere.h create mode 100644 builtins/bvh/src/bvh/v2/tri.h diff --git a/builtins/bvh/CMakeLists.txt b/builtins/bvh/CMakeLists.txt new file mode 100644 index 0000000000000..d6811a05b398b --- /dev/null +++ b/builtins/bvh/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.21) +project(bvh VERSION 2.0) + +option(BVH_BUILD_C_API "Builds the C API library wrapper" OFF) +option(BVH_C_API_STATIC_LINK_STDLIB "Link the C API library statically against the standard C++ library (only supported by clang/gcc)" OFF) +option(BVH_C_API_UNSAFE_CASTS "Enable unsafe casts in C API" OFF) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src/bvh/v2) + +if (PROJECT_IS_TOP_LEVEL) + include(CTest) + if (BUILD_TESTING) + add_subdirectory(test) + endif() + + include(cmake/Install.cmake) +endif() diff --git a/builtins/bvh/README.md b/builtins/bvh/README.md new file mode 100644 index 0000000000000..635a6832dcdf3 --- /dev/null +++ b/builtins/bvh/README.md @@ -0,0 +1,83 @@ +# BVH Construction and Traversal Library + +![Build Status](https://github.com/madmann91/bvh/workflows/build-and-test/badge.svg) + +> Note: This is the 2nd version of this library. Check the `v1` branch for the older, first version +> of this library. + +This library is a small, standalone library for BVH construction and traversal. It is licensed +under the MIT license. + +![Example rendering generated by a path tracer using this library](render.jpg) +(Scene by Blend Swap user MaTTeSr, available [here](https://www.blendswap.com/blend/18762), +distributed under CC-BY 3.0) + +## Performance + +Here is a comparison of this library with other alternatives +([Embree](https://github.com/embree/embree), +[Fast-BVH](https://github.com/brandonpelfrey/Fast-BVH), and +[nanort](https://github.com/lighttransport/nanort)): + +![Performance comparison with Embree, nanort, and Fast-BVH](chart.png) + +## Features + +Here is a list of features supported by this library (changes from `v1` are indicated with [NEW]): + +- [NEW] C++20 interface using `std::span` instead of raw pointers, +- Low-level API with direct access to various builders, +- [NEW] High-level `DefaultBuilder` API which selects the best builder depending on the desired + BVH quality level. +- High-quality, single-threaded sweeping SAH builder, +- Fast, medium-quality, single-threaded binned SAH builder inspired by + "On Fast Construction of SAH-based Bounding Volume Hierarchies", by I. Wald, +- Fast, high-quality, multithreaded mini-tree BVH builder inspired by + "Rapid Bounding Volume Hierarchy Generation using Mini Trees", by P. Ganestam et al., +- Reinsertion optimizer based on "Parallel Reinsertion for Bounding Volume Hierarchy + Optimization", by D. Meister and J. Bittner, +- Fast and robust traversal algorithm using "Robust BVH Ray Traversal", by T. Ize. +- Fast ray-triangle intersection algorithm based on + "Fast, Minimum Storage Ray/Triangle Intersection", by T. Möller and B. Trumbore, +- [NEW] Surface area traversal order heuristic for shadow rays based on + "SATO: Surface Area Traversal Order for Shadow Ray Tracing", by J. Nah and D. Manocha, +- Fast ray-sphere intersection routine, +- [NEW] Serialization/deserialization interface, +- [NEW] Variable amount of dimensions (e.g. 2D, 3D, 4D BVHs are supported) and different scalar types + (e.g. `float` or `double`), +- [NEW] Only depends on the standard library (parallelization uses a custom thread pool based on + `std::thread`), +- [NEW] C API for the high-level parts of the library is available. + +## Building + +This library is header-only, and can be added as a CMake subproject by cloning or adding as this +repository as submodule, for instance in `/contrib/bvh`, and then adding this to +`/CMakeLists.txt`: + + add_subdirectory(contrib/bvh) + target_link_library(my_project PUBLIC bvh) + +If you want to build the examples, use: + + mkdir build + cd build + cmake .. -DCMAKE_BUILD_TYPE= -DENABLE_TESTING=ON + cmake --build . + +## C API + +The library can be used via a small set of high-level C bindings. These bindings are not enabled by +default, but can be built by configuring CMake with `-DBVH_BUILD_C_API=ON`. Additionally, if the +intent is to use the library in a pure C environment which does not have the C++ standard library as +a dependency, it might be a good idea to statically the C++ standard library. That can be done by +adding the flag `-DBVH_STATIC_LINK_STDLIB_C_API=ON` to the CMake command line. + +## Usage + +The library contains several examples that are kept up-to-date with the API: + +- A [basic example](test/simple_example.cpp) that traces one ray on a scene made of a couple of triangles, +- A [benchmarking utility](test/benchmark.cpp) that showcases what the library can do. +- A [serialization test](test/serialize.cpp) that shows how to save and load a BVH from a file. +- A [C API example](test/c_api_example.c) that shows how to use the C bindings to this library. diff --git a/builtins/bvh/cmake/Install.cmake b/builtins/bvh/cmake/Install.cmake new file mode 100644 index 0000000000000..e3ecd3f3ff63e --- /dev/null +++ b/builtins/bvh/cmake/Install.cmake @@ -0,0 +1,44 @@ +include(GNUInstallDirs) +set(bvh_targets bvh) +if (BVH_BUILD_C_API) + list(APPEND bvh_targets bvh_c) +endif() + +install( + DIRECTORY ${PROJECT_SOURCE_DIR}/src/bvh + DESTINATION include + FILES_MATCHING PATTERN "*.h" + PATTERN "c_api" EXCLUDE) +install( + FILES ${PROJECT_SOURCE_DIR}/src/bvh/v2/c_api/bvh.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/bvh/v2/c_api/) +install( + TARGETS ${bvh_targets} + EXPORT bvh_exports + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install( + EXPORT bvh_exports + FILE bvh-targets.cmake + NAMESPACE bvh::v2:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/bvh/v2/) + +include(CMakePackageConfigHelpers) +set(CMAKE_INSTALL_CMAKEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/bvh/v2/) + +configure_package_config_file( + "${PROJECT_SOURCE_DIR}/cmake/bvh-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/bvh-config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_CMAKEDIR}) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/bvh-config-version.cmake" + COMPATIBILITY AnyNewerVersion) + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/bvh-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/bvh-config-version.cmake" + DESTINATION ${CMAKE_INSTALL_CMAKEDIR}) diff --git a/builtins/bvh/cmake/bvh-config.cmake.in b/builtins/bvh/cmake/bvh-config.cmake.in new file mode 100644 index 0000000000000..7aa073b09dbd8 --- /dev/null +++ b/builtins/bvh/cmake/bvh-config.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/bvh-targets.cmake") + +check_required_components(bvh_c) diff --git a/builtins/bvh/src/bvh/v2/CMakeLists.txt b/builtins/bvh/src/bvh/v2/CMakeLists.txt index 8bbfd345c3a69..495633a3c854c 100644 --- a/builtins/bvh/src/bvh/v2/CMakeLists.txt +++ b/builtins/bvh/src/bvh/v2/CMakeLists.txt @@ -12,12 +12,6 @@ target_include_directories(bvh INTERFACE set_target_properties(bvh PROPERTIES CXX_STANDARD 20) -install( - DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ - DESTINATION include/bvh/v2 - FILES_MATCHING PATTERN "*.h" - PATTERN "c_api" EXCLUDE) - if (BVH_BUILD_C_API) add_subdirectory(c_api) endif() diff --git a/builtins/bvh/src/bvh/v2/binned_sah_builder.h b/builtins/bvh/src/bvh/v2/binned_sah_builder.h index 8b5b1301b0f8e..1d7d8c066f61a 100644 --- a/builtins/bvh/src/bvh/v2/binned_sah_builder.h +++ b/builtins/bvh/src/bvh/v2/binned_sah_builder.h @@ -29,7 +29,7 @@ class BinnedSahBuilder : public TopDownSahBuilder { public: using typename TopDownSahBuilder::Config; - BVH_ALWAYS_INLINE static Bvh build( + [[nodiscard]] BVH_ALWAYS_INLINE static Bvh build( std::span bboxes, std::span centers, const Config& config = {}) diff --git a/builtins/bvh/src/bvh/v2/bvh.h b/builtins/bvh/src/bvh/v2/bvh.h index 1c4f04ecda9b3..c5724d7d7f394 100644 --- a/builtins/bvh/src/bvh/v2/bvh.h +++ b/builtins/bvh/src/bvh/v2/bvh.h @@ -27,14 +27,8 @@ struct Bvh { Bvh& operator = (Bvh&&) = default; - //bool operator == (const Bvh& other) const = default; - //bool operator != (const Bvh& other) const = default; - bool operator == (const Bvh& other) const { - return other.nodes == nodes && other.prim_ids == prim_ids; - } - bool operator != (const Bvh& other) const { - return other.nodes != nodes || other.prim_ids != prim_ids; - } + bool operator == (const Bvh& other) const = default; + bool operator != (const Bvh& other) const = default; /// Returns whether the node located at the given index is the left child of its parent. static BVH_ALWAYS_INLINE bool is_left_sibling(size_t node_id) { return node_id % 2 == 1; } @@ -60,11 +54,11 @@ struct Bvh { BVH_ALWAYS_INLINE const Node& get_root() const { return nodes[0]; } /// Extracts the BVH rooted at the given node index. - inline Bvh extract_bvh(size_t root_id) const; + [[nodiscard]] inline Bvh extract_bvh(size_t root_id) const; /// Traverses the BVH from the given index in `start` using the provided stack. Every leaf /// encountered on the way is processed using the given `LeafFn` function, and every pair of - /// nodes is processed with the function in `HitFn`, which returns a triplet of booleans + /// nodes is processed with the function in `InnerFn`, which returns a triplet of booleans /// indicating whether the first child should be processed, whether the second child should be /// processed, and whether to traverse the second child first instead of the other way around. template @@ -87,8 +81,11 @@ struct Bvh { template inline void refit(LeafFn&& = {}); + template inline void serialize(OutputStream&) const; - static inline Bvh deserialize(InputStream&); + + template + [[nodiscard]] static inline Bvh deserialize(InputStream&); }; template @@ -146,12 +143,10 @@ void Bvh::traverse_top_down(Index start, Stack& stack, LeafFn&& leaf_fn, I stack.push(far_index); } top = near_index; - } else if (hit_right) { + } else if (hit_right) top = right.index; - } - else [[unlikely]] { + else [[unlikely]] goto restart; - } } [[maybe_unused]] auto was_hit = leaf_fn(top.first_id(), top.first_id() + top.prim_count()); @@ -165,18 +160,19 @@ template template void Bvh::intersect(const Ray& ray, Index start, Stack& stack, LeafFn&& leaf_fn, InnerFn&& inner_fn) const { auto inv_dir = ray.template get_inv_dir(); - auto inv_dir_pad_or_inv_org = IsRobust ? ray.pad_inv_dir(inv_dir) : -inv_dir * ray.org; + auto inv_org = -inv_dir * ray.org; + auto inv_dir_pad = ray.pad_inv_dir(inv_dir); auto octant = ray.get_octant(); traverse_top_down(start, stack, leaf_fn, [&] (const Node& left, const Node& right) { inner_fn(left, right); std::pair intr_left, intr_right; if constexpr (IsRobust) { - intr_left = left .intersect_robust(ray, inv_dir, inv_dir_pad_or_inv_org, octant); - intr_right = right.intersect_robust(ray, inv_dir, inv_dir_pad_or_inv_org, octant); + intr_left = left .intersect_robust(ray, inv_dir, inv_dir_pad, octant); + intr_right = right.intersect_robust(ray, inv_dir, inv_dir_pad, octant); } else { - intr_left = left .intersect_fast(ray, inv_dir, inv_dir_pad_or_inv_org, octant); - intr_right = right.intersect_fast(ray, inv_dir, inv_dir_pad_or_inv_org, octant); + intr_left = left .intersect_fast(ray, inv_dir, inv_org, octant); + intr_right = right.intersect_fast(ray, inv_dir, inv_org, octant); } return std::make_tuple( intr_left.first <= intr_left.second, @@ -222,24 +218,26 @@ void Bvh::refit(LeafFn&& leaf_fn) { } template +template void Bvh::serialize(OutputStream& stream) const { - stream.write(nodes.size()); - stream.write(prim_ids.size()); + stream.write(static_cast(nodes.size())); + stream.write(static_cast(prim_ids.size())); for (auto&& node : nodes) node.serialize(stream); for (auto&& prim_id : prim_ids) - stream.write(prim_id); + stream.write(static_cast(prim_id)); } template +template Bvh Bvh::deserialize(InputStream& stream) { Bvh bvh; - bvh.nodes.resize(stream.read()); - bvh.prim_ids.resize(stream.read()); + bvh.nodes.resize(stream.read()); + bvh.prim_ids.resize(stream.read()); for (auto& node : bvh.nodes) node = Node::deserialize(stream); for (auto& prim_id : bvh.prim_ids) - prim_id = stream.read(); + prim_id = stream.read(); return bvh; } diff --git a/builtins/bvh/src/bvh/v2/c_api/CMakeLists.txt b/builtins/bvh/src/bvh/v2/c_api/CMakeLists.txt new file mode 100644 index 0000000000000..c8d7604858696 --- /dev/null +++ b/builtins/bvh/src/bvh/v2/c_api/CMakeLists.txt @@ -0,0 +1,19 @@ +add_library(bvh_c SHARED bvh.cpp) +target_link_libraries(bvh_c PRIVATE bvh) +target_compile_definitions(bvh_c PRIVATE -DBVH_BUILD_API) +target_include_directories(bvh_c INTERFACE + $ + $) +set_target_properties(bvh_c PROPERTIES + CXX_STANDARD 20 + CXX_VISIBILITY_PRESET hidden + INTERPROCEDURAL_OPTIMIZATION_RELEASE ON) + +if (BVH_C_API_STATIC_LINK_STDLIB) + # Link statically against standard C++ library + target_link_options(bvh_c PRIVATE $<$:-static-libstdc++>) +endif() + +if (BVH_C_API_UNSAFE_CASTS) + target_compile_definitions(bvh_c PRIVATE -DBVH_C_API_UNSAFE_CASTS) +endif() diff --git a/builtins/bvh/src/bvh/v2/c_api/bvh.cpp b/builtins/bvh/src/bvh/v2/c_api/bvh.cpp new file mode 100644 index 0000000000000..df48f93e70844 --- /dev/null +++ b/builtins/bvh/src/bvh/v2/c_api/bvh.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +namespace bvh::v2::c_api { + +BVH_TYPES(float, 2, 2f) +BVH_TYPES(float, 3, 3f) +BVH_TYPES(double, 2, 2d) +BVH_TYPES(double, 3, 3d) + +extern "C" { + +BVH_EXPORT struct bvh_thread_pool* bvh_thread_pool_create(size_t thread_count) { + return reinterpret_cast(new bvh::v2::ThreadPool(thread_count)); +} + +BVH_EXPORT void bvh_thread_pool_destroy(bvh_thread_pool* thread_pool) { + return delete reinterpret_cast(thread_pool); +} + +BVH_IMPL(float, 2, 2f) +BVH_IMPL(float, 3, 3f) +BVH_IMPL(double, 2, 2d) +BVH_IMPL(double, 3, 3d) + +} // extern "C" +} // namespace bvh::v2::c_api diff --git a/builtins/bvh/src/bvh/v2/c_api/bvh.h b/builtins/bvh/src/bvh/v2/c_api/bvh.h new file mode 100644 index 0000000000000..819b0f9d6a7fc --- /dev/null +++ b/builtins/bvh/src/bvh/v2/c_api/bvh.h @@ -0,0 +1,301 @@ +#ifndef BVH_V2_C_API_BVH_H +#define BVH_V2_C_API_BVH_H + +// C API to the BVH library, providing high-level access to BVH construction and traversal. This API +// may have an overhead compared to using the C++ API directly, as it may have to translate types +// between the C interface and the C++ one, and because callbacks are not going to be inlined at the +// API boundary. To mitigate that problem, specialized functions are provided which provide faster +// operation for a small set of situations. + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#define BVH_EXPORT __declspec(dllexport) +#define BVH_IMPORT __declspec(dllimport) +#else +#define BVH_EXPORT __attribute__((visibility("default"))) +#define BVH_IMPORT BVH_EXPORT +#endif + +#ifdef BVH_BUILD_API +#define BVH_API BVH_EXPORT +#else +#define BVH_API BVH_IMPORT +#endif + +#define BVH_ROOT_INDEX 0 +#define BVH_INVALID_PRIM_ID SIZE_MAX + +struct bvh2f; +struct bvh3f; +struct bvh_node2f; +struct bvh_node3f; + +struct bvh2d; +struct bvh3d; +struct bvh_node2d; +struct bvh_node3d; + +struct bvh_thread_pool; + +enum bvh_build_quality { + BVH_BUILD_QUALITY_LOW, + BVH_BUILD_QUALITY_MEDIUM, + BVH_BUILD_QUALITY_HIGH +}; + +struct bvh_build_config { + enum bvh_build_quality quality; + size_t min_leaf_size; + size_t max_leaf_size; + size_t parallel_threshold; +}; + +struct bvh_vec2f { float x, y; }; +struct bvh_vec3f { float x, y, z; }; +struct bvh_vec2d { double x, y; }; +struct bvh_vec3d { double x, y, z; }; + +struct bvh_bbox2f { struct bvh_vec2f min, max; }; +struct bvh_bbox3f { struct bvh_vec3f min, max; }; +struct bvh_bbox2d { struct bvh_vec2d min, max; }; +struct bvh_bbox3d { struct bvh_vec3d min, max; }; + +struct bvh_ray2f { struct bvh_vec2f org, dir; float tmin, tmax; }; +struct bvh_ray3f { struct bvh_vec3f org, dir; float tmin, tmax; }; +struct bvh_ray2d { struct bvh_vec2d org, dir; double tmin, tmax; }; +struct bvh_ray3d { struct bvh_vec3d org, dir; double tmin, tmax; }; + +struct bvh_intersect_callbackf { + void* user_data; + bool (*user_fn)(void*, float*, size_t begin, size_t end); +}; + +struct bvh_intersect_callbackd { + void* user_data; + bool (*user_fn)(void*, double*, size_t begin, size_t end); +}; + +// Thread Pool ------------------------------------------------------------------------------------ + +// A thread count of zero instructs the thread pool to detect the number of threads available on the +// machine. + +BVH_API struct bvh_thread_pool* bvh_thread_pool_create(size_t thread_count); +BVH_API void bvh_thread_pool_destroy(struct bvh_thread_pool*); + +// BVH Construction ------------------------------------------------------------------------------- + +// These construction functions can be called with a `NULL` thread pool, in which case the BVH is +// constructed serially. The configuration can also be `NULL`, in which case the default +// configuration is used. + +BVH_API struct bvh2f* bvh2f_build( + struct bvh_thread_pool* thread_pool, + const struct bvh_bbox2f* bboxes, + const struct bvh_vec2f* centers, + size_t prim_count, + const struct bvh_build_config* config); + +BVH_API struct bvh3f* bvh3f_build( + struct bvh_thread_pool* thread_pool, + const struct bvh_bbox3f* bboxes, + const struct bvh_vec3f* centers, + size_t prim_count, + const struct bvh_build_config* config); + +BVH_API struct bvh2d* bvh2d_build( + struct bvh_thread_pool* thread_pool, + const struct bvh_bbox2d* bboxes, + const struct bvh_vec2d* centers, + size_t prim_count, + const struct bvh_build_config* config); + +BVH_API struct bvh3d* bvh3d_build( + struct bvh_thread_pool* thread_pool, + const struct bvh_bbox3d* bboxes, + const struct bvh_vec3d* centers, + size_t prim_count, + const struct bvh_build_config* config); + +// BVH Destruction -------------------------------------------------------------------------------- + +BVH_API void bvh2f_destroy(struct bvh2f*); +BVH_API void bvh3f_destroy(struct bvh3f*); +BVH_API void bvh2d_destroy(struct bvh2d*); +BVH_API void bvh3d_destroy(struct bvh3d*); + +// Serialization/Deserialization to Files --------------------------------------------------------- + +BVH_API void bvh2f_save(const struct bvh2f*, FILE*); +BVH_API void bvh3f_save(const struct bvh3f*, FILE*); +BVH_API void bvh2d_save(const struct bvh2d*, FILE*); +BVH_API void bvh3d_save(const struct bvh3d*, FILE*); + +BVH_API struct bvh2f* bvh2f_load(FILE*); +BVH_API struct bvh3f* bvh3f_load(FILE*); +BVH_API struct bvh2d* bvh2d_load(FILE*); +BVH_API struct bvh3d* bvh3d_load(FILE*); + +// Accessing Nodes and Primitive Indices ---------------------------------------------------------- + +BVH_API struct bvh_node2f* bvh2f_get_node(struct bvh2f*, size_t); +BVH_API struct bvh_node3f* bvh3f_get_node(struct bvh3f*, size_t); +BVH_API struct bvh_node2d* bvh2d_get_node(struct bvh2d*, size_t); +BVH_API struct bvh_node3d* bvh3d_get_node(struct bvh3d*, size_t); + +BVH_API size_t bvh2f_get_prim_id(const struct bvh2f*, size_t); +BVH_API size_t bvh3f_get_prim_id(const struct bvh3f*, size_t); +BVH_API size_t bvh2d_get_prim_id(const struct bvh2d*, size_t); +BVH_API size_t bvh3d_get_prim_id(const struct bvh3d*, size_t); + +BVH_API size_t bvh2f_get_prim_count(const struct bvh2f*); +BVH_API size_t bvh3f_get_prim_count(const struct bvh3f*); +BVH_API size_t bvh2d_get_prim_count(const struct bvh2d*); +BVH_API size_t bvh3d_get_prim_count(const struct bvh3d*); + +BVH_API size_t bvh2f_get_node_count(const struct bvh2f*); +BVH_API size_t bvh3f_get_node_count(const struct bvh3f*); +BVH_API size_t bvh2d_get_node_count(const struct bvh2d*); +BVH_API size_t bvh3d_get_node_count(const struct bvh3d*); + +// Accessing and Modifying Node properties -------------------------------------------------------- + +BVH_API bool bvh_node2f_is_leaf(const struct bvh_node2f*); +BVH_API bool bvh_node3f_is_leaf(const struct bvh_node3f*); +BVH_API bool bvh_node2d_is_leaf(const struct bvh_node2d*); +BVH_API bool bvh_node3d_is_leaf(const struct bvh_node3d*); + +BVH_API size_t bvh_node2f_get_prim_count(const struct bvh_node2f*); +BVH_API size_t bvh_node3f_get_prim_count(const struct bvh_node3f*); +BVH_API size_t bvh_node2d_get_prim_count(const struct bvh_node2d*); +BVH_API size_t bvh_node3d_get_prim_count(const struct bvh_node3d*); + +BVH_API void bvh_node2f_set_prim_count(struct bvh_node2f*, size_t); +BVH_API void bvh_node3f_set_prim_count(struct bvh_node3f*, size_t); +BVH_API void bvh_node2d_set_prim_count(struct bvh_node2d*, size_t); +BVH_API void bvh_node3d_set_prim_count(struct bvh_node3d*, size_t); + +BVH_API size_t bvh_node2f_get_first_id(const struct bvh_node2f*); +BVH_API size_t bvh_node3f_get_first_id(const struct bvh_node3f*); +BVH_API size_t bvh_node2d_get_first_id(const struct bvh_node2d*); +BVH_API size_t bvh_node3d_get_first_id(const struct bvh_node3d*); + +BVH_API void bvh_node2f_set_first_id(struct bvh_node2f*, size_t); +BVH_API void bvh_node3f_set_first_id(struct bvh_node3f*, size_t); +BVH_API void bvh_node2d_set_first_id(struct bvh_node2d*, size_t); +BVH_API void bvh_node3d_set_first_id(struct bvh_node3d*, size_t); + +BVH_API struct bvh_bbox2f bvh_node2f_get_bbox(const struct bvh_node2f*); +BVH_API struct bvh_bbox3f bvh_node3f_get_bbox(const struct bvh_node3f*); +BVH_API struct bvh_bbox2d bvh_node2d_get_bbox(const struct bvh_node2d*); +BVH_API struct bvh_bbox3d bvh_node3d_get_bbox(const struct bvh_node3d*); + +BVH_API void bvh_node2f_set_bbox(struct bvh_node2f*, const struct bvh_bbox2f*); +BVH_API void bvh_node3f_set_bbox(struct bvh_node3f*, const struct bvh_bbox3f*); +BVH_API void bvh_node2d_set_bbox(struct bvh_node2d*, const struct bvh_bbox2d*); +BVH_API void bvh_node3d_set_bbox(struct bvh_node3d*, const struct bvh_bbox3d*); + +// Refitting and BVH Modification ----------------------------------------------------------------- + +// Refitting functions resize the bounding boxes of the BVH in a bottom-up fashion. This can be +// combined with the optimization function below to allow updating the BVH in an incremental manner. +// IMPORTANT: Appending a node to the BVH invalidates all the node pointers. + +BVH_API void bvh2f_append_node(struct bvh2f*); +BVH_API void bvh3f_append_node(struct bvh3f*); +BVH_API void bvh2d_append_node(struct bvh2d*); +BVH_API void bvh3d_append_node(struct bvh3d*); + +BVH_API void bvh2f_remove_last_node(struct bvh2f*); +BVH_API void bvh3f_remove_last_node(struct bvh3f*); +BVH_API void bvh2d_remove_last_node(struct bvh2d*); +BVH_API void bvh3d_remove_last_node(struct bvh3d*); + +BVH_API void bvh2f_refit(struct bvh2f*); +BVH_API void bvh3f_refit(struct bvh3f*); +BVH_API void bvh2d_refit(struct bvh2d*); +BVH_API void bvh3d_refit(struct bvh3d*); + +BVH_API void bvh2f_optimize(struct bvh_thread_pool*, struct bvh2f*); +BVH_API void bvh3f_optimize(struct bvh_thread_pool*, struct bvh3f*); +BVH_API void bvh2d_optimize(struct bvh_thread_pool*, struct bvh2d*); +BVH_API void bvh3d_optimize(struct bvh_thread_pool*, struct bvh3d*); + +// BVH Intersection ------------------------------------------------------------------------------- + +// Intersection routines: Intersects the BVH with a ray using a callback to intersect the primitives +// contained in the leaves. +// +// The callback takes a pointer to user data, a pointer to the current distance along the ray, and a +// range of primitives. It returns true when an intersection is found, in which case it writes the +// intersection distance in the provided pointer, otherwise, if no intersection is found, it returns +// false and leaves the intersection distance unchanged. The given range is expressed in terms of +// *BVH primitives*, which means that it does not correspond to a range in the original set of +// primitives. Instead, the user has two options: pre-permute the primitives according to the BVH +// primitive indices, or perform an indirection everytime a primitive is accessed. +// +// Here is a basic example of an intersection callback: +// +// struct my_user_data { +// struct bvh3f* bvh; +// struct bvh_ray3f* ray; +// struct my_prim* prims; +// }; +// +// struct my_prim_hit { +// float t; +// }; +// +// // Declared & defined somewhere else. +// bool intersect_prim(const struct my_prim*, const struct bvh_ray3f*, struct my_prim_hit*); +// +// bool my_user_fn(void* user_data, float* t, size_t begin, size_t end) { +// struct my_user_data* my_user_data = (my_user_data)data; +// bool was_hit = false; +// for (size_t i = begin; i < end; ++i) { +// const size_t prim_id = bvh3f_get_prim_id(my_user_data->bvh, i); +// struct my_prim_hit hit; +// if (intersect_prim(&my_user_data->prims[i], my_user_data->ray, &hit)) { +// // Note: It is important to remember the maximum distance so that the BVH +// // traversal routine can cull nodes that are too far away, and so that the primitive +// // intersection routine can exit earlier when that is possible. +// *t = my_user_data->ray->tmax = hit.t; +// was_hit = true; +// } +// } +// return was_hit; +// } +// + +BVH_API void bvh2f_intersect_ray_any(const struct bvh2f*, const struct bvh_ray2f*, const struct bvh_intersect_callbackf*); +BVH_API void bvh3f_intersect_ray_any(const struct bvh3f*, const struct bvh_ray3f*, const struct bvh_intersect_callbackf*); +BVH_API void bvh2d_intersect_ray_any(const struct bvh2d*, const struct bvh_ray2d*, const struct bvh_intersect_callbackd*); +BVH_API void bvh3d_intersect_ray_any(const struct bvh3d*, const struct bvh_ray3d*, const struct bvh_intersect_callbackd*); + +BVH_API void bvh2f_intersect_ray_any_robust(const struct bvh2f*, const struct bvh_ray2f*, const struct bvh_intersect_callbackf*); +BVH_API void bvh3f_intersect_ray_any_robust(const struct bvh3f*, const struct bvh_ray3f*, const struct bvh_intersect_callbackf*); +BVH_API void bvh2d_intersect_ray_any_robust(const struct bvh2d*, const struct bvh_ray2d*, const struct bvh_intersect_callbackd*); +BVH_API void bvh3d_intersect_ray_any_robust(const struct bvh3d*, const struct bvh_ray3d*, const struct bvh_intersect_callbackd*); + +BVH_API void bvh2f_intersect_ray(const struct bvh2f*, const struct bvh_ray2f*, const struct bvh_intersect_callbackf*); +BVH_API void bvh3f_intersect_ray(const struct bvh3f*, const struct bvh_ray3f*, const struct bvh_intersect_callbackf*); +BVH_API void bvh2d_intersect_ray(const struct bvh2d*, const struct bvh_ray2d*, const struct bvh_intersect_callbackd*); +BVH_API void bvh3d_intersect_ray(const struct bvh3d*, const struct bvh_ray3d*, const struct bvh_intersect_callbackd*); + +BVH_API void bvh2f_intersect_ray_robust(const struct bvh2f*, const struct bvh_ray2f*, const struct bvh_intersect_callbackf*); +BVH_API void bvh3f_intersect_ray_robust(const struct bvh3f*, const struct bvh_ray3f*, const struct bvh_intersect_callbackf*); +BVH_API void bvh2d_intersect_ray_robust(const struct bvh2d*, const struct bvh_ray2d*, const struct bvh_intersect_callbackd*); +BVH_API void bvh3d_intersect_ray_robust(const struct bvh3d*, const struct bvh_ray3d*, const struct bvh_intersect_callbackd*); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/builtins/bvh/src/bvh/v2/c_api/bvh_impl.h b/builtins/bvh/src/bvh/v2/c_api/bvh_impl.h new file mode 100644 index 0000000000000..843ef3bfac680 --- /dev/null +++ b/builtins/bvh/src/bvh/v2/c_api/bvh_impl.h @@ -0,0 +1,357 @@ +#ifndef BVH_V2_C_API_BVH_IMPL_H +#define BVH_V2_C_API_BVH_IMPL_H + +#include +#include +#include + +namespace bvh::v2::c_api { + +template +struct BvhTypes {}; + +template +struct BvhCallback {}; + +template <> struct BvhCallback { using Type = bvh_intersect_callbackf; }; +template <> struct BvhCallback { using Type = bvh_intersect_callbackd; }; + +template +static auto translate(enum bvh_build_quality quality) { + switch (quality) { +#ifndef BVH_C_API_UNSAFE_CASTS + case BVH_BUILD_QUALITY_LOW: + return bvh::v2::DefaultBuilder>::Quality::Low; + case BVH_BUILD_QUALITY_MEDIUM: + return bvh::v2::DefaultBuilder>::Quality::Medium; + case BVH_BUILD_QUALITY_HIGH: + return bvh::v2::DefaultBuilder>::Quality::High; +#endif + default: + return static_cast>::Quality>(quality); + } +} + +template +static auto translate(const bvh_build_config* config) { + auto translated_config = typename bvh::v2::DefaultBuilder>::Config {}; + if (config) { + translated_config.quality = translate(config->quality); + translated_config.min_leaf_size = config->min_leaf_size; + translated_config.max_leaf_size = config->max_leaf_size; + translated_config.parallel_threshold = config->parallel_threshold; + } + return translated_config; +} + +template +static auto translate(const typename BvhTypes::Vec& vec) { + static_assert(Dim == 2 || Dim == 3); + if constexpr (Dim == 2) { + return bvh::v2::Vec { vec.x, vec.y }; + } else { + return bvh::v2::Vec { vec.x, vec.y, vec.z }; + } +} + +template +static auto translate(const bvh::v2::Vec& vec) { + static_assert(Dim == 2 || Dim == 3); + if constexpr (Dim == 2) { + return typename BvhTypes::Vec { vec[0], vec[1] }; + } else { + return typename BvhTypes::Vec { vec[0], vec[1], vec[2] }; + } +} + +template +static auto translate(const typename BvhTypes::BBox& bbox) { + return bvh::v2::BBox { translate(bbox.min), translate(bbox.max) }; +} + +template +static auto translate(const bvh::v2::BBox& bbox) { + return typename BvhTypes::BBox { translate(bbox.min), translate(bbox.max) }; +} + +template +static auto translate(const typename BvhTypes::Ray& ray) { + return bvh::v2::Ray { translate(ray.org), translate(ray.dir), ray.tmin, ray.tmax }; +} + +template +static typename BvhTypes::Bvh* bvh_build( + bvh_thread_pool* thread_pool, + const typename BvhTypes::BBox* bboxes, + const typename BvhTypes::Vec* centers, + size_t prim_count, + const bvh_build_config* config) +{ + bvh::v2::BBox* translated_bboxes = nullptr; + bvh::v2::Vec* translated_centers = nullptr; +#ifdef BVH_C_API_UNSAFE_CASTS + translated_bboxes = reinterpret_cast(bboxes); + translated_centers = reinterpret_cast(centers); +#else + std::vector> bbox_buf(prim_count); + std::vector> center_buf(prim_count); + for (size_t i = 0; i < prim_count; ++i) { + bbox_buf[i] = translate(bboxes[i]); + center_buf[i] = translate(centers[i]); + } + translated_bboxes = bbox_buf.data(); + translated_centers = center_buf.data(); +#endif + auto bvh = thread_pool + ? bvh::v2::DefaultBuilder>::build( + *reinterpret_cast(thread_pool), + std::span { translated_bboxes, translated_bboxes + prim_count }, + std::span { translated_centers, translated_centers + prim_count }, + translate(config)) + : bvh::v2::DefaultBuilder>::build( + std::span { translated_bboxes, translated_bboxes + prim_count }, + std::span { translated_centers, translated_centers + prim_count }, + translate(config)); + return reinterpret_cast::Bvh*>(new decltype(bvh) { std::move(bvh) }); +} + +template +static void bvh_save(const typename BvhTypes::Bvh* bvh, FILE* file) { + struct FileOutputStream : bvh::v2::OutputStream { + FILE* file; + FileOutputStream(FILE* file) + : file(file) + {} + bool write_raw(const void* data, size_t size) override { + return fwrite(data, 1, size, file) == size; + } + }; + FileOutputStream stream { file }; + reinterpret_cast>*>(bvh)->serialize(stream); +} + +template +static typename BvhTypes::Bvh* bvh_load(FILE* file) { + struct FileInputStream : bvh::v2::InputStream { + FILE* file; + FileInputStream(FILE* file) + : file(file) + {} + size_t read_raw(void* data, size_t size) override { + return fread(data, 1, size, file); + } + }; + FileInputStream stream { file }; + return reinterpret_cast::Bvh*>( + new bvh::v2::Bvh>(bvh::v2::Bvh>::deserialize(stream))); +} + +template +static typename BvhTypes::Node* bvh_get_node(typename BvhTypes::Bvh* bvh, size_t node_id) { + auto& nodes = reinterpret_cast>*>(bvh)->nodes; + assert(node_id < nodes.size()); + return reinterpret_cast::Node*>(&nodes[node_id]); +} + +template +static size_t bvh_get_prim_id(const typename BvhTypes::Bvh* bvh, size_t i) { + auto& prim_ids = reinterpret_cast>*>(bvh)->prim_ids; + assert(i < prim_ids.size()); + return prim_ids[i]; +} + +template +static size_t bvh_get_prim_count(const typename BvhTypes::Bvh* bvh) { + return reinterpret_cast>*>(bvh)->prim_ids.size(); +} + +template +static size_t bvh_get_node_count(const typename BvhTypes::Bvh* bvh) { + return reinterpret_cast>*>(bvh)->nodes.size(); +} + +template +static bool bvh_node_is_leaf(const typename BvhTypes::Node* node) { + return reinterpret_cast*>(node)->is_leaf(); +} + +template +static size_t bvh_node_get_prim_count(const typename BvhTypes::Node* node) { + return reinterpret_cast*>(node)->index.prim_count(); +} + +template +static void bvh_node_set_prim_count(typename BvhTypes::Node* node, size_t prim_count) { + return reinterpret_cast*>(node)->index.set_prim_count(prim_count); +} + +template +static size_t bvh_node_get_first_id(const typename BvhTypes::Node* node) { + return reinterpret_cast*>(node)->index.first_id(); +} + +template +static void bvh_node_set_first_id(typename BvhTypes::Node* node, size_t first_id) { + reinterpret_cast*>(node)->index.set_first_id(first_id); +} + +template +static typename BvhTypes::BBox bvh_node_get_bbox(const typename BvhTypes::Node* node) { + return translate(reinterpret_cast*>(node)->get_bbox()); +} + +template +static void bvh_node_set_bbox(typename BvhTypes::Node* node, const typename BvhTypes::BBox* bbox) { + reinterpret_cast*>(node)->set_bbox(translate(*bbox)); +} + +template +static void bvh_append_node(typename BvhTypes::Bvh* bvh) { + reinterpret_cast>*>(bvh)->nodes.emplace_back(); +} + +template +static void bvh_remove_last_node(typename BvhTypes::Bvh* bvh) { + reinterpret_cast>*>(bvh)->nodes.pop_back(); +} + +template +static void bvh_refit(typename BvhTypes::Bvh* bvh) { + reinterpret_cast>*>(bvh)->refit(); +} + +template +static void bvh_optimize(bvh_thread_pool* thread_pool, typename BvhTypes::Bvh* bvh) { + if (thread_pool) { + bvh::v2::ReinsertionOptimizer>::optimize( + *reinterpret_cast(thread_pool), + *reinterpret_cast>*>(bvh)); + } else { + bvh::v2::ReinsertionOptimizer>::optimize( + *reinterpret_cast>*>(bvh)); + } +} + +template +static void bvh_intersect_ray( + const typename BvhTypes::Bvh* bvh, + const typename BvhTypes::Ray* ray, + const typename BvhCallback::Type* callback) +{ + static constexpr size_t stack_size = 64; + auto translated_ray = translate(*ray); + auto& translated_bvh = *reinterpret_cast>*>(bvh); + bvh::v2::SmallStack::Index, stack_size> stack; + translated_bvh.template intersect( + translated_ray, translated_bvh.get_root().index, stack, + [&] (size_t begin, size_t end) { + return callback->user_fn(callback->user_data, &translated_ray.tmax, begin, end); + }); +} + +#define BVH_TYPES(T, Dim, suffix) \ + template <> \ + struct BvhTypes { \ + using Bvh = bvh##suffix; \ + using Vec = bvh_vec##suffix; \ + using BBox = bvh_bbox##suffix; \ + using Node = bvh_node##suffix; \ + using Ray = bvh_ray##suffix; \ + }; + +#define BVH_IMPL(T, Dim, suffix) \ + BVH_EXPORT typename BvhTypes::Bvh* bvh##suffix##_build( \ + bvh_thread_pool* thread_pool, \ + const typename BvhTypes::BBox* bboxes, \ + const typename BvhTypes::Vec* centers, \ + size_t prim_count, \ + const bvh_build_config* config) \ + { \ + return bvh_build(thread_pool, bboxes, centers, prim_count, config); \ + } \ + BVH_EXPORT void bvh##suffix##_destroy(typename BvhTypes::Bvh* bvh) { \ + delete reinterpret_cast>*>(bvh); \ + } \ + BVH_EXPORT void bvh##suffix##_save(const typename BvhTypes::Bvh* bvh, FILE* file) { \ + bvh_save(bvh, file); \ + } \ + BVH_EXPORT typename BvhTypes::Bvh* bvh##suffix##_load(FILE* file) { \ + return bvh_load(file); \ + } \ + BVH_EXPORT typename BvhTypes::Node* bvh##suffix##_get_node(typename BvhTypes::Bvh* bvh, size_t node_id) { \ + return bvh_get_node(bvh, node_id); \ + } \ + BVH_EXPORT size_t bvh##suffix##_get_prim_id(const typename BvhTypes::Bvh* bvh, size_t i) { \ + return bvh_get_prim_id(bvh, i); \ + } \ + BVH_EXPORT size_t bvh##suffix##_get_prim_count(const typename BvhTypes::Bvh* bvh) { \ + return bvh_get_prim_count(bvh); \ + } \ + BVH_EXPORT size_t bvh##suffix##_get_node_count(const typename BvhTypes::Bvh* bvh) { \ + return bvh_get_node_count(bvh); \ + } \ + BVH_EXPORT bool bvh_node##suffix##_is_leaf(const typename BvhTypes::Node* node) { \ + return bvh_node_is_leaf(node); \ + } \ + BVH_EXPORT size_t bvh_node##suffix##_get_prim_count(const typename BvhTypes::Node* node) { \ + return bvh_node_get_prim_count(node); \ + } \ + BVH_EXPORT void bvh_node##suffix##_set_prim_count(typename BvhTypes::Node* node, size_t prim_count) { \ + bvh_node_set_prim_count(node, prim_count); \ + } \ + BVH_EXPORT size_t bvh_node##suffix##_get_first_id(const typename BvhTypes::Node* node) { \ + return bvh_node_get_first_id(node); \ + } \ + BVH_EXPORT void bvh_node##suffix##_set_first_id(typename BvhTypes::Node* node, size_t first_id) { \ + bvh_node_set_first_id(node, first_id); \ + } \ + BVH_EXPORT typename BvhTypes::BBox bvh_node##suffix##_get_bbox(const typename BvhTypes::Node* node) { \ + return bvh_node_get_bbox(node); \ + } \ + BVH_EXPORT void bvh_node##suffix##_set_bbox(typename BvhTypes::Node* node, const typename BvhTypes::BBox* bbox) { \ + bvh_node_set_bbox(node, bbox); \ + } \ + BVH_EXPORT void bvh##suffix##_append_node(typename BvhTypes::Bvh* bvh) { \ + bvh_append_node(bvh); \ + } \ + BVH_EXPORT void bvh##suffix##_remove_last_node(typename BvhTypes::Bvh* bvh) { \ + bvh_remove_last_node(bvh); \ + } \ + BVH_EXPORT void bvh##suffix##_refit(typename BvhTypes::Bvh* bvh) { \ + bvh_refit(bvh); \ + } \ + BVH_EXPORT void bvh##suffix##_optimize(bvh_thread_pool* thread_pool, typename BvhTypes::Bvh* bvh) { \ + bvh_optimize(thread_pool, bvh); \ + } \ + BVH_EXPORT void bvh##suffix##_intersect_ray_any( \ + const typename BvhTypes::Bvh* bvh, \ + const typename BvhTypes::Ray* ray, \ + const typename BvhCallback::Type* callback) \ + { \ + bvh_intersect_ray(bvh, ray, callback); \ + } \ + BVH_EXPORT void bvh##suffix##_intersect_ray_any_robust( \ + const typename BvhTypes::Bvh* bvh, \ + const typename BvhTypes::Ray* ray, \ + const typename BvhCallback::Type* callback) \ + { \ + bvh_intersect_ray(bvh, ray, callback); \ + } \ + BVH_EXPORT void bvh##suffix##_intersect_ray( \ + const typename BvhTypes::Bvh* bvh, \ + const typename BvhTypes::Ray* ray, \ + const typename BvhCallback::Type* callback) \ + { \ + bvh_intersect_ray(bvh, ray, callback); \ + } \ + BVH_EXPORT void bvh##suffix##_intersect_ray_robust( \ + const typename BvhTypes::Bvh* bvh, \ + const typename BvhTypes::Ray* ray, \ + const typename BvhCallback::Type* callback) \ + { \ + bvh_intersect_ray(bvh, ray, callback); \ + } + +} // namespace bvh::v2::c_api + +#endif diff --git a/builtins/bvh/src/bvh/v2/default_builder.h b/builtins/bvh/src/bvh/v2/default_builder.h index 57aaee66fa066..e05425d506a6e 100644 --- a/builtins/bvh/src/bvh/v2/default_builder.h +++ b/builtins/bvh/src/bvh/v2/default_builder.h @@ -30,7 +30,7 @@ class DefaultBuilder { }; /// Build a BVH in parallel using the given thread pool. - BVH_ALWAYS_INLINE static Bvh build( + [[nodiscard]] BVH_ALWAYS_INLINE static Bvh build( ThreadPool& thread_pool, std::span bboxes, std::span centers, @@ -46,7 +46,7 @@ class DefaultBuilder { } /// Build a BVH in a single-thread. - BVH_ALWAYS_INLINE static Bvh build( + [[nodiscard]] BVH_ALWAYS_INLINE static Bvh build( std::span bboxes, std::span centers, const Config& config = {}) diff --git a/builtins/bvh/src/bvh/v2/executor.h b/builtins/bvh/src/bvh/v2/executor.h index 8c6b8801caf61..4a20e0dba5731 100644 --- a/builtins/bvh/src/bvh/v2/executor.h +++ b/builtins/bvh/src/bvh/v2/executor.h @@ -13,12 +13,12 @@ namespace bvh::v2 { template struct Executor { template - inline void for_each(size_t begin, size_t end, const Loop& loop) { + BVH_ALWAYS_INLINE void for_each(size_t begin, size_t end, const Loop& loop) { return static_cast(this)->for_each(begin, end, loop); } template - inline T reduce(size_t begin, size_t end, const T& init, const Reduce& reduce, const Join& join) { + BVH_ALWAYS_INLINE T reduce(size_t begin, size_t end, const T& init, const Reduce& reduce, const Join& join) { return static_cast(this)->reduce(begin, end, init, reduce, join); } }; @@ -26,12 +26,12 @@ struct Executor { /// Executor that executes serially. struct SequentialExecutor : Executor { template - void for_each(size_t begin, size_t end, const Loop& loop) { + BVH_ALWAYS_INLINE void for_each(size_t begin, size_t end, const Loop& loop) { loop(begin, end); } template - T reduce(size_t begin, size_t end, const T& init, const Reduce& reduce, const Join&) { + BVH_ALWAYS_INLINE T reduce(size_t begin, size_t end, const T& init, const Reduce& reduce, const Join&) { T result(init); reduce(result, begin, end); return result; @@ -48,20 +48,21 @@ struct ParallelExecutor : Executor { {} template - void for_each(size_t begin, size_t end, const Loop& loop) { + BVH_ALWAYS_INLINE void for_each(size_t begin, size_t end, const Loop& loop) { if (end - begin < parallel_threshold) return loop(begin, end); auto chunk_size = std::max(size_t{1}, (end - begin) / thread_pool.get_thread_count()); + ThreadPool::TaskGroup group; for (size_t i = begin; i < end; i += chunk_size) { size_t next = std::min(end, i + chunk_size); - thread_pool.push([=] (size_t) { loop(i, next); }); + thread_pool.push(group, [=] (size_t) { loop(i, next); }); } - thread_pool.wait(); + thread_pool.wait(group); } template - T reduce(size_t begin, size_t end, const T& init, const Reduce& reduce, const Join& join) { + BVH_ALWAYS_INLINE T reduce(size_t begin, size_t end, const T& init, const Reduce& reduce, const Join& join) { if (end - begin < parallel_threshold) { T result(init); reduce(result, begin, end); @@ -70,14 +71,15 @@ struct ParallelExecutor : Executor { auto chunk_size = std::max(size_t{1}, (end - begin) / thread_pool.get_thread_count()); std::vector per_thread_result(thread_pool.get_thread_count(), init); + ThreadPool::TaskGroup group; for (size_t i = begin; i < end; i += chunk_size) { size_t next = std::min(end, i + chunk_size); - thread_pool.push([&, i, next] (size_t thread_id) { + thread_pool.push(group, [&, i, next] (size_t thread_id) { auto& result = per_thread_result[thread_id]; reduce(result, i, next); }); } - thread_pool.wait(); + thread_pool.wait(group); for (size_t i = 1; i < thread_pool.get_thread_count(); ++i) join(per_thread_result[0], std::move(per_thread_result[i])); return per_thread_result[0]; diff --git a/builtins/bvh/src/bvh/v2/index.h b/builtins/bvh/src/bvh/v2/index.h index c37899459027b..b5137f09a6a17 100644 --- a/builtins/bvh/src/bvh/v2/index.h +++ b/builtins/bvh/src/bvh/v2/index.h @@ -45,14 +45,8 @@ struct Index { Index() = default; explicit Index(Type value) : value(value) {} - //bool operator == (const Index&) const = default; - //bool operator != (const Index&) const = default; - bool operator == (const Index& other) const { - return other.value == value; - } - bool operator != (const Index& other) const { - return other.value != value; - } + bool operator == (const Index&) const = default; + bool operator != (const Index&) const = default; BVH_ALWAYS_INLINE Type first_id() const { return value >> prim_count_bits; } BVH_ALWAYS_INLINE Type prim_count() const { return value & max_prim_count; } @@ -60,11 +54,11 @@ struct Index { BVH_ALWAYS_INLINE bool is_inner() const { return !is_leaf(); } BVH_ALWAYS_INLINE void set_first_id(size_t first_id) { - *this = Index { first_id, prim_count() }; + *this = Index { first_id, static_cast(prim_count()) }; } BVH_ALWAYS_INLINE void set_prim_count(size_t prim_count) { - *this = Index { first_id(), prim_count }; + *this = Index { static_cast(first_id()), prim_count }; } static BVH_ALWAYS_INLINE Index make_leaf(size_t first_prim, size_t prim_count) { diff --git a/builtins/bvh/src/bvh/v2/mini_tree_builder.h b/builtins/bvh/src/bvh/v2/mini_tree_builder.h index 4db478da5670b..65f943b68911f 100644 --- a/builtins/bvh/src/bvh/v2/mini_tree_builder.h +++ b/builtins/bvh/src/bvh/v2/mini_tree_builder.h @@ -44,7 +44,7 @@ class MiniTreeBuilder { /// Starts building a BVH with the given primitive data. The build algorithm is multi-threaded, /// and runs on the given thread pool. - BVH_ALWAYS_INLINE static Bvh build( + [[nodiscard]] BVH_ALWAYS_INLINE static Bvh build( ThreadPool& thread_pool, std::span bboxes, std::span centers, @@ -195,11 +195,12 @@ class MiniTreeBuilder { // Iterate over bins to collect groups of primitives and build BVHs over them in parallel std::vector> mini_trees(final_bins.bins.size()); + ThreadPool::TaskGroup group; for (size_t i = 0; i < final_bins.bins.size(); ++i) { auto task = new BuildTask(this, mini_trees[i], std::move(final_bins[i].ids)); - executor_.thread_pool.push([task] (size_t) { task->run(); delete task; }); + executor_.thread_pool.push(group, [task] (size_t) { task->run(); delete task; }); } - executor_.thread_pool.wait(); + executor_.thread_pool.wait(group); return mini_trees; } diff --git a/builtins/bvh/src/bvh/v2/node.h b/builtins/bvh/src/bvh/v2/node.h index 90e72ce9482fd..c071f6dd09436 100644 --- a/builtins/bvh/src/bvh/v2/node.h +++ b/builtins/bvh/src/bvh/v2/node.h @@ -38,14 +38,8 @@ struct Node { Node() = default; - // bool operator == (const Node&) const = default; - // bool operator != (const Node&) const = default; - bool operator != (const Node& other) const { - return other.bounds == bounds; - } - bool operator == (const Node& other) const { - return other.bounds != bounds; - } + bool operator == (const Node&) const = default; + bool operator != (const Node&) const = default; BVH_ALWAYS_INLINE bool is_leaf() const { return index.is_leaf(); } @@ -71,7 +65,7 @@ struct Node { } /// Robust ray-node intersection routine. See "Robust BVH Ray Traversal", by T. Ize. - BVH_ALWAYS_INLINE std::pair intersect_robust( + [[nodiscard]] BVH_ALWAYS_INLINE std::pair intersect_robust( const Ray& ray, const Vec& inv_dir, const Vec& inv_dir_pad, @@ -82,7 +76,7 @@ struct Node { return make_intersection_result(ray, tmin, tmax); } - BVH_ALWAYS_INLINE std::pair intersect_fast( + [[nodiscard]] BVH_ALWAYS_INLINE std::pair intersect_fast( const Ray& ray, const Vec& inv_dir, const Vec& inv_org, @@ -99,7 +93,7 @@ struct Node { stream.write(index.value); } - static BVH_ALWAYS_INLINE Node deserialize(InputStream& stream) { + [[nodiscard]] static BVH_ALWAYS_INLINE Node deserialize(InputStream& stream) { Node node; for (auto& bound : node.bounds) bound = stream.read(); diff --git a/builtins/bvh/src/bvh/v2/reinsertion_optimizer.h b/builtins/bvh/src/bvh/v2/reinsertion_optimizer.h index 3638a57c499dc..793657f2be374 100644 --- a/builtins/bvh/src/bvh/v2/reinsertion_optimizer.h +++ b/builtins/bvh/src/bvh/v2/reinsertion_optimizer.h @@ -136,7 +136,7 @@ class ReinsertionOptimizer { * -SA(A U C) : For the parent of A and C */ - Reinsertion best_reinsertion { /*.from */ node_id, 0, 0 }; + Reinsertion best_reinsertion { .from = node_id }; auto node_area = bvh_.nodes[node_id].get_bbox().get_half_area(); auto parent_area = bvh_.nodes[parents_[node_id]].get_bbox().get_half_area(); auto area_diff = parent_area; diff --git a/builtins/bvh/src/bvh/v2/sphere.h b/builtins/bvh/src/bvh/v2/sphere.h new file mode 100644 index 0000000000000..a837fa9ac6f8a --- /dev/null +++ b/builtins/bvh/src/bvh/v2/sphere.h @@ -0,0 +1,54 @@ +#ifndef BVH_V2_SPHERE_H +#define BVH_V2_SPHERE_H + +#include "bvh/v2/vec.h" +#include "bvh/v2/ray.h" + +#include +#include +#include + +namespace bvh::v2 { + +/// Sphere primitive defined by a center and a radius. +template +struct Sphere { + Vec center; + T radius; + + BVH_ALWAYS_INLINE Sphere() = default; + BVH_ALWAYS_INLINE Sphere(const Vec& center, T radius) + : center(center), radius(radius) + {} + + BVH_ALWAYS_INLINE Vec get_center() const { return center; } + BVH_ALWAYS_INLINE BBox get_bbox() const { + return BBox(center - Vec(radius), center + Vec(radius)); + } + + /// Intersects a ray with the sphere. If the ray is normalized, a dot product can be saved by + /// setting `AssumeNormalized` to true. + template + [[nodiscard]] BVH_ALWAYS_INLINE std::optional> intersect(const Ray& ray) const { + auto oc = ray.org - center; + auto a = AssumeNormalized ? static_cast(1.) : dot(ray.dir, ray.dir); + auto b = static_cast(2.) * dot(ray.dir, oc); + auto c = dot(oc, oc) - radius * radius; + + auto delta = b * b - static_cast(4.) * a * c; + if (delta >= 0) { + auto inv = -static_cast(0.5) / a; + auto sqrt_delta = std::sqrt(delta); + auto t0 = robust_max((b + sqrt_delta) * inv, ray.tmin); + auto t1 = robust_min((b - sqrt_delta) * inv, ray.tmax); + if (t0 <= t1) + return std::make_optional(std::make_pair(t0, t1)); + } + + return std::nullopt; + } +}; + +} // namespace bvh::v2 + +#endif diff --git a/builtins/bvh/src/bvh/v2/stack.h b/builtins/bvh/src/bvh/v2/stack.h index cb1421290bbe3..c4d22c95a9b7c 100644 --- a/builtins/bvh/src/bvh/v2/stack.h +++ b/builtins/bvh/src/bvh/v2/stack.h @@ -43,10 +43,6 @@ struct GrowingStack { elems.pop_back(); return top; } - - void clear() { - elems.clear(); - } }; } // namespace bvh::v2 diff --git a/builtins/bvh/src/bvh/v2/stream.h b/builtins/bvh/src/bvh/v2/stream.h index 15565d9dc6813..477ef70704b93 100644 --- a/builtins/bvh/src/bvh/v2/stream.h +++ b/builtins/bvh/src/bvh/v2/stream.h @@ -9,6 +9,8 @@ namespace bvh::v2 { /// Stream of data that can be used to deserialize data structures. class InputStream { public: + virtual ~InputStream() = default; + template T read(T&& default_val = {}) { T data; @@ -16,7 +18,6 @@ class InputStream { data = std::move(default_val); return data; } - protected: virtual size_t read_raw(void*, size_t) = 0; }; @@ -24,6 +25,8 @@ class InputStream { /// Stream of data that can be used to serialize data structures. class OutputStream { public: + virtual ~OutputStream() = default; + template bool write(const T& data) { return write_raw(&data, sizeof(T)); } diff --git a/builtins/bvh/src/bvh/v2/sweep_sah_builder.h b/builtins/bvh/src/bvh/v2/sweep_sah_builder.h index 9fb3d370a6433..6a81c502ab98a 100644 --- a/builtins/bvh/src/bvh/v2/sweep_sah_builder.h +++ b/builtins/bvh/src/bvh/v2/sweep_sah_builder.h @@ -27,7 +27,7 @@ class SweepSahBuilder : public TopDownSahBuilder { public: using typename TopDownSahBuilder::Config; - BVH_ALWAYS_INLINE static Bvh build( + [[nodiscard]] BVH_ALWAYS_INLINE static Bvh build( std::span bboxes, std::span centers, const Config& config = {}) diff --git a/builtins/bvh/src/bvh/v2/thread_pool.h b/builtins/bvh/src/bvh/v2/thread_pool.h index 8fdf543ebb5de..b6ff42ef109bd 100644 --- a/builtins/bvh/src/bvh/v2/thread_pool.h +++ b/builtins/bvh/src/bvh/v2/thread_pool.h @@ -1,35 +1,71 @@ #ifndef BVH_V2_THREAD_POOL_H #define BVH_V2_THREAD_POOL_H -#include -#include +#include #include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include namespace bvh::v2 { class ThreadPool { public: - using Task = std::function; + /// Groups multiple tasks under a single unit of work. All tasks in the same group can be + /// be waited for completion using ``ThreadPool::wait()``. + class TaskGroup { + public: + TaskGroup() = default; + + private: + std::atomic remaining_{0}; + + TaskGroup(const TaskGroup&) = delete; + TaskGroup& operator=(const TaskGroup&) = delete; + TaskGroup(TaskGroup&&) = delete; + TaskGroup& operator=(TaskGroup&&) = delete; + + friend ThreadPool; + + inline void increment(); + inline void decrement(); + inline size_t remaining() const; + }; /// Creates a thread pool with the given number of threads (a value of 0 tries to autodetect /// the number of threads and uses that as a thread count). ThreadPool(size_t thread_count = 0) { start(thread_count); } ~ThreadPool() { - wait(); + wait_all(); stop(); join(); } - inline void push(Task&& fun); - inline void wait(); + /// Pushes a task on the thread pool, associated with a given `group`. + template + inline void push(TaskGroup& group, F&& fun); + + /// Wait for completion of all tasks scoped under given `group`. + inline void wait(TaskGroup& group); + + /// Wait for completion of all tasks. + inline void wait_all(); size_t get_thread_count() const { return threads_.size(); } private: + struct Task { + TaskGroup* group = nullptr; + std::function fn; + }; + static inline void worker(ThreadPool*, size_t); inline void start(size_t); @@ -41,21 +77,28 @@ class ThreadPool { std::mutex mutex_; std::vector threads_; std::condition_variable avail_; - std::condition_variable done_; + std::condition_variable group_done_; std::queue tasks_; }; -void ThreadPool::push(Task&& task) { +template +void ThreadPool::push(TaskGroup& group, F&& fun) { { std::unique_lock lock(mutex_); - tasks_.emplace(std::move(task)); + group.increment(); + tasks_.emplace(Task{&group, std::forward(fun)}); } avail_.notify_one(); } -void ThreadPool::wait() { +inline void ThreadPool::wait(TaskGroup& group) { + std::unique_lock lock(mutex_); + group_done_.wait(lock, [&group] { return group.remaining() == 0; }); +} + +inline void ThreadPool::wait_all() { std::unique_lock lock(mutex_); - done_.wait(lock, [this] { return busy_count_ == 0 && tasks_.empty(); }); + group_done_.wait(lock, [this] { return busy_count_ == 0 && tasks_.empty(); }); } void ThreadPool::worker(ThreadPool* pool, size_t thread_id) { @@ -66,16 +109,22 @@ void ThreadPool::worker(ThreadPool* pool, size_t thread_id) { pool->avail_.wait(lock, [pool] { return pool->should_stop_ || !pool->tasks_.empty(); }); if (pool->should_stop_ && pool->tasks_.empty()) break; + task = std::move(pool->tasks_.front()); pool->tasks_.pop(); pool->busy_count_++; } - task(thread_id); + + task.fn(thread_id); + task.group->decrement(); + { std::unique_lock lock(pool->mutex_); pool->busy_count_--; } - pool->done_.notify_one(); + + if (task.group->remaining() == 0) + pool->group_done_.notify_all(); } } @@ -99,6 +148,18 @@ void ThreadPool::join() { thread.join(); } +inline void ThreadPool::TaskGroup::increment() { + remaining_.fetch_add(1, std::memory_order_release); +} + +inline void ThreadPool::TaskGroup::decrement() { + remaining_.fetch_sub(1, std::memory_order_release); +} + +inline size_t ThreadPool::TaskGroup::remaining() const { + return remaining_.load(std::memory_order_acquire); +} + } // namespace bvh::v2 #endif diff --git a/builtins/bvh/src/bvh/v2/top_down_sah_builder.h b/builtins/bvh/src/bvh/v2/top_down_sah_builder.h index d1b25a8fc5c4b..435db07e94e73 100644 --- a/builtins/bvh/src/bvh/v2/top_down_sah_builder.h +++ b/builtins/bvh/src/bvh/v2/top_down_sah_builder.h @@ -5,13 +5,9 @@ #include "bvh/v2/vec.h" #include "bvh/v2/bbox.h" #include "bvh/v2/split_heuristic.h" + #include -#if __has_include() #include -#else -// Falling back to ROOT span -#include "ROOT/span.hxx" -#endif #include #include #include @@ -68,6 +64,7 @@ class TopDownSahBuilder { assert(config.min_leaf_size <= config.max_leaf_size); } + virtual ~TopDownSahBuilder() = default; virtual std::vector& get_prim_ids() = 0; virtual std::optional try_split(const BBox& bbox, size_t begin, size_t end) = 0; diff --git a/builtins/bvh/src/bvh/v2/tri.h b/builtins/bvh/src/bvh/v2/tri.h new file mode 100644 index 0000000000000..f5d4612afe83f --- /dev/null +++ b/builtins/bvh/src/bvh/v2/tri.h @@ -0,0 +1,78 @@ +#ifndef BVH_V2_TRI_H +#define BVH_V2_TRI_H + +#include "bvh/v2/vec.h" +#include "bvh/v2/ray.h" +#include "bvh/v2/bbox.h" + +#include +#include +#include + +namespace bvh::v2 { + +template +struct Tri { + Vec p0, p1, p2; + + Tri() = default; + + BVH_ALWAYS_INLINE Tri(const Vec& p0, const Vec& p1, const Vec& p2) + : p0(p0), p1(p1), p2(p2) + {} + + BVH_ALWAYS_INLINE BBox get_bbox() const { return BBox(p0).extend(p1).extend(p2); } + BVH_ALWAYS_INLINE Vec get_center() const { return (p0 + p1 + p2) * static_cast(1. / 3.); } +}; + +/// A 3d triangle, represented as two edges and a point, with an (unnormalized, left-handed) normal. +template +struct PrecomputedTri { + Vec p0, e1, e2, n; + + PrecomputedTri() = default; + + BVH_ALWAYS_INLINE PrecomputedTri(const Vec& p0, const Vec& p1, const Vec& p2) + : p0(p0), e1(p0 - p1), e2(p2 - p0), n(cross(e1, e2)) + {} + + BVH_ALWAYS_INLINE PrecomputedTri(const Tri& triangle) + : PrecomputedTri(triangle.p0, triangle.p1, triangle.p2) + {} + + BVH_ALWAYS_INLINE Tri convert_to_tri() const { return Tri(p0, p0 - e1, e2 + p0); } + BVH_ALWAYS_INLINE BBox get_bbox() const { return convert_to_tri().get_bbox(); } + BVH_ALWAYS_INLINE Vec get_center() const { return convert_to_tri().get_center(); } + + /// Returns a tuple containing the distance at which the ray intersects the triangle, and the + /// barycentric coordinates of the hit point if the given ray intersects the triangle, otherwise + /// returns nothing. The tolerance can be adjusted to account for numerical precision issues. + [[nodiscard]] BVH_ALWAYS_INLINE std::optional> intersect( + const Ray& ray, + T tolerance = -std::numeric_limits::epsilon()) const; +}; + +template +std::optional> PrecomputedTri::intersect(const Ray& ray, T tolerance) const { + auto c = p0 - ray.org; + auto r = cross(ray.dir, c); + auto inv_det = static_cast(1.) / dot(n, ray.dir); + + auto u = dot(r, e2) * inv_det; + auto v = dot(r, e1) * inv_det; + auto w = static_cast(1.) - u - v; + + // These comparisons are designed to return false + // when one of t, u, or v is a NaN + if (u >= tolerance && v >= tolerance && w >= tolerance) { + auto t = dot(n, c) * inv_det; + if (t >= ray.tmin && t <= ray.tmax) + return std::make_optional(std::make_tuple(t, u, v)); + } + + return std::nullopt; +} + +} // namespace bvh::v2 + +#endif diff --git a/builtins/bvh/src/bvh/v2/vec.h b/builtins/bvh/src/bvh/v2/vec.h index 15d060a30a2ff..d0d1c0ec2c5fb 100644 --- a/builtins/bvh/src/bvh/v2/vec.h +++ b/builtins/bvh/src/bvh/v2/vec.h @@ -96,8 +96,7 @@ BVH_ALWAYS_INLINE Vec robust_max(const Vec& a, const Vec& b) { template BVH_ALWAYS_INLINE T dot(const Vec& a, const Vec& b) { - // return std::transform_reduce(a.values, a.values + N, b.values, T(0)); - return std::inner_product(a.values, a.values + N, b.values, T(0)); + return std::transform_reduce(a.values, a.values + N, b.values, T(0)); } template @@ -124,7 +123,7 @@ BVH_ALWAYS_INLINE T length(const Vec& v) { } template -BVH_ALWAYS_INLINE Vec normalize(const Vec& v) { +[[nodiscard]] BVH_ALWAYS_INLINE Vec normalize(const Vec& v) { return v * (static_cast(1.) / length(v)); } From d30d496a7cf359999002dd0341f34300deb51ce6 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 23 Sep 2026 16:03:00 +0200 Subject: [PATCH 7/7] [bvh] proper CMake target linking --- CMakeLists.txt | 1 + builtins/bvh/src/bvh/v2/CMakeLists.txt | 2 ++ geom/geom/CMakeLists.txt | 7 +++---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61a9cc38d98a8..fa136e50cedad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -416,6 +416,7 @@ else() endif() add_subdirectory (montecarlo) if(geom) + add_subdirectory (builtins/bvh) # hard coded builtin for geom/geom TODO mv to full builtin ROOT build option add_subdirectory (geom) endif() if(NOT WIN32) diff --git a/builtins/bvh/src/bvh/v2/CMakeLists.txt b/builtins/bvh/src/bvh/v2/CMakeLists.txt index 495633a3c854c..7737e0a3a517e 100644 --- a/builtins/bvh/src/bvh/v2/CMakeLists.txt +++ b/builtins/bvh/src/bvh/v2/CMakeLists.txt @@ -12,6 +12,8 @@ target_include_directories(bvh INTERFACE set_target_properties(bvh PROPERTIES CXX_STANDARD 20) +add_library(bvh::bvh ALIAS bvh) + if (BVH_BUILD_C_API) add_subdirectory(c_api) endif() diff --git a/geom/geom/CMakeLists.txt b/geom/geom/CMakeLists.txt index ae7ca776821a1..9bc8b38e10bb0 100644 --- a/geom/geom/CMakeLists.txt +++ b/geom/geom/CMakeLists.txt @@ -128,12 +128,11 @@ ROOT_STANDARD_LIBRARY_PACKAGE(Geom Hist GenVector Imt + LIBRARIES + bvh::bvh ) -target_include_directories(Geom PRIVATE res -../../builtins/bvh/src # for bvh hard-coded builtin, forked from https://github.com/madmann91/bvh/commit/66e445b92f68801a6dd8ef943fe3038976ecb4ff -# TODO: move this to a proper isolated builtin with find_package, and add ROOT builtin_bvh option -) +target_include_directories(Geom PRIVATE res) # GCC has bugs with -O3 or -Ofast that break Geom if(CMAKE_COMPILER_IS_GNUCXX) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5 OR CMAKE_SIZEOF_VOID_P LESS 8)