Add Chromium-only Blender WebEngine parity work
This commit is contained in:
77
blender-5.2.0/intern/cycles/bvh/CMakeLists.txt
Normal file
77
blender-5.2.0/intern/cycles/bvh/CMakeLists.txt
Normal file
@@ -0,0 +1,77 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
octree.cpp
|
||||
bvh.cpp
|
||||
bvh2.cpp
|
||||
binning.cpp
|
||||
build.cpp
|
||||
embree.cpp
|
||||
hiprt.cpp
|
||||
multi.cpp
|
||||
node.cpp
|
||||
optix.cpp
|
||||
sort.cpp
|
||||
split.cpp
|
||||
unaligned.cpp
|
||||
)
|
||||
|
||||
set(SRC_METAL
|
||||
metal.mm
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_DEVICE_METAL)
|
||||
list(APPEND SRC
|
||||
${SRC_METAL}
|
||||
)
|
||||
add_definitions(-DWITH_METAL)
|
||||
endif()
|
||||
|
||||
set(SRC_HEADERS
|
||||
octree.h
|
||||
bvh.h
|
||||
bvh2.h
|
||||
binning.h
|
||||
build.h
|
||||
embree.h
|
||||
hiprt.h
|
||||
multi.h
|
||||
node.h
|
||||
optix.h
|
||||
params.h
|
||||
sort.h
|
||||
split.h
|
||||
unaligned.h
|
||||
metal.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PUBLIC cycles_scene
|
||||
PUBLIC cycles_util
|
||||
PUBLIC bf::dependencies::optional::embree
|
||||
)
|
||||
|
||||
include_directories(${INC})
|
||||
include_directories(SYSTEM ${INC_SYS})
|
||||
|
||||
if(WITH_CYCLES_EMBREE)
|
||||
list(APPEND LIB
|
||||
${EMBREE_LIBRARIES}
|
||||
)
|
||||
if(EMBREE_SYCL_SUPPORT)
|
||||
list(APPEND LIB
|
||||
${SYCL_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
cycles_add_library(cycles_bvh "${LIB}" ${SRC} ${SRC_HEADERS})
|
||||
277
blender-5.2.0/intern/cycles/bvh/binning.cpp
Normal file
277
blender-5.2.0/intern/cycles/bvh/binning.cpp
Normal file
@@ -0,0 +1,277 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2011 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2012-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from Intel Corporation. */
|
||||
|
||||
// #define __KERNEL_SSE__
|
||||
|
||||
#include "bvh/binning.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "util/algorithm.h"
|
||||
#include "util/boundbox.h"
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* SSE replacements */
|
||||
|
||||
__forceinline void prefetch_L1(const void * /*ptr*/) {}
|
||||
__forceinline void prefetch_L2(const void * /*ptr*/) {}
|
||||
__forceinline void prefetch_L3(const void * /*ptr*/) {}
|
||||
__forceinline void prefetch_NTA(const void * /*ptr*/) {}
|
||||
|
||||
template<size_t src> __forceinline float extract(const int4 &b)
|
||||
{
|
||||
return b[src];
|
||||
}
|
||||
template<size_t dst> __forceinline const float4 insert(const float4 &a, const float b)
|
||||
{
|
||||
float4 r = a;
|
||||
r[dst] = b;
|
||||
return r;
|
||||
}
|
||||
|
||||
__forceinline int get_best_dimension(const float4 &bestSAH)
|
||||
{
|
||||
// return (int)__bsf(movemask(reduce_min(bestSAH) == bestSAH));
|
||||
|
||||
const float minSAH = min(bestSAH.x, min(bestSAH.y, bestSAH.z));
|
||||
|
||||
if (bestSAH.x == minSAH) {
|
||||
return 0;
|
||||
}
|
||||
if (bestSAH.y == minSAH) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* BVH Object Binning */
|
||||
|
||||
BVHObjectBinning::BVHObjectBinning(const BVHRange &job,
|
||||
BVHReference *prims,
|
||||
const BVHUnaligned *unaligned_heuristic,
|
||||
const Transform *aligned_space)
|
||||
: BVHRange(job),
|
||||
splitSAH(FLT_MAX),
|
||||
dim(0),
|
||||
pos(0),
|
||||
unaligned_heuristic_(unaligned_heuristic),
|
||||
aligned_space_(aligned_space)
|
||||
{
|
||||
if (aligned_space_ == nullptr) {
|
||||
bounds_ = bounds();
|
||||
cent_bounds_ = cent_bounds();
|
||||
}
|
||||
else {
|
||||
/* TODO(sergey): With some additional storage we can avoid
|
||||
* need in re-calculating this.
|
||||
*/
|
||||
bounds_ = unaligned_heuristic->compute_aligned_boundbox(
|
||||
*this, prims, *aligned_space, ¢_bounds_);
|
||||
}
|
||||
|
||||
/* compute number of bins to use and precompute scaling factor for binning */
|
||||
num_bins = min(size_t(MAX_BINS), size_t(4.0f + 0.05f * size()));
|
||||
scale = safe_divide(make_float3((float)num_bins), cent_bounds_.size());
|
||||
|
||||
/* initialize binning counter and bounds */
|
||||
BoundBox bin_bounds[MAX_BINS][4]; /* bounds for every bin in every dimension */
|
||||
int4 bin_count[MAX_BINS]; /* number of primitives mapped to bin */
|
||||
|
||||
for (size_t i = 0; i < num_bins; i++) {
|
||||
bin_count[i] = make_int4(0);
|
||||
bin_bounds[i][0] = bin_bounds[i][1] = bin_bounds[i][2] = BoundBox::empty;
|
||||
}
|
||||
|
||||
/* map geometry to bins, unrolled once */
|
||||
{
|
||||
int64_t i;
|
||||
|
||||
for (i = 0; i < int64_t(size()) - 1; i += 2) {
|
||||
prefetch_L2(&prims[start() + i + 8]);
|
||||
|
||||
/* map even and odd primitive to bin */
|
||||
const BVHReference &prim0 = prims[start() + i + 0];
|
||||
const BVHReference &prim1 = prims[start() + i + 1];
|
||||
|
||||
const BoundBox bounds0 = get_prim_bounds(prim0);
|
||||
const BoundBox bounds1 = get_prim_bounds(prim1);
|
||||
|
||||
const int4 bin0 = get_bin(bounds0);
|
||||
const int4 bin1 = get_bin(bounds1);
|
||||
|
||||
/* increase bounds for bins for even primitive */
|
||||
const int b00 = (int)extract<0>(bin0);
|
||||
bin_count[b00][0]++;
|
||||
bin_bounds[b00][0].grow(bounds0);
|
||||
const int b01 = (int)extract<1>(bin0);
|
||||
bin_count[b01][1]++;
|
||||
bin_bounds[b01][1].grow(bounds0);
|
||||
const int b02 = (int)extract<2>(bin0);
|
||||
bin_count[b02][2]++;
|
||||
bin_bounds[b02][2].grow(bounds0);
|
||||
|
||||
/* increase bounds of bins for odd primitive */
|
||||
const int b10 = (int)extract<0>(bin1);
|
||||
bin_count[b10][0]++;
|
||||
bin_bounds[b10][0].grow(bounds1);
|
||||
const int b11 = (int)extract<1>(bin1);
|
||||
bin_count[b11][1]++;
|
||||
bin_bounds[b11][1].grow(bounds1);
|
||||
const int b12 = (int)extract<2>(bin1);
|
||||
bin_count[b12][2]++;
|
||||
bin_bounds[b12][2].grow(bounds1);
|
||||
}
|
||||
|
||||
/* for uneven number of primitives */
|
||||
if (i < int64_t(size())) {
|
||||
/* map primitive to bin */
|
||||
const BVHReference &prim0 = prims[start() + i];
|
||||
const BoundBox bounds0 = get_prim_bounds(prim0);
|
||||
const int4 bin0 = get_bin(bounds0);
|
||||
|
||||
/* increase bounds of bins */
|
||||
const int b00 = (int)extract<0>(bin0);
|
||||
bin_count[b00][0]++;
|
||||
bin_bounds[b00][0].grow(bounds0);
|
||||
const int b01 = (int)extract<1>(bin0);
|
||||
bin_count[b01][1]++;
|
||||
bin_bounds[b01][1].grow(bounds0);
|
||||
const int b02 = (int)extract<2>(bin0);
|
||||
bin_count[b02][2]++;
|
||||
bin_bounds[b02][2].grow(bounds0);
|
||||
}
|
||||
}
|
||||
|
||||
/* sweep from right to left and compute parallel prefix of merged bounds */
|
||||
float4 r_area[MAX_BINS]; /* area of bounds of primitives on the right */
|
||||
float4 r_count[MAX_BINS]; /* number of primitives on the right */
|
||||
int4 count = make_int4(0);
|
||||
|
||||
BoundBox bx = BoundBox::empty;
|
||||
BoundBox by = BoundBox::empty;
|
||||
BoundBox bz = BoundBox::empty;
|
||||
|
||||
for (size_t i = num_bins - 1; i > 0; i--) {
|
||||
count = count + bin_count[i];
|
||||
r_count[i] = blocks(count);
|
||||
|
||||
bx = merge(bx, bin_bounds[i][0]);
|
||||
r_area[i][0] = bx.half_area();
|
||||
by = merge(by, bin_bounds[i][1]);
|
||||
r_area[i][1] = by.half_area();
|
||||
bz = merge(bz, bin_bounds[i][2]);
|
||||
r_area[i][2] = bz.half_area();
|
||||
r_area[i][3] = r_area[i][2];
|
||||
}
|
||||
|
||||
/* sweep from left to right and compute SAH */
|
||||
int4 ii = make_int4(1);
|
||||
float4 bestSAH = make_float4(FLT_MAX);
|
||||
int4 bestSplit = make_int4(-1);
|
||||
|
||||
count = make_int4(0);
|
||||
|
||||
bx = BoundBox::empty;
|
||||
by = BoundBox::empty;
|
||||
bz = BoundBox::empty;
|
||||
|
||||
for (size_t i = 1; i < num_bins; i++, ii += make_int4(1)) {
|
||||
count = count + bin_count[i - 1];
|
||||
|
||||
bx = merge(bx, bin_bounds[i - 1][0]);
|
||||
const float Ax = bx.half_area();
|
||||
by = merge(by, bin_bounds[i - 1][1]);
|
||||
const float Ay = by.half_area();
|
||||
bz = merge(bz, bin_bounds[i - 1][2]);
|
||||
const float Az = bz.half_area();
|
||||
|
||||
const float4 lCount = blocks(count);
|
||||
const float4 lArea = make_float4(Ax, Ay, Az, Az);
|
||||
const float4 sah = lArea * lCount + r_area[i] * r_count[i];
|
||||
|
||||
bestSplit = select(sah < bestSAH, ii, bestSplit);
|
||||
bestSAH = min(sah, bestSAH);
|
||||
}
|
||||
|
||||
const int4 mask = make_float4(cent_bounds_.size()) <= zero_float4();
|
||||
bestSAH = insert<3>(select(mask, make_float4(FLT_MAX), bestSAH), FLT_MAX);
|
||||
|
||||
/* find best dimension */
|
||||
dim = get_best_dimension(bestSAH);
|
||||
splitSAH = bestSAH[dim];
|
||||
pos = bestSplit[dim];
|
||||
leafSAH = bounds_.half_area() * blocks(size());
|
||||
}
|
||||
|
||||
void BVHObjectBinning::split(BVHReference *prims,
|
||||
BVHObjectBinning &left_o,
|
||||
BVHObjectBinning &right_o) const
|
||||
{
|
||||
const size_t N = size();
|
||||
|
||||
BoundBox lgeom_bounds = BoundBox::empty;
|
||||
BoundBox rgeom_bounds = BoundBox::empty;
|
||||
BoundBox lcent_bounds = BoundBox::empty;
|
||||
BoundBox rcent_bounds = BoundBox::empty;
|
||||
|
||||
int64_t l = 0;
|
||||
int64_t r = N - 1;
|
||||
|
||||
while (l <= r) {
|
||||
prefetch_L2(&prims[start() + l + 8]);
|
||||
prefetch_L2(&prims[start() + r - 8]);
|
||||
|
||||
const BVHReference prim = prims[start() + l];
|
||||
const BoundBox unaligned_bounds = get_prim_bounds(prim);
|
||||
const float3 unaligned_center = unaligned_bounds.center2();
|
||||
const float3 center = prim.bounds().center2();
|
||||
|
||||
if (get_bin(unaligned_center)[dim] < pos) {
|
||||
lgeom_bounds.grow(prim.bounds());
|
||||
lcent_bounds.grow(center);
|
||||
l++;
|
||||
}
|
||||
else {
|
||||
rgeom_bounds.grow(prim.bounds());
|
||||
rcent_bounds.grow(center);
|
||||
swap(prims[start() + l], prims[start() + r]);
|
||||
r--;
|
||||
}
|
||||
}
|
||||
/* finish */
|
||||
if (l != 0 && N - 1 - r != 0) {
|
||||
right_o = BVHObjectBinning(BVHRange(rgeom_bounds, rcent_bounds, start() + l, N - 1 - r),
|
||||
prims);
|
||||
left_o = BVHObjectBinning(BVHRange(lgeom_bounds, lcent_bounds, start(), l), prims);
|
||||
return;
|
||||
}
|
||||
|
||||
/* object medium split if we did not make progress, can happen when all
|
||||
* primitives have same centroid */
|
||||
lgeom_bounds = BoundBox::empty;
|
||||
rgeom_bounds = BoundBox::empty;
|
||||
lcent_bounds = BoundBox::empty;
|
||||
rcent_bounds = BoundBox::empty;
|
||||
|
||||
for (size_t i = 0; i < N / 2; i++) {
|
||||
lgeom_bounds.grow(prims[start() + i].bounds());
|
||||
lcent_bounds.grow(prims[start() + i].bounds().center2());
|
||||
}
|
||||
|
||||
for (size_t i = N / 2; i < N; i++) {
|
||||
rgeom_bounds.grow(prims[start() + i].bounds());
|
||||
rcent_bounds.grow(prims[start() + i].bounds().center2());
|
||||
}
|
||||
|
||||
right_o = BVHObjectBinning(BVHRange(rgeom_bounds, rcent_bounds, start() + N / 2, N / 2 + N % 2),
|
||||
prims);
|
||||
left_o = BVHObjectBinning(BVHRange(lgeom_bounds, lcent_bounds, start(), N / 2), prims);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
98
blender-5.2.0/intern/cycles/bvh/binning.h
Normal file
98
blender-5.2.0/intern/cycles/bvh/binning.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2011 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2012-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from Intel Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bvh/params.h"
|
||||
#include "bvh/unaligned.h"
|
||||
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHBuild;
|
||||
|
||||
/* Single threaded object binner. Finds the split with the best SAH heuristic
|
||||
* by testing for each dimension multiple partitionings for regular spaced
|
||||
* partition locations. A partitioning for a partition location is computed,
|
||||
* by putting primitives whose centroid is on the left and right of the split
|
||||
* location to different sets. The SAH is evaluated by computing the number of
|
||||
* blocks occupied by the primitives in the partitions. */
|
||||
|
||||
class BVHObjectBinning : public BVHRange {
|
||||
public:
|
||||
__forceinline BVHObjectBinning() : leafSAH(FLT_MAX) {}
|
||||
|
||||
BVHObjectBinning(const BVHRange &job,
|
||||
BVHReference *prims,
|
||||
const BVHUnaligned *unaligned_heuristic = nullptr,
|
||||
const Transform *aligned_space = nullptr);
|
||||
|
||||
void split(BVHReference *prims, BVHObjectBinning &left_o, BVHObjectBinning &right_o) const;
|
||||
|
||||
__forceinline const BoundBox &unaligned_bounds()
|
||||
{
|
||||
return bounds_;
|
||||
}
|
||||
|
||||
float splitSAH; /* SAH cost of the best split */
|
||||
float leafSAH; /* SAH cost of creating a leaf */
|
||||
|
||||
protected:
|
||||
int dim; /* best split dimension */
|
||||
int pos; /* best split position */
|
||||
size_t num_bins; /* actual number of bins to use */
|
||||
float3 scale; /* scaling factor to compute bin */
|
||||
|
||||
/* Effective bounds and centroid bounds. */
|
||||
BoundBox bounds_;
|
||||
BoundBox cent_bounds_;
|
||||
|
||||
const BVHUnaligned *unaligned_heuristic_;
|
||||
const Transform *aligned_space_;
|
||||
|
||||
enum { MAX_BINS = 32 };
|
||||
enum { LOG_BLOCK_SIZE = 2 };
|
||||
|
||||
/* computes the bin numbers for each dimension for a box. */
|
||||
__forceinline int4 get_bin(const BoundBox &box) const
|
||||
{
|
||||
const int4 a = make_int4((box.center2() - cent_bounds_.min) * scale - make_float3(0.5f));
|
||||
const int4 mn = make_int4(0);
|
||||
const int4 mx = make_int4((int)num_bins - 1);
|
||||
|
||||
return clamp(a, mn, mx);
|
||||
}
|
||||
|
||||
/* computes the bin numbers for each dimension for a point. */
|
||||
__forceinline int4 get_bin(const float3 &c) const
|
||||
{
|
||||
return make_int4((c - cent_bounds_.min) * scale - make_float3(0.5f));
|
||||
}
|
||||
|
||||
/* compute the number of blocks occupied for each dimension. */
|
||||
__forceinline float4 blocks(const int4 &a) const
|
||||
{
|
||||
return make_float4((a + make_int4((1 << LOG_BLOCK_SIZE) - 1)) >> LOG_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
/* compute the number of blocks occupied in one dimension. */
|
||||
__forceinline int blocks(const size_t a) const
|
||||
{
|
||||
return (int)((a + ((1LL << LOG_BLOCK_SIZE) - 1)) >> LOG_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
__forceinline BoundBox get_prim_bounds(const BVHReference &prim) const
|
||||
{
|
||||
if (aligned_space_ == nullptr) {
|
||||
return prim.bounds();
|
||||
}
|
||||
return unaligned_heuristic_->compute_aligned_prim_boundbox(prim, *aligned_space_);
|
||||
}
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
1291
blender-5.2.0/intern/cycles/bvh/build.cpp
Normal file
1291
blender-5.2.0/intern/cycles/bvh/build.cpp
Normal file
File diff suppressed because it is too large
Load Diff
144
blender-5.2.0/intern/cycles/bvh/build.h
Normal file
144
blender-5.2.0/intern/cycles/bvh/build.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#include "bvh/params.h"
|
||||
#include "bvh/unaligned.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/task.h"
|
||||
#include "util/unique_ptr.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Boundbox;
|
||||
class BVHBuildTask;
|
||||
class BVHNode;
|
||||
class BVHSpatialSplitBuildTask;
|
||||
class BVHParams;
|
||||
class InnerNode;
|
||||
class Geometry;
|
||||
class Hair;
|
||||
class Mesh;
|
||||
class Object;
|
||||
class PointCloud;
|
||||
class Progress;
|
||||
|
||||
/* BVH Builder */
|
||||
|
||||
class BVHBuild {
|
||||
public:
|
||||
/* Constructor/Destructor */
|
||||
BVHBuild(const vector<Object *> &objects,
|
||||
array<int> &prim_type,
|
||||
array<int> &prim_index,
|
||||
array<int> &prim_object,
|
||||
array<float2> &prim_time,
|
||||
const BVHParams ¶ms,
|
||||
Progress &progress);
|
||||
~BVHBuild();
|
||||
|
||||
unique_ptr<BVHNode> run();
|
||||
|
||||
protected:
|
||||
friend class BVHMixedSplit;
|
||||
friend class BVHObjectSplit;
|
||||
friend class BVHSpatialSplit;
|
||||
friend class BVHBuildTask;
|
||||
friend class BVHSpatialSplitBuildTask;
|
||||
friend class BVHObjectBinning;
|
||||
|
||||
/* Adding references. */
|
||||
void add_reference_triangles(BoundBox &root,
|
||||
BoundBox ¢er,
|
||||
Mesh *mesh,
|
||||
const int object_index);
|
||||
void add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair, const int object_index);
|
||||
void add_reference_points(BoundBox &root, BoundBox ¢er, PointCloud *pointcloud, const int i);
|
||||
void add_reference_geometry(BoundBox &root,
|
||||
BoundBox ¢er,
|
||||
Geometry *geom,
|
||||
const int object_index);
|
||||
void add_reference_object(BoundBox &root, BoundBox ¢er, Object *ob, const int i);
|
||||
void add_references(BVHRange &root);
|
||||
|
||||
/* Building. */
|
||||
unique_ptr<BVHNode> build_node(const BVHRange &range,
|
||||
vector<BVHReference> &references,
|
||||
const int level,
|
||||
BVHSpatialStorage *storage);
|
||||
unique_ptr<BVHNode> build_node(const BVHObjectBinning &range, const int level);
|
||||
unique_ptr<BVHNode> create_leaf_node(const BVHRange &range,
|
||||
const vector<BVHReference> &references);
|
||||
unique_ptr<BVHNode> create_object_leaf_nodes(const BVHReference *ref,
|
||||
const int start,
|
||||
const int num);
|
||||
|
||||
bool range_within_max_leaf_size(const BVHRange &range,
|
||||
const vector<BVHReference> &references) const;
|
||||
|
||||
/* Threads. */
|
||||
enum { THREAD_TASK_SIZE = 4096 };
|
||||
void thread_build_node(InnerNode *inner,
|
||||
const int child,
|
||||
const BVHObjectBinning &range,
|
||||
const int level);
|
||||
void thread_build_spatial_split_node(InnerNode *inner,
|
||||
const int child,
|
||||
const BVHRange &range,
|
||||
vector<BVHReference> &references,
|
||||
int level);
|
||||
thread_mutex build_mutex;
|
||||
|
||||
/* Progress. */
|
||||
void progress_update();
|
||||
|
||||
/* Tree rotations. */
|
||||
void rotate(BVHNode *node, const int max_depth);
|
||||
void rotate(BVHNode *node, const int max_depth, const int iterations);
|
||||
|
||||
/* Objects and primitive references. */
|
||||
vector<Object *> objects;
|
||||
vector<BVHReference> references;
|
||||
int num_original_references;
|
||||
|
||||
/* Output primitive indexes and objects. */
|
||||
array<int> &prim_type;
|
||||
array<int> &prim_index;
|
||||
array<int> &prim_object;
|
||||
array<float2> &prim_time;
|
||||
|
||||
bool need_prim_time;
|
||||
|
||||
/* Build parameters. */
|
||||
BVHParams params;
|
||||
|
||||
/* Progress reporting. */
|
||||
Progress &progress;
|
||||
double progress_start_time;
|
||||
size_t progress_count;
|
||||
size_t progress_total;
|
||||
size_t progress_original_total;
|
||||
|
||||
/* Spatial splitting. */
|
||||
float spatial_min_overlap;
|
||||
enumerable_thread_specific<BVHSpatialStorage> spatial_storage;
|
||||
size_t spatial_free_index;
|
||||
thread_spin_lock spatial_spin_lock;
|
||||
|
||||
/* Threads. */
|
||||
TaskPool task_pool;
|
||||
|
||||
/* Unaligned building. */
|
||||
BVHUnaligned unaligned_heuristic;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
150
blender-5.2.0/intern/cycles/bvh/bvh.cpp
Normal file
150
blender-5.2.0/intern/cycles/bvh/bvh.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#include "bvh/bvh.h"
|
||||
|
||||
#include "bvh/bvh2.h"
|
||||
#include "bvh/multi.h"
|
||||
|
||||
#ifdef WITH_EMBREE
|
||||
# include "bvh/embree.h"
|
||||
#endif
|
||||
#ifdef WITH_HIPRT
|
||||
# include "bvh/hiprt.h"
|
||||
#endif
|
||||
#ifdef WITH_METAL
|
||||
# include "bvh/metal.h"
|
||||
#endif
|
||||
#ifdef WITH_OPTIX
|
||||
# include "bvh/optix.h"
|
||||
#endif
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* BVH Parameters. */
|
||||
|
||||
const char *bvh_layout_name(BVHLayout layout)
|
||||
{
|
||||
switch (layout) {
|
||||
case BVH_LAYOUT_NONE:
|
||||
return "NONE";
|
||||
case BVH_LAYOUT_BVH2:
|
||||
return "BVH2";
|
||||
case BVH_LAYOUT_EMBREE:
|
||||
return "EMBREE";
|
||||
case BVH_LAYOUT_OPTIX:
|
||||
return "OPTIX";
|
||||
case BVH_LAYOUT_METAL:
|
||||
return "METAL";
|
||||
case BVH_LAYOUT_HIPRT:
|
||||
return "HIPRT";
|
||||
case BVH_LAYOUT_EMBREEGPU:
|
||||
return "EMBREEGPU";
|
||||
case BVH_LAYOUT_MULTI_OPTIX:
|
||||
case BVH_LAYOUT_MULTI_METAL:
|
||||
case BVH_LAYOUT_MULTI_HIPRT:
|
||||
case BVH_LAYOUT_MULTI_EMBREEGPU:
|
||||
case BVH_LAYOUT_MULTI_OPTIX_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_METAL_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_HIPRT_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_EMBREEGPU_EMBREE:
|
||||
return "MULTI";
|
||||
case BVH_LAYOUT_ALL:
|
||||
return "ALL";
|
||||
}
|
||||
LOG_DFATAL << "Unsupported BVH layout was passed.";
|
||||
return "";
|
||||
}
|
||||
|
||||
BVHLayout BVHParams::best_bvh_layout(BVHLayout requested_layout, BVHLayoutMask supported_layouts)
|
||||
{
|
||||
const BVHLayoutMask requested_layout_mask = (BVHLayoutMask)requested_layout;
|
||||
/* Check whether requested layout is supported, if so -- no need to do
|
||||
* any extra computation.
|
||||
*/
|
||||
if (supported_layouts & requested_layout_mask) {
|
||||
return requested_layout;
|
||||
}
|
||||
/* Some bit magic to get widest supported BVH layout. */
|
||||
/* This is a mask of supported BVH layouts which are narrower than the
|
||||
* requested one.
|
||||
*/
|
||||
BVHLayoutMask allowed_layouts_mask = (supported_layouts & (requested_layout_mask - 1));
|
||||
/* If the requested layout is not supported, choose from the supported layouts instead. */
|
||||
if (allowed_layouts_mask == 0) {
|
||||
allowed_layouts_mask = supported_layouts;
|
||||
}
|
||||
/* We get widest from allowed ones and convert mask to actual layout. */
|
||||
const BVHLayoutMask widest_allowed_layout_mask = __bsr((uint32_t)allowed_layouts_mask);
|
||||
return (BVHLayout)(1 << widest_allowed_layout_mask);
|
||||
}
|
||||
|
||||
/* BVH */
|
||||
|
||||
BVH::BVH(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_)
|
||||
: params(params_), geometry(geometry_), objects(objects_)
|
||||
{
|
||||
}
|
||||
|
||||
unique_ptr<BVH> BVH::create(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *device)
|
||||
{
|
||||
switch (params.bvh_layout) {
|
||||
case BVH_LAYOUT_BVH2:
|
||||
return make_unique<BVH2>(params, geometry, objects);
|
||||
case BVH_LAYOUT_EMBREE:
|
||||
case BVH_LAYOUT_EMBREEGPU:
|
||||
#ifdef WITH_EMBREE
|
||||
return make_unique<BVHEmbree>(params, geometry, objects);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
case BVH_LAYOUT_OPTIX:
|
||||
#ifdef WITH_OPTIX
|
||||
return make_unique<BVHOptiX>(params, geometry, objects, device);
|
||||
#else
|
||||
(void)device;
|
||||
break;
|
||||
#endif
|
||||
case BVH_LAYOUT_METAL:
|
||||
#ifdef WITH_METAL
|
||||
return bvh_metal_create(params, geometry, objects, device);
|
||||
#else
|
||||
(void)device;
|
||||
break;
|
||||
#endif
|
||||
case BVH_LAYOUT_HIPRT:
|
||||
#ifdef WITH_HIPRT
|
||||
return make_unique<BVHHIPRT>(params, geometry, objects, device);
|
||||
#else
|
||||
(void)device;
|
||||
break;
|
||||
#endif
|
||||
case BVH_LAYOUT_MULTI_OPTIX:
|
||||
case BVH_LAYOUT_MULTI_METAL:
|
||||
case BVH_LAYOUT_MULTI_HIPRT:
|
||||
case BVH_LAYOUT_MULTI_EMBREEGPU:
|
||||
case BVH_LAYOUT_MULTI_OPTIX_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_METAL_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_HIPRT_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_EMBREEGPU_EMBREE:
|
||||
return make_unique<BVHMulti>(params, geometry, objects);
|
||||
case BVH_LAYOUT_NONE:
|
||||
case BVH_LAYOUT_ALL:
|
||||
break;
|
||||
}
|
||||
LOG_DFATAL << "Requested unsupported BVH layout.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
92
blender-5.2.0/intern/cycles/bvh/bvh.h
Normal file
92
blender-5.2.0/intern/cycles/bvh/bvh.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bvh/params.h"
|
||||
#include "util/array.h"
|
||||
#include "util/types.h"
|
||||
#include "util/unique_ptr.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BoundBox;
|
||||
class BVHNode;
|
||||
class BVHParams;
|
||||
class Device;
|
||||
class DeviceScene;
|
||||
class Geometry;
|
||||
class LeafNode;
|
||||
class Object;
|
||||
class Progress;
|
||||
class Stats;
|
||||
|
||||
#define BVH_ALIGN 4096 // NOLINT
|
||||
#define TRI_NODE_SIZE 3 // NOLINT
|
||||
|
||||
/* Packed BVH
|
||||
*
|
||||
* BVH stored as it will be used for traversal on the rendering device. */
|
||||
|
||||
struct PackedBVH {
|
||||
/* BVH nodes storage, one node is 4x int4, and contains two bounding boxes,
|
||||
* and child, triangle or object indexes depending on the node type */
|
||||
array<int4> nodes;
|
||||
/* BVH leaf nodes storage. */
|
||||
array<int4> leaf_nodes;
|
||||
/* object index to BVH node index mapping for instances */
|
||||
array<int> object_node;
|
||||
/* primitive type - triangle or strand */
|
||||
array<int> prim_type;
|
||||
/* Visibility visibilities for primitives. */
|
||||
array<uint> prim_visibility;
|
||||
/* mapping from BVH primitive index to true primitive index, as primitives
|
||||
* may be duplicated due to spatial splits. -1 for instances. */
|
||||
array<int> prim_index;
|
||||
/* mapping from BVH primitive index, to the object id of that primitive. */
|
||||
array<int> prim_object;
|
||||
/* Time range of BVH primitive. */
|
||||
array<float2> prim_time;
|
||||
|
||||
/* index of the root node. */
|
||||
int root_index;
|
||||
|
||||
PackedBVH()
|
||||
{
|
||||
root_index = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/* BVH */
|
||||
|
||||
class BVH {
|
||||
public:
|
||||
BVHParams params;
|
||||
vector<Geometry *> geometry;
|
||||
vector<Object *> objects;
|
||||
|
||||
static unique_ptr<BVH> create(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *device);
|
||||
virtual ~BVH() = default;
|
||||
|
||||
virtual void replace_geometry(const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects)
|
||||
{
|
||||
this->geometry = geometry;
|
||||
this->objects = objects;
|
||||
}
|
||||
|
||||
protected:
|
||||
BVH(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
644
blender-5.2.0/intern/cycles/bvh/bvh2.cpp
Normal file
644
blender-5.2.0/intern/cycles/bvh/bvh2.cpp
Normal file
@@ -0,0 +1,644 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "bvh/bvh2.h"
|
||||
|
||||
#include "scene/hair.h"
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/object.h"
|
||||
#include "scene/pointcloud.h"
|
||||
|
||||
#include "bvh/build.h"
|
||||
#include "bvh/node.h"
|
||||
#include "bvh/unaligned.h"
|
||||
|
||||
#include "util/progress.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHStackEntry::BVHStackEntry(const BVHNode *n, const int i) : node(n), idx(i) {}
|
||||
|
||||
int BVHStackEntry::encodeIdx() const
|
||||
{
|
||||
return (node->is_leaf()) ? ~idx : idx;
|
||||
}
|
||||
|
||||
BVH2::BVH2(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_)
|
||||
: BVH(params_, geometry_, objects_)
|
||||
{
|
||||
}
|
||||
|
||||
void BVH2::build(Progress &progress, Stats * /*unused*/)
|
||||
{
|
||||
progress.set_substatus("Building BVH");
|
||||
|
||||
/* build nodes */
|
||||
BVHBuild bvh_build(objects,
|
||||
pack.prim_type,
|
||||
pack.prim_index,
|
||||
pack.prim_object,
|
||||
pack.prim_time,
|
||||
params,
|
||||
progress);
|
||||
unique_ptr<BVHNode> bvh2_root = bvh_build.run();
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* BVH builder returns tree in a binary mode (with two children per inner
|
||||
* node. Need to adopt that for a wider BVH implementations. */
|
||||
const unique_ptr<BVHNode> root = widen_children_nodes(std::move(bvh2_root));
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* pack triangles */
|
||||
progress.set_substatus("Packing BVH triangles and strands");
|
||||
pack_primitives();
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* pack nodes */
|
||||
progress.set_substatus("Packing BVH nodes");
|
||||
pack_nodes(root.get());
|
||||
}
|
||||
|
||||
void BVH2::refit(Progress &progress)
|
||||
{
|
||||
progress.set_substatus("Packing BVH primitives");
|
||||
pack_primitives();
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
progress.set_substatus("Refitting BVH nodes");
|
||||
refit_nodes();
|
||||
}
|
||||
|
||||
unique_ptr<BVHNode> BVH2::widen_children_nodes(unique_ptr<BVHNode> &&root)
|
||||
{
|
||||
return std::move(root);
|
||||
}
|
||||
|
||||
void BVH2::pack_leaf(const BVHStackEntry &e, const LeafNode *leaf)
|
||||
{
|
||||
assert(e.idx + BVH_NODE_LEAF_SIZE <= pack.leaf_nodes.size());
|
||||
int4 data[BVH_NODE_LEAF_SIZE];
|
||||
std::fill_n(data, BVH_NODE_LEAF_SIZE, zero_int4());
|
||||
if (leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) {
|
||||
/* object */
|
||||
data[0].x = ~(leaf->lo);
|
||||
data[0].y = 0;
|
||||
}
|
||||
else {
|
||||
/* triangle */
|
||||
data[0].x = leaf->lo;
|
||||
data[0].y = leaf->hi;
|
||||
}
|
||||
data[0].z = leaf->visibility;
|
||||
if (leaf->num_triangles() != 0) {
|
||||
data[0].w = pack.prim_type[leaf->lo];
|
||||
}
|
||||
|
||||
std::copy_n(data, BVH_NODE_LEAF_SIZE, &pack.leaf_nodes[e.idx]);
|
||||
}
|
||||
|
||||
void BVH2::pack_inner(const BVHStackEntry &e, const BVHStackEntry &e0, const BVHStackEntry &e1)
|
||||
{
|
||||
if (e0.node->is_unaligned || e1.node->is_unaligned) {
|
||||
pack_unaligned_inner(e, e0, e1);
|
||||
}
|
||||
else {
|
||||
pack_aligned_inner(e, e0, e1);
|
||||
}
|
||||
}
|
||||
|
||||
void BVH2::pack_aligned_inner(const BVHStackEntry &e,
|
||||
const BVHStackEntry &e0,
|
||||
const BVHStackEntry &e1)
|
||||
{
|
||||
pack_aligned_node(e.idx,
|
||||
e0.node->bounds,
|
||||
e1.node->bounds,
|
||||
e0.encodeIdx(),
|
||||
e1.encodeIdx(),
|
||||
e0.node->visibility,
|
||||
e1.node->visibility);
|
||||
}
|
||||
|
||||
void BVH2::pack_aligned_node(const int idx,
|
||||
const BoundBox &b0,
|
||||
const BoundBox &b1,
|
||||
int c0,
|
||||
int c1,
|
||||
uint visibility0,
|
||||
uint visibility1)
|
||||
{
|
||||
assert(idx + BVH_NODE_SIZE <= pack.nodes.size());
|
||||
assert(c0 < 0 || c0 < pack.nodes.size());
|
||||
assert(c1 < 0 || c1 < pack.nodes.size());
|
||||
|
||||
int4 data[BVH_NODE_SIZE] = {
|
||||
make_int4(visibility0 & ~PATH_RAY_VISIBILITY_NODE_UNALIGNED,
|
||||
visibility1 & ~PATH_RAY_VISIBILITY_NODE_UNALIGNED,
|
||||
c0,
|
||||
c1),
|
||||
make_int4(__float_as_int(b0.min.x),
|
||||
__float_as_int(b1.min.x),
|
||||
__float_as_int(b0.max.x),
|
||||
__float_as_int(b1.max.x)),
|
||||
make_int4(__float_as_int(b0.min.y),
|
||||
__float_as_int(b1.min.y),
|
||||
__float_as_int(b0.max.y),
|
||||
__float_as_int(b1.max.y)),
|
||||
make_int4(__float_as_int(b0.min.z),
|
||||
__float_as_int(b1.min.z),
|
||||
__float_as_int(b0.max.z),
|
||||
__float_as_int(b1.max.z)),
|
||||
};
|
||||
|
||||
std::copy_n(data, BVH_NODE_SIZE, &pack.nodes[idx]);
|
||||
}
|
||||
|
||||
void BVH2::pack_unaligned_inner(const BVHStackEntry &e,
|
||||
const BVHStackEntry &e0,
|
||||
const BVHStackEntry &e1)
|
||||
{
|
||||
pack_unaligned_node(e.idx,
|
||||
e0.node->get_aligned_space(),
|
||||
e1.node->get_aligned_space(),
|
||||
e0.node->bounds,
|
||||
e1.node->bounds,
|
||||
e0.encodeIdx(),
|
||||
e1.encodeIdx(),
|
||||
e0.node->visibility,
|
||||
e1.node->visibility);
|
||||
}
|
||||
|
||||
void BVH2::pack_unaligned_node(const int idx,
|
||||
const Transform &aligned_space0,
|
||||
const Transform &aligned_space1,
|
||||
const BoundBox &b0,
|
||||
const BoundBox &b1,
|
||||
int c0,
|
||||
int c1,
|
||||
uint visibility0,
|
||||
uint visibility1)
|
||||
{
|
||||
assert(idx + BVH_UNALIGNED_NODE_SIZE <= pack.nodes.size());
|
||||
assert(c0 < 0 || c0 < pack.nodes.size());
|
||||
assert(c1 < 0 || c1 < pack.nodes.size());
|
||||
|
||||
int4 data[BVH_UNALIGNED_NODE_SIZE];
|
||||
const Transform space0 = BVHUnaligned::compute_node_transform(b0, aligned_space0);
|
||||
const Transform space1 = BVHUnaligned::compute_node_transform(b1, aligned_space1);
|
||||
data[0] = make_int4(visibility0 | PATH_RAY_VISIBILITY_NODE_UNALIGNED,
|
||||
visibility1 | PATH_RAY_VISIBILITY_NODE_UNALIGNED,
|
||||
c0,
|
||||
c1);
|
||||
|
||||
data[1] = __float4_as_int4(space0.x);
|
||||
data[2] = __float4_as_int4(space0.y);
|
||||
data[3] = __float4_as_int4(space0.z);
|
||||
data[4] = __float4_as_int4(space1.x);
|
||||
data[5] = __float4_as_int4(space1.y);
|
||||
data[6] = __float4_as_int4(space1.z);
|
||||
|
||||
std::copy_n(data, BVH_UNALIGNED_NODE_SIZE, &pack.nodes[idx]);
|
||||
}
|
||||
|
||||
void BVH2::pack_nodes(const BVHNode *root)
|
||||
{
|
||||
const size_t num_nodes = root->getSubtreeSize(BVH_STAT_NODE_COUNT);
|
||||
const size_t num_leaf_nodes = root->getSubtreeSize(BVH_STAT_LEAF_COUNT);
|
||||
assert(num_leaf_nodes <= num_nodes);
|
||||
const size_t num_inner_nodes = num_nodes - num_leaf_nodes;
|
||||
size_t node_size;
|
||||
if (params.use_unaligned_nodes) {
|
||||
const size_t num_unaligned_nodes = root->getSubtreeSize(BVH_STAT_UNALIGNED_INNER_COUNT);
|
||||
node_size = (num_unaligned_nodes * BVH_UNALIGNED_NODE_SIZE) +
|
||||
(num_inner_nodes - num_unaligned_nodes) * BVH_NODE_SIZE;
|
||||
}
|
||||
else {
|
||||
node_size = num_inner_nodes * BVH_NODE_SIZE;
|
||||
}
|
||||
/* Resize arrays */
|
||||
pack.nodes.clear();
|
||||
pack.leaf_nodes.clear();
|
||||
/* For top level BVH, first merge existing BVH's so we know the offsets. */
|
||||
if (params.top_level) {
|
||||
pack_instances(node_size, num_leaf_nodes * BVH_NODE_LEAF_SIZE);
|
||||
}
|
||||
else {
|
||||
pack.nodes.resize(node_size);
|
||||
pack.leaf_nodes.resize(num_leaf_nodes * BVH_NODE_LEAF_SIZE);
|
||||
}
|
||||
|
||||
int nextNodeIdx = 0;
|
||||
int nextLeafNodeIdx = 0;
|
||||
|
||||
vector<BVHStackEntry> stack;
|
||||
stack.reserve(BVHParams::MAX_DEPTH * 2);
|
||||
if (root->is_leaf()) {
|
||||
stack.push_back(BVHStackEntry(root, nextLeafNodeIdx++));
|
||||
}
|
||||
else {
|
||||
stack.push_back(BVHStackEntry(root, nextNodeIdx));
|
||||
nextNodeIdx += root->has_unaligned() ? BVH_UNALIGNED_NODE_SIZE : BVH_NODE_SIZE;
|
||||
}
|
||||
|
||||
while (!stack.empty()) {
|
||||
const BVHStackEntry e = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
if (e.node->is_leaf()) {
|
||||
/* leaf node */
|
||||
const LeafNode *leaf = reinterpret_cast<const LeafNode *>(e.node);
|
||||
pack_leaf(e, leaf);
|
||||
}
|
||||
else {
|
||||
/* inner node */
|
||||
int idx[2];
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (e.node->get_child(i)->is_leaf()) {
|
||||
idx[i] = nextLeafNodeIdx++;
|
||||
}
|
||||
else {
|
||||
idx[i] = nextNodeIdx;
|
||||
nextNodeIdx += e.node->get_child(i)->has_unaligned() ? BVH_UNALIGNED_NODE_SIZE :
|
||||
BVH_NODE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
stack.push_back(BVHStackEntry(e.node->get_child(0), idx[0]));
|
||||
stack.push_back(BVHStackEntry(e.node->get_child(1), idx[1]));
|
||||
|
||||
pack_inner(e, stack[stack.size() - 2], stack[stack.size() - 1]);
|
||||
}
|
||||
}
|
||||
assert(node_size == nextNodeIdx);
|
||||
/* root index to start traversal at, to handle case of single leaf node */
|
||||
pack.root_index = (root->is_leaf()) ? -1 : 0;
|
||||
}
|
||||
|
||||
void BVH2::refit_nodes()
|
||||
{
|
||||
assert(!params.top_level);
|
||||
|
||||
BoundBox bbox = BoundBox::empty;
|
||||
uint visibility = 0;
|
||||
refit_node(0, (pack.root_index == -1) ? true : false, bbox, visibility);
|
||||
}
|
||||
|
||||
void BVH2::refit_node(const int idx, bool leaf, BoundBox &bbox, uint &visibility)
|
||||
{
|
||||
if (leaf) {
|
||||
/* refit leaf node */
|
||||
assert(idx + BVH_NODE_LEAF_SIZE <= pack.leaf_nodes.size());
|
||||
const int4 *data = &pack.leaf_nodes[idx];
|
||||
const int c0 = data[0].x;
|
||||
const int c1 = data[0].y;
|
||||
|
||||
refit_primitives(c0, c1, bbox, visibility);
|
||||
|
||||
/* TODO(sergey): De-duplicate with pack_leaf(). */
|
||||
int4 leaf_data[BVH_NODE_LEAF_SIZE];
|
||||
leaf_data[0].x = c0;
|
||||
leaf_data[0].y = c1;
|
||||
leaf_data[0].z = visibility;
|
||||
leaf_data[0].w = data[0].w;
|
||||
std::copy_n(leaf_data, BVH_NODE_LEAF_SIZE, &pack.leaf_nodes[idx]);
|
||||
}
|
||||
else {
|
||||
assert(idx + BVH_NODE_SIZE <= pack.nodes.size());
|
||||
|
||||
const int4 *data = &pack.nodes[idx];
|
||||
const bool is_unaligned = (data[0].x & PATH_RAY_VISIBILITY_NODE_UNALIGNED) != 0;
|
||||
const int c0 = data[0].z;
|
||||
const int c1 = data[0].w;
|
||||
/* refit inner node, set bbox from children */
|
||||
BoundBox bbox0 = BoundBox::empty;
|
||||
BoundBox bbox1 = BoundBox::empty;
|
||||
uint visibility0 = 0;
|
||||
uint visibility1 = 0;
|
||||
|
||||
refit_node((c0 < 0) ? -c0 - 1 : c0, (c0 < 0), bbox0, visibility0);
|
||||
refit_node((c1 < 0) ? -c1 - 1 : c1, (c1 < 0), bbox1, visibility1);
|
||||
|
||||
if (is_unaligned) {
|
||||
const Transform aligned_space = transform_identity();
|
||||
pack_unaligned_node(
|
||||
idx, aligned_space, aligned_space, bbox0, bbox1, c0, c1, visibility0, visibility1);
|
||||
}
|
||||
else {
|
||||
pack_aligned_node(idx, bbox0, bbox1, c0, c1, visibility0, visibility1);
|
||||
}
|
||||
|
||||
bbox.grow(bbox0);
|
||||
bbox.grow(bbox1);
|
||||
visibility = visibility0 | visibility1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Refitting */
|
||||
|
||||
void BVH2::refit_primitives(const int start, const int end, BoundBox &bbox, uint &visibility)
|
||||
{
|
||||
/* Refit range of primitives. */
|
||||
for (int prim = start; prim < end; prim++) {
|
||||
const int pidx = pack.prim_index[prim];
|
||||
const int tob = pack.prim_object[prim];
|
||||
Object *ob = objects[tob];
|
||||
|
||||
if (pidx == -1) {
|
||||
/* Object instance. */
|
||||
bbox.grow(ob->bounds);
|
||||
}
|
||||
else {
|
||||
/* Primitives. */
|
||||
if (pack.prim_type[prim] & PRIMITIVE_CURVE) {
|
||||
/* Curves. */
|
||||
const Hair *hair = static_cast<const Hair *>(ob->get_geometry());
|
||||
const int prim_offset = (params.top_level) ? hair->prim_offset : 0;
|
||||
const Hair::Curve curve = hair->get_curve(pidx - prim_offset);
|
||||
const int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
|
||||
|
||||
curve.bounds_grow(k, hair->get_position(), hair->get_radius(), bbox);
|
||||
|
||||
/* Motion curves. */
|
||||
if (hair->get_use_motion_blur()) {
|
||||
const Attribute *attr_P = hair->attributes.find(ATTR_STD_POSITION);
|
||||
const Attribute *attr_R = hair->attributes.find(ATTR_STD_RADIUS);
|
||||
|
||||
if (attr_P->has_motion()) {
|
||||
for (int attr_step = 1; attr_step < attr_P->num_motion_steps(); attr_step++) {
|
||||
curve.bounds_grow(
|
||||
k, attr_P->data<packed_float3>(attr_step), attr_R->data<float>(attr_step), bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pack.prim_type[prim] & PRIMITIVE_POINT) {
|
||||
/* Points. */
|
||||
const PointCloud *pointcloud = static_cast<const PointCloud *>(ob->get_geometry());
|
||||
const int prim_offset = (params.top_level) ? pointcloud->prim_offset : 0;
|
||||
const packed_float3 *points = pointcloud->get_position();
|
||||
const float *radius = pointcloud->get_radius();
|
||||
const PointCloud::Point point = pointcloud->get_point(pidx - prim_offset);
|
||||
|
||||
point.bounds_grow(points, radius, bbox);
|
||||
|
||||
/* Motion points. */
|
||||
if (pointcloud->get_use_motion_blur()) {
|
||||
const Attribute *attr_P = pointcloud->attributes.find(ATTR_STD_POSITION);
|
||||
|
||||
if (attr_P->has_motion()) {
|
||||
const Attribute *attr_R = pointcloud->attributes.find(ATTR_STD_RADIUS);
|
||||
for (int attr_step = 1; attr_step < attr_P->num_motion_steps(); attr_step++) {
|
||||
const float3 P = attr_P->data<packed_float3>(attr_step)[point.index];
|
||||
const float r = attr_R->data<float>(attr_step)[point.index];
|
||||
bbox.grow(P, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Triangles. */
|
||||
const Mesh *mesh = static_cast<const Mesh *>(ob->get_geometry());
|
||||
const int prim_offset = (params.top_level) ? mesh->prim_offset : 0;
|
||||
const Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_offset);
|
||||
const packed_float3 *vpos = mesh->get_position();
|
||||
|
||||
triangle.bounds_grow(vpos, bbox);
|
||||
|
||||
/* Motion triangles. */
|
||||
if (mesh->use_motion_blur) {
|
||||
const Attribute *attr_P = mesh->attributes.find(ATTR_STD_POSITION);
|
||||
|
||||
if (attr_P->has_motion()) {
|
||||
for (int attr_step = 1; attr_step < attr_P->num_motion_steps(); attr_step++) {
|
||||
triangle.bounds_grow(attr_P->data<packed_float3>(attr_step), bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
visibility |= ob->visibility_for_tracing();
|
||||
}
|
||||
}
|
||||
|
||||
/* Triangles */
|
||||
|
||||
void BVH2::pack_primitives()
|
||||
{
|
||||
const size_t tidx_size = pack.prim_index.size();
|
||||
/* Reserve size for arrays. */
|
||||
pack.prim_visibility.clear();
|
||||
pack.prim_visibility.resize(tidx_size);
|
||||
/* Fill in all the arrays. */
|
||||
for (unsigned int i = 0; i < tidx_size; i++) {
|
||||
if (pack.prim_index[i] != -1) {
|
||||
const int tob = pack.prim_object[i];
|
||||
Object *ob = objects[tob];
|
||||
pack.prim_visibility[i] = ob->visibility_for_tracing();
|
||||
}
|
||||
else {
|
||||
pack.prim_visibility[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pack Instances */
|
||||
|
||||
void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
|
||||
{
|
||||
/* Adjust primitive index to point to the triangle in the global array, for
|
||||
* geometry with transform applied and already in the top level BVH.
|
||||
*/
|
||||
for (size_t i = 0; i < pack.prim_index.size(); i++) {
|
||||
if (pack.prim_index[i] != -1) {
|
||||
pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/* track offsets of instanced BVH data in global array */
|
||||
size_t prim_offset = pack.prim_index.size();
|
||||
size_t nodes_offset = nodes_size;
|
||||
size_t nodes_leaf_offset = leaf_nodes_size;
|
||||
|
||||
/* clear array that gives the node indexes for instanced objects */
|
||||
pack.object_node.clear();
|
||||
|
||||
/* reserve */
|
||||
size_t prim_index_size = pack.prim_index.size();
|
||||
|
||||
size_t pack_prim_index_offset = prim_index_size;
|
||||
size_t pack_nodes_offset = nodes_size;
|
||||
size_t pack_leaf_nodes_offset = leaf_nodes_size;
|
||||
size_t object_offset = 0;
|
||||
|
||||
for (Geometry *geom : geometry) {
|
||||
BVH2 *bvh = static_cast<BVH2 *>(geom->bvh.get());
|
||||
|
||||
if (geom->need_build_bvh(params.bvh_layout)) {
|
||||
prim_index_size += bvh->pack.prim_index.size();
|
||||
nodes_size += bvh->pack.nodes.size();
|
||||
leaf_nodes_size += bvh->pack.leaf_nodes.size();
|
||||
}
|
||||
}
|
||||
|
||||
pack.prim_index.resize(prim_index_size);
|
||||
pack.prim_type.resize(prim_index_size);
|
||||
pack.prim_object.resize(prim_index_size);
|
||||
pack.prim_visibility.resize(prim_index_size);
|
||||
pack.nodes.resize(nodes_size);
|
||||
pack.leaf_nodes.resize(leaf_nodes_size);
|
||||
pack.object_node.resize(objects.size());
|
||||
|
||||
if (params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0 ||
|
||||
params.num_motion_point_steps > 0)
|
||||
{
|
||||
pack.prim_time.resize(prim_index_size);
|
||||
}
|
||||
|
||||
int *pack_prim_index = (pack.prim_index.size()) ? pack.prim_index.data() : nullptr;
|
||||
int *pack_prim_type = (pack.prim_type.size()) ? pack.prim_type.data() : nullptr;
|
||||
int *pack_prim_object = (pack.prim_object.size()) ? pack.prim_object.data() : nullptr;
|
||||
uint *pack_prim_visibility = (pack.prim_visibility.size()) ? pack.prim_visibility.data() :
|
||||
nullptr;
|
||||
int4 *pack_nodes = (pack.nodes.size()) ? pack.nodes.data() : nullptr;
|
||||
int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? pack.leaf_nodes.data() : nullptr;
|
||||
float2 *pack_prim_time = (pack.prim_time.size()) ? pack.prim_time.data() : nullptr;
|
||||
|
||||
unordered_map<Geometry *, int> geometry_map;
|
||||
|
||||
/* merge */
|
||||
for (Object *ob : objects) {
|
||||
Geometry *geom = ob->get_geometry();
|
||||
|
||||
/* We assume that if mesh doesn't need own BVH it was already included
|
||||
* into a top-level BVH and no packing here is needed.
|
||||
*/
|
||||
if (!geom->need_build_bvh(params.bvh_layout)) {
|
||||
pack.object_node[object_offset++] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if mesh already added once, don't add it again, but used set
|
||||
* node offset for this object */
|
||||
const unordered_map<Geometry *, int>::iterator it = geometry_map.find(geom);
|
||||
|
||||
if (geometry_map.contains(geom)) {
|
||||
const int noffset = it->second;
|
||||
pack.object_node[object_offset++] = noffset;
|
||||
continue;
|
||||
}
|
||||
|
||||
BVH2 *bvh = static_cast<BVH2 *>(geom->bvh.get());
|
||||
|
||||
const int noffset = nodes_offset;
|
||||
const int noffset_leaf = nodes_leaf_offset;
|
||||
const int geom_prim_offset = geom->prim_offset;
|
||||
|
||||
/* fill in node indexes for instances */
|
||||
if (bvh->pack.root_index == -1) {
|
||||
pack.object_node[object_offset++] = -noffset_leaf - 1;
|
||||
}
|
||||
else {
|
||||
pack.object_node[object_offset++] = noffset;
|
||||
}
|
||||
|
||||
geometry_map[geom] = pack.object_node[object_offset - 1];
|
||||
|
||||
/* merge primitive, object and triangle indexes */
|
||||
if (bvh->pack.prim_index.size()) {
|
||||
const size_t bvh_prim_index_size = bvh->pack.prim_index.size();
|
||||
int *bvh_prim_index = bvh->pack.prim_index.data();
|
||||
int *bvh_prim_type = bvh->pack.prim_type.data();
|
||||
uint *bvh_prim_visibility = bvh->pack.prim_visibility.data();
|
||||
float2 *bvh_prim_time = bvh->pack.prim_time.size() ? bvh->pack.prim_time.data() : nullptr;
|
||||
|
||||
for (size_t i = 0; i < bvh_prim_index_size; i++) {
|
||||
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
|
||||
pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
|
||||
pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i];
|
||||
pack_prim_object[pack_prim_index_offset] = 0; // unused for instances
|
||||
if (bvh_prim_time != nullptr) {
|
||||
pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
|
||||
}
|
||||
pack_prim_index_offset++;
|
||||
}
|
||||
}
|
||||
|
||||
/* merge nodes */
|
||||
if (bvh->pack.leaf_nodes.size()) {
|
||||
int4 *leaf_nodes_offset = bvh->pack.leaf_nodes.data();
|
||||
const size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size();
|
||||
for (size_t i = 0; i < leaf_nodes_offset_size; i += BVH_NODE_LEAF_SIZE) {
|
||||
int4 data = leaf_nodes_offset[i];
|
||||
data.x += prim_offset;
|
||||
data.y += prim_offset;
|
||||
pack_leaf_nodes[pack_leaf_nodes_offset] = data;
|
||||
for (int j = 1; j < BVH_NODE_LEAF_SIZE; ++j) {
|
||||
pack_leaf_nodes[pack_leaf_nodes_offset + j] = leaf_nodes_offset[i + j];
|
||||
}
|
||||
pack_leaf_nodes_offset += BVH_NODE_LEAF_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bvh->pack.nodes.size()) {
|
||||
int4 *bvh_nodes = bvh->pack.nodes.data();
|
||||
const size_t bvh_nodes_size = bvh->pack.nodes.size();
|
||||
|
||||
for (size_t i = 0; i < bvh_nodes_size;) {
|
||||
size_t nsize;
|
||||
size_t nsize_bbox;
|
||||
if (bvh_nodes[i].x & PATH_RAY_VISIBILITY_NODE_UNALIGNED) {
|
||||
nsize = BVH_UNALIGNED_NODE_SIZE;
|
||||
nsize_bbox = 0;
|
||||
}
|
||||
else {
|
||||
nsize = BVH_NODE_SIZE;
|
||||
nsize_bbox = 0;
|
||||
}
|
||||
|
||||
std::copy_n(bvh_nodes + i, nsize_bbox, pack_nodes + pack_nodes_offset);
|
||||
|
||||
/* Modify offsets into arrays */
|
||||
int4 data = bvh_nodes[i + nsize_bbox];
|
||||
data.z += (data.z < 0) ? -noffset_leaf : noffset;
|
||||
data.w += (data.w < 0) ? -noffset_leaf : noffset;
|
||||
pack_nodes[pack_nodes_offset + nsize_bbox] = data;
|
||||
|
||||
/* Usually this copies nothing, but we better
|
||||
* be prepared for possible node size extension.
|
||||
*/
|
||||
std::copy_n(&bvh_nodes[i + nsize_bbox + 1],
|
||||
(nsize - (nsize_bbox + 1)),
|
||||
&pack_nodes[pack_nodes_offset + nsize_bbox + 1]);
|
||||
|
||||
pack_nodes_offset += nsize;
|
||||
i += nsize;
|
||||
}
|
||||
}
|
||||
|
||||
nodes_offset += bvh->pack.nodes.size();
|
||||
nodes_leaf_offset += bvh->pack.leaf_nodes.size();
|
||||
prim_offset += bvh->pack.prim_index.size();
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
97
blender-5.2.0/intern/cycles/bvh/bvh2.h
Normal file
97
blender-5.2.0/intern/cycles/bvh/bvh2.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bvh/bvh.h"
|
||||
#include "bvh/params.h"
|
||||
|
||||
#include "util/types.h"
|
||||
#include "util/unique_ptr.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
// NOLINTBEGIN
|
||||
#define BVH_NODE_SIZE 4
|
||||
#define BVH_NODE_LEAF_SIZE 1
|
||||
#define BVH_UNALIGNED_NODE_SIZE 7
|
||||
// NOLINTEND
|
||||
|
||||
/* Pack Utility */
|
||||
struct BVHStackEntry {
|
||||
const BVHNode *node;
|
||||
int idx;
|
||||
|
||||
BVHStackEntry(const BVHNode *n = nullptr, const int i = 0);
|
||||
int encodeIdx() const;
|
||||
};
|
||||
|
||||
/* BVH2
|
||||
*
|
||||
* Typical BVH with each node having two children.
|
||||
*/
|
||||
class BVH2 : public BVH {
|
||||
public:
|
||||
BVH2(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects);
|
||||
|
||||
void build(Progress &progress, Stats *stats);
|
||||
void refit(Progress &progress);
|
||||
|
||||
PackedBVH pack;
|
||||
|
||||
protected:
|
||||
/* Building process. */
|
||||
virtual unique_ptr<BVHNode> widen_children_nodes(unique_ptr<BVHNode> &&root);
|
||||
|
||||
/* pack */
|
||||
void pack_nodes(const BVHNode *root);
|
||||
|
||||
void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf);
|
||||
void pack_inner(const BVHStackEntry &e, const BVHStackEntry &e0, const BVHStackEntry &e1);
|
||||
|
||||
void pack_aligned_inner(const BVHStackEntry &e,
|
||||
const BVHStackEntry &e0,
|
||||
const BVHStackEntry &e1);
|
||||
void pack_aligned_node(const int idx,
|
||||
const BoundBox &b0,
|
||||
const BoundBox &b1,
|
||||
int c0,
|
||||
int c1,
|
||||
uint visibility0,
|
||||
uint visibility1);
|
||||
|
||||
void pack_unaligned_inner(const BVHStackEntry &e,
|
||||
const BVHStackEntry &e0,
|
||||
const BVHStackEntry &e1);
|
||||
void pack_unaligned_node(const int idx,
|
||||
const Transform &aligned_space0,
|
||||
const Transform &aligned_space1,
|
||||
const BoundBox &b0,
|
||||
const BoundBox &b1,
|
||||
int c0,
|
||||
int c1,
|
||||
uint visibility0,
|
||||
uint visibility1);
|
||||
|
||||
/* refit */
|
||||
void refit_nodes();
|
||||
void refit_node(const int idx, bool leaf, BoundBox &bbox, uint &visibility);
|
||||
|
||||
/* Refit range of primitives. */
|
||||
void refit_primitives(const int start, const int end, BoundBox &bbox, uint &visibility);
|
||||
|
||||
/* triangles and strands */
|
||||
void pack_primitives();
|
||||
|
||||
/* merge instance BVH's */
|
||||
void pack_instances(const size_t nodes_size, const size_t leaf_nodes_size);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
765
blender-5.2.0/intern/cycles/bvh/embree.cpp
Normal file
765
blender-5.2.0/intern/cycles/bvh/embree.cpp
Normal file
@@ -0,0 +1,765 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* This class implements a ray accelerator for Cycles using Intel's Embree library.
|
||||
* It supports triangles, curves, object and deformation blur and instancing.
|
||||
*
|
||||
* Since Embree allows object to be either curves or triangles but not both, Cycles object IDs are
|
||||
* mapped to Embree IDs by multiplying by two and adding one for curves.
|
||||
*
|
||||
* This implementation shares RTCDevices between Cycles instances. Eventually each instance should
|
||||
* get a separate RTCDevice to correctly keep track of memory usage.
|
||||
*
|
||||
* Vertex and index buffers are duplicated between Cycles device arrays and Embree. These could be
|
||||
* merged, which would require changes to intersection refinement, shader setup, mesh light
|
||||
* sampling and a few other places in Cycles where direct access to vertex data is required.
|
||||
*/
|
||||
|
||||
#ifdef WITH_EMBREE
|
||||
|
||||
# include <embree4/rtcore_geometry.h>
|
||||
|
||||
# include "bvh/embree.h"
|
||||
|
||||
# include "scene/hair.h"
|
||||
# include "scene/mesh.h"
|
||||
# include "scene/object.h"
|
||||
# include "scene/pointcloud.h"
|
||||
|
||||
# include "util/log.h"
|
||||
# include "util/progress.h"
|
||||
# include "util/stats.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static_assert(Object::MAX_MOTION_STEPS <= RTC_MAX_TIME_STEP_COUNT,
|
||||
"Object and Embree max motion steps inconsistent");
|
||||
static_assert(Object::MAX_MOTION_STEPS == Geometry::MAX_MOTION_STEPS,
|
||||
"Object and Geometry max motion steps inconsistent");
|
||||
|
||||
static size_t unaccounted_mem = 0;
|
||||
|
||||
static bool rtc_memory_monitor_func(void *userPtr, const ssize_t bytes, const bool /*unused*/)
|
||||
{
|
||||
Stats *stats = (Stats *)userPtr;
|
||||
if (stats) {
|
||||
if (bytes > 0) {
|
||||
stats->mem_alloc(bytes);
|
||||
}
|
||||
else {
|
||||
stats->mem_free(-bytes);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* A stats pointer may not yet be available. Keep track of the memory usage for later. */
|
||||
if (bytes >= 0) {
|
||||
atomic_add_and_fetch_z(&unaccounted_mem, bytes);
|
||||
}
|
||||
else {
|
||||
atomic_sub_and_fetch_z(&unaccounted_mem, -bytes);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void rtc_error_func(void * /*unused*/, enum RTCError /*unused*/, const char *str)
|
||||
{
|
||||
LOG_WARNING << str;
|
||||
}
|
||||
|
||||
static double progress_start_time = 0.0;
|
||||
|
||||
static bool rtc_progress_func(void *user_ptr, const double n)
|
||||
{
|
||||
Progress *progress = (Progress *)user_ptr;
|
||||
|
||||
if (time_dt() - progress_start_time < 0.25) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const string msg = string_printf("Building BVH %.0f%%", n * 100.0);
|
||||
progress->set_substatus(msg);
|
||||
progress_start_time = time_dt();
|
||||
|
||||
return !progress->get_cancel();
|
||||
}
|
||||
|
||||
BVHEmbree::BVHEmbree(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_)
|
||||
: BVH(params_, geometry_, objects_),
|
||||
scene(nullptr),
|
||||
rtc_device(nullptr),
|
||||
rtc_device_is_sycl(false),
|
||||
build_quality(RTC_BUILD_QUALITY_REFIT)
|
||||
{
|
||||
SIMD_SET_FLUSH_TO_ZERO;
|
||||
}
|
||||
|
||||
BVHEmbree::~BVHEmbree()
|
||||
{
|
||||
if (scene) {
|
||||
rtcReleaseScene(scene);
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::build(Progress &progress,
|
||||
Stats *stats,
|
||||
RTCDevice rtc_device_,
|
||||
const bool rtc_device_is_sycl_)
|
||||
{
|
||||
rtc_device = rtc_device_;
|
||||
rtc_device_is_sycl = rtc_device_is_sycl_;
|
||||
assert(rtc_device);
|
||||
|
||||
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, nullptr);
|
||||
rtcSetDeviceMemoryMonitorFunction(rtc_device, rtc_memory_monitor_func, stats);
|
||||
|
||||
progress.set_substatus("Building BVH");
|
||||
|
||||
if (scene) {
|
||||
rtcReleaseScene(scene);
|
||||
scene = nullptr;
|
||||
}
|
||||
|
||||
const bool dynamic = params.bvh_type == BVH_TYPE_DYNAMIC;
|
||||
const bool compact = params.use_compact_structure;
|
||||
|
||||
scene = rtcNewScene(rtc_device);
|
||||
const RTCSceneFlags scene_flags = (dynamic ? RTC_SCENE_FLAG_DYNAMIC : RTC_SCENE_FLAG_NONE) |
|
||||
(compact ? RTC_SCENE_FLAG_COMPACT : RTC_SCENE_FLAG_NONE) |
|
||||
RTC_SCENE_FLAG_ROBUST |
|
||||
RTC_SCENE_FLAG_FILTER_FUNCTION_IN_ARGUMENTS;
|
||||
rtcSetSceneFlags(scene, scene_flags);
|
||||
build_quality = dynamic ? RTC_BUILD_QUALITY_LOW :
|
||||
(params.use_spatial_split ? RTC_BUILD_QUALITY_HIGH :
|
||||
RTC_BUILD_QUALITY_MEDIUM);
|
||||
if (build_quality == RTC_BUILD_QUALITY_HIGH && rtc_device_is_sycl) {
|
||||
/* To work around a known issue in the Intel GPU driver regarding the High
|
||||
* quality BVH build option, we reduce it to Medium. There is no impact on
|
||||
* render quality from this change. There is a small expected performance
|
||||
* impact on intersection speed and BVH building speed, but this is
|
||||
* unavoidable at the moment - using High quality leads to crashes, so we
|
||||
* have no choice. This workaround can be removed once the fix appears in
|
||||
* public drivers and we raise the minimum oneAPI backend driver version
|
||||
* accordingly. */
|
||||
/* This workaround is applied only to GPU, per Sergey. See #158123. */
|
||||
LOG_INFO
|
||||
<< "Due to a known issue in Intel GPU drivers, overriding RTC_BUILD_QUALITY_HIGH to "
|
||||
"RTC_BUILD_QUALITY_MEDIUM to prevent crashes. This workaround will be removed only "
|
||||
"in a future Blender release.";
|
||||
build_quality = RTC_BUILD_QUALITY_MEDIUM;
|
||||
}
|
||||
rtcSetSceneBuildQuality(scene, build_quality);
|
||||
|
||||
int i = 0;
|
||||
for (Object *ob : objects) {
|
||||
if (params.top_level) {
|
||||
if (!ob->is_traceable()) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (!ob->get_geometry()->is_instanced()) {
|
||||
add_object(ob, i);
|
||||
}
|
||||
else {
|
||||
add_instance(ob, i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
add_object(ob, i);
|
||||
}
|
||||
++i;
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress);
|
||||
rtcCommitScene(scene);
|
||||
}
|
||||
|
||||
const char *BVHEmbree::get_error_string(RTCError error_code)
|
||||
{
|
||||
# if RTC_VERSION >= 40303
|
||||
return rtcGetErrorString(error_code);
|
||||
# else
|
||||
switch (error_code) {
|
||||
case RTC_ERROR_NONE:
|
||||
return "no error";
|
||||
case RTC_ERROR_UNKNOWN:
|
||||
return "unknown error";
|
||||
case RTC_ERROR_INVALID_ARGUMENT:
|
||||
return "invalid argument error";
|
||||
case RTC_ERROR_INVALID_OPERATION:
|
||||
return "invalid operation error";
|
||||
case RTC_ERROR_OUT_OF_MEMORY:
|
||||
return "out of memory error";
|
||||
case RTC_ERROR_UNSUPPORTED_CPU:
|
||||
return "unsupported cpu error";
|
||||
case RTC_ERROR_CANCELLED:
|
||||
return "cancelled";
|
||||
default:
|
||||
/* We should never end here unless enum for RTC errors would change. */
|
||||
return "unknown error";
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
# if defined(WITH_EMBREE_GPU) && RTC_VERSION >= 40302
|
||||
/* offload_scenes_to_gpu() uses rtcGetDeviceError() which also resets Embree error status,
|
||||
* we propagate its value so it doesn't get lost. */
|
||||
RTCError BVHEmbree::offload_scenes_to_gpu(const vector<RTCScene> &scenes)
|
||||
{
|
||||
/* Having BVH on GPU is more performance-critical than texture data.
|
||||
* In order to ensure good performance even when running out of GPU
|
||||
* memory, we force BVH to migrate to GPU before allocating other textures
|
||||
* that may not fit. */
|
||||
for (const RTCScene &embree_scene : scenes) {
|
||||
RTCSceneFlags scene_flags = rtcGetSceneFlags(embree_scene);
|
||||
scene_flags = scene_flags | RTC_SCENE_FLAG_PREFETCH_USM_SHARED_ON_GPU;
|
||||
rtcSetSceneFlags(embree_scene, scene_flags);
|
||||
rtcCommitScene(embree_scene);
|
||||
/* In case of any errors from Embree, we should stop
|
||||
* the execution and propagate the error. */
|
||||
RTCError error_code = rtcGetDeviceError(rtc_device);
|
||||
if (error_code != RTC_ERROR_NONE) {
|
||||
return error_code;
|
||||
}
|
||||
}
|
||||
return RTC_ERROR_NONE;
|
||||
}
|
||||
# endif
|
||||
|
||||
void BVHEmbree::add_object(Object *ob, const int i)
|
||||
{
|
||||
Geometry *geom = ob->get_geometry();
|
||||
|
||||
if (geom->is_mesh() || geom->is_volume()) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (mesh->num_triangles() > 0) {
|
||||
add_triangles(ob, mesh, i);
|
||||
}
|
||||
}
|
||||
else if (geom->is_hair()) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
if (hair->is_traceable()) {
|
||||
add_curves(ob, hair, i);
|
||||
}
|
||||
}
|
||||
else if (geom->is_pointcloud()) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
|
||||
if (pointcloud->num_points() > 0) {
|
||||
add_points(ob, pointcloud, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::add_instance(Object *ob, const int i)
|
||||
{
|
||||
BVHEmbree *instance_bvh = static_cast<BVHEmbree *>(ob->get_geometry()->bvh.get());
|
||||
assert(instance_bvh != nullptr);
|
||||
|
||||
const size_t num_object_motion_steps = ob->use_motion() ? ob->get_motion().size() : 1;
|
||||
const size_t num_motion_steps = min(num_object_motion_steps, (size_t)RTC_MAX_TIME_STEP_COUNT);
|
||||
assert(num_object_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
|
||||
|
||||
RTCGeometry geom_id = rtcNewGeometry(rtc_device, RTC_GEOMETRY_TYPE_INSTANCE);
|
||||
rtcSetGeometryInstancedScene(geom_id, instance_bvh->scene);
|
||||
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
|
||||
|
||||
if (ob->use_motion()) {
|
||||
array<DecomposedTransform> decomp(ob->get_motion().size());
|
||||
transform_motion_decompose(decomp.data(), ob->get_motion().data(), ob->get_motion().size());
|
||||
for (size_t step = 0; step < num_motion_steps; ++step) {
|
||||
RTCQuaternionDecomposition rtc_decomp;
|
||||
rtcInitQuaternionDecomposition(&rtc_decomp);
|
||||
rtcQuaternionDecompositionSetQuaternion(
|
||||
&rtc_decomp, decomp[step].x.w, decomp[step].x.x, decomp[step].x.y, decomp[step].x.z);
|
||||
rtcQuaternionDecompositionSetScale(
|
||||
&rtc_decomp, decomp[step].y.w, decomp[step].z.w, decomp[step].w.w);
|
||||
rtcQuaternionDecompositionSetTranslation(
|
||||
&rtc_decomp, decomp[step].y.x, decomp[step].y.y, decomp[step].y.z);
|
||||
rtcQuaternionDecompositionSetSkew(
|
||||
&rtc_decomp, decomp[step].z.x, decomp[step].z.y, decomp[step].w.x);
|
||||
rtcSetGeometryTransformQuaternion(geom_id, step, &rtc_decomp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
rtcSetGeometryTransform(
|
||||
geom_id, 0, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float *)&ob->get_tfm());
|
||||
}
|
||||
|
||||
rtcSetGeometryUserData(geom_id,
|
||||
# if RTC_VERSION >= 40400
|
||||
(void *)rtcGetSceneTraversable(instance_bvh->scene)
|
||||
# else
|
||||
(void *)instance_bvh->scene
|
||||
# endif
|
||||
);
|
||||
|
||||
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
|
||||
rtcSetGeometryEnableFilterFunctionFromArguments(geom_id, true);
|
||||
|
||||
rtcCommitGeometry(geom_id);
|
||||
rtcAttachGeometryByID(scene, geom_id, i * 2);
|
||||
rtcReleaseGeometry(geom_id);
|
||||
}
|
||||
|
||||
void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, const int i)
|
||||
{
|
||||
const size_t prim_offset = mesh->prim_offset;
|
||||
|
||||
const Attribute *attr_P = nullptr;
|
||||
size_t num_motion_steps = 1;
|
||||
if (mesh->has_motion_blur()) {
|
||||
attr_P = mesh->attributes.find(ATTR_STD_POSITION);
|
||||
if (attr_P->has_motion()) {
|
||||
num_motion_steps = mesh->get_motion_steps();
|
||||
}
|
||||
}
|
||||
|
||||
assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
|
||||
num_motion_steps = min(num_motion_steps, (size_t)RTC_MAX_TIME_STEP_COUNT);
|
||||
|
||||
const size_t num_triangles = mesh->num_triangles();
|
||||
|
||||
RTCGeometry geom_id = rtcNewGeometry(rtc_device, RTC_GEOMETRY_TYPE_TRIANGLE);
|
||||
rtcSetGeometryBuildQuality(geom_id, build_quality);
|
||||
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
|
||||
|
||||
const int *triangles = mesh->get_triangles().data();
|
||||
if (!rtc_device_is_sycl) {
|
||||
rtcSetSharedGeometryBuffer(geom_id,
|
||||
RTC_BUFFER_TYPE_INDEX,
|
||||
0,
|
||||
RTC_FORMAT_UINT3,
|
||||
triangles,
|
||||
0,
|
||||
sizeof(int) * 3,
|
||||
num_triangles);
|
||||
}
|
||||
else {
|
||||
/* NOTE(sirgienko): If the Embree device is a SYCL device, then Embree execution will
|
||||
* happen on GPU, and we cannot use standard host pointers at this point. So instead
|
||||
* of making a shared geometry buffer - a new Embree buffer will be created and data
|
||||
* will be copied. */
|
||||
int *triangles_buffer = nullptr;
|
||||
# if RTC_VERSION >= 40400
|
||||
rtcSetNewGeometryBufferHostDevice(
|
||||
# else
|
||||
triangles_buffer = (int *)rtcSetNewGeometryBuffer(
|
||||
# endif
|
||||
geom_id,
|
||||
RTC_BUFFER_TYPE_INDEX,
|
||||
0,
|
||||
RTC_FORMAT_UINT3,
|
||||
sizeof(int) * 3,
|
||||
num_triangles
|
||||
# if RTC_VERSION >= 40400
|
||||
,
|
||||
(void **)(&triangles_buffer),
|
||||
nullptr
|
||||
# endif
|
||||
);
|
||||
assert(triangles_buffer);
|
||||
if (triangles_buffer) {
|
||||
static_assert(sizeof(int) == sizeof(uint));
|
||||
std::memcpy(triangles_buffer, triangles, sizeof(int) * 3 * (num_triangles));
|
||||
}
|
||||
}
|
||||
set_tri_vertex_buffer(geom_id, mesh, false);
|
||||
|
||||
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
|
||||
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
|
||||
rtcSetGeometryEnableFilterFunctionFromArguments(geom_id, true);
|
||||
|
||||
rtcCommitGeometry(geom_id);
|
||||
rtcAttachGeometryByID(scene, geom_id, i * 2);
|
||||
rtcReleaseGeometry(geom_id);
|
||||
}
|
||||
|
||||
void BVHEmbree::set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, const bool update)
|
||||
{
|
||||
const Attribute *attr_P = mesh->attributes.find(ATTR_STD_POSITION);
|
||||
size_t num_motion_steps = 1;
|
||||
if (mesh->has_motion_blur() && attr_P->has_motion()) {
|
||||
num_motion_steps = mesh->get_motion_steps();
|
||||
if (num_motion_steps > RTC_MAX_TIME_STEP_COUNT) {
|
||||
assert(0);
|
||||
num_motion_steps = RTC_MAX_TIME_STEP_COUNT;
|
||||
}
|
||||
}
|
||||
const size_t num_verts = mesh->num_verts();
|
||||
|
||||
for (int t = 0; t < num_motion_steps; ++t) {
|
||||
const packed_float3 *verts = attr_P->data_at_time_step<packed_float3>(t, num_motion_steps);
|
||||
|
||||
if (update) {
|
||||
rtcUpdateGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
|
||||
}
|
||||
else {
|
||||
if (!rtc_device_is_sycl) {
|
||||
rtcSetSharedGeometryBuffer(geom_id,
|
||||
RTC_BUFFER_TYPE_VERTEX,
|
||||
t,
|
||||
RTC_FORMAT_FLOAT3,
|
||||
verts,
|
||||
0,
|
||||
sizeof(packed_float3),
|
||||
num_verts);
|
||||
}
|
||||
else {
|
||||
/* NOTE(sirgienko): If the Embree device is a SYCL device, then Embree execution will
|
||||
* happen on GPU, and we cannot use standard host pointers at this point. So instead
|
||||
* of making a shared geometry buffer - a new Embree buffer will be created and data
|
||||
* will be copied. */
|
||||
/* As float3 is packed on GPU side, we map it to packed_float3. */
|
||||
/* There is no need for additional padding in rtcSetNewGeometryBuffer since Embree 3.6:
|
||||
* "Fixed automatic vertex buffer padding when using rtcSetNewGeometry API function". */
|
||||
packed_float3 *verts_buffer = nullptr;
|
||||
# if RTC_VERSION >= 40400
|
||||
rtcSetNewGeometryBufferHostDevice(
|
||||
# else
|
||||
verts_buffer = (packed_float3 *)rtcSetNewGeometryBuffer(
|
||||
# endif
|
||||
geom_id,
|
||||
RTC_BUFFER_TYPE_VERTEX,
|
||||
t,
|
||||
RTC_FORMAT_FLOAT3,
|
||||
sizeof(packed_float3),
|
||||
num_verts
|
||||
# if RTC_VERSION >= 40400
|
||||
,
|
||||
(void **)(&verts_buffer),
|
||||
nullptr
|
||||
# endif
|
||||
);
|
||||
assert(verts_buffer);
|
||||
if (verts_buffer) {
|
||||
for (size_t i = (size_t)0; i < num_verts; ++i) {
|
||||
verts_buffer[i].x = verts[i].x;
|
||||
verts_buffer[i].y = verts[i].y;
|
||||
verts_buffer[i].z = verts[i].z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs the hair motion curve data control variables (CVs) into float4s as [x y z radius]
|
||||
*/
|
||||
template<typename T>
|
||||
void pack_motion_verts(const size_t num_curves,
|
||||
const Hair *hair,
|
||||
const T *verts,
|
||||
const float *curve_radius,
|
||||
float4 *rtc_verts,
|
||||
CurveShapeType curve_shape)
|
||||
{
|
||||
for (size_t j = 0; j < num_curves; ++j) {
|
||||
const Hair::Curve c = hair->get_curve(j);
|
||||
int fk = c.first_key;
|
||||
|
||||
if (curve_shape == CURVE_THICK_LINEAR) {
|
||||
for (int k = 0; k < c.num_keys; ++k, ++fk) {
|
||||
rtc_verts[k].x = verts[fk].x;
|
||||
rtc_verts[k].y = verts[fk].y;
|
||||
rtc_verts[k].z = verts[fk].z;
|
||||
rtc_verts[k].w = curve_radius[fk];
|
||||
}
|
||||
rtc_verts += c.num_keys;
|
||||
}
|
||||
else {
|
||||
for (int k = 1; k < c.num_keys + 1; ++k, ++fk) {
|
||||
rtc_verts[k].x = verts[fk].x;
|
||||
rtc_verts[k].y = verts[fk].y;
|
||||
rtc_verts[k].z = verts[fk].z;
|
||||
rtc_verts[k].w = curve_radius[fk];
|
||||
}
|
||||
/* Duplicate Embree's Catmull-Rom spline CVs at the start and end of each curve. */
|
||||
rtc_verts[0] = rtc_verts[1];
|
||||
rtc_verts[c.num_keys + 1] = rtc_verts[c.num_keys];
|
||||
rtc_verts += c.num_keys + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, const bool update)
|
||||
{
|
||||
const Attribute *attr_P = hair->attributes.find(ATTR_STD_POSITION);
|
||||
const Attribute *attr_R = hair->attributes.find(ATTR_STD_RADIUS);
|
||||
size_t num_motion_steps = 1;
|
||||
if (hair->has_motion_blur() && attr_P->has_motion()) {
|
||||
num_motion_steps = hair->get_motion_steps();
|
||||
}
|
||||
|
||||
const size_t num_curves = hair->num_curves();
|
||||
size_t num_keys = 0;
|
||||
for (size_t j = 0; j < num_curves; ++j) {
|
||||
const Hair::Curve c = hair->get_curve(j);
|
||||
num_keys += c.num_keys;
|
||||
}
|
||||
|
||||
/* Catmull-Rom splines need extra CVs at the beginning and end of each curve. */
|
||||
size_t num_keys_embree = num_keys;
|
||||
num_keys_embree += num_curves * 2;
|
||||
|
||||
/* Copy the CV data to Embree */
|
||||
for (int t = 0; t < num_motion_steps; ++t) {
|
||||
float4 *rtc_verts = nullptr;
|
||||
if (update) {
|
||||
rtc_verts = (float4 *)rtcGetGeometryBufferData(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
|
||||
}
|
||||
else {
|
||||
# if RTC_VERSION >= 40400
|
||||
rtcSetNewGeometryBufferHostDevice(
|
||||
# else
|
||||
rtc_verts = (float4 *)rtcSetNewGeometryBuffer(
|
||||
# endif
|
||||
geom_id,
|
||||
RTC_BUFFER_TYPE_VERTEX,
|
||||
t,
|
||||
RTC_FORMAT_FLOAT4,
|
||||
sizeof(float) * 4,
|
||||
num_keys_embree
|
||||
# if RTC_VERSION >= 40400
|
||||
,
|
||||
(void **)(&rtc_verts),
|
||||
nullptr
|
||||
# endif
|
||||
);
|
||||
}
|
||||
|
||||
assert(rtc_verts);
|
||||
if (rtc_verts) {
|
||||
const size_t num_curves = hair->num_curves();
|
||||
pack_motion_verts<packed_float3>(
|
||||
num_curves,
|
||||
hair,
|
||||
attr_P->data_at_time_step<packed_float3>(t, num_motion_steps),
|
||||
attr_R->data_at_time_step<float>(t, num_motion_steps),
|
||||
rtc_verts,
|
||||
hair->curve_shape);
|
||||
}
|
||||
|
||||
if (update) {
|
||||
rtcUpdateGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::set_point_vertex_buffer(RTCGeometry geom_id,
|
||||
const PointCloud *pointcloud,
|
||||
const bool update)
|
||||
{
|
||||
const Attribute *attr_P = pointcloud->attributes.find(ATTR_STD_POSITION);
|
||||
const Attribute *attr_R = pointcloud->attributes.find(ATTR_STD_RADIUS);
|
||||
size_t num_motion_steps = 1;
|
||||
if (pointcloud->has_motion_blur() && attr_P->has_motion()) {
|
||||
num_motion_steps = pointcloud->get_motion_steps();
|
||||
}
|
||||
|
||||
const size_t num_points = pointcloud->num_points();
|
||||
|
||||
/* Copy the point data to Embree. */
|
||||
for (int t = 0; t < num_motion_steps; ++t) {
|
||||
|
||||
float4 *rtc_verts = nullptr;
|
||||
if (update) {
|
||||
rtc_verts = (float4 *)rtcGetGeometryBufferData(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
|
||||
}
|
||||
else {
|
||||
# if RTC_VERSION >= 40400
|
||||
rtcSetNewGeometryBufferHostDevice(
|
||||
# else
|
||||
rtc_verts = (float4 *)rtcSetNewGeometryBuffer(
|
||||
# endif
|
||||
geom_id,
|
||||
RTC_BUFFER_TYPE_VERTEX,
|
||||
t,
|
||||
RTC_FORMAT_FLOAT4,
|
||||
sizeof(float) * 4,
|
||||
num_points
|
||||
# if RTC_VERSION >= 40400
|
||||
,
|
||||
(void **)(&rtc_verts),
|
||||
nullptr
|
||||
# endif
|
||||
);
|
||||
}
|
||||
|
||||
assert(rtc_verts);
|
||||
if (rtc_verts) {
|
||||
const packed_float3 *verts = attr_P->data_at_time_step<packed_float3>(t, num_motion_steps);
|
||||
const float *radius = attr_R->data_at_time_step<float>(t, num_motion_steps);
|
||||
for (size_t j = 0; j < num_points; ++j) {
|
||||
rtc_verts[j] = make_float4(float3(verts[j]), radius[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (update) {
|
||||
rtcUpdateGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::add_points(const Object *ob, const PointCloud *pointcloud, const int i)
|
||||
{
|
||||
const size_t prim_offset = pointcloud->prim_offset;
|
||||
|
||||
size_t num_motion_steps = 1;
|
||||
if (pointcloud->has_motion_blur()) {
|
||||
const Attribute *attr_P = pointcloud->attributes.find(ATTR_STD_POSITION);
|
||||
if (attr_P->has_motion()) {
|
||||
num_motion_steps = pointcloud->get_motion_steps();
|
||||
}
|
||||
}
|
||||
|
||||
const enum RTCGeometryType type = RTC_GEOMETRY_TYPE_SPHERE_POINT;
|
||||
|
||||
RTCGeometry geom_id = rtcNewGeometry(rtc_device, type);
|
||||
|
||||
rtcSetGeometryBuildQuality(geom_id, build_quality);
|
||||
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
|
||||
|
||||
set_point_vertex_buffer(geom_id, pointcloud, false);
|
||||
|
||||
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
|
||||
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
|
||||
rtcSetGeometryEnableFilterFunctionFromArguments(geom_id, true);
|
||||
|
||||
rtcCommitGeometry(geom_id);
|
||||
rtcAttachGeometryByID(scene, geom_id, i * 2);
|
||||
rtcReleaseGeometry(geom_id);
|
||||
}
|
||||
|
||||
void BVHEmbree::add_curves(const Object *ob, const Hair *hair, const int i)
|
||||
{
|
||||
const size_t prim_offset = hair->curve_segment_offset;
|
||||
|
||||
const Attribute *attr_P = hair->attributes.find(ATTR_STD_POSITION);
|
||||
size_t num_motion_steps = 1;
|
||||
if (hair->has_motion_blur() && attr_P->has_motion()) {
|
||||
num_motion_steps = hair->get_motion_steps();
|
||||
}
|
||||
|
||||
assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
|
||||
num_motion_steps = min(num_motion_steps, (size_t)RTC_MAX_TIME_STEP_COUNT);
|
||||
|
||||
const size_t num_curves = hair->num_curves();
|
||||
size_t num_segments = 0;
|
||||
for (size_t j = 0; j < num_curves; ++j) {
|
||||
const Hair::Curve c = hair->get_curve(j);
|
||||
assert(c.num_segments() > 0);
|
||||
num_segments += c.num_segments();
|
||||
}
|
||||
|
||||
const enum RTCGeometryType type = (hair->curve_shape == CURVE_THICK_LINEAR ?
|
||||
RTC_GEOMETRY_TYPE_ROUND_LINEAR_CURVE :
|
||||
hair->curve_shape == CURVE_RIBBON ?
|
||||
RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE :
|
||||
RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE);
|
||||
|
||||
RTCGeometry geom_id = rtcNewGeometry(rtc_device, type);
|
||||
rtcSetGeometryTessellationRate(geom_id, params.curve_subdivisions + 1);
|
||||
unsigned *rtc_indices = nullptr;
|
||||
# if RTC_VERSION >= 40400
|
||||
rtcSetNewGeometryBufferHostDevice(
|
||||
# else
|
||||
rtc_indices = (unsigned *)rtcSetNewGeometryBuffer(
|
||||
# endif
|
||||
geom_id,
|
||||
RTC_BUFFER_TYPE_INDEX,
|
||||
0,
|
||||
RTC_FORMAT_UINT,
|
||||
sizeof(int),
|
||||
num_segments
|
||||
# if RTC_VERSION >= 40400
|
||||
,
|
||||
(void **)(&rtc_indices),
|
||||
nullptr
|
||||
# endif
|
||||
);
|
||||
|
||||
size_t rtc_index = 0;
|
||||
for (size_t j = 0; j < num_curves; ++j) {
|
||||
const Hair::Curve c = hair->get_curve(j);
|
||||
for (size_t k = 0; k < c.num_segments(); ++k) {
|
||||
rtc_indices[rtc_index] = c.first_key + k;
|
||||
if (hair->curve_shape != CURVE_THICK_LINEAR) {
|
||||
/* Room for extra CVs at Catmull-Rom splines. */
|
||||
rtc_indices[rtc_index] += j * 2;
|
||||
}
|
||||
|
||||
++rtc_index;
|
||||
}
|
||||
}
|
||||
|
||||
rtcSetGeometryBuildQuality(geom_id, build_quality);
|
||||
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
|
||||
|
||||
set_curve_vertex_buffer(geom_id, hair, false);
|
||||
|
||||
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
|
||||
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
|
||||
rtcSetGeometryEnableFilterFunctionFromArguments(geom_id, true);
|
||||
|
||||
rtcCommitGeometry(geom_id);
|
||||
rtcAttachGeometryByID(scene, geom_id, i * 2 + 1);
|
||||
rtcReleaseGeometry(geom_id);
|
||||
}
|
||||
|
||||
void BVHEmbree::refit(Progress &progress)
|
||||
{
|
||||
progress.set_substatus("Refitting BVH nodes");
|
||||
|
||||
/* Update all vertex buffers, then tell Embree to rebuild/-fit the BVHs. */
|
||||
unsigned geom_id = 0;
|
||||
for (Object *ob : objects) {
|
||||
if (!params.top_level || (ob->is_traceable() && !ob->get_geometry()->is_instanced())) {
|
||||
Geometry *geom = ob->get_geometry();
|
||||
|
||||
if (geom->is_mesh() || geom->is_volume()) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (mesh->num_triangles() > 0) {
|
||||
RTCGeometry geom = rtcGetGeometry(scene, geom_id);
|
||||
set_tri_vertex_buffer(geom, mesh, true);
|
||||
rtcSetGeometryUserData(geom, (void *)mesh->prim_offset);
|
||||
rtcCommitGeometry(geom);
|
||||
}
|
||||
}
|
||||
else if (geom->is_hair()) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
if (hair->is_traceable()) {
|
||||
RTCGeometry geom = rtcGetGeometry(scene, geom_id + 1);
|
||||
set_curve_vertex_buffer(geom, hair, true);
|
||||
rtcSetGeometryUserData(geom, (void *)hair->curve_segment_offset);
|
||||
rtcCommitGeometry(geom);
|
||||
}
|
||||
}
|
||||
else if (geom->is_pointcloud()) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
|
||||
if (pointcloud->num_points() > 0) {
|
||||
RTCGeometry geom = rtcGetGeometry(scene, geom_id);
|
||||
set_point_vertex_buffer(geom, pointcloud, true);
|
||||
rtcCommitGeometry(geom);
|
||||
}
|
||||
}
|
||||
}
|
||||
geom_id += 2;
|
||||
}
|
||||
|
||||
rtcCommitScene(scene);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_EMBREE */
|
||||
65
blender-5.2.0/intern/cycles/bvh/embree.h
Normal file
65
blender-5.2.0/intern/cycles/bvh/embree.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_EMBREE
|
||||
|
||||
# include <embree4/rtcore.h>
|
||||
# include <embree4/rtcore_scene.h>
|
||||
|
||||
# include "bvh/bvh.h"
|
||||
# include "bvh/params.h"
|
||||
|
||||
# include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Hair;
|
||||
class Mesh;
|
||||
class PointCloud;
|
||||
|
||||
class BVHEmbree : public BVH {
|
||||
public:
|
||||
void build(Progress &progress,
|
||||
Stats *stats,
|
||||
RTCDevice rtc_device,
|
||||
const bool rtc_device_is_sycl_ = false);
|
||||
void refit(Progress &progress);
|
||||
|
||||
# if defined(WITH_EMBREE_GPU) && RTC_VERSION >= 40302
|
||||
RTCError offload_scenes_to_gpu(const vector<RTCScene> &scenes);
|
||||
# endif
|
||||
|
||||
const char *get_error_string(RTCError error_code);
|
||||
|
||||
RTCScene scene;
|
||||
|
||||
BVHEmbree(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects);
|
||||
~BVHEmbree() override;
|
||||
|
||||
protected:
|
||||
void add_object(Object *ob, const int i);
|
||||
void add_instance(Object *ob, const int i);
|
||||
void add_curves(const Object *ob, const Hair *hair, const int i);
|
||||
void add_points(const Object *ob, const PointCloud *pointcloud, const int i);
|
||||
void add_triangles(const Object *ob, const Mesh *mesh, const int i);
|
||||
|
||||
private:
|
||||
void set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, const bool update);
|
||||
void set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, const bool update);
|
||||
void set_point_vertex_buffer(RTCGeometry geom_id,
|
||||
const PointCloud *pointcloud,
|
||||
const bool update);
|
||||
|
||||
RTCDevice rtc_device;
|
||||
bool rtc_device_is_sycl;
|
||||
enum RTCBuildQuality build_quality;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_EMBREE */
|
||||
42
blender-5.2.0/intern/cycles/bvh/hiprt.cpp
Normal file
42
blender-5.2.0/intern/cycles/bvh/hiprt.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef WITH_HIPRT
|
||||
|
||||
# include "bvh/hiprt.h"
|
||||
|
||||
# include "scene/mesh.h"
|
||||
# include "scene/object.h"
|
||||
|
||||
# include "device/hiprt/device_impl.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHHIPRT::BVHHIPRT(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *in_device)
|
||||
: BVH(params, geometry, objects),
|
||||
hiprt_geom(nullptr),
|
||||
custom_primitive_bound(in_device, "Custom Primitive Bound", MEM_READ_ONLY),
|
||||
triangle_index(in_device, "HIPRT Triangle Index", MEM_READ_ONLY),
|
||||
vertex_data(in_device, "vertex_data", MEM_READ_ONLY),
|
||||
aabb_overlap_ratio(0.0f),
|
||||
device(in_device)
|
||||
{
|
||||
triangle_mesh = {nullptr};
|
||||
custom_prim_aabb = {nullptr};
|
||||
}
|
||||
|
||||
BVHHIPRT::~BVHHIPRT()
|
||||
{
|
||||
custom_primitive_bound.free();
|
||||
triangle_index.free();
|
||||
vertex_data.free();
|
||||
device->release_bvh(this);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
54
blender-5.2.0/intern/cycles/bvh/hiprt.h
Normal file
54
blender-5.2.0/intern/cycles/bvh/hiprt.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef WITH_HIPRT
|
||||
|
||||
# pragma once
|
||||
|
||||
# include "bvh/bvh.h"
|
||||
# include "bvh/params.h"
|
||||
# include "device/memory.h"
|
||||
|
||||
# include <hiprt/hiprt_types.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHHIPRT : public BVH {
|
||||
public:
|
||||
friend class HIPDevice;
|
||||
|
||||
bool is_tlas()
|
||||
{
|
||||
return params.top_level;
|
||||
}
|
||||
|
||||
hiprtGeometry hiprt_geom;
|
||||
hiprtTriangleMeshPrimitive triangle_mesh;
|
||||
hiprtAABBListPrimitive custom_prim_aabb;
|
||||
hiprtGeometryBuildInput geom_input;
|
||||
|
||||
vector<int2> custom_prim_info; /* x: prim_id, y: prim_type */
|
||||
vector<float2> prims_time;
|
||||
|
||||
/* Custom primitives. */
|
||||
device_vector<BoundBox> custom_primitive_bound;
|
||||
device_vector<int> triangle_index;
|
||||
device_vector<float> vertex_data;
|
||||
|
||||
float aabb_overlap_ratio;
|
||||
|
||||
BVHHIPRT(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *in_device);
|
||||
|
||||
~BVHHIPRT() override;
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
22
blender-5.2.0/intern/cycles/bvh/metal.h
Normal file
22
blender-5.2.0/intern/cycles/bvh/metal.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_METAL
|
||||
|
||||
# include "bvh/bvh.h"
|
||||
|
||||
# include "util/unique_ptr.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
unique_ptr<BVH> bvh_metal_create(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *device);
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_METAL */
|
||||
23
blender-5.2.0/intern/cycles/bvh/metal.mm
Normal file
23
blender-5.2.0/intern/cycles/bvh/metal.mm
Normal file
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef WITH_METAL
|
||||
|
||||
# include "device/metal/bvh.h"
|
||||
|
||||
# include "util/unique_ptr.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
unique_ptr<BVH> bvh_metal_create(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *device)
|
||||
{
|
||||
return make_unique<BVHMetal>(params, geometry, objects, device);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_METAL */
|
||||
24
blender-5.2.0/intern/cycles/bvh/multi.cpp
Normal file
24
blender-5.2.0/intern/cycles/bvh/multi.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "bvh/multi.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHMulti::BVHMulti(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_)
|
||||
: BVH(params_, geometry_, objects_)
|
||||
{
|
||||
}
|
||||
|
||||
void BVHMulti::replace_geometry(const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects)
|
||||
{
|
||||
for (unique_ptr<BVH> &bvh : sub_bvhs) {
|
||||
bvh->replace_geometry(geometry, objects);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
28
blender-5.2.0/intern/cycles/bvh/multi.h
Normal file
28
blender-5.2.0/intern/cycles/bvh/multi.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bvh/bvh.h"
|
||||
#include "bvh/params.h"
|
||||
|
||||
#include <util/unique_ptr.h>
|
||||
#include <util/vector.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHMulti : public BVH {
|
||||
public:
|
||||
vector<unique_ptr<BVH>> sub_bvhs;
|
||||
|
||||
BVHMulti(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects);
|
||||
|
||||
protected:
|
||||
void replace_geometry(const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects) override;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
211
blender-5.2.0/intern/cycles/bvh/node.cpp
Normal file
211
blender-5.2.0/intern/cycles/bvh/node.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#include "bvh/node.h"
|
||||
|
||||
#include "bvh/build.h"
|
||||
#include "bvh/bvh.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* BVH Node */
|
||||
|
||||
int BVHNode::getSubtreeSize(BVH_STAT stat) const
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
switch (stat) {
|
||||
case BVH_STAT_NODE_COUNT:
|
||||
cnt = 1;
|
||||
break;
|
||||
case BVH_STAT_LEAF_COUNT:
|
||||
cnt = is_leaf() ? 1 : 0;
|
||||
break;
|
||||
case BVH_STAT_INNER_COUNT:
|
||||
cnt = is_leaf() ? 0 : 1;
|
||||
break;
|
||||
case BVH_STAT_TRIANGLE_COUNT:
|
||||
cnt = is_leaf() ? reinterpret_cast<const LeafNode *>(this)->num_triangles() : 0;
|
||||
break;
|
||||
case BVH_STAT_CHILDNODE_COUNT:
|
||||
cnt = num_children();
|
||||
break;
|
||||
case BVH_STAT_ALIGNED_COUNT:
|
||||
if (!is_unaligned) {
|
||||
cnt = 1;
|
||||
}
|
||||
break;
|
||||
case BVH_STAT_UNALIGNED_COUNT:
|
||||
if (is_unaligned) {
|
||||
cnt = 1;
|
||||
}
|
||||
break;
|
||||
case BVH_STAT_ALIGNED_INNER_COUNT:
|
||||
if (!is_leaf()) {
|
||||
bool has_unaligned = false;
|
||||
for (int j = 0; j < num_children(); j++) {
|
||||
has_unaligned |= get_child(j)->is_unaligned;
|
||||
}
|
||||
cnt += has_unaligned ? 0 : 1;
|
||||
}
|
||||
break;
|
||||
case BVH_STAT_UNALIGNED_INNER_COUNT:
|
||||
if (!is_leaf()) {
|
||||
bool has_unaligned = false;
|
||||
for (int j = 0; j < num_children(); j++) {
|
||||
has_unaligned |= get_child(j)->is_unaligned;
|
||||
}
|
||||
cnt += has_unaligned ? 1 : 0;
|
||||
}
|
||||
break;
|
||||
case BVH_STAT_ALIGNED_LEAF_COUNT:
|
||||
cnt = (is_leaf() && !is_unaligned) ? 1 : 0;
|
||||
break;
|
||||
case BVH_STAT_UNALIGNED_LEAF_COUNT:
|
||||
cnt = (is_leaf() && is_unaligned) ? 1 : 0;
|
||||
break;
|
||||
case BVH_STAT_DEPTH:
|
||||
if (is_leaf()) {
|
||||
cnt = 1;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < num_children(); i++) {
|
||||
cnt = max(cnt, get_child(i)->getSubtreeSize(stat));
|
||||
}
|
||||
cnt += 1;
|
||||
}
|
||||
return cnt;
|
||||
default:
|
||||
assert(0); /* unknown mode */
|
||||
}
|
||||
|
||||
if (!is_leaf()) {
|
||||
for (int i = 0; i < num_children(); i++) {
|
||||
cnt += get_child(i)->getSubtreeSize(stat);
|
||||
}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
float BVHNode::computeSubtreeSAHCost(const BVHParams &p, const float probability) const
|
||||
{
|
||||
float SAH = probability * p.cost(num_children(), num_triangles());
|
||||
|
||||
for (int i = 0; i < num_children(); i++) {
|
||||
BVHNode *child = get_child(i);
|
||||
SAH += child->computeSubtreeSAHCost(
|
||||
p, probability * child->bounds.safe_area() / bounds.safe_area());
|
||||
}
|
||||
|
||||
return SAH;
|
||||
}
|
||||
|
||||
uint BVHNode::update_visibility()
|
||||
{
|
||||
if (!is_leaf() && visibility == 0) {
|
||||
InnerNode *inner = (InnerNode *)this;
|
||||
BVHNode *child0 = inner->children[0].get();
|
||||
BVHNode *child1 = inner->children[1].get();
|
||||
|
||||
visibility = child0->update_visibility() | child1->update_visibility();
|
||||
}
|
||||
|
||||
return visibility;
|
||||
}
|
||||
|
||||
void BVHNode::update_time()
|
||||
{
|
||||
if (!is_leaf()) {
|
||||
InnerNode *inner = (InnerNode *)this;
|
||||
BVHNode *child0 = inner->children[0].get();
|
||||
BVHNode *child1 = inner->children[1].get();
|
||||
child0->update_time();
|
||||
child1->update_time();
|
||||
time_from = min(child0->time_from, child1->time_from);
|
||||
time_to = max(child0->time_to, child1->time_to);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct DumpTraversalContext {
|
||||
/* Descriptor of while where writing is happening. */
|
||||
FILE *stream;
|
||||
/* Unique identifier of the node current. */
|
||||
int id;
|
||||
};
|
||||
|
||||
void dump_subtree(DumpTraversalContext *context,
|
||||
const BVHNode *node,
|
||||
const BVHNode *parent = nullptr)
|
||||
{
|
||||
if (node->is_leaf()) {
|
||||
fprintf(context->stream,
|
||||
" node_%p [label=\"%d\",fillcolor=\"#ccccee\",style=filled]\n",
|
||||
node,
|
||||
context->id);
|
||||
}
|
||||
else {
|
||||
fprintf(context->stream,
|
||||
" node_%p [label=\"%d\",fillcolor=\"#cceecc\",style=filled]\n",
|
||||
node,
|
||||
context->id);
|
||||
}
|
||||
if (parent != nullptr) {
|
||||
fprintf(context->stream, " node_%p -> node_%p;\n", parent, node);
|
||||
}
|
||||
context->id += 1;
|
||||
for (int i = 0; i < node->num_children(); ++i) {
|
||||
dump_subtree(context, node->get_child(i), node);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void BVHNode::dump_graph(const char *filename)
|
||||
{
|
||||
DumpTraversalContext context;
|
||||
context.stream = fopen(filename, "w");
|
||||
if (context.stream == nullptr) {
|
||||
return;
|
||||
}
|
||||
context.id = 0;
|
||||
fprintf(context.stream, "digraph BVH {\n");
|
||||
dump_subtree(&context, this);
|
||||
fprintf(context.stream, "}\n");
|
||||
fclose(context.stream);
|
||||
}
|
||||
|
||||
/* Inner Node */
|
||||
|
||||
void InnerNode::print(const int depth) const
|
||||
{
|
||||
for (int i = 0; i < depth; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printf("inner node %p\n", (void *)this);
|
||||
|
||||
if (children[0]) {
|
||||
children[0]->print(depth + 1);
|
||||
}
|
||||
if (children[1]) {
|
||||
children[1]->print(depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void LeafNode::print(const int depth) const
|
||||
{
|
||||
for (int i = 0; i < depth; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printf("leaf node %d to %d\n", lo, hi);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
202
blender-5.2.0/intern/cycles/bvh/node.h
Normal file
202
blender-5.2.0/intern/cycles/bvh/node.h
Normal file
@@ -0,0 +1,202 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/boundbox.h"
|
||||
#include "util/types.h"
|
||||
#include "util/unique_ptr.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
enum BVH_STAT {
|
||||
BVH_STAT_NODE_COUNT,
|
||||
BVH_STAT_INNER_COUNT,
|
||||
BVH_STAT_LEAF_COUNT,
|
||||
BVH_STAT_TRIANGLE_COUNT,
|
||||
BVH_STAT_CHILDNODE_COUNT,
|
||||
BVH_STAT_ALIGNED_COUNT,
|
||||
BVH_STAT_UNALIGNED_COUNT,
|
||||
BVH_STAT_ALIGNED_INNER_COUNT,
|
||||
BVH_STAT_UNALIGNED_INNER_COUNT,
|
||||
BVH_STAT_ALIGNED_LEAF_COUNT,
|
||||
BVH_STAT_UNALIGNED_LEAF_COUNT,
|
||||
BVH_STAT_DEPTH,
|
||||
};
|
||||
|
||||
class BVHParams;
|
||||
|
||||
class BVHNode {
|
||||
public:
|
||||
virtual ~BVHNode() = default;
|
||||
|
||||
virtual bool is_leaf() const = 0;
|
||||
virtual int num_children() const = 0;
|
||||
virtual BVHNode *get_child(const int i) const = 0;
|
||||
virtual int num_triangles() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual void print(const int depth = 0) const = 0;
|
||||
|
||||
void set_aligned_space(const Transform &aligned_space)
|
||||
{
|
||||
is_unaligned = true;
|
||||
if (this->aligned_space == nullptr) {
|
||||
this->aligned_space = make_unique<Transform>(aligned_space);
|
||||
}
|
||||
else {
|
||||
*this->aligned_space = aligned_space;
|
||||
}
|
||||
}
|
||||
|
||||
Transform get_aligned_space() const
|
||||
{
|
||||
if (aligned_space == nullptr) {
|
||||
return transform_identity();
|
||||
}
|
||||
return *aligned_space;
|
||||
}
|
||||
|
||||
bool has_unaligned() const
|
||||
{
|
||||
if (is_leaf()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < num_children(); ++i) {
|
||||
if (get_child(i)->is_unaligned) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Subtree functions
|
||||
int getSubtreeSize(BVH_STAT stat = BVH_STAT_NODE_COUNT) const;
|
||||
float computeSubtreeSAHCost(const BVHParams &p, const float probability = 1.0f) const;
|
||||
|
||||
uint update_visibility();
|
||||
void update_time();
|
||||
|
||||
/* Dump the content of the tree as a graphviz file. */
|
||||
void dump_graph(const char *filename);
|
||||
|
||||
// Properties.
|
||||
BoundBox bounds;
|
||||
uint visibility = 0;
|
||||
|
||||
bool is_unaligned = false;
|
||||
|
||||
/* TODO(sergey): Can be stored as 3x3 matrix, but better to have some
|
||||
* utilities and type defines in util_transform first.
|
||||
*/
|
||||
unique_ptr<Transform> aligned_space;
|
||||
|
||||
float time_from = 0.0f, time_to = 1.0f;
|
||||
|
||||
protected:
|
||||
explicit BVHNode(const BoundBox &bounds) : bounds(bounds) {}
|
||||
|
||||
explicit BVHNode(const BVHNode &other)
|
||||
: bounds(other.bounds),
|
||||
visibility(other.visibility),
|
||||
is_unaligned(other.is_unaligned),
|
||||
|
||||
time_from(other.time_from),
|
||||
time_to(other.time_to)
|
||||
{
|
||||
if (other.aligned_space != nullptr) {
|
||||
assert(other.is_unaligned);
|
||||
aligned_space = make_unique<Transform>(*other.aligned_space);
|
||||
}
|
||||
else {
|
||||
assert(!other.is_unaligned);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class InnerNode : public BVHNode {
|
||||
public:
|
||||
static constexpr int kNumMaxChildren = 8;
|
||||
|
||||
InnerNode(const BoundBox &bounds, unique_ptr<BVHNode> &&child0, unique_ptr<BVHNode> &&child1)
|
||||
: BVHNode(bounds), num_children_(2)
|
||||
{
|
||||
if (child0 && child1) {
|
||||
visibility = child0->visibility | child1->visibility;
|
||||
}
|
||||
else {
|
||||
/* Happens on build cancel. */
|
||||
visibility = 0;
|
||||
}
|
||||
|
||||
children[0] = std::move(child0);
|
||||
children[1] = std::move(child1);
|
||||
}
|
||||
|
||||
/* NOTE: This function is only used during binary BVH builder, and it's
|
||||
* supposed to be configured to have 2 children which will be filled-in in a
|
||||
* bit. */
|
||||
explicit InnerNode(const BoundBox &bounds) : BVHNode(bounds), num_children_(0)
|
||||
{
|
||||
visibility = 0;
|
||||
num_children_ = 2;
|
||||
}
|
||||
|
||||
bool is_leaf() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int num_children() const override
|
||||
{
|
||||
return num_children_;
|
||||
}
|
||||
BVHNode *get_child(const int i) const override
|
||||
{
|
||||
assert(i >= 0 && i < num_children_);
|
||||
return children[i].get();
|
||||
}
|
||||
void print(const int depth) const override;
|
||||
|
||||
int num_children_;
|
||||
unique_ptr<BVHNode> children[kNumMaxChildren];
|
||||
};
|
||||
|
||||
class LeafNode : public BVHNode {
|
||||
public:
|
||||
LeafNode(const BoundBox &bounds, const uint visibility, const int lo, const int hi)
|
||||
: BVHNode(bounds), lo(lo), hi(hi)
|
||||
{
|
||||
this->bounds = bounds;
|
||||
this->visibility = visibility;
|
||||
}
|
||||
|
||||
LeafNode(const LeafNode &other) = default;
|
||||
|
||||
bool is_leaf() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
int num_children() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
BVHNode *get_child(int /*i*/) const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
int num_triangles() const override
|
||||
{
|
||||
return hi - lo;
|
||||
}
|
||||
void print(const int depth) const override;
|
||||
|
||||
int lo;
|
||||
int hi;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
473
blender-5.2.0/intern/cycles/bvh/octree.cpp
Normal file
473
blender-5.2.0/intern/cycles/bvh/octree.cpp
Normal file
@@ -0,0 +1,473 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "bvh/octree.h"
|
||||
|
||||
#include "scene/object.h"
|
||||
#include "scene/volume.h"
|
||||
|
||||
#include "integrator/shader_eval.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/progress.h"
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
# include <openvdb/tools/FindActiveValues.h>
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
__forceinline int Octree::flatten_index(int x, int y, int z) const
|
||||
{
|
||||
return x + resolution_ * (y + z * resolution_);
|
||||
}
|
||||
|
||||
Extrema<float> Octree::get_extrema(const int3 index_min, const int3 index_max) const
|
||||
{
|
||||
const blocked_range3d<int> range(
|
||||
index_min.x, index_max.x, 32, index_min.y, index_max.y, 32, index_min.z, index_max.z, 32);
|
||||
|
||||
const Extrema<float> identity = {FLT_MAX, -FLT_MAX};
|
||||
|
||||
auto reduction_func = [&](const blocked_range3d<int> &r, Extrema<float> init) -> Extrema<float> {
|
||||
for (int z = r.cols().begin(); z < r.cols().end(); ++z) {
|
||||
for (int y = r.rows().begin(); y < r.rows().end(); ++y) {
|
||||
for (int x = r.pages().begin(); x < r.pages().end(); ++x) {
|
||||
init = merge(init, sigmas_[flatten_index(x, y, z)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return init;
|
||||
};
|
||||
|
||||
auto join_func = [](Extrema<float> a, Extrema<float> b) -> Extrema<float> {
|
||||
return merge(a, b);
|
||||
};
|
||||
|
||||
return parallel_reduce(range, identity, reduction_func, join_func);
|
||||
}
|
||||
|
||||
__forceinline float3 Octree::position_to_index(const float3 p) const
|
||||
{
|
||||
return (p - bbox_min) * position_to_index_scale_;
|
||||
}
|
||||
|
||||
int3 Octree::position_to_floor_index(const float3 p) const
|
||||
{
|
||||
const float3 index = round(position_to_index(p));
|
||||
return clamp(make_int3(int(index.x), int(index.y), int(index.z)), 0, resolution_ - 1);
|
||||
}
|
||||
|
||||
int3 Octree::position_to_ceil_index(const float3 p) const
|
||||
{
|
||||
if (any_zero(position_to_index_scale_)) {
|
||||
/* Octree with degenerate shape, force max index. */
|
||||
return make_int3(resolution_);
|
||||
}
|
||||
const float3 index = round(position_to_index(p));
|
||||
return clamp(make_int3(int(index.x), int(index.y), int(index.z)), 1, resolution_);
|
||||
}
|
||||
|
||||
__forceinline float3 Octree::index_to_position(int x, int y, int z) const
|
||||
{
|
||||
return bbox_min + make_float3(x, y, z) * index_to_position_scale_;
|
||||
}
|
||||
|
||||
__forceinline float3 Octree::voxel_size() const
|
||||
{
|
||||
return index_to_position_scale_;
|
||||
}
|
||||
|
||||
bool Octree::should_split(std::shared_ptr<OctreeNode> &node) const
|
||||
{
|
||||
const int3 index_min = position_to_floor_index(node->bbox.min);
|
||||
const int3 index_max = position_to_ceil_index(node->bbox.max);
|
||||
node->sigma = get_extrema(index_min, index_max);
|
||||
|
||||
const float3 bbox_size = node->bbox.size();
|
||||
if (any_zero(bbox_size)) {
|
||||
/* Octree with degenerate shape, can happen for implicit volume. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The threshold is set so that ideally only one sample needs to be taken per node. Value taken
|
||||
* from "Volume Rendering for Pixar's Elemental". */
|
||||
return (node->sigma.range() * len(bbox_size) * scale_ > 1.442f &&
|
||||
node->depth < VOLUME_OCTREE_MAX_DEPTH);
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
/* Check if a interior mask grid intersects with a bounding box defined by `p_min` and `p_max`. */
|
||||
static bool vdb_voxel_intersect(const float3 p_min,
|
||||
const float3 p_max,
|
||||
openvdb::BoolGrid::ConstPtr &grid,
|
||||
const openvdb::tools::FindActiveValues<openvdb::BoolTree> &find)
|
||||
{
|
||||
if (grid->empty()) {
|
||||
/* Non-mesh volume or open mesh. */
|
||||
return true;
|
||||
}
|
||||
|
||||
const openvdb::math::CoordBBox coord_bbox(
|
||||
openvdb::Coord::floor(grid->worldToIndex({p_min.x, p_min.y, p_min.z})),
|
||||
openvdb::Coord::ceil(grid->worldToIndex({p_max.x, p_max.y, p_max.z})));
|
||||
|
||||
/* Check if the bounding box lies inside or partially overlaps the mesh.
|
||||
* For interior mask grids, all the interior voxels are active. */
|
||||
return find.anyActiveValues(coord_bbox, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Fill in coordinates for shading the volume density. */
|
||||
static void fill_shader_input(device_vector<KernelShaderEvalInput> &d_input,
|
||||
const Octree *octree,
|
||||
const Object *object,
|
||||
const Shader *shader,
|
||||
#ifdef WITH_OPENVDB
|
||||
openvdb::BoolGrid::ConstPtr &interior_mask,
|
||||
#endif
|
||||
const int resolution)
|
||||
{
|
||||
const int object_id = object->get_device_index();
|
||||
const uint shader_id = shader->id;
|
||||
|
||||
KernelShaderEvalInput *d_input_data = d_input.data();
|
||||
|
||||
const float3 voxel_size = octree->voxel_size();
|
||||
/* Dilate the voxel in case we miss features at the boundary. */
|
||||
const float3 pad = 0.2f * voxel_size;
|
||||
const float3 padded_size = voxel_size + pad * 2.0f;
|
||||
|
||||
const blocked_range3d<int> range(0, resolution, 8, 0, resolution, 8, 0, resolution, 8);
|
||||
parallel_for(range, [&](const blocked_range3d<int> &r) {
|
||||
#ifdef WITH_OPENVDB
|
||||
/* One accessor per thread is important for cached access. */
|
||||
const auto find = openvdb::tools::FindActiveValues(interior_mask->tree());
|
||||
#endif
|
||||
|
||||
for (int z = r.cols().begin(); z < r.cols().end(); ++z) {
|
||||
for (int y = r.rows().begin(); y < r.rows().end(); ++y) {
|
||||
for (int x = r.pages().begin(); x < r.pages().end(); ++x) {
|
||||
const int offset = octree->flatten_index(x, y, z);
|
||||
const float3 p = octree->index_to_position(x, y, z);
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
/* Zero density for cells outside of the mesh. */
|
||||
if (!vdb_voxel_intersect(p, p + voxel_size, interior_mask, find)) {
|
||||
d_input_data[offset * 2].object = OBJECT_NONE;
|
||||
d_input_data[offset * 2 + 1].object = SHADER_NONE;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
KernelShaderEvalInput in;
|
||||
in.object = object_id;
|
||||
in.prim = __float_as_int(p.x - pad.x);
|
||||
in.u = p.y - pad.y;
|
||||
in.v = p.z - pad.z;
|
||||
d_input_data[offset * 2] = in;
|
||||
|
||||
in.object = shader_id;
|
||||
in.prim = __float_as_int(padded_size.x);
|
||||
in.u = padded_size.y;
|
||||
in.v = padded_size.z;
|
||||
d_input_data[offset * 2 + 1] = in;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Read back the volume density. */
|
||||
static void read_shader_output(const device_vector<float> &d_output,
|
||||
const Octree *octree,
|
||||
const int num_channels,
|
||||
const int resolution,
|
||||
vector<Extrema<float>> &sigmas)
|
||||
{
|
||||
const float *d_output_data = d_output.data();
|
||||
const blocked_range3d<int> range(0, resolution, 32, 0, resolution, 32, 0, resolution, 32);
|
||||
|
||||
parallel_for(range, [&](const blocked_range3d<int> &r) {
|
||||
for (int z = r.cols().begin(); z < r.cols().end(); ++z) {
|
||||
for (int y = r.rows().begin(); y < r.rows().end(); ++y) {
|
||||
for (int x = r.pages().begin(); x < r.pages().end(); ++x) {
|
||||
const int index = octree->flatten_index(x, y, z);
|
||||
sigmas[index].min = d_output_data[index * num_channels + 0];
|
||||
sigmas[index].max = d_output_data[index * num_channels + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Octree::evaluate_volume_density(Device *device,
|
||||
Progress &progress,
|
||||
#ifdef WITH_OPENVDB
|
||||
openvdb::BoolGrid::ConstPtr &interior_mask,
|
||||
#endif
|
||||
const Object *object,
|
||||
const Shader *shader)
|
||||
{
|
||||
/* For heterogeneous volume, the grid resolution is 2^max_depth in each 3D dimension;
|
||||
* for homogeneous volume, only one grid is needed. */
|
||||
resolution_ = VolumeManager::is_homogeneous_volume(object, shader) ?
|
||||
1 :
|
||||
power_of_2(VOLUME_OCTREE_MAX_DEPTH);
|
||||
index_to_position_scale_ = root_->bbox.size() / float(resolution_);
|
||||
position_to_index_scale_ = safe_divide(one_float3(), index_to_position_scale_);
|
||||
|
||||
/* Initialize density field. */
|
||||
/* TODO(weizhen): maybe lower the resolution depending on the object size. */
|
||||
const int size = resolution_ * resolution_ * resolution_;
|
||||
sigmas_.resize(size);
|
||||
parallel_for(0, size, [&](int i) { sigmas_[i] = {0.0f, 0.0f}; });
|
||||
|
||||
/* Min and max. */
|
||||
const int num_channels = 2;
|
||||
|
||||
/* Need the size of two `KernelShaderEvalInput`s per voxel for evaluating the shader. */
|
||||
const int num_inputs = size * 2;
|
||||
|
||||
/* Evaluate shader on device. */
|
||||
ShaderEval shader_eval(device, progress);
|
||||
shader_eval.eval(
|
||||
SHADER_EVAL_VOLUME_DENSITY,
|
||||
num_inputs,
|
||||
num_channels,
|
||||
[&](device_vector<KernelShaderEvalInput> &d_input) {
|
||||
#ifdef WITH_OPENVDB
|
||||
fill_shader_input(d_input, this, object, shader, interior_mask, resolution_);
|
||||
#else
|
||||
fill_shader_input(d_input, this, object, shader, resolution_);
|
||||
#endif
|
||||
return size;
|
||||
},
|
||||
[&](device_vector<float> &d_output) {
|
||||
read_shader_output(d_output, this, num_channels, resolution_, sigmas_);
|
||||
});
|
||||
}
|
||||
|
||||
float Octree::volume_scale(const Object *object) const
|
||||
{
|
||||
const Geometry *geom = object->get_geometry();
|
||||
if (geom->is_volume()) {
|
||||
const Volume *volume = static_cast<const Volume *>(geom);
|
||||
if (volume->get_object_space()) {
|
||||
/* The density changes with object scale, we scale the density accordingly in the final
|
||||
* render. */
|
||||
if (volume->transform_applied) {
|
||||
const float3 unit = normalize(one_float3());
|
||||
return 1.0f / len(transform_direction(&object->get_tfm(), unit));
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* The density does not change with object scale, we scale the node in the viewport to it's
|
||||
* true size. */
|
||||
if (!volume->transform_applied) {
|
||||
const float3 unit = normalize(one_float3());
|
||||
return len(transform_direction(&object->get_tfm(), unit));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* TODO(weizhen): use the maximal scale of all instances. */
|
||||
if (!geom->transform_applied) {
|
||||
const float3 unit = normalize(one_float3());
|
||||
return len(transform_direction(&object->get_tfm(), unit));
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
std::shared_ptr<OctreeInternalNode> Octree::make_internal(std::shared_ptr<OctreeNode> &node)
|
||||
{
|
||||
num_nodes_ += 8;
|
||||
auto internal = std::make_shared<OctreeInternalNode>(*node);
|
||||
|
||||
/* Create bounding boxes for children. */
|
||||
const float3 center = internal->bbox.center();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
const float3 t = make_float3(i & 1, (i >> 1) & 1, (i >> 2) & 1);
|
||||
const BoundBox bbox(mix(internal->bbox.min, center, t), mix(center, internal->bbox.max, t));
|
||||
internal->children_[i] = std::make_shared<OctreeNode>(bbox, internal->depth + 1);
|
||||
}
|
||||
|
||||
return internal;
|
||||
}
|
||||
|
||||
void Octree::recursive_build(std::shared_ptr<OctreeNode> &octree_node)
|
||||
{
|
||||
if (!should_split(octree_node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make the current node an internal node. */
|
||||
auto internal = make_internal(octree_node);
|
||||
|
||||
for (auto &child : internal->children_) {
|
||||
task_pool_.push([&] { recursive_build(child); });
|
||||
}
|
||||
|
||||
octree_node = internal;
|
||||
}
|
||||
|
||||
void Octree::flatten(KernelOctreeNode *knodes,
|
||||
const int current_index,
|
||||
const std::shared_ptr<OctreeNode> &node,
|
||||
int &child_index) const
|
||||
{
|
||||
KernelOctreeNode &knode = knodes[current_index];
|
||||
knode.sigma = node->sigma;
|
||||
|
||||
if (auto internal_ptr = std::dynamic_pointer_cast<OctreeInternalNode>(node)) {
|
||||
knode.first_child = child_index;
|
||||
child_index += 8;
|
||||
/* Loop through all the children and flatten in breadth-first manner, so that children are
|
||||
* stored in contiguous indices. */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
knodes[knode.first_child + i].parent = current_index;
|
||||
flatten(knodes, knode.first_child + i, internal_ptr->children_[i], child_index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
knode.first_child = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void Octree::set_flattened(const bool flattened)
|
||||
{
|
||||
is_flattened_ = flattened;
|
||||
}
|
||||
|
||||
bool Octree::is_flattened() const
|
||||
{
|
||||
return is_flattened_;
|
||||
}
|
||||
|
||||
void Octree::build(Device *device,
|
||||
Progress &progress,
|
||||
#ifdef WITH_OPENVDB
|
||||
openvdb::BoolGrid::ConstPtr &interior_mask,
|
||||
#endif
|
||||
const Object *object,
|
||||
const Shader *shader)
|
||||
{
|
||||
const char *name = object->get_asset_name().c_str();
|
||||
progress.set_substatus(string_printf("Evaluating density for %s", name));
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
evaluate_volume_density(device, progress, interior_mask, object, shader);
|
||||
#else
|
||||
evaluate_volume_density(device, progress, object, shader);
|
||||
#endif
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
progress.set_substatus(string_printf("Building octree for %s", name));
|
||||
|
||||
scale_ = volume_scale(object);
|
||||
recursive_build(root_);
|
||||
|
||||
task_pool_.wait_work();
|
||||
|
||||
is_built_ = true;
|
||||
sigmas_.clear();
|
||||
}
|
||||
|
||||
Octree::Octree(const BoundBox &bbox)
|
||||
{
|
||||
bbox_min = bbox.min;
|
||||
root_ = std::make_shared<OctreeNode>(bbox, 0);
|
||||
is_built_ = false;
|
||||
is_flattened_ = false;
|
||||
}
|
||||
|
||||
bool Octree::is_built() const
|
||||
{
|
||||
return is_built_;
|
||||
}
|
||||
|
||||
int Octree::get_num_nodes() const
|
||||
{
|
||||
return num_nodes_;
|
||||
}
|
||||
|
||||
std::shared_ptr<OctreeNode> Octree::get_root() const
|
||||
{
|
||||
return root_;
|
||||
}
|
||||
|
||||
void OctreeNode::visualize(std::string &str) const
|
||||
{
|
||||
const auto *internal = dynamic_cast<const OctreeInternalNode *>(this);
|
||||
|
||||
if (!internal) {
|
||||
/* Skip leaf nodes. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create three orthogonal faces for inner nodes. */
|
||||
const float3 mid = bbox.center();
|
||||
const float3 max = bbox.max;
|
||||
const float3 min = bbox.min;
|
||||
const std::string mid_x = to_string(mid.x), mid_y = to_string(mid.y), mid_z = to_string(mid.z),
|
||||
min_x = to_string(min.x), min_y = to_string(min.y), min_z = to_string(min.z),
|
||||
max_x = to_string(max.x), max_y = to_string(max.y), max_z = to_string(max.z);
|
||||
// clang-format off
|
||||
str += "(" + mid_x + "," + mid_y + "," + min_z + "), "
|
||||
"(" + mid_x + "," + mid_y + "," + max_z + "), "
|
||||
"(" + mid_x + "," + max_y + "," + max_z + "), "
|
||||
"(" + mid_x + "," + max_y + "," + min_z + "), "
|
||||
"(" + mid_x + "," + min_y + "," + min_z + "), "
|
||||
"(" + mid_x + "," + min_y + "," + max_z + "), ";
|
||||
str += "(" + min_x + "," + mid_y + "," + mid_z + "), "
|
||||
"(" + max_x + "," + mid_y + "," + mid_z + "), "
|
||||
"(" + max_x + "," + mid_y + "," + max_z + "), "
|
||||
"(" + min_x + "," + mid_y + "," + max_z + "), "
|
||||
"(" + min_x + "," + mid_y + "," + min_z + "), "
|
||||
"(" + max_x + "," + mid_y + "," + min_z + "), ";
|
||||
str += "(" + mid_x + "," + min_y + "," + mid_z + "), "
|
||||
"(" + mid_x + "," + max_y + "," + mid_z + "), "
|
||||
"(" + max_x + "," + max_y + "," + mid_z + "), "
|
||||
"(" + max_x + "," + min_y + "," + mid_z + "), "
|
||||
"(" + min_x + "," + min_y + "," + mid_z + "), "
|
||||
"(" + min_x + "," + max_y + "," + mid_z + "), ";
|
||||
// clang-format on
|
||||
for (const auto &child : internal->children_) {
|
||||
child->visualize(str);
|
||||
}
|
||||
}
|
||||
|
||||
void Octree::visualize(std::ofstream &file, const std::string object_name) const
|
||||
{
|
||||
std::string str = "vertices = [";
|
||||
root_->visualize(str);
|
||||
str +=
|
||||
"]\nr = range(len(vertices))\n"
|
||||
"edges = [(i, i+1 if i%6<5 else i-4) for i in r]\n"
|
||||
"mesh = bpy.data.meshes.new('Octree')\n"
|
||||
"mesh.from_pydata(vertices, edges, [])\n"
|
||||
"mesh.update()\n"
|
||||
"obj = bpy.data.objects.new('" +
|
||||
object_name +
|
||||
"', mesh)\n"
|
||||
"octree.objects.link(obj)\n"
|
||||
"bpy.context.view_layer.objects.active = obj\n"
|
||||
"bpy.ops.object.mode_set(mode='EDIT')\n";
|
||||
file << str;
|
||||
|
||||
const float3 center = root_->bbox.center();
|
||||
const float3 size = root_->bbox.size() * 0.5f;
|
||||
file << "bpy.ops.mesh.primitive_cube_add(location = " << center << ", scale = " << size << ")\n";
|
||||
file << "bpy.ops.mesh.delete(type='ONLY_FACE')\n"
|
||||
"bpy.ops.object.mode_set(mode='OBJECT')\n"
|
||||
"obj.select_set(True)\n";
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
145
blender-5.2.0/intern/cycles/bvh/octree.h
Normal file
145
blender-5.2.0/intern/cycles/bvh/octree.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* The volume octree is used to determine the necessary step size when rendering the volume. One
|
||||
* volume per object per shader is built, and a node splits in eight when the density difference
|
||||
* inside the node exceeds a certain threshold. */
|
||||
|
||||
#ifndef __OCTREE_H__
|
||||
#define __OCTREE_H__
|
||||
|
||||
#include "util/boundbox.h"
|
||||
#include "util/task.h"
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
# include <openvdb/openvdb.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <iosfwd>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Device;
|
||||
class Object;
|
||||
class Progress;
|
||||
class Shader;
|
||||
struct KernelOctreeNode;
|
||||
|
||||
struct OctreeNode {
|
||||
/* Bounding box of the node. */
|
||||
BoundBox bbox;
|
||||
|
||||
/* Depth of the node in the octree. */
|
||||
int depth;
|
||||
|
||||
/* Minimal and maximal volume density inside the node. */
|
||||
Extrema<float> sigma = {0.0f, 0.0f};
|
||||
|
||||
OctreeNode() : bbox(BoundBox::empty), depth(0) {}
|
||||
OctreeNode(BoundBox bbox_, int depth_) : bbox(bbox_), depth(depth_) {}
|
||||
virtual ~OctreeNode() = default;
|
||||
|
||||
/* Visualize node. */
|
||||
void visualize(std::string &str) const;
|
||||
};
|
||||
|
||||
struct OctreeInternalNode : public OctreeNode {
|
||||
OctreeInternalNode(OctreeNode &node) : children_(8)
|
||||
{
|
||||
bbox = node.bbox;
|
||||
depth = node.depth;
|
||||
sigma = node.sigma;
|
||||
}
|
||||
|
||||
vector<std::shared_ptr<OctreeNode>> children_;
|
||||
};
|
||||
|
||||
class Octree {
|
||||
public:
|
||||
Octree(const BoundBox &bbox);
|
||||
~Octree() = default;
|
||||
|
||||
/* Build the octree according to the volume density. */
|
||||
#ifdef WITH_OPENVDB
|
||||
void build(Device *, Progress &, openvdb::BoolGrid::ConstPtr &, const Object *, const Shader *);
|
||||
#else
|
||||
void build(Device *, Progress &, const Object *, const Shader *);
|
||||
#endif
|
||||
|
||||
/* Convert the octree into an array of nodes for uploading to the kernel. */
|
||||
void flatten(KernelOctreeNode *, const int, const std::shared_ptr<OctreeNode> &, int &) const;
|
||||
void set_flattened(const bool = true);
|
||||
bool is_flattened() const;
|
||||
|
||||
/* Flatten a 3D coordinate in the grid to a 1D index. */
|
||||
int flatten_index(int x, int y, int z) const;
|
||||
/* Convert from index to the position of the lower left corner of the voxel. */
|
||||
float3 index_to_position(int x, int y, int z) const;
|
||||
/* Size of a voxel. */
|
||||
float3 voxel_size() const;
|
||||
|
||||
int get_num_nodes() const;
|
||||
std::shared_ptr<OctreeNode> get_root() const;
|
||||
bool is_built() const;
|
||||
|
||||
/* Draw octree nodes as empty boxes with Blender Python API. */
|
||||
void visualize(std::ofstream &file, const std::string object_name) const;
|
||||
|
||||
private:
|
||||
/* The bounding box of the octree is divided into a regular grid with the same resolution in each
|
||||
* dimension. */
|
||||
int resolution_;
|
||||
/* Extrema of volume densities in the grid. */
|
||||
vector<Extrema<float>> sigmas_;
|
||||
/* Compute the extrema of all the `sigmas_` in a coordinate bounding box defined by `index_min`
|
||||
* and `index_max`. */
|
||||
Extrema<float> get_extrema(const int3 index_min, const int3 index_max) const;
|
||||
/* Randomly sample positions inside the grid to evaluate the shader for the density. */
|
||||
#ifdef WITH_OPENVDB
|
||||
void evaluate_volume_density(
|
||||
Device *, Progress &, openvdb::BoolGrid::ConstPtr &, const Object *, const Shader *);
|
||||
#else
|
||||
void evaluate_volume_density(Device *, Progress &, const Object *, const Shader *);
|
||||
#endif
|
||||
/* Convert from position in object space to grid index space. */
|
||||
float3 position_to_index_scale_;
|
||||
float3 index_to_position_scale_;
|
||||
float3 position_to_index(const float3 p) const;
|
||||
int3 position_to_floor_index(const float3 p) const;
|
||||
int3 position_to_ceil_index(const float3 p) const;
|
||||
|
||||
/* Whether a node should be split into child nodes. */
|
||||
bool should_split(std::shared_ptr<OctreeNode> &node) const;
|
||||
/* Scale the node size so that the octree has the similar subdivision levels in viewport and
|
||||
* final render. */
|
||||
float volume_scale(const Object *object) const;
|
||||
float scale_;
|
||||
/* Recursively build a node and its child nodes. */
|
||||
void recursive_build(std::shared_ptr<OctreeNode> &);
|
||||
/* Turn a node into an internal node. */
|
||||
std::shared_ptr<OctreeInternalNode> make_internal(std::shared_ptr<OctreeNode> &node);
|
||||
|
||||
/* Root node. */
|
||||
std::shared_ptr<OctreeNode> root_;
|
||||
|
||||
/* Bounding box min of the octree, used for computing the indices. */
|
||||
float3 bbox_min;
|
||||
|
||||
/* Whether the octree is already built. */
|
||||
bool is_built_;
|
||||
|
||||
/* Whether the octree is already flattened into an array. */
|
||||
bool is_flattened_;
|
||||
|
||||
/* Number of nodes in the octree. Incremented while building the tree. */
|
||||
std::atomic<int> num_nodes_ = 1;
|
||||
|
||||
/* Task pool for building the octree in parallel. */
|
||||
TaskPool task_pool_;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* __OCTREE_H__ */
|
||||
37
blender-5.2.0/intern/cycles/bvh/optix.cpp
Normal file
37
blender-5.2.0/intern/cycles/bvh/optix.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/* SPDX-FileCopyrightText: 2019 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef WITH_OPTIX
|
||||
|
||||
# include "device/device.h"
|
||||
|
||||
# include "bvh/optix.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHOptiX::BVHOptiX(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_,
|
||||
Device *device)
|
||||
: BVH(params_, geometry_, objects_),
|
||||
device(device),
|
||||
traversable_handle(0),
|
||||
as_data(make_unique<device_only_memory<char>>(
|
||||
device, params.top_level ? "optix tlas" : "optix blas", false)),
|
||||
motion_transform_data(
|
||||
make_unique<device_only_memory<char>>(device, "optix motion transform", false))
|
||||
{
|
||||
}
|
||||
|
||||
BVHOptiX::~BVHOptiX()
|
||||
{
|
||||
/* Acceleration structure memory is delayed freed on device, since deleting the
|
||||
* BVH may happen while still being used for rendering. */
|
||||
device->release_bvh(this);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_OPTIX */
|
||||
35
blender-5.2.0/intern/cycles/bvh/optix.h
Normal file
35
blender-5.2.0/intern/cycles/bvh/optix.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* SPDX-FileCopyrightText: 2019 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_OPTIX
|
||||
|
||||
# include "bvh/bvh.h"
|
||||
# include "bvh/params.h"
|
||||
|
||||
# include "device/memory.h"
|
||||
|
||||
# include "util/unique_ptr.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHOptiX : public BVH {
|
||||
public:
|
||||
Device *device;
|
||||
uint64_t traversable_handle;
|
||||
unique_ptr<device_only_memory<char>> as_data;
|
||||
unique_ptr<device_only_memory<char>> motion_transform_data;
|
||||
|
||||
BVHOptiX(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *device);
|
||||
~BVHOptiX() override;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_OPTIX */
|
||||
325
blender-5.2.0/intern/cycles/bvh/params.h
Normal file
325
blender-5.2.0/intern/cycles/bvh/params.h
Normal file
@@ -0,0 +1,325 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/boundbox.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Layout of BVH tree.
|
||||
*
|
||||
* For example, how wide BVH tree is, in terms of number of children
|
||||
* per node.
|
||||
*/
|
||||
using BVHLayout = KernelBVHLayout;
|
||||
|
||||
/* Type of BVH, in terms whether it is supported dynamic updates of meshes
|
||||
* or whether modifying geometry requires full BVH rebuild.
|
||||
*/
|
||||
enum BVHType {
|
||||
/* BVH supports dynamic updates of geometry.
|
||||
*
|
||||
* Faster for updating BVH tree when doing modifications in viewport,
|
||||
* but slower for rendering.
|
||||
*/
|
||||
BVH_TYPE_DYNAMIC = 0,
|
||||
/* BVH tree is calculated for specific scene, updates in geometry
|
||||
* requires full tree rebuild.
|
||||
*
|
||||
* Slower to update BVH tree when modifying objects in viewport, also
|
||||
* slower to build final BVH tree but gives best possible render speed.
|
||||
*/
|
||||
BVH_TYPE_STATIC = 1,
|
||||
|
||||
BVH_NUM_TYPES,
|
||||
};
|
||||
|
||||
/* Names bit-flag type to denote which BVH layouts are supported by
|
||||
* particular area.
|
||||
*
|
||||
* Bit-flags are the BVH_LAYOUT_* values.
|
||||
*/
|
||||
using BVHLayoutMask = int;
|
||||
|
||||
/* Get human readable name of BVH layout. */
|
||||
const char *bvh_layout_name(BVHLayout layout);
|
||||
|
||||
/* BVH Parameters */
|
||||
|
||||
class BVHParams {
|
||||
public:
|
||||
/* spatial split area threshold */
|
||||
bool use_spatial_split;
|
||||
float spatial_split_alpha;
|
||||
|
||||
/* Unaligned nodes creation threshold */
|
||||
float unaligned_split_threshold;
|
||||
|
||||
/* SAH costs */
|
||||
float sah_node_cost;
|
||||
float sah_primitive_cost;
|
||||
|
||||
/* number of primitives in leaf */
|
||||
int min_leaf_size;
|
||||
int max_triangle_leaf_size;
|
||||
int max_motion_triangle_leaf_size;
|
||||
int max_curve_leaf_size;
|
||||
int max_motion_curve_leaf_size;
|
||||
int max_point_leaf_size;
|
||||
int max_motion_point_leaf_size;
|
||||
|
||||
/* object or mesh level bvh */
|
||||
bool top_level;
|
||||
|
||||
/* BVH layout to be built. */
|
||||
BVHLayout bvh_layout;
|
||||
|
||||
/* Use unaligned bounding boxes.
|
||||
* Only used for curves BVH.
|
||||
*/
|
||||
bool use_unaligned_nodes;
|
||||
|
||||
/* Use compact acceleration structure (Embree). */
|
||||
bool use_compact_structure;
|
||||
|
||||
/* Split time range to this number of steps and create leaf node for each
|
||||
* of this time steps.
|
||||
*
|
||||
* Speeds up rendering of motion primitives at the cost of higher memory usage.
|
||||
*/
|
||||
|
||||
/* Same as above, but for triangle primitives. */
|
||||
int num_motion_triangle_steps;
|
||||
int num_motion_curve_steps;
|
||||
int num_motion_point_steps;
|
||||
|
||||
/* Same as in SceneParams. */
|
||||
int bvh_type;
|
||||
|
||||
/* These are needed for Embree. */
|
||||
int curve_subdivisions;
|
||||
|
||||
/* fixed parameters */
|
||||
enum { MAX_DEPTH = 64, MAX_SPATIAL_DEPTH = 48, NUM_SPATIAL_BINS = 32 };
|
||||
|
||||
BVHParams()
|
||||
{
|
||||
use_spatial_split = true;
|
||||
spatial_split_alpha = 1e-5f;
|
||||
|
||||
unaligned_split_threshold = 0.7f;
|
||||
|
||||
/* todo: see if splitting up primitive cost to be separate for triangles
|
||||
* and curves can help. so far in tests it doesn't help, but why? */
|
||||
sah_node_cost = 1.0f;
|
||||
sah_primitive_cost = 1.0f;
|
||||
|
||||
min_leaf_size = 1;
|
||||
max_triangle_leaf_size = 8;
|
||||
max_motion_triangle_leaf_size = 8;
|
||||
max_curve_leaf_size = 1;
|
||||
max_motion_curve_leaf_size = 4;
|
||||
max_point_leaf_size = 8;
|
||||
max_motion_point_leaf_size = 8;
|
||||
|
||||
top_level = false;
|
||||
bvh_layout = BVH_LAYOUT_BVH2;
|
||||
use_compact_structure = false;
|
||||
use_unaligned_nodes = false;
|
||||
|
||||
num_motion_curve_steps = 0;
|
||||
num_motion_triangle_steps = 0;
|
||||
num_motion_point_steps = 0;
|
||||
|
||||
bvh_type = 0;
|
||||
|
||||
curve_subdivisions = 4;
|
||||
}
|
||||
|
||||
/* SAH costs */
|
||||
__forceinline float cost(const int num_nodes, const int num_primitives) const
|
||||
{
|
||||
return node_cost(num_nodes) + primitive_cost(num_primitives);
|
||||
}
|
||||
|
||||
__forceinline float primitive_cost(const int n) const
|
||||
{
|
||||
return n * sah_primitive_cost;
|
||||
}
|
||||
|
||||
__forceinline float node_cost(const int n) const
|
||||
{
|
||||
return n * sah_node_cost;
|
||||
}
|
||||
|
||||
__forceinline bool small_enough_for_leaf(const int size, const int level)
|
||||
{
|
||||
return (size <= min_leaf_size || level >= MAX_DEPTH);
|
||||
}
|
||||
|
||||
bool use_motion_steps()
|
||||
{
|
||||
return num_motion_curve_steps > 0 || num_motion_triangle_steps > 0 ||
|
||||
num_motion_point_steps > 0;
|
||||
}
|
||||
|
||||
/* Gets best matching BVH.
|
||||
*
|
||||
* If the requested layout is supported by the device, it will be used.
|
||||
* Otherwise, widest supported layout below that will be used.
|
||||
*/
|
||||
static BVHLayout best_bvh_layout(BVHLayout requested_layout, BVHLayoutMask supported_layouts);
|
||||
};
|
||||
|
||||
/* BVH Reference
|
||||
*
|
||||
* Reference to a primitive. Primitive index and object are sneakily packed
|
||||
* into BoundBox to reduce memory usage and align nicely */
|
||||
|
||||
class BVHReference {
|
||||
public:
|
||||
__forceinline BVHReference() = default;
|
||||
|
||||
__forceinline BVHReference(const BoundBox &bounds_,
|
||||
const int prim_index_,
|
||||
const int prim_object_,
|
||||
const int prim_type,
|
||||
float time_from = 0.0f,
|
||||
float time_to = 1.0f)
|
||||
: rbounds(bounds_), time_from_(time_from), time_to_(time_to)
|
||||
{
|
||||
rbounds.min.w = __int_as_float(prim_index_);
|
||||
rbounds.max.w = __int_as_float(prim_object_);
|
||||
type = prim_type;
|
||||
}
|
||||
|
||||
__forceinline const BoundBox &bounds() const
|
||||
{
|
||||
return rbounds;
|
||||
}
|
||||
__forceinline int prim_index() const
|
||||
{
|
||||
return __float_as_int(rbounds.min.w);
|
||||
}
|
||||
__forceinline int prim_object() const
|
||||
{
|
||||
return __float_as_int(rbounds.max.w);
|
||||
}
|
||||
__forceinline int prim_type() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
__forceinline float time_from() const
|
||||
{
|
||||
return time_from_;
|
||||
}
|
||||
__forceinline float time_to() const
|
||||
{
|
||||
return time_to_;
|
||||
}
|
||||
|
||||
BVHReference &operator=(const BVHReference &arg) = default;
|
||||
|
||||
protected:
|
||||
BoundBox rbounds;
|
||||
uint type;
|
||||
float time_from_, time_to_;
|
||||
};
|
||||
|
||||
/* BVH Range
|
||||
*
|
||||
* Build range used during construction, to indicate the bounds and place in
|
||||
* the reference array of a subset of primitives Again uses trickery to pack
|
||||
* integers into BoundBox for alignment purposes. */
|
||||
|
||||
class BVHRange {
|
||||
public:
|
||||
__forceinline BVHRange()
|
||||
{
|
||||
rbounds.min.w = __int_as_float(0);
|
||||
rbounds.max.w = __int_as_float(0);
|
||||
}
|
||||
|
||||
__forceinline BVHRange(const BoundBox &bounds_, int start_, int size_) : rbounds(bounds_)
|
||||
{
|
||||
rbounds.min.w = __int_as_float(start_);
|
||||
rbounds.max.w = __int_as_float(size_);
|
||||
}
|
||||
|
||||
__forceinline BVHRange(const BoundBox &bounds_, const BoundBox &cbounds_, int start_, int size_)
|
||||
: rbounds(bounds_), cbounds(cbounds_)
|
||||
{
|
||||
rbounds.min.w = __int_as_float(start_);
|
||||
rbounds.max.w = __int_as_float(size_);
|
||||
}
|
||||
|
||||
__forceinline void set_start(const int start_)
|
||||
{
|
||||
rbounds.min.w = __int_as_float(start_);
|
||||
}
|
||||
|
||||
__forceinline const BoundBox &bounds() const
|
||||
{
|
||||
return rbounds;
|
||||
}
|
||||
__forceinline const BoundBox ¢_bounds() const
|
||||
{
|
||||
return cbounds;
|
||||
}
|
||||
__forceinline int start() const
|
||||
{
|
||||
return __float_as_int(rbounds.min.w);
|
||||
}
|
||||
__forceinline int size() const
|
||||
{
|
||||
return __float_as_int(rbounds.max.w);
|
||||
}
|
||||
__forceinline int end() const
|
||||
{
|
||||
return start() + size();
|
||||
}
|
||||
|
||||
protected:
|
||||
BoundBox rbounds;
|
||||
BoundBox cbounds;
|
||||
};
|
||||
|
||||
/* BVH Spatial Bin */
|
||||
|
||||
struct BVHSpatialBin {
|
||||
BoundBox bounds;
|
||||
int enter;
|
||||
int exit;
|
||||
|
||||
__forceinline BVHSpatialBin() = default;
|
||||
};
|
||||
|
||||
/* BVH Spatial Storage
|
||||
*
|
||||
* The idea of this storage is have thread-specific storage for the spatial
|
||||
* splitters. We can pre-allocate this storage in advance and avoid heavy memory
|
||||
* operations during split process.
|
||||
*/
|
||||
|
||||
struct BVHSpatialStorage {
|
||||
/* Accumulated bounds when sweeping from right to left. */
|
||||
vector<BoundBox> right_bounds;
|
||||
|
||||
/* Bins used for histogram when selecting best split plane. */
|
||||
BVHSpatialBin bins[3][BVHParams::NUM_SPATIAL_BINS];
|
||||
|
||||
/* Temporary storage for the new references. Used by spatial split to store
|
||||
* new references in before they're getting inserted into actual array,
|
||||
*/
|
||||
vector<BVHReference> new_references;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
190
blender-5.2.0/intern/cycles/bvh/sort.cpp
Normal file
190
blender-5.2.0/intern/cycles/bvh/sort.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#include "bvh/sort.h"
|
||||
|
||||
#include "bvh/params.h"
|
||||
#include "bvh/unaligned.h"
|
||||
|
||||
#include "util/algorithm.h"
|
||||
#include "util/task.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static const int BVH_SORT_THRESHOLD = 4096;
|
||||
|
||||
struct BVHReferenceCompare {
|
||||
public:
|
||||
int dim;
|
||||
const BVHUnaligned *unaligned_heuristic;
|
||||
const Transform *aligned_space;
|
||||
|
||||
BVHReferenceCompare(const int dim,
|
||||
const BVHUnaligned *unaligned_heuristic,
|
||||
const Transform *aligned_space)
|
||||
: dim(dim), unaligned_heuristic(unaligned_heuristic), aligned_space(aligned_space)
|
||||
{
|
||||
}
|
||||
|
||||
__forceinline BoundBox get_prim_bounds(const BVHReference &prim) const
|
||||
{
|
||||
return (aligned_space != nullptr) ?
|
||||
unaligned_heuristic->compute_aligned_prim_boundbox(prim, *aligned_space) :
|
||||
prim.bounds();
|
||||
}
|
||||
|
||||
/* Compare two references.
|
||||
*
|
||||
* Returns value is similar to return value of `strcmp()`.
|
||||
*/
|
||||
__forceinline int compare(const BVHReference &ra, const BVHReference &rb) const
|
||||
{
|
||||
BoundBox ra_bounds = get_prim_bounds(ra);
|
||||
BoundBox rb_bounds = get_prim_bounds(rb);
|
||||
const float ca = ra_bounds.min[dim] + ra_bounds.max[dim];
|
||||
const float cb = rb_bounds.min[dim] + rb_bounds.max[dim];
|
||||
|
||||
if (ca < cb) {
|
||||
return -1;
|
||||
}
|
||||
if (ca > cb) {
|
||||
return 1;
|
||||
}
|
||||
if (ra.prim_object() < rb.prim_object()) {
|
||||
return -1;
|
||||
}
|
||||
if (ra.prim_object() > rb.prim_object()) {
|
||||
return 1;
|
||||
}
|
||||
if (ra.prim_index() < rb.prim_index()) {
|
||||
return -1;
|
||||
}
|
||||
if (ra.prim_index() > rb.prim_index()) {
|
||||
return 1;
|
||||
}
|
||||
if (ra.prim_type() < rb.prim_type()) {
|
||||
return -1;
|
||||
}
|
||||
if (ra.prim_type() > rb.prim_type()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator()(const BVHReference &ra, const BVHReference &rb)
|
||||
{
|
||||
return (compare(ra, rb) < 0);
|
||||
}
|
||||
};
|
||||
|
||||
static void bvh_reference_sort_threaded(TaskPool *task_pool,
|
||||
BVHReference *data,
|
||||
const int job_start,
|
||||
const int job_end,
|
||||
const BVHReferenceCompare &compare);
|
||||
|
||||
/* Multi-threaded reference sort. */
|
||||
static void bvh_reference_sort_threaded(TaskPool *task_pool,
|
||||
BVHReference *data,
|
||||
const int job_start,
|
||||
const int job_end,
|
||||
const BVHReferenceCompare &compare)
|
||||
{
|
||||
int start = job_start;
|
||||
int end = job_end;
|
||||
bool have_work = (start < end);
|
||||
while (have_work) {
|
||||
const int count = job_end - job_start;
|
||||
if (count < BVH_SORT_THRESHOLD) {
|
||||
/* Number of reference low enough, faster to finish the job
|
||||
* in one thread rather than to spawn more threads.
|
||||
*/
|
||||
sort(data + job_start, data + job_end + 1, compare);
|
||||
break;
|
||||
}
|
||||
/* Single QSort step.
|
||||
* Use median-of-three method for the pivot point.
|
||||
*/
|
||||
int left = start;
|
||||
int right = end;
|
||||
const int center = (left + right) >> 1;
|
||||
if (compare.compare(data[left], data[center]) > 0) {
|
||||
swap(data[left], data[center]);
|
||||
}
|
||||
if (compare.compare(data[left], data[right]) > 0) {
|
||||
swap(data[left], data[right]);
|
||||
}
|
||||
if (compare.compare(data[center], data[right]) > 0) {
|
||||
swap(data[center], data[right]);
|
||||
}
|
||||
swap(data[center], data[right - 1]);
|
||||
const BVHReference median = data[right - 1];
|
||||
do {
|
||||
while (compare.compare(data[left], median) < 0) {
|
||||
++left;
|
||||
}
|
||||
while (compare.compare(data[right], median) > 0) {
|
||||
--right;
|
||||
}
|
||||
if (left <= right) {
|
||||
swap(data[left], data[right]);
|
||||
++left;
|
||||
--right;
|
||||
}
|
||||
} while (left <= right);
|
||||
/* We only create one new task here to reduce downside effects of
|
||||
* latency in TaskScheduler.
|
||||
* So generally current thread keeps working on the left part of the
|
||||
* array, and we create new task for the right side.
|
||||
* However, if there's nothing to be done in the left side of the array
|
||||
* we don't create any tasks and make it so current thread works on the
|
||||
* right side.
|
||||
*/
|
||||
have_work = false;
|
||||
if (left < end) {
|
||||
if (start < right) {
|
||||
task_pool->push([task_pool, data, left, end, compare] {
|
||||
bvh_reference_sort_threaded(task_pool, data, left, end, compare);
|
||||
});
|
||||
}
|
||||
else {
|
||||
start = left;
|
||||
have_work = true;
|
||||
}
|
||||
}
|
||||
if (start < right) {
|
||||
end = right;
|
||||
have_work = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bvh_reference_sort(const int start,
|
||||
const int end,
|
||||
BVHReference *data,
|
||||
const int dim,
|
||||
const BVHUnaligned *unaligned_heuristic,
|
||||
const Transform *aligned_space)
|
||||
{
|
||||
const int count = end - start;
|
||||
const BVHReferenceCompare compare(dim, unaligned_heuristic, aligned_space);
|
||||
if (count < BVH_SORT_THRESHOLD) {
|
||||
/* It is important to not use any mutex if array is small enough,
|
||||
* otherwise we end up in situation when we're going to sleep far
|
||||
* too often.
|
||||
*/
|
||||
sort(data + start, data + end, compare);
|
||||
}
|
||||
else {
|
||||
TaskPool task_pool;
|
||||
bvh_reference_sort_threaded(&task_pool, data, start, end - 1, compare);
|
||||
task_pool.wait_work();
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
23
blender-5.2.0/intern/cycles/bvh/sort.h
Normal file
23
blender-5.2.0/intern/cycles/bvh/sort.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHReference;
|
||||
class BVHUnaligned;
|
||||
struct Transform;
|
||||
|
||||
void bvh_reference_sort(const int start,
|
||||
const int end,
|
||||
BVHReference *data,
|
||||
const int dim,
|
||||
const BVHUnaligned *unaligned_heuristic = nullptr,
|
||||
const Transform *aligned_space = nullptr);
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
576
blender-5.2.0/intern/cycles/bvh/split.cpp
Normal file
576
blender-5.2.0/intern/cycles/bvh/split.cpp
Normal file
@@ -0,0 +1,576 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#include "bvh/split.h"
|
||||
|
||||
#include "bvh/build.h"
|
||||
#include "bvh/sort.h"
|
||||
|
||||
#include "scene/hair.h"
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/object.h"
|
||||
#include "scene/pointcloud.h"
|
||||
|
||||
#include "util/algorithm.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Object Split */
|
||||
|
||||
BVHObjectSplit::BVHObjectSplit(BVHBuild *builder,
|
||||
BVHSpatialStorage *storage,
|
||||
const BVHRange &range,
|
||||
vector<BVHReference> &references,
|
||||
const float nodeSAH,
|
||||
const BVHUnaligned *unaligned_heuristic,
|
||||
const Transform *aligned_space)
|
||||
: sah(FLT_MAX),
|
||||
dim(0),
|
||||
num_left(0),
|
||||
left_bounds(BoundBox::empty),
|
||||
right_bounds(BoundBox::empty),
|
||||
storage_(storage),
|
||||
references_(&references),
|
||||
unaligned_heuristic_(unaligned_heuristic),
|
||||
aligned_space_(aligned_space)
|
||||
{
|
||||
const BVHReference *ref_ptr = &references_->at(range.start());
|
||||
float min_sah = FLT_MAX;
|
||||
|
||||
for (int dim = 0; dim < 3; dim++) {
|
||||
/* Sort references. */
|
||||
bvh_reference_sort(range.start(),
|
||||
range.end(),
|
||||
&references_->at(0),
|
||||
dim,
|
||||
unaligned_heuristic_,
|
||||
aligned_space_);
|
||||
|
||||
// Resize must be called after every bvh_reference_sort, as sorting may use a task pool for
|
||||
// large ranges. This may cause another BVHObjectSplit to use and resize the storage on the
|
||||
// same thread.
|
||||
storage_->right_bounds.resize(range.size());
|
||||
|
||||
/* sweep right to left and determine bounds. */
|
||||
BoundBox right_bounds = BoundBox::empty;
|
||||
for (int i = range.size() - 1; i > 0; i--) {
|
||||
const BoundBox prim_bounds = get_prim_bounds(ref_ptr[i]);
|
||||
right_bounds.grow(prim_bounds);
|
||||
storage_->right_bounds[i - 1] = right_bounds;
|
||||
}
|
||||
|
||||
/* sweep left to right and select lowest SAH. */
|
||||
BoundBox left_bounds = BoundBox::empty;
|
||||
|
||||
for (int i = 1; i < range.size(); i++) {
|
||||
const BoundBox prim_bounds = get_prim_bounds(ref_ptr[i - 1]);
|
||||
left_bounds.grow(prim_bounds);
|
||||
right_bounds = storage_->right_bounds[i - 1];
|
||||
|
||||
const float sah = nodeSAH + left_bounds.safe_area() * builder->params.primitive_cost(i) +
|
||||
right_bounds.safe_area() *
|
||||
builder->params.primitive_cost(range.size() - i);
|
||||
|
||||
if (sah < min_sah) {
|
||||
min_sah = sah;
|
||||
|
||||
this->sah = sah;
|
||||
this->dim = dim;
|
||||
this->num_left = i;
|
||||
this->left_bounds = left_bounds;
|
||||
this->right_bounds = right_bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHObjectSplit::split(BVHRange &left, BVHRange &right, const BVHRange &range)
|
||||
{
|
||||
assert(!references_->empty());
|
||||
/* sort references according to split */
|
||||
bvh_reference_sort(range.start(),
|
||||
range.end(),
|
||||
&references_->at(0),
|
||||
this->dim,
|
||||
unaligned_heuristic_,
|
||||
aligned_space_);
|
||||
|
||||
BoundBox effective_left_bounds;
|
||||
BoundBox effective_right_bounds;
|
||||
const int num_right = range.size() - this->num_left;
|
||||
if (aligned_space_ == nullptr) {
|
||||
effective_left_bounds = left_bounds;
|
||||
effective_right_bounds = right_bounds;
|
||||
}
|
||||
else {
|
||||
effective_left_bounds = BoundBox::empty;
|
||||
effective_right_bounds = BoundBox::empty;
|
||||
for (int i = 0; i < this->num_left; ++i) {
|
||||
const BoundBox prim_boundbox = references_->at(range.start() + i).bounds();
|
||||
effective_left_bounds.grow(prim_boundbox);
|
||||
}
|
||||
for (int i = 0; i < num_right; ++i) {
|
||||
const BoundBox prim_boundbox = references_->at(range.start() + this->num_left + i).bounds();
|
||||
effective_right_bounds.grow(prim_boundbox);
|
||||
}
|
||||
}
|
||||
|
||||
/* split node ranges */
|
||||
left = BVHRange(effective_left_bounds, range.start(), this->num_left);
|
||||
right = BVHRange(effective_right_bounds, left.end(), num_right);
|
||||
}
|
||||
|
||||
/* Spatial Split */
|
||||
|
||||
BVHSpatialSplit::BVHSpatialSplit(const BVHBuild &builder,
|
||||
BVHSpatialStorage *storage,
|
||||
const BVHRange &range,
|
||||
vector<BVHReference> &references,
|
||||
const float nodeSAH,
|
||||
const BVHUnaligned *unaligned_heuristic,
|
||||
const Transform *aligned_space)
|
||||
: sah(FLT_MAX),
|
||||
dim(0),
|
||||
pos(0.0f),
|
||||
storage_(storage),
|
||||
references_(&references),
|
||||
unaligned_heuristic_(unaligned_heuristic),
|
||||
aligned_space_(aligned_space)
|
||||
{
|
||||
/* initialize bins. */
|
||||
BoundBox range_bounds;
|
||||
if (aligned_space == nullptr) {
|
||||
range_bounds = range.bounds();
|
||||
}
|
||||
else {
|
||||
range_bounds = unaligned_heuristic->compute_aligned_boundbox(
|
||||
range, &references_->at(0), *aligned_space);
|
||||
}
|
||||
|
||||
float3 origin = range_bounds.min;
|
||||
float3 binSize = (range_bounds.max - origin) * (1.0f / (float)BVHParams::NUM_SPATIAL_BINS);
|
||||
const float3 invBinSize = safe_divide(make_float3(1.0f), binSize);
|
||||
|
||||
for (int dim = 0; dim < 3; dim++) {
|
||||
for (int i = 0; i < BVHParams::NUM_SPATIAL_BINS; i++) {
|
||||
BVHSpatialBin &bin = storage_->bins[dim][i];
|
||||
|
||||
bin.bounds = BoundBox::empty;
|
||||
bin.enter = 0;
|
||||
bin.exit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* chop references into bins. */
|
||||
for (unsigned int refIdx = range.start(); refIdx < range.end(); refIdx++) {
|
||||
const BVHReference &ref = references_->at(refIdx);
|
||||
const BoundBox prim_bounds = get_prim_bounds(ref);
|
||||
const float3 firstBinf = (prim_bounds.min - origin) * invBinSize;
|
||||
const float3 lastBinf = (prim_bounds.max - origin) * invBinSize;
|
||||
int3 firstBin = make_int3((int)firstBinf.x, (int)firstBinf.y, (int)firstBinf.z);
|
||||
int3 lastBin = make_int3((int)lastBinf.x, (int)lastBinf.y, (int)lastBinf.z);
|
||||
|
||||
firstBin = clamp(firstBin, 0, BVHParams::NUM_SPATIAL_BINS - 1);
|
||||
lastBin = clamp(lastBin, firstBin, BVHParams::NUM_SPATIAL_BINS - 1);
|
||||
|
||||
for (int dim = 0; dim < 3; dim++) {
|
||||
BVHReference currRef(
|
||||
get_prim_bounds(ref), ref.prim_index(), ref.prim_object(), ref.prim_type());
|
||||
|
||||
for (int i = firstBin[dim]; i < lastBin[dim]; i++) {
|
||||
BVHReference leftRef;
|
||||
BVHReference rightRef;
|
||||
|
||||
split_reference(
|
||||
builder, leftRef, rightRef, currRef, dim, origin[dim] + binSize[dim] * (float)(i + 1));
|
||||
storage_->bins[dim][i].bounds.grow(leftRef.bounds());
|
||||
currRef = rightRef;
|
||||
}
|
||||
|
||||
storage_->bins[dim][lastBin[dim]].bounds.grow(currRef.bounds());
|
||||
storage_->bins[dim][firstBin[dim]].enter++;
|
||||
storage_->bins[dim][lastBin[dim]].exit++;
|
||||
}
|
||||
}
|
||||
|
||||
/* select best split plane. */
|
||||
storage_->right_bounds.resize(BVHParams::NUM_SPATIAL_BINS);
|
||||
for (int dim = 0; dim < 3; dim++) {
|
||||
/* sweep right to left and determine bounds. */
|
||||
BoundBox right_bounds = BoundBox::empty;
|
||||
for (int i = BVHParams::NUM_SPATIAL_BINS - 1; i > 0; i--) {
|
||||
right_bounds.grow(storage_->bins[dim][i].bounds);
|
||||
storage_->right_bounds[i - 1] = right_bounds;
|
||||
}
|
||||
|
||||
/* sweep left to right and select lowest SAH. */
|
||||
BoundBox left_bounds = BoundBox::empty;
|
||||
int leftNum = 0;
|
||||
int rightNum = range.size();
|
||||
|
||||
for (int i = 1; i < BVHParams::NUM_SPATIAL_BINS; i++) {
|
||||
left_bounds.grow(storage_->bins[dim][i - 1].bounds);
|
||||
leftNum += storage_->bins[dim][i - 1].enter;
|
||||
rightNum -= storage_->bins[dim][i - 1].exit;
|
||||
|
||||
const float sah = nodeSAH +
|
||||
left_bounds.safe_area() * builder.params.primitive_cost(leftNum) +
|
||||
storage_->right_bounds[i - 1].safe_area() *
|
||||
builder.params.primitive_cost(rightNum);
|
||||
|
||||
if (sah < this->sah) {
|
||||
this->sah = sah;
|
||||
this->dim = dim;
|
||||
this->pos = origin[dim] + binSize[dim] * (float)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split(BVHBuild *builder,
|
||||
BVHRange &left,
|
||||
BVHRange &right,
|
||||
const BVHRange &range)
|
||||
{
|
||||
/* Categorize references and compute bounds.
|
||||
*
|
||||
* Left-hand side: [left_start, left_end[
|
||||
* Uncategorized/split: [left_end, right_start[
|
||||
* Right-hand side: [right_start, refs.size()[ */
|
||||
|
||||
vector<BVHReference> &refs = *references_;
|
||||
const int left_start = range.start();
|
||||
int left_end = left_start;
|
||||
int right_start = range.end();
|
||||
int right_end = range.end();
|
||||
BoundBox left_bounds = BoundBox::empty;
|
||||
BoundBox right_bounds = BoundBox::empty;
|
||||
|
||||
for (int i = left_end; i < right_start; i++) {
|
||||
BoundBox prim_bounds = get_prim_bounds(refs[i]);
|
||||
if (prim_bounds.max[this->dim] <= this->pos) {
|
||||
/* entirely on the left-hand side */
|
||||
left_bounds.grow(prim_bounds);
|
||||
swap(refs[i], refs[left_end++]);
|
||||
}
|
||||
else if (prim_bounds.min[this->dim] >= this->pos) {
|
||||
/* entirely on the right-hand side */
|
||||
right_bounds.grow(prim_bounds);
|
||||
swap(refs[i--], refs[--right_start]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Duplicate or unsplit references intersecting both sides.
|
||||
*
|
||||
* Duplication happens into a temporary pre-allocated vector in order to
|
||||
* reduce number of `memmove()` calls happening in `vector.insert()`.
|
||||
*/
|
||||
vector<BVHReference> &new_refs = storage_->new_references;
|
||||
new_refs.clear();
|
||||
new_refs.reserve(right_start - left_end);
|
||||
while (left_end < right_start) {
|
||||
/* split reference. */
|
||||
const BVHReference curr_ref(get_prim_bounds(refs[left_end]),
|
||||
refs[left_end].prim_index(),
|
||||
refs[left_end].prim_object(),
|
||||
refs[left_end].prim_type());
|
||||
BVHReference lref;
|
||||
BVHReference rref;
|
||||
split_reference(*builder, lref, rref, curr_ref, this->dim, this->pos);
|
||||
|
||||
/* compute SAH for duplicate/unsplit candidates. */
|
||||
BoundBox lub = left_bounds; // Unsplit to left: new left-hand bounds.
|
||||
BoundBox rub = right_bounds; // Unsplit to right: new right-hand bounds.
|
||||
BoundBox ldb = left_bounds; // Duplicate: new left-hand bounds.
|
||||
BoundBox rdb = right_bounds; // Duplicate: new right-hand bounds.
|
||||
|
||||
lub.grow(curr_ref.bounds());
|
||||
rub.grow(curr_ref.bounds());
|
||||
ldb.grow(lref.bounds());
|
||||
rdb.grow(rref.bounds());
|
||||
|
||||
const float lac = builder->params.primitive_cost(left_end - left_start);
|
||||
const float rac = builder->params.primitive_cost(right_end - right_start);
|
||||
const float lbc = builder->params.primitive_cost(left_end - left_start + 1);
|
||||
const float rbc = builder->params.primitive_cost(right_end - right_start + 1);
|
||||
|
||||
const float unsplitLeftSAH = lub.safe_area() * lbc + right_bounds.safe_area() * rac;
|
||||
const float unsplitRightSAH = left_bounds.safe_area() * lac + rub.safe_area() * rbc;
|
||||
const float duplicateSAH = ldb.safe_area() * lbc + rdb.safe_area() * rbc;
|
||||
const float minSAH = min(min(unsplitLeftSAH, unsplitRightSAH), duplicateSAH);
|
||||
|
||||
if (minSAH == unsplitLeftSAH) {
|
||||
/* unsplit to left */
|
||||
left_bounds = lub;
|
||||
left_end++;
|
||||
}
|
||||
else if (minSAH == unsplitRightSAH) {
|
||||
/* unsplit to right */
|
||||
right_bounds = rub;
|
||||
swap(refs[left_end], refs[--right_start]);
|
||||
}
|
||||
else {
|
||||
/* duplicate */
|
||||
left_bounds = ldb;
|
||||
right_bounds = rdb;
|
||||
refs[left_end++] = lref;
|
||||
new_refs.push_back(rref);
|
||||
right_end++;
|
||||
}
|
||||
}
|
||||
/* Insert duplicated references into actual array in one go. */
|
||||
if (!new_refs.empty()) {
|
||||
refs.insert(refs.begin() + (right_end - new_refs.size()), new_refs.begin(), new_refs.end());
|
||||
}
|
||||
if (aligned_space_ != nullptr) {
|
||||
left_bounds = right_bounds = BoundBox::empty;
|
||||
for (int i = left_start; i < left_end - left_start; ++i) {
|
||||
const BoundBox prim_boundbox = references_->at(i).bounds();
|
||||
left_bounds.grow(prim_boundbox);
|
||||
}
|
||||
for (int i = right_start; i < right_end - right_start; ++i) {
|
||||
const BoundBox prim_boundbox = references_->at(i).bounds();
|
||||
right_bounds.grow(prim_boundbox);
|
||||
}
|
||||
}
|
||||
left = BVHRange(left_bounds, left_start, left_end - left_start);
|
||||
right = BVHRange(right_bounds, right_start, right_end - right_start);
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_triangle_primitive(const Mesh *mesh,
|
||||
const Transform *tfm,
|
||||
const int prim_index,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
const Mesh::Triangle t = mesh->get_triangle(prim_index);
|
||||
const packed_float3 *verts = mesh->get_position();
|
||||
float3 v1 = tfm ? transform_point(tfm, verts[t.v[2]]) : float3(verts[t.v[2]]);
|
||||
v1 = get_unaligned_point(v1);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
float3 v0 = v1;
|
||||
const int vindex = t.v[i];
|
||||
v1 = tfm ? transform_point(tfm, verts[vindex]) : float3(verts[vindex]);
|
||||
v1 = get_unaligned_point(v1);
|
||||
const float v0p = v0[dim];
|
||||
const float v1p = v1[dim];
|
||||
|
||||
/* insert vertex to the boxes it belongs to. */
|
||||
if (v0p <= pos) {
|
||||
left_bounds.grow(v0);
|
||||
}
|
||||
|
||||
if (v0p >= pos) {
|
||||
right_bounds.grow(v0);
|
||||
}
|
||||
|
||||
/* edge intersects the plane => insert intersection to both boxes. */
|
||||
if ((v0p < pos && v1p > pos) || (v0p > pos && v1p < pos)) {
|
||||
const float3 t = mix(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
|
||||
left_bounds.grow(t);
|
||||
right_bounds.grow(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_curve_primitive(const Hair *hair,
|
||||
const Transform *tfm,
|
||||
const int prim_index,
|
||||
const int segment_index,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
/* curve split: NOTE - Currently ignores curve width and needs to be fixed. */
|
||||
const Hair::Curve curve = hair->get_curve(prim_index);
|
||||
const int k0 = curve.first_key + segment_index;
|
||||
const int k1 = k0 + 1;
|
||||
const packed_float3 *curve_keys = hair->get_position();
|
||||
float3 v0 = curve_keys[k0];
|
||||
float3 v1 = curve_keys[k1];
|
||||
|
||||
if (tfm != nullptr) {
|
||||
v0 = transform_point(tfm, v0);
|
||||
v1 = transform_point(tfm, v1);
|
||||
}
|
||||
v0 = get_unaligned_point(v0);
|
||||
v1 = get_unaligned_point(v1);
|
||||
|
||||
const float v0p = v0[dim];
|
||||
const float v1p = v1[dim];
|
||||
|
||||
/* insert vertex to the boxes it belongs to. */
|
||||
if (v0p <= pos) {
|
||||
left_bounds.grow(v0);
|
||||
}
|
||||
|
||||
if (v0p >= pos) {
|
||||
right_bounds.grow(v0);
|
||||
}
|
||||
|
||||
if (v1p <= pos) {
|
||||
left_bounds.grow(v1);
|
||||
}
|
||||
|
||||
if (v1p >= pos) {
|
||||
right_bounds.grow(v1);
|
||||
}
|
||||
|
||||
/* edge intersects the plane => insert intersection to both boxes. */
|
||||
if ((v0p < pos && v1p > pos) || (v0p > pos && v1p < pos)) {
|
||||
const float3 t = mix(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
|
||||
left_bounds.grow(t);
|
||||
right_bounds.grow(t);
|
||||
}
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_point_primitive(const PointCloud *pointcloud,
|
||||
const Transform *tfm,
|
||||
const int prim_index,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
/* No real splitting support for points, assume they are small enough for it
|
||||
* not to matter. */
|
||||
float3 point = float3(pointcloud->get_position()[prim_index]);
|
||||
const float radius = pointcloud->get_radius()[prim_index];
|
||||
|
||||
if (tfm != nullptr) {
|
||||
point = transform_point(tfm, point);
|
||||
}
|
||||
point = get_unaligned_point(point);
|
||||
|
||||
if (point[dim] - radius <= pos) {
|
||||
left_bounds.grow(point, radius);
|
||||
}
|
||||
|
||||
if (point[dim] + radius >= pos) {
|
||||
right_bounds.grow(point, radius);
|
||||
}
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_triangle_reference(const BVHReference &ref,
|
||||
const Mesh *mesh,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
split_triangle_primitive(mesh, nullptr, ref.prim_index(), dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_curve_reference(const BVHReference &ref,
|
||||
const Hair *hair,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
split_curve_primitive(hair,
|
||||
nullptr,
|
||||
ref.prim_index(),
|
||||
PRIMITIVE_UNPACK_SEGMENT(ref.prim_type()),
|
||||
dim,
|
||||
pos,
|
||||
left_bounds,
|
||||
right_bounds);
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_point_reference(const BVHReference &ref,
|
||||
const PointCloud *pointcloud,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
split_point_primitive(
|
||||
pointcloud, nullptr, ref.prim_index(), dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_object_reference(const Object *object,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
Geometry *geom = object->get_geometry();
|
||||
|
||||
if (geom->is_mesh() || geom->is_volume()) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
for (int tri_idx = 0; tri_idx < mesh->num_triangles(); ++tri_idx) {
|
||||
split_triangle_primitive(
|
||||
mesh, &object->get_tfm(), tri_idx, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
}
|
||||
else if (geom->is_hair()) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
for (int curve_idx = 0; curve_idx < hair->num_curves(); ++curve_idx) {
|
||||
const Hair::Curve curve = hair->get_curve(curve_idx);
|
||||
for (int segment_idx = 0; segment_idx < curve.num_keys - 1; ++segment_idx) {
|
||||
split_curve_primitive(
|
||||
hair, &object->get_tfm(), curve_idx, segment_idx, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (geom->is_pointcloud()) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
|
||||
for (int point_idx = 0; point_idx < pointcloud->num_points(); ++point_idx) {
|
||||
split_point_primitive(
|
||||
pointcloud, &object->get_tfm(), point_idx, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_reference(const BVHBuild &builder,
|
||||
BVHReference &left,
|
||||
BVHReference &right,
|
||||
const BVHReference &ref,
|
||||
const int dim,
|
||||
const float pos)
|
||||
{
|
||||
/* Initialize bounding-boxes. */
|
||||
BoundBox left_bounds = BoundBox::empty;
|
||||
BoundBox right_bounds = BoundBox::empty;
|
||||
|
||||
/* loop over vertices/edges. */
|
||||
const Object *ob = builder.objects[ref.prim_object()];
|
||||
|
||||
if (ref.prim_type() & PRIMITIVE_TRIANGLE) {
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->get_geometry());
|
||||
split_triangle_reference(ref, mesh, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
else if (ref.prim_type() & PRIMITIVE_CURVE) {
|
||||
Hair *hair = static_cast<Hair *>(ob->get_geometry());
|
||||
split_curve_reference(ref, hair, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
else if (ref.prim_type() & PRIMITIVE_POINT) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(ob->get_geometry());
|
||||
split_point_reference(ref, pointcloud, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
else {
|
||||
split_object_reference(ob, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
|
||||
/* intersect with original bounds. */
|
||||
left_bounds.max[dim] = pos;
|
||||
right_bounds.min[dim] = pos;
|
||||
|
||||
left_bounds.intersect(ref.bounds());
|
||||
right_bounds.intersect(ref.bounds());
|
||||
|
||||
/* set references */
|
||||
left = BVHReference(left_bounds, ref.prim_index(), ref.prim_object(), ref.prim_type());
|
||||
right = BVHReference(right_bounds, ref.prim_index(), ref.prim_object(), ref.prim_type());
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
234
blender-5.2.0/intern/cycles/bvh/split.h
Normal file
234
blender-5.2.0/intern/cycles/bvh/split.h
Normal file
@@ -0,0 +1,234 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bvh/build.h"
|
||||
#include "bvh/params.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHBuild;
|
||||
class Hair;
|
||||
class Mesh;
|
||||
class PointCloud;
|
||||
struct Transform;
|
||||
|
||||
/* Object Split */
|
||||
|
||||
class BVHObjectSplit {
|
||||
public:
|
||||
float sah;
|
||||
int dim;
|
||||
int num_left;
|
||||
BoundBox left_bounds;
|
||||
BoundBox right_bounds;
|
||||
|
||||
BVHObjectSplit() = default;
|
||||
BVHObjectSplit(BVHBuild *builder,
|
||||
BVHSpatialStorage *storage,
|
||||
const BVHRange &range,
|
||||
vector<BVHReference> &references,
|
||||
const float nodeSAH,
|
||||
const BVHUnaligned *unaligned_heuristic = nullptr,
|
||||
const Transform *aligned_space = nullptr);
|
||||
|
||||
void split(BVHRange &left, BVHRange &right, const BVHRange &range);
|
||||
|
||||
protected:
|
||||
BVHSpatialStorage *storage_;
|
||||
vector<BVHReference> *references_;
|
||||
const BVHUnaligned *unaligned_heuristic_;
|
||||
const Transform *aligned_space_;
|
||||
|
||||
__forceinline BoundBox get_prim_bounds(const BVHReference &prim) const
|
||||
{
|
||||
if (aligned_space_ == nullptr) {
|
||||
return prim.bounds();
|
||||
}
|
||||
return unaligned_heuristic_->compute_aligned_prim_boundbox(prim, *aligned_space_);
|
||||
}
|
||||
};
|
||||
|
||||
/* Spatial Split */
|
||||
|
||||
class BVHSpatialSplit {
|
||||
public:
|
||||
float sah;
|
||||
int dim;
|
||||
float pos;
|
||||
|
||||
BVHSpatialSplit() : sah(FLT_MAX), dim(0), pos(0.0f), storage_(nullptr), references_(nullptr) {}
|
||||
BVHSpatialSplit(const BVHBuild &builder,
|
||||
BVHSpatialStorage *storage,
|
||||
const BVHRange &range,
|
||||
vector<BVHReference> &references,
|
||||
const float nodeSAH,
|
||||
const BVHUnaligned *unaligned_heuristic = nullptr,
|
||||
const Transform *aligned_space = nullptr);
|
||||
|
||||
void split(BVHBuild *builder, BVHRange &left, BVHRange &right, const BVHRange &range);
|
||||
|
||||
void split_reference(const BVHBuild &builder,
|
||||
BVHReference &left,
|
||||
BVHReference &right,
|
||||
const BVHReference &ref,
|
||||
const int dim,
|
||||
float pos);
|
||||
|
||||
protected:
|
||||
BVHSpatialStorage *storage_;
|
||||
vector<BVHReference> *references_;
|
||||
const BVHUnaligned *unaligned_heuristic_;
|
||||
const Transform *aligned_space_;
|
||||
|
||||
/* Lower-level functions which calculates boundaries of left and right nodes
|
||||
* needed for spatial split.
|
||||
*
|
||||
* Operates directly with primitive specified by its index, reused by higher
|
||||
* level splitting functions.
|
||||
*/
|
||||
void split_triangle_primitive(const Mesh *mesh,
|
||||
const Transform *tfm,
|
||||
const int prim_index,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds);
|
||||
void split_curve_primitive(const Hair *hair,
|
||||
const Transform *tfm,
|
||||
const int prim_index,
|
||||
const int segment_index,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds);
|
||||
void split_point_primitive(const PointCloud *pointcloud,
|
||||
const Transform *tfm,
|
||||
const int prim_index,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds);
|
||||
|
||||
/* Lower-level functions which calculates boundaries of left and right nodes
|
||||
* needed for spatial split.
|
||||
*
|
||||
* Operates with BVHReference, internally uses lower level API functions.
|
||||
*/
|
||||
void split_triangle_reference(const BVHReference &ref,
|
||||
const Mesh *mesh,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds);
|
||||
void split_curve_reference(const BVHReference &ref,
|
||||
const Hair *hair,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds);
|
||||
void split_point_reference(const BVHReference &ref,
|
||||
const PointCloud *pointcloud,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds);
|
||||
void split_object_reference(const Object *object,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds);
|
||||
|
||||
__forceinline BoundBox get_prim_bounds(const BVHReference &prim) const
|
||||
{
|
||||
if (aligned_space_ == nullptr) {
|
||||
return prim.bounds();
|
||||
}
|
||||
return unaligned_heuristic_->compute_aligned_prim_boundbox(prim, *aligned_space_);
|
||||
}
|
||||
|
||||
__forceinline float3 get_unaligned_point(const float3 &point) const
|
||||
{
|
||||
if (aligned_space_ == nullptr) {
|
||||
return point;
|
||||
}
|
||||
return transform_point(aligned_space_, point);
|
||||
}
|
||||
};
|
||||
|
||||
/* Mixed Object-Spatial Split */
|
||||
|
||||
class BVHMixedSplit {
|
||||
public:
|
||||
BVHObjectSplit object;
|
||||
BVHSpatialSplit spatial;
|
||||
|
||||
float leafSAH;
|
||||
float nodeSAH;
|
||||
float minSAH;
|
||||
|
||||
bool no_split;
|
||||
|
||||
BoundBox bounds;
|
||||
|
||||
BVHMixedSplit() = default;
|
||||
|
||||
__forceinline BVHMixedSplit(BVHBuild *builder,
|
||||
BVHSpatialStorage *storage,
|
||||
const BVHRange &range,
|
||||
vector<BVHReference> &references,
|
||||
const int level,
|
||||
const BVHUnaligned *unaligned_heuristic = nullptr,
|
||||
const Transform *aligned_space = nullptr)
|
||||
{
|
||||
if (aligned_space == nullptr) {
|
||||
bounds = range.bounds();
|
||||
}
|
||||
else {
|
||||
bounds = unaligned_heuristic->compute_aligned_boundbox(
|
||||
range, &references.at(0), *aligned_space);
|
||||
}
|
||||
/* find split candidates. */
|
||||
const float area = bounds.safe_area();
|
||||
|
||||
leafSAH = area * builder->params.primitive_cost(range.size());
|
||||
nodeSAH = area * builder->params.node_cost(2);
|
||||
|
||||
object = BVHObjectSplit(
|
||||
builder, storage, range, references, nodeSAH, unaligned_heuristic, aligned_space);
|
||||
|
||||
if (builder->params.use_spatial_split && level < BVHParams::MAX_SPATIAL_DEPTH) {
|
||||
BoundBox overlap = object.left_bounds;
|
||||
overlap.intersect(object.right_bounds);
|
||||
|
||||
if (overlap.safe_area() >= builder->spatial_min_overlap) {
|
||||
spatial = BVHSpatialSplit(
|
||||
*builder, storage, range, references, nodeSAH, unaligned_heuristic, aligned_space);
|
||||
}
|
||||
}
|
||||
|
||||
/* leaf SAH is the lowest => create leaf. */
|
||||
minSAH = min(min(leafSAH, object.sah), spatial.sah);
|
||||
no_split = (minSAH == leafSAH && builder->range_within_max_leaf_size(range, references));
|
||||
}
|
||||
|
||||
__forceinline void split(BVHBuild *builder,
|
||||
BVHRange &left,
|
||||
BVHRange &right,
|
||||
const BVHRange &range)
|
||||
{
|
||||
if (builder->params.use_spatial_split && minSAH == spatial.sah) {
|
||||
spatial.split(builder, left, right, range);
|
||||
}
|
||||
if (!left.size() || !right.size()) {
|
||||
object.split(left, right, range);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
152
blender-5.2.0/intern/cycles/bvh/unaligned.cpp
Normal file
152
blender-5.2.0/intern/cycles/bvh/unaligned.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "bvh/unaligned.h"
|
||||
|
||||
#include "scene/hair.h"
|
||||
#include "scene/object.h"
|
||||
|
||||
#include "bvh/binning.h"
|
||||
#include "bvh/params.h"
|
||||
|
||||
#include "util/boundbox.h"
|
||||
#include "util/transform.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHUnaligned::BVHUnaligned(const vector<Object *> &objects) : objects_(objects) {}
|
||||
|
||||
Transform BVHUnaligned::compute_aligned_space(const BVHObjectBinning &range,
|
||||
const BVHReference *references) const
|
||||
{
|
||||
for (int i = range.start(); i < range.end(); ++i) {
|
||||
const BVHReference &ref = references[i];
|
||||
Transform aligned_space;
|
||||
/* Use first primitive which defines correct direction to define
|
||||
* the orientation space.
|
||||
*/
|
||||
if (compute_aligned_space(ref, &aligned_space)) {
|
||||
return aligned_space;
|
||||
}
|
||||
}
|
||||
return transform_identity();
|
||||
}
|
||||
|
||||
Transform BVHUnaligned::compute_aligned_space(const BVHRange &range,
|
||||
const BVHReference *references) const
|
||||
{
|
||||
for (int i = range.start(); i < range.end(); ++i) {
|
||||
const BVHReference &ref = references[i];
|
||||
Transform aligned_space;
|
||||
/* Use first primitive which defines correct direction to define
|
||||
* the orientation space.
|
||||
*/
|
||||
if (compute_aligned_space(ref, &aligned_space)) {
|
||||
return aligned_space;
|
||||
}
|
||||
}
|
||||
return transform_identity();
|
||||
}
|
||||
|
||||
bool BVHUnaligned::compute_aligned_space(const BVHReference &ref, Transform *aligned_space) const
|
||||
{
|
||||
const Object *object = objects_[ref.prim_object()];
|
||||
const int packed_type = ref.prim_type();
|
||||
const int type = (packed_type & PRIMITIVE_ALL);
|
||||
/* No motion blur curves here, we can't fit them to aligned boxes well. */
|
||||
if ((type & PRIMITIVE_CURVE) && !(type & PRIMITIVE_MOTION)) {
|
||||
const int curve_index = ref.prim_index();
|
||||
const int segment = PRIMITIVE_UNPACK_SEGMENT(packed_type);
|
||||
const Hair *hair = static_cast<const Hair *>(object->get_geometry());
|
||||
const Hair::Curve &curve = hair->get_curve(curve_index);
|
||||
const int key = curve.first_key + segment;
|
||||
const packed_float3 *curve_keys = hair->get_position();
|
||||
const float3 v1 = curve_keys[key];
|
||||
const float3 v2 = curve_keys[key + 1];
|
||||
float length;
|
||||
const float3 axis = normalize_len(v2 - v1, &length);
|
||||
if (length > 1e-6f) {
|
||||
*aligned_space = make_transform_frame(axis);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*aligned_space = transform_identity();
|
||||
return false;
|
||||
}
|
||||
|
||||
BoundBox BVHUnaligned::compute_aligned_prim_boundbox(const BVHReference &prim,
|
||||
const Transform &aligned_space) const
|
||||
{
|
||||
BoundBox bounds = BoundBox::empty;
|
||||
const Object *object = objects_[prim.prim_object()];
|
||||
const int packed_type = prim.prim_type();
|
||||
const int type = (packed_type & PRIMITIVE_ALL);
|
||||
/* No motion blur curves here, we can't fit them to aligned boxes well. */
|
||||
if ((type & PRIMITIVE_CURVE) && !(type & PRIMITIVE_MOTION)) {
|
||||
const int curve_index = prim.prim_index();
|
||||
const int segment = PRIMITIVE_UNPACK_SEGMENT(packed_type);
|
||||
const Hair *hair = static_cast<const Hair *>(object->get_geometry());
|
||||
const Hair::Curve &curve = hair->get_curve(curve_index);
|
||||
curve.bounds_grow(segment, hair->get_position(), hair->get_radius(), aligned_space, bounds);
|
||||
}
|
||||
else {
|
||||
bounds = prim.bounds().transformed(&aligned_space);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
BoundBox BVHUnaligned::compute_aligned_boundbox(const BVHObjectBinning &range,
|
||||
const BVHReference *references,
|
||||
const Transform &aligned_space,
|
||||
BoundBox *cent_bounds) const
|
||||
{
|
||||
BoundBox bounds = BoundBox::empty;
|
||||
if (cent_bounds != nullptr) {
|
||||
*cent_bounds = BoundBox::empty;
|
||||
}
|
||||
for (int i = range.start(); i < range.end(); ++i) {
|
||||
const BVHReference &ref = references[i];
|
||||
const BoundBox ref_bounds = compute_aligned_prim_boundbox(ref, aligned_space);
|
||||
bounds.grow(ref_bounds);
|
||||
if (cent_bounds != nullptr) {
|
||||
cent_bounds->grow(ref_bounds.center2());
|
||||
}
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
BoundBox BVHUnaligned::compute_aligned_boundbox(const BVHRange &range,
|
||||
const BVHReference *references,
|
||||
const Transform &aligned_space,
|
||||
BoundBox *cent_bounds) const
|
||||
{
|
||||
BoundBox bounds = BoundBox::empty;
|
||||
if (cent_bounds != nullptr) {
|
||||
*cent_bounds = BoundBox::empty;
|
||||
}
|
||||
for (int i = range.start(); i < range.end(); ++i) {
|
||||
const BVHReference &ref = references[i];
|
||||
const BoundBox ref_bounds = compute_aligned_prim_boundbox(ref, aligned_space);
|
||||
bounds.grow(ref_bounds);
|
||||
if (cent_bounds != nullptr) {
|
||||
cent_bounds->grow(ref_bounds.center2());
|
||||
}
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
Transform BVHUnaligned::compute_node_transform(const BoundBox &bounds,
|
||||
const Transform &aligned_space)
|
||||
{
|
||||
Transform space = aligned_space;
|
||||
space.x.w -= bounds.min.x;
|
||||
space.y.w -= bounds.min.y;
|
||||
space.z.w -= bounds.min.z;
|
||||
const float3 dim = bounds.max - bounds.min;
|
||||
return transform_scale(
|
||||
1.0f / max(1e-18f, dim.x), 1.0f / max(1e-18f, dim.y), 1.0f / max(1e-18f, dim.z)) *
|
||||
space;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
58
blender-5.2.0/intern/cycles/bvh/unaligned.h
Normal file
58
blender-5.2.0/intern/cycles/bvh/unaligned.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BoundBox;
|
||||
class BVHObjectBinning;
|
||||
class BVHRange;
|
||||
class BVHReference;
|
||||
struct Transform;
|
||||
class Object;
|
||||
|
||||
/* Helper class to perform calculations needed for unaligned nodes. */
|
||||
class BVHUnaligned {
|
||||
public:
|
||||
BVHUnaligned(const vector<Object *> &objects);
|
||||
|
||||
/* Calculate alignment for the oriented node for a given range. */
|
||||
Transform compute_aligned_space(const BVHObjectBinning &range,
|
||||
const BVHReference *references) const;
|
||||
Transform compute_aligned_space(const BVHRange &range, const BVHReference *references) const;
|
||||
|
||||
/* Calculate alignment for the oriented node for a given reference.
|
||||
*
|
||||
* Return true when space was calculated successfully.
|
||||
*/
|
||||
bool compute_aligned_space(const BVHReference &ref, Transform *aligned_space) const;
|
||||
|
||||
/* Calculate primitive's bounding box in given space. */
|
||||
BoundBox compute_aligned_prim_boundbox(const BVHReference &prim,
|
||||
const Transform &aligned_space) const;
|
||||
|
||||
/* Calculate bounding box in given space. */
|
||||
BoundBox compute_aligned_boundbox(const BVHObjectBinning &range,
|
||||
const BVHReference *references,
|
||||
const Transform &aligned_space,
|
||||
BoundBox *cent_bounds = nullptr) const;
|
||||
BoundBox compute_aligned_boundbox(const BVHRange &range,
|
||||
const BVHReference *references,
|
||||
const Transform &aligned_space,
|
||||
BoundBox *cent_bounds = nullptr) const;
|
||||
|
||||
/* Calculate affine transform for node packing.
|
||||
* Bounds will be in the range of 0..1.
|
||||
*/
|
||||
static Transform compute_node_transform(const BoundBox &bounds, const Transform &aligned_space);
|
||||
|
||||
protected:
|
||||
/* List of objects BVH is being created for. */
|
||||
const vector<Object *> &objects_;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
Reference in New Issue
Block a user