Cycles: Wrap spatial split storage into own structure

This has following advantages:

- Localizes all the run-time storage into a single structure,
  which could easily be extended further.

- Storage could be created per-thread, so once builder is
  threaded we wouldn't have any conflicts between threads.

- Global nature of the storage avoids memory re-allocation
  on the runtime, keeping builder as fast as possible.

Currently it's just API changes, which don't affect user at all.
This commit is contained in:
Sergey Sharybin 2016-02-21 15:39:02 +01:00
parent 9c420e5e48
commit bbbbe68473
5 changed files with 85 additions and 28 deletions

@ -230,8 +230,23 @@ BVHNode* BVHBuild::run()
} }
spatial_min_overlap = root.bounds().safe_area() * params.spatial_split_alpha; spatial_min_overlap = root.bounds().safe_area() * params.spatial_split_alpha;
spatial_right_bounds.clear();
spatial_right_bounds.resize(max(root.size(), (int)BVHParams::NUM_SPATIAL_BINS) - 1); if(params.use_spatial_split) {
/* NOTE: The API here tries to be as much ready for multi-threaded build
* as possible, but at the same time it tries not to introduce any
* changes in behavior for until all refactoring needed for threading is
* finished.
*
* So we currently allocate single storage for now, which is only used by
* the only thread working on the spatial BVH build.
*/
spatial_storage.resize(1);
size_t num_bins = max(root.size(), (int)BVHParams::NUM_SPATIAL_BINS) - 1;
foreach(BVHSpatialStorage &storage, spatial_storage) {
storage.spatial_right_bounds.clear();
storage.spatial_right_bounds.resize(num_bins);
}
}
/* init progress updates */ /* init progress updates */
double build_start_time; double build_start_time;
@ -407,7 +422,7 @@ BVHNode* BVHBuild::build_node(const BVHRange& range, int level)
} }
/* splitting test */ /* splitting test */
BVHMixedSplit split(this, range, level); BVHMixedSplit split(this, &spatial_storage[0], range, level);
if(!(range.size() > 0 && params.top_level && level == 0)) { if(!(range.size() > 0 && params.top_level && level == 0)) {
if(split.no_split) { if(split.no_split) {

@ -114,8 +114,7 @@ protected:
/* spatial splitting */ /* spatial splitting */
float spatial_min_overlap; float spatial_min_overlap;
vector<BoundBox> spatial_right_bounds; vector<BVHSpatialStorage> spatial_storage;
BVHSpatialBin spatial_bins[3][BVHParams::NUM_SPATIAL_BINS];
/* threads */ /* threads */
TaskPool task_pool; TaskPool task_pool;

@ -175,6 +175,18 @@ struct BVHSpatialBin
} }
}; };
/* 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 {
vector<BoundBox> spatial_right_bounds;
BVHSpatialBin spatial_bins[3][BVHParams::NUM_SPATIAL_BINS];
};
CCL_NAMESPACE_END CCL_NAMESPACE_END
#endif /* __BVH_PARAMS_H__ */ #endif /* __BVH_PARAMS_H__ */

