Skip to content

Commit

Permalink
added cache to BVH constructor to reuse previous memory. Removed tran…
Browse files Browse the repository at this point in the history
…slate and copy constructor, as their use cases are covered by the cache form.
  • Loading branch information
anicusan committed Nov 23, 2024
1 parent 66fac29 commit e51feb1
Show file tree
Hide file tree
Showing 8 changed files with 35 additions and 190 deletions.
1 change: 0 additions & 1 deletion benchmark/Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[deps]
AcceleratedKernels = "6a4ca0a5-0e36-4168-a932-d9be78d558f1"
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
Cthulhu = "f68482b8-f384-11e8-15f7-abe071a5a75f"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
ImplicitBVH = "932a18dc-bb55-4cd5-bdd6-1368ec9cea29"
Expand Down
6 changes: 5 additions & 1 deletion benchmark/bvh_build.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ using BenchmarkTools
using Profile
using PProf

using CUDA: CuArray
# using CUDA: CuArray


# Types used
Expand All @@ -38,6 +38,10 @@ bvh = BVH(bounding_spheres, NodeType, MortonType)
println("BVH creation including Morton encoding:")
display(@benchmark(BVH(bounding_spheres, NodeType, MortonType)))

println("BVH with cached memory reuse:")
display(@benchmark(BVH(bvh.leaves, NodeType, MortonType, 1, bvh)))


# Collect a pprof profile of the complete build
# Profile.clear()
# @profile BVH(bounding_spheres, NodeType, MortonType)
Expand Down
6 changes: 0 additions & 6 deletions docs/src/bounding_volumes.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,3 @@ ImplicitBVH.iscontact
ImplicitBVH.isintersection
ImplicitBVH.center
```

## Miscellaneous

```@docs
ImplicitBVH.translate
```
13 changes: 0 additions & 13 deletions src/bounding_volumes/bbox.jl
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,3 @@ end
center(b::BBox{T}) where T = (T(0.5) * (b.lo[1] + b.up[1]),
T(0.5) * (b.lo[2] + b.up[2]),
T(0.5) * (b.lo[3] + b.up[3]))


# Overloaded translate function
function translate(b::BBox{T}, dx) where T
dx1, dx2, dx3 = T(dx[1]), T(dx[2]), T(dx[3])
new_lo = (b.lo[1] + dx1,
b.lo[2] + dx2,
b.lo[3] + dx3)
new_up = (b.up[1] + dx1,
b.up[2] + dx2,
b.up[3] + dx3)
BBox{T}(new_lo, new_up)
end
9 changes: 0 additions & 9 deletions src/bounding_volumes/bounding_volumes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,6 @@ Get the coordinates of a bounding volume's centre, as a NTuple{3, T}.
function center end


"""
translate(b::BSphere{T}, dx) where T
translate(b::BBox{T}, dx) where T
Get a new bounding volume translated by dx; dx can be any iterable with 3 elements.
"""
function translate end


# Sub-includes
include("bsphere.jl")
include("bbox.jl")
Expand Down
9 changes: 0 additions & 9 deletions src/bounding_volumes/bsphere.jl
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,3 @@ end

# Overloaded center function
center(b::BSphere) = b.x


# Overloaded translate function
function translate(b::BSphere{T}, dx) where T
new_center = (b.x[1] + T(dx[1]),
b.x[2] + T(dx[2]),
b.x[3] + T(dx[3]))
BSphere{T}(new_center, b.r)
end
120 changes: 29 additions & 91 deletions src/build.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,11 @@ Tree Level Nodes & Leaves Build Up Traverse Down
bounding_volumes::AbstractVector{L},
node_type::Type{N}=L,
morton_type::Type{U}=UInt32,
built_level=1;
built_level=1,
cache::Union{Nothing, BVH}=nothing;
options=BVHOptions(),
) where {L, N, U <: MortonUnsigned}
# Copy constructor reusing previous memory; previous
# bounding volumes are moved to new_positions.
BVH(
prev::BVH,
new_positions::AbstractMatrix,
built_level=1;
options=BVHOptions(),
)
# Fields
- `tree::`[`ImplicitTree`](@ref)`{I <: Integer}`
- `nodes::VN <: AbstractVector`
Expand Down Expand Up @@ -116,11 +108,11 @@ bvh = BVH(bounding_spheres, BBox{Float32}, UInt32, 2)
traversal = traverse(bvh, 3, traversal)
```
Update previous BVH bounding volumes' positions and rebuild BVH *reusing previous memory*:
Reuse previous BVH memory for a new BVH (specifically, the nodes, mortons, and order vectors, but
not the leaves):
```julia
new_positions = rand(3, 5)
bvh_rebuilt = BVH(bvh, new_positions)
bvh = BVH(bounding_spheres, BBox{Float32}, UInt32, 1, bvh)
```
"""
struct BVH{
Expand Down Expand Up @@ -161,7 +153,8 @@ function BVH(
bounding_volumes::AbstractVector{L},
node_type::Type{N}=L,
morton_type::Type{U}=UInt32,
built_level=1;
built_level=1,
cache::Union{Nothing, BVH}=nothing;
options=BVHOptions(),
) where {L, N, U <: MortonUnsigned}

Expand All @@ -186,101 +179,46 @@ function BVH(
built_ilevel = compute_build_level(tree, built_level)

# Compute morton codes for the bounding volumes
mortons = similar(bounding_volumes, morton_type)
if isnothing(cache)
mortons = similar(bounding_volumes, U)
else
@argcheck eltype(cache.mortons) === U
mortons = cache.mortons
length(mortons) == numbv || resize!(mortons, numbv)
end
@inbounds morton_encode!(mortons, bounding_volumes, options)

# Compute indices that sort codes along the Z-curve - closer objects have closer Morton codes
order = similar(mortons, I)
if isnothing(cache)
order = similar(mortons, I)
else
@argcheck eltype(cache.order) === I
order = cache.order
length(order) == numbv || resize!(order, numbv)
end

if mortons isa AbstractGPUVector
AK.sortperm!(order, mortons, block_size=options.block_size)
else
sortperm!(order, mortons)
end

# Pre-allocate vector of bounding volumes for the real nodes above the bottom level
bvh_nodes = similar(bounding_volumes, N, Int(tree.real_nodes - tree.real_leaves))

# Aggregate bounding volumes up to built_ilevel
if tree.real_nodes >= 2
aggregate_oibvh!(bvh_nodes, bounding_volumes, tree, order, built_ilevel, options)
end

BVH(I(built_ilevel), tree, bvh_nodes, bounding_volumes, mortons, order)
end


# Copy constructor reusing previous memory; previous bounding volumes are moved to new_positions.
function BVH(
prev::BVH,
new_positions::AbstractMatrix,
built_level=1;
options=BVHOptions(),
)

# Ensure correctness
@argcheck size(new_positions, 1) == 3 "new_positions need 3D coordinates on first axis"
@argcheck size(new_positions, 2) == length(prev.leaves) "Different number of new_positions"
@argcheck firstindex(new_positions, 1) == 1 "BVH vector types must be 1-indexed"
@argcheck firstindex(new_positions, 2) == 1 "BVH vector types must be 1-indexed"

# Get index type from exemplar
index_type = get_index_type(options)

# Extract previous BVH fields
tree = prev.tree
bvh_nodes = prev.nodes
bounding_volumes = prev.leaves
mortons = prev.mortons
order = prev.order

# Compute level up to which tree should be built
built_ilevel = index_type(compute_build_level(tree, built_level))

# Move the centres of previous bounding volumes to the coordinates of new_positions; ensure we
# use the same type as in prev
@inbounds @inline function _move_bv!(bounding_volumes, new_positions, i)
bv = bounding_volumes[i]
c = center(bv)
T = eltype(c)

# Special-case BSphere which can be translated by just overwriting centre
if bv isa BSphere
new_x = (T(new_positions[1, i]), T(new_positions[2, i]), T(new_positions[3, i]))
bounding_volumes[i] = BSphere{T}(new_x, bv.r)
else
dx = (T(new_positions[1, i]) - c[1],
T(new_positions[2, i]) - c[2],
T(new_positions[3, i]) - c[3])
bounding_volumes[i] = translate(bv, dx)
end
end

AK.foreachindex(
bounding_volumes,
block_size=options.block_size,
scheduler=options.scheduler,
max_tasks=options.num_threads,
min_elems=options.min_boundings_per_thread,
) do i
_move_bv!(bounding_volumes, new_positions, i)
end

# Recompute Morton codes
@inbounds morton_encode!(mortons, bounding_volumes, options)

# Compute indices that sort codes along the Z-curve - closer objects have closer Morton codes
if mortons isa AbstractGPUVector
AK.merge_sortperm_lowmem!(order, mortons, block_size=options.block_size)
num_nodes = Int(tree.real_nodes - tree.real_leaves)
if isnothing(cache)
bvh_nodes = similar(bounding_volumes, N, num_nodes)
else
sortperm!(order, mortons)
@argcheck eltype(cache.nodes) === N
bvh_nodes = cache.nodes
length(bvh_nodes) == num_nodes || resize!(bvh_nodes, num_nodes)
end

# Aggregate bounding volumes up to built_ilevel
if tree.real_nodes >= 2
aggregate_oibvh!(bvh_nodes, bounding_volumes, tree, order, built_ilevel, options)
end

BVH(built_ilevel, tree, bvh_nodes, bounding_volumes, mortons, order)
BVH(I(built_ilevel), tree, bvh_nodes, bounding_volumes, mortons, order)
end


Expand Down
61 changes: 1 addition & 60 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -707,66 +707,6 @@ end
end




@testset "bvh_translate" begin

using ImplicitBVH: center

# Simple, ordered bounding spheres traversal test
bvs = [
BSphere([0., 0, 0], 0.5),
BSphere([0., 0, 1], 0.6),
BSphere([0., 0, 2], 0.5),
BSphere([0., 0, 3], 0.4),
BSphere([0., 0, 4], 0.6),
]

# Translate BVH made of BSphere
bvh = BVH(bvs)
new_positions = [
1. 0 0
1. 0 2
1. 0 4
1. 0 6
1. 0 8
]'
translated = BVH(bvh, new_positions)
@test center(translated.leaves[1]) (1, 0, 0)
@test center(translated.leaves[2]) (1, 0, 2)
@test center(translated.leaves[3]) (1, 0, 4)
@test center(translated.leaves[4]) (1, 0, 6)
@test center(translated.leaves[5]) (1, 0, 8)

# Simple, ordered bounding box traversal test
bvs = [
BBox(BSphere([0., 0, 0], 0.5)),
BBox(BSphere([0., 0, 1], 0.6)),
BBox(BSphere([0., 0, 2], 0.5)),
BBox(BSphere([0., 0, 3], 0.4)),
BBox(BSphere([0., 0, 4], 0.6)),
]

# Translate BVH made of BBox
bvh = BVH(bvs)
new_positions = [
1. 0 0
1. 0 2
1. 0 4
1. 0 6
1. 0 8
]'
translated = BVH(bvh, new_positions)
@test center(translated.leaves[1]) (1, 0, 0)
@test center(translated.leaves[2]) (1, 0, 2)
@test center(translated.leaves[3]) (1, 0, 4)
@test center(translated.leaves[4]) (1, 0, 6)
@test center(translated.leaves[5]) (1, 0, 8)
end




@testset "bvh_single_randomised" begin
# Random bounding volumes of different densities; BSphere leaves, BSphere nodes
Random.seed!(42)
Expand Down Expand Up @@ -847,6 +787,7 @@ end
BVH(bvs, BBox{Float64}, UInt32, 0.0)
BVH(bvs, BBox{Float64}, UInt32, 0.5)
BVH(bvs, BBox{Float64}, UInt32, 1.0)
BVH(bvs, BBox{Float64}, UInt32, 3, bvh)

traverse(bvh, 3)
traverse(bvh, 3, traversal)
Expand Down

0 comments on commit e51feb1

Please sign in to comment.