Skip to content

Update OBVHS to 0.3#8

Merged
DGriffin91 merged 145 commits into
mainfrom
insertion_removal
Feb 1, 2026
Merged

Update OBVHS to 0.3#8
DGriffin91 merged 145 commits into
mainfrom
insertion_removal

Conversation

@DGriffin91

@DGriffin91 DGriffin91 commented Dec 9, 2025

Copy link
Copy Markdown
Owner

Overview of changes

Features for dynamically updating a Bvh2

  • bvh.refit_all() Refits the whole BVH from the leaf nodes up. Sizing each Aabb to exactly contain its children.
  • bvh.remove_leaf() Removes and returns the leaf specified by node_id.
  • bvh.insert_leaf() Searches the tree recursively to find the best sibling for the node being inserted.
  • bvh.remove_primitive() Removes the leaf that contains the given primitive.
  • bvh.insert_primitive() Searches the tree recursively to find the best sibling for the primitive being inserted.
  • bvh.primitives_to_nodes Optional mapping from primitives back to nodes. Functions that modify the BVH will keep the mapping valid. Use with bvh.init_primitives_to_nodes() & bvh.recompute_primitives_to_nodes()
  • bvh.parents Optional mapping from node ids to parent ids. Functions that modify the BVH will keep the mapping valid. Use with bvh.init_parents() & bvh.recompute_parents()
  • bvh.reinsert_node() Find if there might be a better spot in the BVH for this node and move it there.
  • PlocBuilder::partial_rebuild() The given set of leaves and the subtrees that do not include any of the given leaves will be built into a new bvh using Ploc.
  • ReinsertionOptimizer::run_with_candidates() Restructures the BVH, optimizing given node locations.

New ray traversal functions for Bvh2 & Cwbvh.

These were created because ray_traverse_dynamic() doesn't have a unified interface between Bvh2 and Cwbvh anymore and is a requires a bit more boilerplate. (With the tradeoff being that Bvh2 ray traversal it is now much faster)

  • bvh.ray_traverse() Unchanged, used for closest hit.
  • bvh.ray_traverse_miss() Helper for miss (eg. for shadow rendering)
  • bvh.ray_traverse_anyhit() Helper for intersecting all primitives along ray (eg. for transparency)

Ways to reuse allocations

  • PlocBuilder::build_with_bvh() Builds a new BVH using the existing allocations from an old one.
  • ReinsertionOptimizer can now be initially created with ::default() and then subsequently .run() multiple times, reusing allocations.

Update to Bvh2Node

There's now two different versions of the Bvh2Node depending on the new small_bvh2_node feature.

  • When the small_bvh2_node feature is disabled, the Bvh2Node is the same size that it was previously (48 bytes) but adds 2x32 bit user meta data fields where there was previously padding.
  • When the small_bvh2_node feature is enabled, the Bvh2Node size shrinks to 32 bytes, and is cast directly to an Aabb. (Taking inspiration from parry)

Examples

  • New physics.rs example showcases how some of new features could be used for updating a Bvh2 that is being used in physics simulations.
  • New obj_cwbvh.rs example shows loading and rendering an obj. This exists mostly to render a small obj scene that was added for testing.
  • demoscene.rs, cornell_box_cwbvh.rs, obj_cwbvh.rs, and physics.rs examples can dynamically show render progress in a window.

API changes

  • Bvh2::ray_traverse_dynamic() is now a bit lower level requiring the intersection_fn to test each primitive in the node and update the ray.tmax, and hit info, and return whether to halt traversal or continue. ray_traverse_dynamic no longer yields on hits requiring the intersection_fn to control updates and traversal. This allows for a much faster method of traversal. ray_traverse(), ray_traverse_miss(), and ray_traverse_anyhit() have been added for convince and to regain some api overlap between cwbvh and bvh2.
  • uses_spatial_splits is now tracked directly on the CwBvh or Bvh2 so the respective validate() functions no longer take this parameter.

Misc

  • The Bvh2 ploc builder has had various multi-threaded and single-threaded performance improvements.
  • Various parts of bvh building/traversal use a new FastStack trait that allows for creating a stack on the stack or heap depending on initialization capacity using specialization to avoid additional branching while using the stack.
  • The ploc Bvh2 builder now sets a value for max_depth on the Bvh2. This is used to initialize the FastStack for traversal allowing for deep BVHs without overrunning the stack length. Note: Generally, very deep BVHs are indicative of something pathological in the scene (like thousands of instances perfectly overlapping geometry) that will result in a BVH that is very slow to traverse. (This isn't a limitation of OBVHS, many scenes with these kinds of issues will be slow to traverse with any BVH, and would even be slow to rasterize)
  • Bvh2::update_primitives_to_nodes() & Bvh2::init_primitives_to_nodes_if_uninit() are for computing the mapping from primitive index to node index (Ex. let node_id = primitives_to_nodes[primitive_id];). The result is stored on the Bvh2 and is used in various node insertion and removal functions. Methods that modify the Bvh2 will keep this mapping up to date if it has been initialized.
  • Bvh2::update_parents() & Bvh2::init_parents_if_uninit() are used for computing the mapping from a given node index to that node's parent for each node in the Bvh2. They are also stored on the Bvh2, used by various other functions, and kept up-to-date by methods that modify the Bvh2 once initialized.

DGriffin91 and others added 5 commits January 5, 2026 02:28
wrt non-contiguous primitive_indices (some entries are INVALID, freelist in use, etc..)
Add `PlocBuilder::full_rebuild`
@DGriffin91 DGriffin91 merged commit 267378b into main Feb 1, 2026
amirreiter pushed a commit to amirreiter/obvhs that referenced this pull request Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants