Skip to content

Commit 2f9107f

Browse files
authored
Merge pull request #19 from StellaOrg/df_traversal
More scalable Leaf-vs-Tree traversal
2 parents 5295798 + 8645c44 commit 2f9107f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+4234
-1634
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
name = "ImplicitBVH"
22
uuid = "932a18dc-bb55-4cd5-bdd6-1368ec9cea29"
3+
version = "0.7.0"
34
authors = ["Andrei Leonard Nicusan <[email protected]> and contributors"]
4-
version = "0.6.0"
55

66
[deps]
77
AcceleratedKernels = "6a4ca0a5-0e36-4168-a932-d9be78d558f1"
8+
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
89
ArgCheck = "dce04be8-c92d-5529-be00-80e4d2c0e197"
910
Atomix = "a9b6321e-bd34-4604-b9c9-b65b8de01458"
1011
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
@@ -14,6 +15,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1415

1516
[compat]
1617
AcceleratedKernels = "0.4"
18+
Adapt = "4.4.0"
1719
ArgCheck = "2"
1820
Atomix = "0.1, 1"
1921
DocStringExtensions = "0.9"

README.md

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@
55
[![Docs Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://stellaorg.github.io/ImplicitBVH.jl/stable)
66
[![Docs Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://stellaorg.github.io/ImplicitBVH.jl/dev)
77
[![Build Status](https://github.com/StellaOrg/ImplicitBVH.jl/workflows/CI/badge.svg)](https://github.com/StellaOrg/ImplicitBVH.jl/actions/workflows/ci.yml)
8+
[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)
89

910

1011
# ImplicitBVH.jl
1112
*High-Performance Cross-Architecture Bounding Volume Hierarchy for Collision Detection and Ray Tracing*
1213

13-
**New in v0.5.0: Ray Tracing and GPU acceleration via [AcceleratedKernels.jl](https://github.com/anicusan/AcceleratedKernels.jl)/[KernelAbstractions.jl](https://github.com/JuliaGPU/KernelAbstractions.jl) targeting all JuliaGPU backends, i.e. Nvidia CUDA, AMD ROCm, Intel oneAPI, Apple Metal.**
14-
1514
It uses an implicit bounding volume hierarchy constructed from an iterable of some geometric
1615
primitives' (e.g. triangles in a mesh) bounding volumes forming the `ImplicitTree` leaves. The leaves
17-
and merged nodes above them can have different types - e.g. `BSphere{Float64}` leaves merged into
18-
larger `BBox{Float64}`.
16+
and merged nodes above them can have different types - e.g. `BSphere{Float32}` leaves merged into
17+
larger `BBox{Float32}`.
1918

2019
The initial geometric primitives are sorted according to their Morton-encoded coordinates; the
2120
unsigned integer type used for the Morton encoding can be chosen between `UInt16`, `UInt32` and `UInt64`.
@@ -51,11 +50,11 @@ traversal = traverse(bvh)
5150
@show traversal.contacts
5251

5352
# output
54-
traversal.contacts = [(1, 2), (2, 3), (4, 5)]
53+
traversal.contacts = Tuple{Int32, Int32}[(1, 2), (2, 3), (4, 5)]
5554
```
5655

57-
Using `Float32` bounding spheres for leaves, `Float32` bounding boxes for nodes above, and `UInt32`
58-
Morton codes:
56+
Using `Float32` bounding spheres for leaves, `Float32` bounding boxes for nodes above, `UInt32`
57+
Morton codes and `Int32` indices:
5958

6059
```julia
6160
using ImplicitBVH
@@ -71,29 +70,28 @@ bounding_spheres = [
7170
]
7271

7372
# Build BVH
74-
bvh = BVH(bounding_spheres, BBox{Float32}, UInt32)
73+
options = BVHOptions(index=Int32, morton=DefaultMortonAlgorithm(UInt32))
74+
bvh = BVH(bounding_spheres, BBox{Float32}, options=options)
7575

7676
# Traverse BVH for contact detection
77-
traversal = traverse(bvh)
77+
traversal = traverse(bvh, options=options)
7878
@show traversal.contacts
7979

8080
# output
8181
traversal.contacts = [(1, 2), (2, 3), (4, 5)]
8282
```
8383

84-
Build BVH up to level 2 and start traversing down from level 3, reusing the previous traversal
85-
cache:
84+
Update previous BVH bounding volumes' positions and rebuild BVH *reusing previous memory*:
8685

8786
```julia
88-
bvh = BVH(bounding_spheres, BBox{Float32}, UInt32, 2)
89-
traversal = traverse(bvh, 3, traversal)
90-
```
87+
# Use different numbers of threads (on CPUs) and block size (on GPUs)
88+
options = BVHOptions(num_threads=16, block_size=512)
9189

92-
Update previous BVH bounding volumes' positions and rebuild BVH *reusing previous memory*:
90+
# ...update bvh.leaves positions in-place, then...
91+
bvh = BVH(bvh.leaves, BBox{Float32}, cache=bvh, options=options)
9392

94-
```julia
95-
new_positions = rand(3, 5)
96-
bvh_rebuilt = BVH(bvh, new_positions)
93+
# Reuse previous traversal cache memory
94+
traversal = traverse(bvh, LVTTraversal(), cache=traversal)
9795
```
9896

9997
Compute contacts between two different BVH trees (e.g. two different robotic parts):
@@ -115,18 +113,11 @@ bounding_spheres2 = [
115113
]
116114

117115
# Build BVHs using bounding boxes for nodes
118-
bvh1 = BVH(bounding_spheres1, BBox{Float32}, UInt32)
119-
bvh2 = BVH(bounding_spheres2, BBox{Float32}, UInt32)
116+
bvh1 = BVH(bounding_spheres1, BBox{Float32})
117+
bvh2 = BVH(bounding_spheres2, BBox{Float32})
120118

121119
# Traverse BVH for contact detection
122-
traversal = traverse(
123-
bvh1,
124-
bvh2,
125-
default_start_level(bvh1),
126-
default_start_level(bvh2),
127-
# previous_traversal_cache,
128-
# options=BVHOptions(),
129-
)
120+
traversal = traverse(bvh1, bvh2)
130121
```
131122

132123
Check out the `benchmark` folder for an example traversing an STL model.
@@ -153,7 +144,7 @@ bounding_spheres = ROCArray([
153144
])
154145

155146
# Build BVH
156-
bvh = BVH(bounding_spheres, BBox{Float32}, UInt32)
147+
bvh = BVH(bounding_spheres, BBox{Float32})
157148

158149
# Traverse BVH for contact detection
159150
traversal = traverse(bvh)
@@ -179,7 +170,7 @@ mesh = load("xyzrgb_dragon.obj")
179170
bounding_spheres = [BSphere{Float32}(tri) for tri in mesh]
180171

181172
# Build BVH
182-
bvh = BVH(bounding_spheres, BBox{Float32}, UInt32)
173+
bvh = BVH(bounding_spheres, BBox{Float32})
183174

184175
# Generate some rays
185176
points = rand(Float32, 3, 1000)

benchmark/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ Cthulhu = "f68482b8-f384-11e8-15f7-abe071a5a75f"
55
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
66
ImplicitBVH = "932a18dc-bb55-4cd5-bdd6-1368ec9cea29"
77
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
8+
KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c"
89
MeshIO = "7269a6da-0436-5bbc-96c2-40638cbb6118"
10+
Metal = "dde4c033-4e86-420c-a63e-0dd931031962"
911
PProf = "e4faabce-9ead-11e9-39d9-4379958e3056"
1012
Profile = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"
1113
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"

benchmark/bvh_build.jl

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ using PProf
1818

1919

2020
# Types used
21-
const LeafType = BSphere{Float32}
22-
const NodeType = BBox{Float32}
21+
const FloatType = Float32
22+
const LeafType = BSphere{FloatType}
23+
const NodeType = BBox{FloatType}
2324
const MortonType = UInt32
25+
const IndexType = Int32
26+
options = BVHOptions(index=IndexType, morton=DefaultMortonAlgorithm(MortonType))
2427

2528

2629
# Load mesh and compute bounding spheres for each triangle. Can download mesh from:
@@ -32,21 +35,26 @@ bounding_spheres = [LeafType(tri) for tri in mesh]
3235
# bounding_spheres = CuArray(bounding_spheres)
3336

3437
# Pre-compile BVH build
35-
bvh = BVH(bounding_spheres, NodeType, MortonType)
38+
bvh = BVH(bounding_spheres, NodeType, options=options)
3639

3740
# Benchmark BVH creation including Morton encoding
3841
println("BVH creation including Morton encoding:")
39-
display(@benchmark(BVH(bounding_spheres, NodeType, MortonType)))
42+
display(@benchmark BVH(bounding_spheres, NodeType, options=options))
4043

4144
println("BVH with cached memory reuse:")
42-
display(@benchmark(BVH(bvh.leaves, NodeType, MortonType, 1, bvh)))
45+
display(@benchmark BVH(bvh.leaves, NodeType, cache=bvh, options=options))
4346

4447

45-
# Collect a pprof profile of the complete build
48+
# # Collect a pprof profile of the complete build
49+
# BVH(bvh.leaves, NodeType, options=options)
4650
# Profile.clear()
47-
# @profile BVH(bounding_spheres, NodeType, MortonType)
48-
49-
# Export pprof profile and open interactive profiling web interface.
51+
# @profile begin
52+
# for _ in 1:100
53+
# BVH(bounding_spheres, NodeType, options=options)
54+
# end
55+
# end
56+
#
57+
# # Export pprof profile and open interactive profiling web interface.
5058
# pprof(; out="bvh_build.pb.gz")
5159

5260

benchmark/bvh_contact.jl

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,17 @@ using BenchmarkTools
1414
using Profile
1515
using PProf
1616

17+
# using Metal: MtlArray
18+
1719

1820
# Types used
19-
const LeafType = BSphere{Float32}
20-
const NodeType = BBox{Float32}
21+
const FloatType = Float32
22+
const LeafType = BSphere{FloatType}
23+
const NodeType = BBox{FloatType}
2124
const MortonType = UInt32
25+
const IndexType = Int32
26+
const alg = LVTTraversal()
27+
options = BVHOptions(index=IndexType, morton=DefaultMortonAlgorithm(MortonType))
2228

2329

2430
# Load mesh and compute bounding spheres for each triangle. Can download mesh from:
@@ -28,32 +34,26 @@ display(mesh)
2834
@show Threads.nthreads()
2935

3036
bounding_spheres = [LeafType(tri) for tri in mesh]
37+
# bounding_spheres = MtlArray(bounding_spheres)
3138

3239
# Pre-compile BVH traversal
33-
bvh = BVH(bounding_spheres, NodeType, MortonType)
34-
@show traversal = traverse(bvh)
35-
36-
# Print algorithmic efficiency
37-
eff = traversal.num_checks / (length(bounding_spheres) * length(bounding_spheres) / 2)
38-
println("Did $eff of the total checks needed for brute-force contact detection")
40+
bvh = BVH(bounding_spheres, NodeType, options=options)
41+
@show traversal = traverse(bvh, alg)
3942

4043
# Benchmark BVH traversal anew
41-
println("BVH traversal with dynamic buffer resizing:")
42-
display(@benchmark(traverse(bvh), samples=100))
44+
println("BVH traversal:")
45+
display(@benchmark traverse(bvh, alg))
4346

44-
# Benchmark BVH creation reusing previous cache
45-
println("BVH traversal without dynamic buffer resizing:")
46-
display(@benchmark(traverse(bvh, bvh.tree.levels ÷ 2, traversal), samples=100))
4747

48-
# Collect a pprof profile
48+
# # Collect a pprof profile
4949
# Profile.clear()
5050
# @profile begin
51-
# for _ in 1:1000
52-
# traverse(bvh, bvh.tree.levels ÷ 2, traversal)
51+
# for _ in 1:10
52+
# traverse(bvh, alg)
5353
# end
5454
# end
55-
56-
# Export pprof profile and open interactive profiling web interface.
55+
#
56+
# # Export pprof profile and open interactive profiling web interface.
5757
# pprof(; out="bvh_contact.pb.gz")
5858

5959

benchmark/bvh_contact_pair.jl

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@ using BenchmarkTools
1414
using Profile
1515
using PProf
1616

17-
using CUDA: CuArray
17+
# using Metal: MtlArray
1818

1919

2020
# Types used
21-
const LeafType = BSphere{Float32}
22-
const NodeType = BBox{Float32}
21+
const FloatType = Float32
22+
const LeafType = BSphere{FloatType}
23+
const NodeType = BBox{FloatType}
2324
const MortonType = UInt32
25+
const IndexType = Int32
26+
const alg = LVTTraversal()
27+
options = BVHOptions(index=IndexType, morton=DefaultMortonAlgorithm(MortonType))
2428

2529

2630
# Load mesh and compute bounding spheres for each triangle. Can download mesh from:
@@ -30,29 +34,22 @@ display(mesh)
3034
@show Threads.nthreads()
3135

3236
bounding_spheres = [LeafType(tri) for tri in mesh]
33-
bounding_spheres = CuArray(bounding_spheres)
37+
# bounding_spheres = MtlArray(bounding_spheres)
3438

3539
# Pre-compile BVH traversal
36-
bvh = BVH(bounding_spheres, NodeType, MortonType)
37-
@show traversal = traverse(bvh, bvh)
40+
bvh = BVH(bounding_spheres, NodeType, options=options)
41+
@show traversal = traverse(bvh, bvh, alg)
3842

39-
# Print algorithmic efficiency
40-
eff = traversal.num_checks / length(bounding_spheres)^2
41-
println("Did $eff of the total checks needed for brute-force contact detection")
43+
# Benchmark BVH traversal anew
44+
println("BVH traversal with dynamic buffer resizing:")
45+
display(@benchmark traverse(bvh, bvh, alg))
4246

43-
# # Benchmark BVH traversal anew
44-
# println("BVH traversal with dynamic buffer resizing:")
45-
# display(@benchmark(traverse(bvh, bvh), samples=100))
46-
#
47-
# # Benchmark BVH creation reusing previous cache
48-
# println("BVH traversal without dynamic buffer resizing:")
49-
# display(@benchmark(traverse(bvh, bvh, bvh.tree.levels ÷ 2, bvh.tree.levels ÷ 2, traversal), samples=100))
5047

5148
# Collect a pprof profile
5249
# Profile.clear()
5350
# @profile begin
54-
# for _ in 1:1000
55-
# traverse(bvh, bvh.tree.levels ÷ 2, traversal)
51+
# for _ in 1:10
52+
# traverse(bvh, bvh, cache=traversal)
5653
# end
5754
# end
5855

benchmark/bvh_extended.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using ImplicitBVH
2+
using ImplicitBVH: BSphere, BBox
3+
4+
using MeshIO
5+
using FileIO
6+
7+
using BenchmarkTools
8+
using Profile
9+
using PProf
10+
11+
12+
# Types used
13+
const LeafType = BSphere{Float32}
14+
const NodeType = BBox{Float32}
15+
options = BVHOptions(index_exemplar=Int32(0), morton=ExtendedMortonAlgorithm(UInt32(0)))
16+
17+
18+
# Load mesh and compute bounding spheres for each triangle. Can download mesh from:
19+
# https://github.com/alecjacobson/common-3d-test-models/blob/master/data/xyzrgb_dragon.obj
20+
mesh = load(joinpath(@__DIR__, "xyzrgb_dragon.obj"))
21+
display(mesh)
22+
23+
bounding_spheres = [LeafType(tri) for tri in mesh]
24+
25+
# Pre-compile BVH build
26+
bvh = BVH(bounding_spheres, NodeType, options=options)

0 commit comments

Comments
 (0)