BVHNode* BVHBuild::run()
{
	BVHRange root;

	/* add references */
	add_references(root);

	if(progress.get_cancel())
		return NULL;

	/* init spatial splits */
	if(params.top_level) /* todo: get rid of this */
		params.use_spatial_split = false;

	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);

	/* init progress updates */
	progress_start_time = time_dt();
	progress_count = 0;
	progress_total = references.size();
	progress_original_total = progress_total;

	prim_segment.resize(references.size());
	prim_index.resize(references.size());
	prim_object.resize(references.size());

	/* build recursively */
	BVHNode *rootnode;

	if(params.use_spatial_split) {
		/* singlethreaded spatial split build */
		rootnode = build_node(root, 0);
	}
	else {
		/* multithreaded binning build */
		BVHObjectBinning rootbin(root, (references.size())? &references[0]: NULL);
		rootnode = build_node(rootbin, 0);
		task_pool.wait_work();
	}

	/* delete if we canceled */
	if(rootnode) {
		if(progress.get_cancel()) {
			rootnode->deleteSubtree();
			rootnode = NULL;
		}
		else if(!params.use_spatial_split) {
			/*rotate(rootnode, 4, 5);*/
			rootnode->update_visibility();
		}
	}

	return rootnode;
}
Exemple #2
0
void BVHObjectSplit::split(BVHBuild *builder, BVHRange& left, BVHRange& right, const BVHRange& range)
{
	/* sort references according to split */
	bvh_reference_sort(range.start(), range.end(), &builder->references[0], this->dim);

	/* split node ranges */
	left = BVHRange(this->left_bounds, range.start(), this->num_left);
	right = BVHRange(this->right_bounds, left.end(), range.size() - this->num_left);

}
Exemple #3
0
CCL_NAMESPACE_BEGIN

/* Object Split */

BVHObjectSplit::BVHObjectSplit(BVHBuild *builder,
                               BVHSpatialStorage *storage,
                               const BVHRange& range,
                               vector<BVHReference> *references,
                               float nodeSAH)
: sah(FLT_MAX),
  dim(0),
  num_left(0),
  left_bounds(BoundBox::empty),
  right_bounds(BoundBox::empty),
  storage_(storage),
  references_(references)
{
	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);

		/* sweep right to left and determine bounds. */
		BoundBox right_bounds = BoundBox::empty;

		storage_->right_bounds.resize(range.size());
		for(int i = range.size() - 1; i > 0; i--) {
			right_bounds.grow(ref_ptr[i].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++) {
			left_bounds.grow(ref_ptr[i - 1].bounds());
			right_bounds = storage_->right_bounds[i - 1];

			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;
			}
		}
	}
}
Exemple #4
0
	BVHSpatialSplitBuildTask(BVHBuild *build,
	                         InnerNode *node,
	                         int child,
	                         const BVHRange& range,
	                         const vector<BVHReference>& references,
	                         int level)
	: range_(range),
	  references_(references.begin() + range.start(),
	              references.begin() + range.end())
	{
		range_.set_start(0);
		run = function_bind(&BVHBuild::thread_build_spatial_split_node,
		                    build,
		                    node,
		                    child,
		                    &range_,
		                    &references_,
		                    level,
		                    _1);
	}
Exemple #5
0
bool BVHBuild::range_within_max_leaf_size(const BVHRange& range)
{
    size_t size = range.size();
    size_t max_leaf_size = max(params.max_triangle_leaf_size, params.max_curve_leaf_size);

    if(size > max_leaf_size)
        return false;

    size_t num_triangles = 0;
    size_t num_curves = 0;

    for(int i = 0; i < size; i++) {
        BVHReference& ref = references[range.start() + i];

        if(ref.prim_type() & PRIMITIVE_ALL_CURVE)
            num_curves++;
        else if(ref.prim_type() & PRIMITIVE_ALL_TRIANGLE)
            num_triangles++;
    }

    return (num_triangles < params.max_triangle_leaf_size) && (num_curves < params.max_curve_leaf_size);
}
Exemple #6
0
BVHNode* BVHBuild::run()
{
	BVHRange root;

	/* add references */
	add_references(root);

	if(progress.get_cancel())
		return NULL;

	/* init spatial splits */
	if(params.top_level) {
		/* NOTE: Technically it is supported by the builder but it's not really
		 * optimized for speed yet and not really clear yet if it has measurable
		 * improvement on render time. Needs some extra investigation before
		 * enabling spatial split for top level BVH.
		 */
		params.use_spatial_split = false;
	}

	spatial_min_overlap = root.bounds().safe_area() * params.spatial_split_alpha;
	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(TaskScheduler::num_threads() + 1);
		size_t num_bins = max(root.size(), (int)BVHParams::NUM_SPATIAL_BINS) - 1;
		foreach(BVHSpatialStorage &storage, spatial_storage) {
			storage.right_bounds.clear();
		}
		spatial_storage[0].right_bounds.resize(num_bins);
	}
Exemple #7
0
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_;
	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++) {
		if(refs[i].bounds().max[this->dim] <= this->pos) {
			/* entirely on the left-hand side */
			left_bounds.grow(refs[i].bounds());
			swap(refs[i], refs[left_end++]);
		}
		else if(refs[i].bounds().min[this->dim] >= this->pos) {
			/* entirely on the right-hand side */
			right_bounds.grow(refs[i].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. */
		BVHReference lref, rref;
		split_reference(*builder, lref, rref, refs[left_end], 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(refs[left_end].bounds());
		rub.grow(refs[left_end].bounds());
		ldb.grow(lref.bounds());
		rdb.grow(rref.bounds());

		float lac = builder->params.primitive_cost(left_end - left_start);
		float rac = builder->params.primitive_cost(right_end - right_start);
		float lbc = builder->params.primitive_cost(left_end - left_start + 1);
		float rbc = builder->params.primitive_cost(right_end - right_start + 1);

		float unsplitLeftSAH = lub.safe_area() * lbc + right_bounds.safe_area() * rac;
		float unsplitRightSAH = left_bounds.safe_area() * lac + rub.safe_area() * rbc;
		float duplicateSAH = ldb.safe_area() * lbc + rdb.safe_area() * rbc;
		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.size() != 0) {
		refs.insert(refs.begin() + (right_end - new_refs.size()),
		            new_refs.begin(),
		            new_refs.end());
	}
	left = BVHRange(left_bounds, left_start, left_end - left_start);
	right = BVHRange(right_bounds, right_start, right_end - right_start);
}
Exemple #8
0
BVHSpatialSplit::BVHSpatialSplit(const BVHBuild& builder,
                                 BVHSpatialStorage *storage,
                                 const BVHRange& range,
                                 vector<BVHReference> *references,
                                 float nodeSAH)
: sah(FLT_MAX),
  dim(0),
  pos(0.0f),
  storage_(storage),
  references_(references)
{
	/* initialize bins. */
	float3 origin = range.bounds().min;
	float3 binSize = (range.bounds().max - origin) * (1.0f / (float)BVHParams::NUM_SPATIAL_BINS);
	float3 invBinSize = 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);
		float3 firstBinf = (ref.bounds().min - origin) * invBinSize;
		float3 lastBinf = (ref.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 = ref;

			for(int i = firstBin[dim]; i < lastBin[dim]; i++) {
				BVHReference leftRef, 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. */
	for(int dim = 0; dim < 3; dim++) {
		/* sweep right to left and determine bounds. */
		BoundBox right_bounds = BoundBox::empty;

		storage_->right_bounds.resize(BVHParams::NUM_SPATIAL_BINS);
		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;

			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;
			}
		}
	}
}