@ -28,8 +28,16 @@ CCL_NAMESPACE_BEGIN
/* Object Split */ /* Object Split */
BVHObjectSplit::BVHObjectSplit(BVHBuild *builder, const BVHRange& range, float nodeSAH) BVHObjectSplit::BVHObjectSplit(BVHBuild *builder,
: sah(FLT_MAX), dim(0), num_left(0), left_bounds(BoundBox::empty), right_bounds(BoundBox::empty) BVHSpatialStorage *storage,
const BVHRange& range,
float nodeSAH)
: sah(FLT_MAX),
dim(0),
num_left(0),
left_bounds(BoundBox::empty),
right_bounds(BoundBox::empty),
storage_(storage)
{ {
const BVHReference *ref_ptr = &builder->references[range.start()]; const BVHReference *ref_ptr = &builder->references[range.start()];
float min_sah = FLT_MAX; float min_sah = FLT_MAX;
@ -43,7 +51,7 @@ BVHObjectSplit::BVHObjectSplit(BVHBuild *builder, const BVHRange& range, float n
for(int i = range.size() - 1; i > 0; i--) { for(int i = range.size() - 1; i > 0; i--) {
right_bounds.grow(ref_ptr[i].bounds()); right_bounds.grow(ref_ptr[i].bounds());
builder->spatial_right_bounds[i - 1] = right_bounds; storage_->spatial_right_bounds[i - 1] = right_bounds;
} }
/* sweep left to right and select lowest SAH. */ /* sweep left to right and select lowest SAH. */
@ -51,7 +59,7 @@ BVHObjectSplit::BVHObjectSplit(BVHBuild *builder, const BVHRange& range, float n
for(int i = 1; i < range.size(); i++) { for(int i = 1; i < range.size(); i++) {
left_bounds.grow(ref_ptr[i - 1].bounds()); left_bounds.grow(ref_ptr[i - 1].bounds());
right_bounds = builder->spatial_right_bounds[i - 1]; right_bounds = storage_->spatial_right_bounds[i - 1];
float sah = nodeSAH + float sah = nodeSAH +
left_bounds.safe_area() * builder->params.primitive_cost(i) + left_bounds.safe_area() * builder->params.primitive_cost(i) +
@ -83,8 +91,14 @@ void BVHObjectSplit::split(BVHBuild *builder, BVHRange& left, BVHRange& right, c
/* Spatial Split */ /* Spatial Split */
BVHSpatialSplit::BVHSpatialSplit(BVHBuild *builder, const BVHRange& range, float nodeSAH) BVHSpatialSplit::BVHSpatialSplit(BVHBuild *builder,
: sah(FLT_MAX), dim(0), pos(0.0f) BVHSpatialStorage *storage,
const BVHRange& range,
float nodeSAH)
: sah(FLT_MAX),
dim(0),
pos(0.0f),
storage_(storage)
{ {
/* initialize bins. */ /* initialize bins. */
float3 origin = range.bounds().min; float3 origin = range.bounds().min;
@ -93,7 +107,7 @@ BVHSpatialSplit::BVHSpatialSplit(BVHBuild *builder, const BVHRange& range, float
for(int dim = 0; dim < 3; dim++) { for(int dim = 0; dim < 3; dim++) {
for(int i = 0; i < BVHParams::NUM_SPATIAL_BINS; i++) { for(int i = 0; i < BVHParams::NUM_SPATIAL_BINS; i++) {
BVHSpatialBin& bin = builder->spatial_bins[dim][i]; BVHSpatialBin& bin = storage_->spatial_bins[dim][i];
bin.bounds = BoundBox::empty; bin.bounds = BoundBox::empty;
bin.enter = 0; bin.enter = 0;
@ -119,13 +133,13 @@ BVHSpatialSplit::BVHSpatialSplit(BVHBuild *builder, const BVHRange& range, float
BVHReference leftRef, rightRef; BVHReference leftRef, rightRef;
split_reference(builder, leftRef, rightRef, currRef, dim, origin[dim] + binSize[dim] * (float)(i + 1)); split_reference(builder, leftRef, rightRef, currRef, dim, origin[dim] + binSize[dim] * (float)(i + 1));
builder->spatial_bins[dim][i].bounds.grow(leftRef.bounds()); storage_->spatial_bins[dim][i].bounds.grow(leftRef.bounds());
currRef = rightRef; currRef = rightRef;
} }
builder->spatial_bins[dim][lastBin[dim]].bounds.grow(currRef.bounds()); storage_->spatial_bins[dim][lastBin[dim]].bounds.grow(currRef.bounds());
builder->spatial_bins[dim][firstBin[dim]].enter++; storage_->spatial_bins[dim][firstBin[dim]].enter++;
builder->spatial_bins[dim][lastBin[dim]].exit++; storage_->spatial_bins[dim][lastBin[dim]].exit++;
} }
} }
@ -135,8 +149,8 @@ BVHSpatialSplit::BVHSpatialSplit(BVHBuild *builder, const BVHRange& range, float
BoundBox right_bounds = BoundBox::empty; BoundBox right_bounds = BoundBox::empty;
for(int i = BVHParams::NUM_SPATIAL_BINS - 1; i > 0; i--) { for(int i = BVHParams::NUM_SPATIAL_BINS - 1; i > 0; i--) {
right_bounds.grow(builder->spatial_bins[dim][i].bounds); right_bounds.grow(storage_->spatial_bins[dim][i].bounds);
builder->spatial_right_bounds[i - 1] = right_bounds; storage_->spatial_right_bounds[i - 1] = right_bounds;
} }
/* sweep left to right and select lowest SAH. */ /* sweep left to right and select lowest SAH. */
@ -145,13 +159,13 @@ BVHSpatialSplit::BVHSpatialSplit(BVHBuild *builder, const BVHRange& range, float
int rightNum = range.size(); int rightNum = range.size();
for(int i = 1; i < BVHParams::NUM_SPATIAL_BINS; i++) { for(int i = 1; i < BVHParams::NUM_SPATIAL_BINS; i++) {
left_bounds.grow(builder->spatial_bins[dim][i - 1].bounds); left_bounds.grow(storage_->spatial_bins[dim][i - 1].bounds);
leftNum += builder->spatial_bins[dim][i - 1].enter; leftNum += storage_->spatial_bins[dim][i - 1].enter;
rightNum -= builder->spatial_bins[dim][i - 1].exit; rightNum -= storage_->spatial_bins[dim][i - 1].exit;
float sah = nodeSAH + float sah = nodeSAH +
left_bounds.safe_area() * builder->params.primitive_cost(leftNum) + left_bounds.safe_area() * builder->params.primitive_cost(leftNum) +
builder->spatial_right_bounds[i - 1].safe_area() * builder->params.primitive_cost(rightNum); storage_->spatial_right_bounds[i - 1].safe_area() * builder->params.primitive_cost(rightNum);
if(sah < this->sah) { if(sah < this->sah) {
this->sah = sah; this->sah = sah;

@ -37,9 +37,18 @@ public:
BoundBox right_bounds; BoundBox right_bounds;
BVHObjectSplit() {} BVHObjectSplit() {}
BVHObjectSplit(BVHBuild *builder, const BVHRange& range, float nodeSAH); BVHObjectSplit(BVHBuild *builder,
BVHSpatialStorage *storage,
const BVHRange& range,
float nodeSAH);
void split(BVHBuild *builder, BVHRange& left, BVHRange& right, const BVHRange& range); void split(BVHBuild *builder,
BVHRange& left,
BVHRange& right,
const BVHRange& range);
protected:
BVHSpatialStorage *storage_;
}; };
/* Spatial Split */ /* Spatial Split */
@ -52,7 +61,10 @@ public:
float pos; float pos;
BVHSpatialSplit() : sah(FLT_MAX), dim(0), pos(0.0f) {} BVHSpatialSplit() : sah(FLT_MAX), dim(0), pos(0.0f) {}
BVHSpatialSplit(BVHBuild *builder, const BVHRange& range, float nodeSAH); BVHSpatialSplit(BVHBuild *builder,
BVHSpatialStorage *storage,
const BVHRange& range,
float nodeSAH);
void split(BVHBuild *builder, BVHRange& left, BVHRange& right, const BVHRange& range); void split(BVHBuild *builder, BVHRange& left, BVHRange& right, const BVHRange& range);
void split_reference(BVHBuild *builder, void split_reference(BVHBuild *builder,
@ -63,6 +75,8 @@ public:
float pos); float pos);
protected: protected:
BVHSpatialStorage *storage_;
/* Lower-level functions which calculates boundaries of left and right nodes /* Lower-level functions which calculates boundaries of left and right nodes
* needed for spatial split. * needed for spatial split.
* *
@ -123,7 +137,10 @@ public:
bool no_split; bool no_split;
__forceinline BVHMixedSplit(BVHBuild *builder, const BVHRange& range, int level) __forceinline BVHMixedSplit(BVHBuild *builder,
BVHSpatialStorage *storage,
const BVHRange& range,
int level)
{ {
/* find split candidates. */ /* find split candidates. */
float area = range.bounds().safe_area(); float area = range.bounds().safe_area();
@ -131,14 +148,14 @@ public:
leafSAH = area * builder->params.primitive_cost(range.size()); leafSAH = area * builder->params.primitive_cost(range.size());
nodeSAH = area * builder->params.node_cost(2); nodeSAH = area * builder->params.node_cost(2);
object = BVHObjectSplit(builder, range, nodeSAH); object = BVHObjectSplit(builder, storage, range, nodeSAH);
if(builder->params.use_spatial_split && level < BVHParams::MAX_SPATIAL_DEPTH) { if(builder->params.use_spatial_split && level < BVHParams::MAX_SPATIAL_DEPTH) {
BoundBox overlap = object.left_bounds; BoundBox overlap = object.left_bounds;
overlap.intersect(object.right_bounds); overlap.intersect(object.right_bounds);
if(overlap.safe_area() >= builder->spatial_min_overlap) if(overlap.safe_area() >= builder->spatial_min_overlap)
spatial = BVHSpatialSplit(builder, range, nodeSAH); spatial = BVHSpatialSplit(builder, storage, range, nodeSAH);
} }
/* leaf SAH is the lowest => create leaf. */ /* leaf SAH is the lowest => create leaf. */