diff options
author | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:17:24 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:21:24 +0300 |
commit | e12c08e8d170b7ca40f204a5b0423c23a9fbc2c1 (patch) | |
tree | 8cf3453d12edb177a218ef8009357518ec6cab6a /intern/cycles/bvh | |
parent | b3dabc200a4b0399ec6b81f2ff2730d07b44fcaa (diff) |
ClangFormat: apply to source, most of intern
Apply clang format as proposed in T53211.
For details on usage and instructions for migrating branches
without conflicts, see:
https://wiki.blender.org/wiki/Tools/ClangFormat
Diffstat (limited to 'intern/cycles/bvh')
24 files changed, 5431 insertions, 5590 deletions
diff --git a/intern/cycles/bvh/CMakeLists.txt b/intern/cycles/bvh/CMakeLists.txt index ea31838d816..36bbd937e1a 100644 --- a/intern/cycles/bvh/CMakeLists.txt +++ b/intern/cycles/bvh/CMakeLists.txt @@ -1,42 +1,42 @@ set(INC - .. + .. ) set(INC_SYS ) set(SRC - bvh.cpp - bvh2.cpp - bvh4.cpp - bvh8.cpp - bvh_binning.cpp - bvh_build.cpp - bvh_embree.cpp - bvh_node.cpp - bvh_sort.cpp - bvh_split.cpp - bvh_unaligned.cpp + bvh.cpp + bvh2.cpp + bvh4.cpp + bvh8.cpp + bvh_binning.cpp + bvh_build.cpp + bvh_embree.cpp + bvh_node.cpp + bvh_sort.cpp + bvh_split.cpp + bvh_unaligned.cpp ) set(SRC_HEADERS - bvh.h - bvh2.h - bvh4.h - bvh8.h - bvh_binning.h - bvh_build.h - bvh_embree.h - bvh_node.h - bvh_params.h - bvh_sort.h - bvh_split.h - bvh_unaligned.h + bvh.h + bvh2.h + bvh4.h + bvh8.h + bvh_binning.h + bvh_build.h + bvh_embree.h + bvh_node.h + bvh_params.h + bvh_sort.h + bvh_split.h + bvh_unaligned.h ) set(LIB - cycles_render + cycles_render ) include_directories(${INC}) diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp index af012bbf3ac..53c66777928 100644 --- a/intern/cycles/bvh/bvh.cpp +++ b/intern/cycles/bvh/bvh.cpp @@ -27,7 +27,7 @@ #include "bvh/bvh_node.h" #ifdef WITH_EMBREE -#include "bvh/bvh_embree.h" +# include "bvh/bvh_embree.h" #endif #include "util/util_foreach.h" @@ -40,533 +40,529 @@ CCL_NAMESPACE_BEGIN const char *bvh_layout_name(BVHLayout layout) { - switch(layout) { - case BVH_LAYOUT_BVH2: return "BVH2"; - case BVH_LAYOUT_BVH4: return "BVH4"; - case BVH_LAYOUT_BVH8: return "BVH8"; - case BVH_LAYOUT_NONE: return "NONE"; - case BVH_LAYOUT_EMBREE: return "EMBREE"; - case BVH_LAYOUT_ALL: return "ALL"; - } - LOG(DFATAL) << "Unsupported BVH layout was passed."; - return ""; + switch (layout) { + case BVH_LAYOUT_BVH2: + return "BVH2"; + case BVH_LAYOUT_BVH4: + return "BVH4"; + case BVH_LAYOUT_BVH8: + return "BVH8"; + case BVH_LAYOUT_NONE: + return "NONE"; + case BVH_LAYOUT_EMBREE: + return "EMBREE"; + 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) +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. - */ - const BVHLayoutMask allowed_layouts_mask = - (supported_layouts & (requested_layout_mask - 1)); - /* We get widest from allowed ones and convert mask to actual layout. */ - const BVHLayoutMask widest_allowed_layout_mask = __bsr(allowed_layouts_mask); - return (BVHLayout)(1 << widest_allowed_layout_mask); + 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. + */ + const BVHLayoutMask allowed_layouts_mask = (supported_layouts & (requested_layout_mask - 1)); + /* We get widest from allowed ones and convert mask to actual layout. */ + const BVHLayoutMask widest_allowed_layout_mask = __bsr(allowed_layouts_mask); + return (BVHLayout)(1 << widest_allowed_layout_mask); } /* Pack Utility */ -BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) - : node(n), idx(i) +BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i) { } int BVHStackEntry::encodeIdx() const { - return (node->is_leaf())? ~idx: idx; + return (node->is_leaf()) ? ~idx : idx; } /* BVH */ -BVH::BVH(const BVHParams& params_, const vector<Object*>& objects_) -: params(params_), objects(objects_) +BVH::BVH(const BVHParams ¶ms_, const vector<Object *> &objects_) + : params(params_), objects(objects_) { } -BVH *BVH::create(const BVHParams& params, const vector<Object*>& objects) +BVH *BVH::create(const BVHParams ¶ms, const vector<Object *> &objects) { - switch(params.bvh_layout) { - case BVH_LAYOUT_BVH2: - return new BVH2(params, objects); - case BVH_LAYOUT_BVH4: - return new BVH4(params, objects); - case BVH_LAYOUT_BVH8: - return new BVH8(params, objects); - case BVH_LAYOUT_EMBREE: + switch (params.bvh_layout) { + case BVH_LAYOUT_BVH2: + return new BVH2(params, objects); + case BVH_LAYOUT_BVH4: + return new BVH4(params, objects); + case BVH_LAYOUT_BVH8: + return new BVH8(params, objects); + case BVH_LAYOUT_EMBREE: #ifdef WITH_EMBREE - return new BVHEmbree(params, objects); + return new BVHEmbree(params, objects); #endif - case BVH_LAYOUT_NONE: - case BVH_LAYOUT_ALL: - break; - } - LOG(DFATAL) << "Requested unsupported BVH layout."; - return NULL; + case BVH_LAYOUT_NONE: + case BVH_LAYOUT_ALL: + break; + } + LOG(DFATAL) << "Requested unsupported BVH layout."; + return NULL; } /* Building */ -void BVH::build(Progress& progress, Stats*) +void BVH::build(Progress &progress, Stats *) { - 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); - BVHNode *bvh2_root = bvh_build.run(); - - if(progress.get_cancel()) { - if(bvh2_root != NULL) { - bvh2_root->deleteSubtree(); - } - return; - } - - /* BVH builder returns tree in a binary mode (with two children per inner - * node. Need to adopt that for a wider BVH implementations. */ - BVHNode *root = widen_children_nodes(bvh2_root); - if(root != bvh2_root) { - bvh2_root->deleteSubtree(); - } - - if(progress.get_cancel()) { - if(root != NULL) { - root->deleteSubtree(); - } - return; - } - - /* pack triangles */ - progress.set_substatus("Packing BVH triangles and strands"); - pack_primitives(); - - if(progress.get_cancel()) { - root->deleteSubtree(); - return; - } - - /* pack nodes */ - progress.set_substatus("Packing BVH nodes"); - pack_nodes(root); - - /* free build nodes */ - root->deleteSubtree(); + 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); + BVHNode *bvh2_root = bvh_build.run(); + + if (progress.get_cancel()) { + if (bvh2_root != NULL) { + bvh2_root->deleteSubtree(); + } + return; + } + + /* BVH builder returns tree in a binary mode (with two children per inner + * node. Need to adopt that for a wider BVH implementations. */ + BVHNode *root = widen_children_nodes(bvh2_root); + if (root != bvh2_root) { + bvh2_root->deleteSubtree(); + } + + if (progress.get_cancel()) { + if (root != NULL) { + root->deleteSubtree(); + } + return; + } + + /* pack triangles */ + progress.set_substatus("Packing BVH triangles and strands"); + pack_primitives(); + + if (progress.get_cancel()) { + root->deleteSubtree(); + return; + } + + /* pack nodes */ + progress.set_substatus("Packing BVH nodes"); + pack_nodes(root); + + /* free build nodes */ + root->deleteSubtree(); } /* Refitting */ -void BVH::refit(Progress& progress) +void BVH::refit(Progress &progress) { - progress.set_substatus("Packing BVH primitives"); - pack_primitives(); + progress.set_substatus("Packing BVH primitives"); + pack_primitives(); - if(progress.get_cancel()) return; + if (progress.get_cancel()) + return; - progress.set_substatus("Refitting BVH nodes"); - refit_nodes(); + progress.set_substatus("Refitting BVH nodes"); + refit_nodes(); } -void BVH::refit_primitives(int start, int end, BoundBox& bbox, uint& visibility) +void BVH::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility) { - /* Refit range of primitives. */ - for(int prim = start; prim < end; prim++) { - int pidx = pack.prim_index[prim]; - int tob = pack.prim_object[prim]; - Object *ob = objects[tob]; - - if(pidx == -1) { - /* Object instance. */ - bbox.grow(ob->bounds); - } - else { - /* Primitives. */ - const Mesh *mesh = ob->mesh; - - if(pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) { - /* Curves. */ - int str_offset = (params.top_level)? mesh->curve_offset: 0; - Mesh::Curve curve = mesh->get_curve(pidx - str_offset); - int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]); - - curve.bounds_grow(k, &mesh->curve_keys[0], &mesh->curve_radius[0], bbox); - - visibility |= PATH_RAY_CURVE; - - /* Motion curves. */ - if(mesh->use_motion_blur) { - Attribute *attr = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - if(attr) { - size_t mesh_size = mesh->curve_keys.size(); - size_t steps = mesh->motion_steps - 1; - float3 *key_steps = attr->data_float3(); - - for(size_t i = 0; i < steps; i++) - curve.bounds_grow(k, key_steps + i*mesh_size, &mesh->curve_radius[0], bbox); - } - } - } - else { - /* Triangles. */ - int tri_offset = (params.top_level)? mesh->tri_offset: 0; - Mesh::Triangle triangle = mesh->get_triangle(pidx - tri_offset); - const float3 *vpos = &mesh->verts[0]; - - triangle.bounds_grow(vpos, bbox); - - /* Motion triangles. */ - if(mesh->use_motion_blur) { - Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - if(attr) { - size_t mesh_size = mesh->verts.size(); - size_t steps = mesh->motion_steps - 1; - float3 *vert_steps = attr->data_float3(); - - for(size_t i = 0; i < steps; i++) - triangle.bounds_grow(vert_steps + i*mesh_size, bbox); - } - } - } - } - visibility |= ob->visibility_for_tracing(); - - } + /* Refit range of primitives. */ + for (int prim = start; prim < end; prim++) { + int pidx = pack.prim_index[prim]; + int tob = pack.prim_object[prim]; + Object *ob = objects[tob]; + + if (pidx == -1) { + /* Object instance. */ + bbox.grow(ob->bounds); + } + else { + /* Primitives. */ + const Mesh *mesh = ob->mesh; + + if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) { + /* Curves. */ + int str_offset = (params.top_level) ? mesh->curve_offset : 0; + Mesh::Curve curve = mesh->get_curve(pidx - str_offset); + int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]); + + curve.bounds_grow(k, &mesh->curve_keys[0], &mesh->curve_radius[0], bbox); + + visibility |= PATH_RAY_CURVE; + + /* Motion curves. */ + if (mesh->use_motion_blur) { + Attribute *attr = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + + if (attr) { + size_t mesh_size = mesh->curve_keys.size(); + size_t steps = mesh->motion_steps - 1; + float3 *key_steps = attr->data_float3(); + + for (size_t i = 0; i < steps; i++) + curve.bounds_grow(k, key_steps + i * mesh_size, &mesh->curve_radius[0], bbox); + } + } + } + else { + /* Triangles. */ + int tri_offset = (params.top_level) ? mesh->tri_offset : 0; + Mesh::Triangle triangle = mesh->get_triangle(pidx - tri_offset); + const float3 *vpos = &mesh->verts[0]; + + triangle.bounds_grow(vpos, bbox); + + /* Motion triangles. */ + if (mesh->use_motion_blur) { + Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + + if (attr) { + size_t mesh_size = mesh->verts.size(); + size_t steps = mesh->motion_steps - 1; + float3 *vert_steps = attr->data_float3(); + + for (size_t i = 0; i < steps; i++) + triangle.bounds_grow(vert_steps + i * mesh_size, bbox); + } + } + } + } + visibility |= ob->visibility_for_tracing(); + } } /* Triangles */ void BVH::pack_triangle(int idx, float4 tri_verts[3]) { - int tob = pack.prim_object[idx]; - assert(tob >= 0 && tob < objects.size()); - const Mesh *mesh = objects[tob]->mesh; - - int tidx = pack.prim_index[idx]; - Mesh::Triangle t = mesh->get_triangle(tidx); - const float3 *vpos = &mesh->verts[0]; - float3 v0 = vpos[t.v[0]]; - float3 v1 = vpos[t.v[1]]; - float3 v2 = vpos[t.v[2]]; - - tri_verts[0] = float3_to_float4(v0); - tri_verts[1] = float3_to_float4(v1); - tri_verts[2] = float3_to_float4(v2); + int tob = pack.prim_object[idx]; + assert(tob >= 0 && tob < objects.size()); + const Mesh *mesh = objects[tob]->mesh; + + int tidx = pack.prim_index[idx]; + Mesh::Triangle t = mesh->get_triangle(tidx); + const float3 *vpos = &mesh->verts[0]; + float3 v0 = vpos[t.v[0]]; + float3 v1 = vpos[t.v[1]]; + float3 v2 = vpos[t.v[2]]; + + tri_verts[0] = float3_to_float4(v0); + tri_verts[1] = float3_to_float4(v1); + tri_verts[2] = float3_to_float4(v2); } void BVH::pack_primitives() { - const size_t tidx_size = pack.prim_index.size(); - size_t num_prim_triangles = 0; - /* Count number of triangles primitives in BVH. */ - for(unsigned int i = 0; i < tidx_size; i++) { - if((pack.prim_index[i] != -1)) { - if((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { - ++num_prim_triangles; - } - } - } - /* Reserve size for arrays. */ - pack.prim_tri_index.clear(); - pack.prim_tri_index.resize(tidx_size); - pack.prim_tri_verts.clear(); - pack.prim_tri_verts.resize(num_prim_triangles * 3); - pack.prim_visibility.clear(); - pack.prim_visibility.resize(tidx_size); - /* Fill in all the arrays. */ - size_t prim_triangle_index = 0; - for(unsigned int i = 0; i < tidx_size; i++) { - if(pack.prim_index[i] != -1) { - int tob = pack.prim_object[i]; - Object *ob = objects[tob]; - if((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { - pack_triangle(i, (float4*)&pack.prim_tri_verts[3 * prim_triangle_index]); - pack.prim_tri_index[i] = 3 * prim_triangle_index; - ++prim_triangle_index; - } - else { - pack.prim_tri_index[i] = -1; - } - pack.prim_visibility[i] = ob->visibility_for_tracing(); - if(pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { - pack.prim_visibility[i] |= PATH_RAY_CURVE; - } - } - else { - pack.prim_tri_index[i] = -1; - pack.prim_visibility[i] = 0; - } - } + const size_t tidx_size = pack.prim_index.size(); + size_t num_prim_triangles = 0; + /* Count number of triangles primitives in BVH. */ + for (unsigned int i = 0; i < tidx_size; i++) { + if ((pack.prim_index[i] != -1)) { + if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { + ++num_prim_triangles; + } + } + } + /* Reserve size for arrays. */ + pack.prim_tri_index.clear(); + pack.prim_tri_index.resize(tidx_size); + pack.prim_tri_verts.clear(); + pack.prim_tri_verts.resize(num_prim_triangles * 3); + pack.prim_visibility.clear(); + pack.prim_visibility.resize(tidx_size); + /* Fill in all the arrays. */ + size_t prim_triangle_index = 0; + for (unsigned int i = 0; i < tidx_size; i++) { + if (pack.prim_index[i] != -1) { + int tob = pack.prim_object[i]; + Object *ob = objects[tob]; + if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { + pack_triangle(i, (float4 *)&pack.prim_tri_verts[3 * prim_triangle_index]); + pack.prim_tri_index[i] = 3 * prim_triangle_index; + ++prim_triangle_index; + } + else { + pack.prim_tri_index[i] = -1; + } + pack.prim_visibility[i] = ob->visibility_for_tracing(); + if (pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { + pack.prim_visibility[i] |= PATH_RAY_CURVE; + } + } + else { + pack.prim_tri_index[i] = -1; + pack.prim_visibility[i] = 0; + } + } } /* Pack Instances */ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size) { - /* The BVH's for instances are built separately, but for traversal all - * BVH's are stored in global arrays. This function merges them into the - * top level BVH, adjusting indexes and offsets where appropriate. - */ - const bool use_qbvh = (params.bvh_layout == BVH_LAYOUT_BVH4); - const bool use_obvh = (params.bvh_layout == BVH_LAYOUT_BVH8); - - /* Adjust primitive index to point to the triangle in the global array, for - * meshes 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) { - if(pack.prim_type[i] & PRIMITIVE_ALL_CURVE) - pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->curve_offset; - else - pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->tri_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 prim_tri_verts_size = pack.prim_tri_verts.size(); - - size_t pack_prim_index_offset = prim_index_size; - size_t pack_prim_tri_verts_offset = prim_tri_verts_size; - size_t pack_nodes_offset = nodes_size; - size_t pack_leaf_nodes_offset = leaf_nodes_size; - size_t object_offset = 0; - - map<Mesh*, int> mesh_map; - - foreach(Object *ob, objects) { - Mesh *mesh = ob->mesh; - BVH *bvh = mesh->bvh; - - if(mesh->need_build_bvh()) { - if(mesh_map.find(mesh) == mesh_map.end()) { - prim_index_size += bvh->pack.prim_index.size(); - prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); - nodes_size += bvh->pack.nodes.size(); - leaf_nodes_size += bvh->pack.leaf_nodes.size(); - - mesh_map[mesh] = 1; - } - } - } - - mesh_map.clear(); - - 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.prim_tri_verts.resize(prim_tri_verts_size); - pack.prim_tri_index.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) { - pack.prim_time.resize(prim_index_size); - } - - int *pack_prim_index = (pack.prim_index.size())? &pack.prim_index[0]: NULL; - int *pack_prim_type = (pack.prim_type.size())? &pack.prim_type[0]: NULL; - int *pack_prim_object = (pack.prim_object.size())? &pack.prim_object[0]: NULL; - uint *pack_prim_visibility = (pack.prim_visibility.size())? &pack.prim_visibility[0]: NULL; - float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size())? &pack.prim_tri_verts[0]: NULL; - uint *pack_prim_tri_index = (pack.prim_tri_index.size())? &pack.prim_tri_index[0]: NULL; - int4 *pack_nodes = (pack.nodes.size())? &pack.nodes[0]: NULL; - int4 *pack_leaf_nodes = (pack.leaf_nodes.size())? &pack.leaf_nodes[0]: NULL; - float2 *pack_prim_time = (pack.prim_time.size())? &pack.prim_time[0]: NULL; - - /* merge */ - foreach(Object *ob, objects) { - Mesh *mesh = ob->mesh; - - /* 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(!mesh->need_build_bvh()) { - 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 */ - map<Mesh*, int>::iterator it = mesh_map.find(mesh); - - if(mesh_map.find(mesh) != mesh_map.end()) { - int noffset = it->second; - pack.object_node[object_offset++] = noffset; - continue; - } - - BVH *bvh = mesh->bvh; - - int noffset = nodes_offset; - int noffset_leaf = nodes_leaf_offset; - int mesh_tri_offset = mesh->tri_offset; - int mesh_curve_offset = mesh->curve_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; - - mesh_map[mesh] = pack.object_node[object_offset-1]; - - /* merge primitive, object and triangle indexes */ - if(bvh->pack.prim_index.size()) { - size_t bvh_prim_index_size = bvh->pack.prim_index.size(); - int *bvh_prim_index = &bvh->pack.prim_index[0]; - int *bvh_prim_type = &bvh->pack.prim_type[0]; - uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0]; - uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0]; - float2 *bvh_prim_time = bvh->pack.prim_time.size()? &bvh->pack.prim_time[0]: NULL; - - for(size_t i = 0; i < bvh_prim_index_size; i++) { - if(bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { - pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_curve_offset; - pack_prim_tri_index[pack_prim_index_offset] = -1; - } - else { - pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_tri_offset; - pack_prim_tri_index[pack_prim_index_offset] = - bvh_prim_tri_index[i] + pack_prim_tri_verts_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 != NULL) { - pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i]; - } - pack_prim_index_offset++; - } - } - - /* Merge triangle vertices data. */ - if(bvh->pack.prim_tri_verts.size()) { - const size_t prim_tri_size = bvh->pack.prim_tri_verts.size(); - memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset, - &bvh->pack.prim_tri_verts[0], - prim_tri_size*sizeof(float4)); - pack_prim_tri_verts_offset += prim_tri_size; - } - - /* merge nodes */ - if(bvh->pack.leaf_nodes.size()) { - int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0]; - size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size(); - for(size_t i = 0, j = 0; - i < leaf_nodes_offset_size; - i += BVH_NODE_LEAF_SIZE, j++) - { - 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[0]; - size_t bvh_nodes_size = bvh->pack.nodes.size(); - - for(size_t i = 0, j = 0; i < bvh_nodes_size; j++) { - size_t nsize, nsize_bbox; - if(bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) { - if(use_obvh) { - nsize = BVH_UNALIGNED_ONODE_SIZE; - nsize_bbox = BVH_UNALIGNED_ONODE_SIZE-1; - } - else { - nsize = use_qbvh - ? BVH_UNALIGNED_QNODE_SIZE - : BVH_UNALIGNED_NODE_SIZE; - nsize_bbox = (use_qbvh) ? BVH_UNALIGNED_QNODE_SIZE-1 : 0; - } - } - else { - if(use_obvh) { - nsize = BVH_ONODE_SIZE; - nsize_bbox = BVH_ONODE_SIZE-1; - } - else { - nsize = (use_qbvh)? BVH_QNODE_SIZE: BVH_NODE_SIZE; - nsize_bbox = (use_qbvh)? BVH_QNODE_SIZE-1 : 0; - } - } - - memcpy(pack_nodes + pack_nodes_offset, - bvh_nodes + i, - nsize_bbox*sizeof(int4)); - - /* Modify offsets into arrays */ - int4 data = bvh_nodes[i + nsize_bbox]; - int4 data1 = bvh_nodes[i + nsize_bbox-1]; - if(use_obvh) { - data.z += (data.z < 0) ? -noffset_leaf : noffset; - data.w += (data.w < 0) ? -noffset_leaf : noffset; - data.x += (data.x < 0) ? -noffset_leaf : noffset; - data.y += (data.y < 0) ? -noffset_leaf : noffset; - data1.z += (data1.z < 0) ? -noffset_leaf : noffset; - data1.w += (data1.w < 0) ? -noffset_leaf : noffset; - data1.x += (data1.x < 0) ? -noffset_leaf : noffset; - data1.y += (data1.y < 0) ? -noffset_leaf : noffset; - } - else { - data.z += (data.z < 0) ? -noffset_leaf : noffset; - data.w += (data.w < 0) ? -noffset_leaf : noffset; - if(use_qbvh) { - data.x += (data.x < 0)? -noffset_leaf: noffset; - data.y += (data.y < 0)? -noffset_leaf: noffset; - } - } - pack_nodes[pack_nodes_offset + nsize_bbox] = data; - if(use_obvh) { - pack_nodes[pack_nodes_offset + nsize_bbox - 1] = data1; - } - - /* Usually this copies nothing, but we better - * be prepared for possible node size extension. - */ - memcpy(&pack_nodes[pack_nodes_offset + nsize_bbox+1], - &bvh_nodes[i + nsize_bbox+1], - sizeof(int4) * (nsize - (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(); - } + /* The BVH's for instances are built separately, but for traversal all + * BVH's are stored in global arrays. This function merges them into the + * top level BVH, adjusting indexes and offsets where appropriate. + */ + const bool use_qbvh = (params.bvh_layout == BVH_LAYOUT_BVH4); + const bool use_obvh = (params.bvh_layout == BVH_LAYOUT_BVH8); + + /* Adjust primitive index to point to the triangle in the global array, for + * meshes 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) { + if (pack.prim_type[i] & PRIMITIVE_ALL_CURVE) + pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->curve_offset; + else + pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->tri_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 prim_tri_verts_size = pack.prim_tri_verts.size(); + + size_t pack_prim_index_offset = prim_index_size; + size_t pack_prim_tri_verts_offset = prim_tri_verts_size; + size_t pack_nodes_offset = nodes_size; + size_t pack_leaf_nodes_offset = leaf_nodes_size; + size_t object_offset = 0; + + map<Mesh *, int> mesh_map; + + foreach (Object *ob, objects) { + Mesh *mesh = ob->mesh; + BVH *bvh = mesh->bvh; + + if (mesh->need_build_bvh()) { + if (mesh_map.find(mesh) == mesh_map.end()) { + prim_index_size += bvh->pack.prim_index.size(); + prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); + nodes_size += bvh->pack.nodes.size(); + leaf_nodes_size += bvh->pack.leaf_nodes.size(); + + mesh_map[mesh] = 1; + } + } + } + + mesh_map.clear(); + + 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.prim_tri_verts.resize(prim_tri_verts_size); + pack.prim_tri_index.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) { + pack.prim_time.resize(prim_index_size); + } + + int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL; + int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL; + int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL; + uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : NULL; + float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL; + uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL; + int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : NULL; + int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : NULL; + float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : NULL; + + /* merge */ + foreach (Object *ob, objects) { + Mesh *mesh = ob->mesh; + + /* 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 (!mesh->need_build_bvh()) { + 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 */ + map<Mesh *, int>::iterator it = mesh_map.find(mesh); + + if (mesh_map.find(mesh) != mesh_map.end()) { + int noffset = it->second; + pack.object_node[object_offset++] = noffset; + continue; + } + + BVH *bvh = mesh->bvh; + + int noffset = nodes_offset; + int noffset_leaf = nodes_leaf_offset; + int mesh_tri_offset = mesh->tri_offset; + int mesh_curve_offset = mesh->curve_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; + + mesh_map[mesh] = pack.object_node[object_offset - 1]; + + /* merge primitive, object and triangle indexes */ + if (bvh->pack.prim_index.size()) { + size_t bvh_prim_index_size = bvh->pack.prim_index.size(); + int *bvh_prim_index = &bvh->pack.prim_index[0]; + int *bvh_prim_type = &bvh->pack.prim_type[0]; + uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0]; + uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0]; + float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL; + + for (size_t i = 0; i < bvh_prim_index_size; i++) { + if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { + pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_curve_offset; + pack_prim_tri_index[pack_prim_index_offset] = -1; + } + else { + pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_tri_offset; + pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] + + pack_prim_tri_verts_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 != NULL) { + pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i]; + } + pack_prim_index_offset++; + } + } + + /* Merge triangle vertices data. */ + if (bvh->pack.prim_tri_verts.size()) { + const size_t prim_tri_size = bvh->pack.prim_tri_verts.size(); + memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset, + &bvh->pack.prim_tri_verts[0], + prim_tri_size * sizeof(float4)); + pack_prim_tri_verts_offset += prim_tri_size; + } + + /* merge nodes */ + if (bvh->pack.leaf_nodes.size()) { + int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0]; + size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size(); + for (size_t i = 0, j = 0; i < leaf_nodes_offset_size; i += BVH_NODE_LEAF_SIZE, j++) { + 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[0]; + size_t bvh_nodes_size = bvh->pack.nodes.size(); + + for (size_t i = 0, j = 0; i < bvh_nodes_size; j++) { + size_t nsize, nsize_bbox; + if (bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) { + if (use_obvh) { + nsize = BVH_UNALIGNED_ONODE_SIZE; + nsize_bbox = BVH_UNALIGNED_ONODE_SIZE - 1; + } + else { + nsize = use_qbvh ? BVH_UNALIGNED_QNODE_SIZE : BVH_UNALIGNED_NODE_SIZE; + nsize_bbox = (use_qbvh) ? BVH_UNALIGNED_QNODE_SIZE - 1 : 0; + } + } + else { + if (use_obvh) { + nsize = BVH_ONODE_SIZE; + nsize_bbox = BVH_ONODE_SIZE - 1; + } + else { + nsize = (use_qbvh) ? BVH_QNODE_SIZE : BVH_NODE_SIZE; + nsize_bbox = (use_qbvh) ? BVH_QNODE_SIZE - 1 : 0; + } + } + + memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox * sizeof(int4)); + + /* Modify offsets into arrays */ + int4 data = bvh_nodes[i + nsize_bbox]; + int4 data1 = bvh_nodes[i + nsize_bbox - 1]; + if (use_obvh) { + data.z += (data.z < 0) ? -noffset_leaf : noffset; + data.w += (data.w < 0) ? -noffset_leaf : noffset; + data.x += (data.x < 0) ? -noffset_leaf : noffset; + data.y += (data.y < 0) ? -noffset_leaf : noffset; + data1.z += (data1.z < 0) ? -noffset_leaf : noffset; + data1.w += (data1.w < 0) ? -noffset_leaf : noffset; + data1.x += (data1.x < 0) ? -noffset_leaf : noffset; + data1.y += (data1.y < 0) ? -noffset_leaf : noffset; + } + else { + data.z += (data.z < 0) ? -noffset_leaf : noffset; + data.w += (data.w < 0) ? -noffset_leaf : noffset; + if (use_qbvh) { + data.x += (data.x < 0) ? -noffset_leaf : noffset; + data.y += (data.y < 0) ? -noffset_leaf : noffset; + } + } + pack_nodes[pack_nodes_offset + nsize_bbox] = data; + if (use_obvh) { + pack_nodes[pack_nodes_offset + nsize_bbox - 1] = data1; + } + + /* Usually this copies nothing, but we better + * be prepared for possible node size extension. + */ + memcpy(&pack_nodes[pack_nodes_offset + nsize_bbox + 1], + &bvh_nodes[i + nsize_bbox + 1], + sizeof(int4) * (nsize - (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 diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h index 33a069eeaf5..edce3ca6f2a 100644 --- a/intern/cycles/bvh/bvh.h +++ b/intern/cycles/bvh/bvh.h @@ -34,96 +34,92 @@ class LeafNode; class Object; class Progress; -#define BVH_ALIGN 4096 +#define BVH_ALIGN 4096 #define TRI_NODE_SIZE 3 /* 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; - /* Mapping from primitive index to index in triangle array. */ - array<uint> prim_tri_index; - /* Continuous storage of triangle vertices. */ - array<float4> prim_tri_verts; - /* primitive type - triangle or strand */ - array<int> prim_type; - /* visibility visibilitys 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 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; + /* Mapping from primitive index to index in triangle array. */ + array<uint> prim_tri_index; + /* Continuous storage of triangle vertices. */ + array<float4> prim_tri_verts; + /* primitive type - triangle or strand */ + array<int> prim_type; + /* visibility visibilitys 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; + } }; -enum BVH_TYPE { - bvh2, - bvh4, - bvh8 -}; +enum BVH_TYPE { bvh2, bvh4, bvh8 }; /* BVH */ -class BVH -{ -public: - PackedBVH pack; - BVHParams params; - vector<Object*> objects; +class BVH { + public: + PackedBVH pack; + BVHParams params; + vector<Object *> objects; - static BVH *create(const BVHParams& params, const vector<Object*>& objects); - virtual ~BVH() {} + static BVH *create(const BVHParams ¶ms, const vector<Object *> &objects); + virtual ~BVH() + { + } - virtual void build(Progress& progress, Stats *stats=NULL); - void refit(Progress& progress); + virtual void build(Progress &progress, Stats *stats = NULL); + void refit(Progress &progress); -protected: - BVH(const BVHParams& params, const vector<Object*>& objects); + protected: + BVH(const BVHParams ¶ms, const vector<Object *> &objects); - /* Refit range of primitives. */ - void refit_primitives(int start, int end, BoundBox& bbox, uint& visibility); + /* Refit range of primitives. */ + void refit_primitives(int start, int end, BoundBox &bbox, uint &visibility); - /* triangles and strands */ - void pack_primitives(); - void pack_triangle(int idx, float4 storage[3]); + /* triangles and strands */ + void pack_primitives(); + void pack_triangle(int idx, float4 storage[3]); - /* merge instance BVH's */ - void pack_instances(size_t nodes_size, size_t leaf_nodes_size); + /* merge instance BVH's */ + void pack_instances(size_t nodes_size, size_t leaf_nodes_size); - /* for subclasses to implement */ - virtual void pack_nodes(const BVHNode *root) = 0; - virtual void refit_nodes() = 0; + /* for subclasses to implement */ + virtual void pack_nodes(const BVHNode *root) = 0; + virtual void refit_nodes() = 0; - virtual BVHNode *widen_children_nodes(const BVHNode *root) = 0; + virtual BVHNode *widen_children_nodes(const BVHNode *root) = 0; }; /* Pack Utility */ -struct BVHStackEntry -{ - const BVHNode *node; - int idx; +struct BVHStackEntry { + const BVHNode *node; + int idx; - BVHStackEntry(const BVHNode *n = 0, int i = 0); - int encodeIdx() const; + BVHStackEntry(const BVHNode *n = 0, int i = 0); + int encodeIdx() const; }; CCL_NAMESPACE_END -#endif /* __BVH_H__ */ +#endif /* __BVH_H__ */ diff --git a/intern/cycles/bvh/bvh2.cpp b/intern/cycles/bvh/bvh2.cpp index e5dc4e6b1a8..f419d413ef6 100644 --- a/intern/cycles/bvh/bvh2.cpp +++ b/intern/cycles/bvh/bvh2.cpp @@ -25,276 +25,268 @@ CCL_NAMESPACE_BEGIN -BVH2::BVH2(const BVHParams& params_, const vector<Object*>& objects_) -: BVH(params_, objects_) +BVH2::BVH2(const BVHParams ¶ms_, const vector<Object *> &objects_) : BVH(params_, objects_) { } BVHNode *BVH2::widen_children_nodes(const BVHNode *root) { - return const_cast<BVHNode *>(root); + return const_cast<BVHNode *>(root); } -void BVH2::pack_leaf(const BVHStackEntry& e, - const LeafNode *leaf) +void BVH2::pack_leaf(const BVHStackEntry &e, const LeafNode *leaf) { - assert(e.idx + BVH_NODE_LEAF_SIZE <= pack.leaf_nodes.size()); - float4 data[BVH_NODE_LEAF_SIZE]; - memset(data, 0, sizeof(data)); - if(leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) { - /* object */ - data[0].x = __int_as_float(~(leaf->lo)); - data[0].y = __int_as_float(0); - } - else { - /* triangle */ - data[0].x = __int_as_float(leaf->lo); - data[0].y = __int_as_float(leaf->hi); - } - data[0].z = __uint_as_float(leaf->visibility); - if(leaf->num_triangles() != 0) { - data[0].w = __uint_as_float(pack.prim_type[leaf->lo]); - } - - memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4)*BVH_NODE_LEAF_SIZE); + assert(e.idx + BVH_NODE_LEAF_SIZE <= pack.leaf_nodes.size()); + float4 data[BVH_NODE_LEAF_SIZE]; + memset(data, 0, sizeof(data)); + if (leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) { + /* object */ + data[0].x = __int_as_float(~(leaf->lo)); + data[0].y = __int_as_float(0); + } + else { + /* triangle */ + data[0].x = __int_as_float(leaf->lo); + data[0].y = __int_as_float(leaf->hi); + } + data[0].z = __uint_as_float(leaf->visibility); + if (leaf->num_triangles() != 0) { + data[0].w = __uint_as_float(pack.prim_type[leaf->lo]); + } + + memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4) * BVH_NODE_LEAF_SIZE); } -void BVH2::pack_inner(const BVHStackEntry& e, - const BVHStackEntry& e0, - const BVHStackEntry& e1) +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); - } + 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) +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); + 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(int idx, - const BoundBox& b0, - const BoundBox& b1, - int c0, int c1, - uint visibility0, uint visibility1) + 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_NODE_UNALIGNED, - visibility1 & ~PATH_RAY_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)), - }; - - memcpy(&pack.nodes[idx], data, sizeof(int4)*BVH_NODE_SIZE); + 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_NODE_UNALIGNED, visibility1 & ~PATH_RAY_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)), + }; + + memcpy(&pack.nodes[idx], data, sizeof(int4) * BVH_NODE_SIZE); } -void BVH2::pack_unaligned_inner(const BVHStackEntry& e, - const BVHStackEntry& e0, - const BVHStackEntry& e1) +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); + 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(int idx, - const Transform& aligned_space0, - const Transform& aligned_space1, - const BoundBox& bounds0, - const BoundBox& bounds1, - int c0, int c1, - uint visibility0, uint visibility1) + const Transform &aligned_space0, + const Transform &aligned_space1, + const BoundBox &bounds0, + const BoundBox &bounds1, + 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()); - - float4 data[BVH_UNALIGNED_NODE_SIZE]; - Transform space0 = BVHUnaligned::compute_node_transform(bounds0, - aligned_space0); - Transform space1 = BVHUnaligned::compute_node_transform(bounds1, - aligned_space1); - data[0] = make_float4(__int_as_float(visibility0 | PATH_RAY_NODE_UNALIGNED), - __int_as_float(visibility1 | PATH_RAY_NODE_UNALIGNED), - __int_as_float(c0), - __int_as_float(c1)); - - data[1] = space0.x; - data[2] = space0.y; - data[3] = space0.z; - data[4] = space1.x; - data[5] = space1.y; - data[6] = space1.z; - - memcpy(&pack.nodes[idx], data, sizeof(float4)*BVH_UNALIGNED_NODE_SIZE); + assert(idx + BVH_UNALIGNED_NODE_SIZE <= pack.nodes.size()); + assert(c0 < 0 || c0 < pack.nodes.size()); + assert(c1 < 0 || c1 < pack.nodes.size()); + + float4 data[BVH_UNALIGNED_NODE_SIZE]; + Transform space0 = BVHUnaligned::compute_node_transform(bounds0, aligned_space0); + Transform space1 = BVHUnaligned::compute_node_transform(bounds1, aligned_space1); + data[0] = make_float4(__int_as_float(visibility0 | PATH_RAY_NODE_UNALIGNED), + __int_as_float(visibility1 | PATH_RAY_NODE_UNALIGNED), + __int_as_float(c0), + __int_as_float(c1)); + + data[1] = space0.x; + data[2] = space0.y; + data[3] = space0.z; + data[4] = space1.x; + data[5] = space1.y; + data[6] = space1.z; + + memcpy(&pack.nodes[idx], data, sizeof(float4) * BVH_UNALIGNED_NODE_SIZE); } 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, 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.size()) { - 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; + 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, 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.size()) { + 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); + assert(!params.top_level); - BoundBox bbox = BoundBox::empty; - uint visibility = 0; - refit_node(0, (pack.root_index == -1)? true: false, bbox, visibility); + BoundBox bbox = BoundBox::empty; + uint visibility = 0; + refit_node(0, (pack.root_index == -1) ? true : false, bbox, visibility); } -void BVH2::refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility) +void BVH2::refit_node(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; - - BVH::refit_primitives(c0, c1, bbox, visibility); - - /* TODO(sergey): De-duplicate with pack_leaf(). */ - float4 leaf_data[BVH_NODE_LEAF_SIZE]; - leaf_data[0].x = __int_as_float(c0); - leaf_data[0].y = __int_as_float(c1); - leaf_data[0].z = __uint_as_float(visibility); - leaf_data[0].w = __uint_as_float(data[0].w); - memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4)*BVH_NODE_LEAF_SIZE); - } - else { - assert(idx + BVH_NODE_SIZE <= pack.nodes.size()); - - const int4 *data = &pack.nodes[idx]; - const bool is_unaligned = (data[0].x & PATH_RAY_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, bbox1 = BoundBox::empty; - uint visibility0 = 0, 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) { - 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; - } + 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; + + BVH::refit_primitives(c0, c1, bbox, visibility); + + /* TODO(sergey): De-duplicate with pack_leaf(). */ + float4 leaf_data[BVH_NODE_LEAF_SIZE]; + leaf_data[0].x = __int_as_float(c0); + leaf_data[0].y = __int_as_float(c1); + leaf_data[0].z = __uint_as_float(visibility); + leaf_data[0].w = __uint_as_float(data[0].w); + memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4) * BVH_NODE_LEAF_SIZE); + } + else { + assert(idx + BVH_NODE_SIZE <= pack.nodes.size()); + + const int4 *data = &pack.nodes[idx]; + const bool is_unaligned = (data[0].x & PATH_RAY_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, bbox1 = BoundBox::empty; + uint visibility0 = 0, 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) { + 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; + } } CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh2.h b/intern/cycles/bvh/bvh2.h index 6afa6c21796..c6a4e6fa73a 100644 --- a/intern/cycles/bvh/bvh2.h +++ b/intern/cycles/bvh/bvh2.h @@ -34,8 +34,8 @@ class LeafNode; class Object; class Progress; -#define BVH_NODE_SIZE 4 -#define BVH_NODE_LEAF_SIZE 1 +#define BVH_NODE_SIZE 4 +#define BVH_NODE_LEAF_SIZE 1 #define BVH_UNALIGNED_NODE_SIZE 7 /* BVH2 @@ -43,48 +43,49 @@ class Progress; * Typical BVH with each node having two children. */ class BVH2 : public BVH { -protected: - /* constructor */ - friend class BVH; - BVH2(const BVHParams& params, const vector<Object*>& objects); + protected: + /* constructor */ + friend class BVH; + BVH2(const BVHParams ¶ms, const vector<Object *> &objects); - /* Building process. */ - virtual BVHNode *widen_children_nodes(const BVHNode *root) override; + /* Building process. */ + virtual BVHNode *widen_children_nodes(const BVHNode *root) override; - /* pack */ - void pack_nodes(const BVHNode *root) override; + /* pack */ + void pack_nodes(const BVHNode *root) override; - void pack_leaf(const BVHStackEntry& e, - const LeafNode *leaf); - void pack_inner(const BVHStackEntry& e, - const BVHStackEntry& e0, - const BVHStackEntry& e1); + 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(int idx, - const BoundBox& b0, - const BoundBox& b1, - int c0, int c1, - uint visibility0, uint visibility1); + void pack_aligned_inner(const BVHStackEntry &e, + const BVHStackEntry &e0, + const BVHStackEntry &e1); + void pack_aligned_node(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(int idx, - const Transform& aligned_space0, - const Transform& aligned_space1, - 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(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() override; - void refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility); + /* refit */ + void refit_nodes() override; + void refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility); }; CCL_NAMESPACE_END -#endif /* __BVH2_H__ */ +#endif /* __BVH2_H__ */ diff --git a/intern/cycles/bvh/bvh4.cpp b/intern/cycles/bvh/bvh4.cpp index a7c4cea85ce..850bdf5b8b4 100644 --- a/intern/cycles/bvh/bvh4.cpp +++ b/intern/cycles/bvh/bvh4.cpp @@ -31,141 +31,131 @@ CCL_NAMESPACE_BEGIN * life easier all over the place. */ -BVH4::BVH4(const BVHParams& params_, const vector<Object*>& objects_) -: BVH(params_, objects_) +BVH4::BVH4(const BVHParams ¶ms_, const vector<Object *> &objects_) : BVH(params_, objects_) { - params.bvh_layout = BVH_LAYOUT_BVH4; + params.bvh_layout = BVH_LAYOUT_BVH4; } namespace { BVHNode *bvh_node_merge_children_recursively(const BVHNode *node) { - if(node->is_leaf()) { - return new LeafNode(*reinterpret_cast<const LeafNode *>(node)); - } - /* Collect nodes of one layer deeper, allowing us to have more childrem in - * an inner layer. */ - assert(node->num_children() <= 2); - const BVHNode *children[4]; - const BVHNode *child0 = node->get_child(0); - const BVHNode *child1 = node->get_child(1); - int num_children = 0; - if(child0->is_leaf()) { - children[num_children++] = child0; - } - else { - children[num_children++] = child0->get_child(0); - children[num_children++] = child0->get_child(1); - } - if(child1->is_leaf()) { - children[num_children++] = child1; - } - else { - children[num_children++] = child1->get_child(0); - children[num_children++] = child1->get_child(1); - } - /* Merge children in subtrees. */ - BVHNode *children4[4]; - for(int i = 0; i < num_children; ++i) { - children4[i] = bvh_node_merge_children_recursively(children[i]); - } - /* Allocate new node. */ - BVHNode *node4 = new InnerNode(node->bounds, children4, num_children); - /* TODO(sergey): Consider doing this from the InnerNode() constructor. - * But in order to do this nicely need to think of how to pass all the - * parameters there. */ - if(node->is_unaligned) { - node4->is_unaligned = true; - node4->aligned_space = new Transform(); - *node4->aligned_space = *node->aligned_space; - } - return node4; + if (node->is_leaf()) { + return new LeafNode(*reinterpret_cast<const LeafNode *>(node)); + } + /* Collect nodes of one layer deeper, allowing us to have more childrem in + * an inner layer. */ + assert(node->num_children() <= 2); + const BVHNode *children[4]; + const BVHNode *child0 = node->get_child(0); + const BVHNode *child1 = node->get_child(1); + int num_children = 0; + if (child0->is_leaf()) { + children[num_children++] = child0; + } + else { + children[num_children++] = child0->get_child(0); + children[num_children++] = child0->get_child(1); + } + if (child1->is_leaf()) { + children[num_children++] = child1; + } + else { + children[num_children++] = child1->get_child(0); + children[num_children++] = child1->get_child(1); + } + /* Merge children in subtrees. */ + BVHNode *children4[4]; + for (int i = 0; i < num_children; ++i) { + children4[i] = bvh_node_merge_children_recursively(children[i]); + } + /* Allocate new node. */ + BVHNode *node4 = new InnerNode(node->bounds, children4, num_children); + /* TODO(sergey): Consider doing this from the InnerNode() constructor. + * But in order to do this nicely need to think of how to pass all the + * parameters there. */ + if (node->is_unaligned) { + node4->is_unaligned = true; + node4->aligned_space = new Transform(); + *node4->aligned_space = *node->aligned_space; + } + return node4; } } // namespace BVHNode *BVH4::widen_children_nodes(const BVHNode *root) { - if(root == NULL) { - return NULL; - } - if(root->is_leaf()) { - return const_cast<BVHNode *>(root); - } - BVHNode *root4 = bvh_node_merge_children_recursively(root); - /* TODO(sergey): Pack children nodes to parents which has less that 4 - * children. */ - return root4; + if (root == NULL) { + return NULL; + } + if (root->is_leaf()) { + return const_cast<BVHNode *>(root); + } + BVHNode *root4 = bvh_node_merge_children_recursively(root); + /* TODO(sergey): Pack children nodes to parents which has less that 4 + * children. */ + return root4; } -void BVH4::pack_leaf(const BVHStackEntry& e, const LeafNode *leaf) +void BVH4::pack_leaf(const BVHStackEntry &e, const LeafNode *leaf) { - float4 data[BVH_QNODE_LEAF_SIZE]; - memset(data, 0, sizeof(data)); - if(leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) { - /* object */ - data[0].x = __int_as_float(~(leaf->lo)); - data[0].y = __int_as_float(0); - } - else { - /* triangle */ - data[0].x = __int_as_float(leaf->lo); - data[0].y = __int_as_float(leaf->hi); - } - data[0].z = __uint_as_float(leaf->visibility); - if(leaf->num_triangles() != 0) { - data[0].w = __uint_as_float(pack.prim_type[leaf->lo]); - } - - memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4)*BVH_QNODE_LEAF_SIZE); + float4 data[BVH_QNODE_LEAF_SIZE]; + memset(data, 0, sizeof(data)); + if (leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) { + /* object */ + data[0].x = __int_as_float(~(leaf->lo)); + data[0].y = __int_as_float(0); + } + else { + /* triangle */ + data[0].x = __int_as_float(leaf->lo); + data[0].y = __int_as_float(leaf->hi); + } + data[0].z = __uint_as_float(leaf->visibility); + if (leaf->num_triangles() != 0) { + data[0].w = __uint_as_float(pack.prim_type[leaf->lo]); + } + + memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4) * BVH_QNODE_LEAF_SIZE); } -void BVH4::pack_inner(const BVHStackEntry& e, - const BVHStackEntry *en, - int num) +void BVH4::pack_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num) { - bool has_unaligned = false; - /* Check whether we have to create unaligned node or all nodes are aligned - * and we can cut some corner here. - */ - if(params.use_unaligned_nodes) { - for(int i = 0; i < num; i++) { - if(en[i].node->is_unaligned) { - has_unaligned = true; - break; - } - } - } - if(has_unaligned) { - /* There's no unaligned children, pack into AABB node. */ - pack_unaligned_inner(e, en, num); - } - else { - /* Create unaligned node with orientation transform for each of the - * children. - */ - pack_aligned_inner(e, en, num); - } + bool has_unaligned = false; + /* Check whether we have to create unaligned node or all nodes are aligned + * and we can cut some corner here. + */ + if (params.use_unaligned_nodes) { + for (int i = 0; i < num; i++) { + if (en[i].node->is_unaligned) { + has_unaligned = true; + break; + } + } + } + if (has_unaligned) { + /* There's no unaligned children, pack into AABB node. */ + pack_unaligned_inner(e, en, num); + } + else { + /* Create unaligned node with orientation transform for each of the + * children. + */ + pack_aligned_inner(e, en, num); + } } -void BVH4::pack_aligned_inner(const BVHStackEntry& e, - const BVHStackEntry *en, - int num) +void BVH4::pack_aligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num) { - BoundBox bounds[4]; - int child[4]; - for(int i = 0; i < num; ++i) { - bounds[i] = en[i].node->bounds; - child[i] = en[i].encodeIdx(); - } - pack_aligned_node(e.idx, - bounds, - child, - e.node->visibility, - e.node->time_from, - e.node->time_to, - num); + BoundBox bounds[4]; + int child[4]; + for (int i = 0; i < num; ++i) { + bounds[i] = en[i].node->bounds; + child[i] = en[i].encodeIdx(); + } + pack_aligned_node( + e.idx, bounds, child, e.node->visibility, e.node->time_from, e.node->time_to, num); } void BVH4::pack_aligned_node(int idx, @@ -176,66 +166,64 @@ void BVH4::pack_aligned_node(int idx, const float time_to, const int num) { - float4 data[BVH_QNODE_SIZE]; - memset(data, 0, sizeof(data)); + float4 data[BVH_QNODE_SIZE]; + memset(data, 0, sizeof(data)); - data[0].x = __uint_as_float(visibility & ~PATH_RAY_NODE_UNALIGNED); - data[0].y = time_from; - data[0].z = time_to; + data[0].x = __uint_as_float(visibility & ~PATH_RAY_NODE_UNALIGNED); + data[0].y = time_from; + data[0].z = time_to; - for(int i = 0; i < num; i++) { - float3 bb_min = bounds[i].min; - float3 bb_max = bounds[i].max; + for (int i = 0; i < num; i++) { + float3 bb_min = bounds[i].min; + float3 bb_max = bounds[i].max; - data[1][i] = bb_min.x; - data[2][i] = bb_max.x; - data[3][i] = bb_min.y; - data[4][i] = bb_max.y; - data[5][i] = bb_min.z; - data[6][i] = bb_max.z; + data[1][i] = bb_min.x; + data[2][i] = bb_max.x; + data[3][i] = bb_min.y; + data[4][i] = bb_max.y; + data[5][i] = bb_min.z; + data[6][i] = bb_max.z; - data[7][i] = __int_as_float(child[i]); - } + data[7][i] = __int_as_float(child[i]); + } - for(int i = num; i < 4; i++) { - /* We store BB which would never be recorded as intersection - * so kernel might safely assume there are always 4 child nodes. - */ - data[1][i] = FLT_MAX; - data[2][i] = -FLT_MAX; + for (int i = num; i < 4; i++) { + /* We store BB which would never be recorded as intersection + * so kernel might safely assume there are always 4 child nodes. + */ + data[1][i] = FLT_MAX; + data[2][i] = -FLT_MAX; - data[3][i] = FLT_MAX; - data[4][i] = -FLT_MAX; + data[3][i] = FLT_MAX; + data[4][i] = -FLT_MAX; - data[5][i] = FLT_MAX; - data[6][i] = -FLT_MAX; + data[5][i] = FLT_MAX; + data[6][i] = -FLT_MAX; - data[7][i] = __int_as_float(0); - } + data[7][i] = __int_as_float(0); + } - memcpy(&pack.nodes[idx], data, sizeof(float4)*BVH_QNODE_SIZE); + memcpy(&pack.nodes[idx], data, sizeof(float4) * BVH_QNODE_SIZE); } -void BVH4::pack_unaligned_inner(const BVHStackEntry& e, - const BVHStackEntry *en, - int num) +void BVH4::pack_unaligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num) { - Transform aligned_space[4]; - BoundBox bounds[4]; - int child[4]; - for(int i = 0; i < num; ++i) { - aligned_space[i] = en[i].node->get_aligned_space(); - bounds[i] = en[i].node->bounds; - child[i] = en[i].encodeIdx(); - } - pack_unaligned_node(e.idx, - aligned_space, - bounds, - child, - e.node->visibility, - e.node->time_from, - e.node->time_to, - num); + Transform aligned_space[4]; + BoundBox bounds[4]; + int child[4]; + for (int i = 0; i < num; ++i) { + aligned_space[i] = en[i].node->get_aligned_space(); + bounds[i] = en[i].node->bounds; + child[i] = en[i].encodeIdx(); + } + pack_unaligned_node(e.idx, + aligned_space, + bounds, + child, + e.node->visibility, + e.node->time_from, + e.node->time_to, + num); } void BVH4::pack_unaligned_node(int idx, @@ -247,235 +235,211 @@ void BVH4::pack_unaligned_node(int idx, const float time_to, const int num) { - float4 data[BVH_UNALIGNED_QNODE_SIZE]; - memset(data, 0, sizeof(data)); + float4 data[BVH_UNALIGNED_QNODE_SIZE]; + memset(data, 0, sizeof(data)); - data[0].x = __uint_as_float(visibility | PATH_RAY_NODE_UNALIGNED); - data[0].y = time_from; - data[0].z = time_to; + data[0].x = __uint_as_float(visibility | PATH_RAY_NODE_UNALIGNED); + data[0].y = time_from; + data[0].z = time_to; - for(int i = 0; i < num; i++) { - Transform space = BVHUnaligned::compute_node_transform( - bounds[i], - aligned_space[i]); + for (int i = 0; i < num; i++) { + Transform space = BVHUnaligned::compute_node_transform(bounds[i], aligned_space[i]); - data[1][i] = space.x.x; - data[2][i] = space.x.y; - data[3][i] = space.x.z; + data[1][i] = space.x.x; + data[2][i] = space.x.y; + data[3][i] = space.x.z; - data[4][i] = space.y.x; - data[5][i] = space.y.y; - data[6][i] = space.y.z; + data[4][i] = space.y.x; + data[5][i] = space.y.y; + data[6][i] = space.y.z; - data[7][i] = space.z.x; - data[8][i] = space.z.y; - data[9][i] = space.z.z; + data[7][i] = space.z.x; + data[8][i] = space.z.y; + data[9][i] = space.z.z; - data[10][i] = space.x.w; - data[11][i] = space.y.w; - data[12][i] = space.z.w; + data[10][i] = space.x.w; + data[11][i] = space.y.w; + data[12][i] = space.z.w; - data[13][i] = __int_as_float(child[i]); - } + data[13][i] = __int_as_float(child[i]); + } - for(int i = num; i < 4; i++) { - /* We store BB which would never be recorded as intersection - * so kernel might safely assume there are always 4 child nodes. - */ + for (int i = num; i < 4; i++) { + /* We store BB which would never be recorded as intersection + * so kernel might safely assume there are always 4 child nodes. + */ - data[1][i] = NAN; - data[2][i] = NAN; - data[3][i] = NAN; + data[1][i] = NAN; + data[2][i] = NAN; + data[3][i] = NAN; - data[4][i] = NAN; - data[5][i] = NAN; - data[6][i] = NAN; + data[4][i] = NAN; + data[5][i] = NAN; + data[6][i] = NAN; - data[7][i] = NAN; - data[8][i] = NAN; - data[9][i] = NAN; + data[7][i] = NAN; + data[8][i] = NAN; + data[9][i] = NAN; - data[10][i] = NAN; - data[11][i] = NAN; - data[12][i] = NAN; + data[10][i] = NAN; + data[11][i] = NAN; + data[12][i] = NAN; - data[13][i] = __int_as_float(0); - } + data[13][i] = __int_as_float(0); + } - memcpy(&pack.nodes[idx], data, sizeof(float4)*BVH_UNALIGNED_QNODE_SIZE); + memcpy(&pack.nodes[idx], data, sizeof(float4) * BVH_UNALIGNED_QNODE_SIZE); } /* Quad SIMD Nodes */ void BVH4::pack_nodes(const BVHNode *root) { - /* Calculate size of the arrays required. */ - 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_QNODE_SIZE) + - (num_inner_nodes - num_unaligned_nodes) * BVH_QNODE_SIZE; - } - else { - node_size = num_inner_nodes * BVH_QNODE_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_QNODE_LEAF_SIZE); - } - else { - pack.nodes.resize(node_size); - pack.leaf_nodes.resize(num_leaf_nodes*BVH_QNODE_LEAF_SIZE); - } - - int nextNodeIdx = 0, 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_QNODE_SIZE - : BVH_QNODE_SIZE; - } - - while(stack.size()) { - 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. */ - /* Collect nodes. */ - const BVHNode *children[4]; - const int num_children = e.node->num_children(); - /* Push entries on the stack. */ - for(int i = 0; i < num_children; ++i) { - int idx; - children[i] = e.node->get_child(i); - assert(children[i] != NULL); - if(children[i]->is_leaf()) { - idx = nextLeafNodeIdx++; - } - else { - idx = nextNodeIdx; - nextNodeIdx += children[i]->has_unaligned() - ? BVH_UNALIGNED_QNODE_SIZE - : BVH_QNODE_SIZE; - } - stack.push_back(BVHStackEntry(children[i], idx)); - } - /* Set node. */ - pack_inner(e, &stack[stack.size() - num_children], num_children); - } - } - - 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; + /* Calculate size of the arrays required. */ + 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_QNODE_SIZE) + + (num_inner_nodes - num_unaligned_nodes) * BVH_QNODE_SIZE; + } + else { + node_size = num_inner_nodes * BVH_QNODE_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_QNODE_LEAF_SIZE); + } + else { + pack.nodes.resize(node_size); + pack.leaf_nodes.resize(num_leaf_nodes * BVH_QNODE_LEAF_SIZE); + } + + int nextNodeIdx = 0, 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_QNODE_SIZE : BVH_QNODE_SIZE; + } + + while (stack.size()) { + 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. */ + /* Collect nodes. */ + const BVHNode *children[4]; + const int num_children = e.node->num_children(); + /* Push entries on the stack. */ + for (int i = 0; i < num_children; ++i) { + int idx; + children[i] = e.node->get_child(i); + assert(children[i] != NULL); + if (children[i]->is_leaf()) { + idx = nextLeafNodeIdx++; + } + else { + idx = nextNodeIdx; + nextNodeIdx += children[i]->has_unaligned() ? BVH_UNALIGNED_QNODE_SIZE : BVH_QNODE_SIZE; + } + stack.push_back(BVHStackEntry(children[i], idx)); + } + /* Set node. */ + pack_inner(e, &stack[stack.size() - num_children], num_children); + } + } + + 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 BVH4::refit_nodes() { - assert(!params.top_level); + assert(!params.top_level); - BoundBox bbox = BoundBox::empty; - uint visibility = 0; - refit_node(0, (pack.root_index == -1)? true: false, bbox, visibility); + BoundBox bbox = BoundBox::empty; + uint visibility = 0; + refit_node(0, (pack.root_index == -1) ? true : false, bbox, visibility); } -void BVH4::refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility) +void BVH4::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility) { - if(leaf) { - /* Refit leaf node. */ - int4 *data = &pack.leaf_nodes[idx]; - int4 c = data[0]; - - BVH::refit_primitives(c.x, c.y, bbox, visibility); - - /* TODO(sergey): This is actually a copy of pack_leaf(), - * but this chunk of code only knows actual data and has - * no idea about BVHNode. - * - * Would be nice to de-duplicate code, but trying to make - * making code more general ends up in much nastier code - * in my opinion so far. - * - * Same applies to the inner nodes case below. - */ - float4 leaf_data[BVH_QNODE_LEAF_SIZE]; - leaf_data[0].x = __int_as_float(c.x); - leaf_data[0].y = __int_as_float(c.y); - leaf_data[0].z = __uint_as_float(visibility); - leaf_data[0].w = __uint_as_float(c.w); - memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4)*BVH_QNODE_LEAF_SIZE); - } - else { - int4 *data = &pack.nodes[idx]; - bool is_unaligned = (data[0].x & PATH_RAY_NODE_UNALIGNED) != 0; - int4 c; - if(is_unaligned) { - c = data[13]; - } - else { - c = data[7]; - } - /* Refit inner node, set bbox from children. */ - BoundBox child_bbox[4] = {BoundBox::empty, - BoundBox::empty, - BoundBox::empty, - BoundBox::empty}; - uint child_visibility[4] = {0}; - int num_nodes = 0; - - for(int i = 0; i < 4; ++i) { - if(c[i] != 0) { - refit_node((c[i] < 0)? -c[i]-1: c[i], (c[i] < 0), - child_bbox[i], child_visibility[i]); - ++num_nodes; - bbox.grow(child_bbox[i]); - visibility |= child_visibility[i]; - } - } - - if(is_unaligned) { - Transform aligned_space[4] = {transform_identity(), - transform_identity(), - transform_identity(), - transform_identity()}; - pack_unaligned_node(idx, - aligned_space, - child_bbox, - &c[0], - visibility, - 0.0f, - 1.0f, - num_nodes); - } - else { - pack_aligned_node(idx, - child_bbox, - &c[0], - visibility, - 0.0f, - 1.0f, - num_nodes); - } - } + if (leaf) { + /* Refit leaf node. */ + int4 *data = &pack.leaf_nodes[idx]; + int4 c = data[0]; + + BVH::refit_primitives(c.x, c.y, bbox, visibility); + + /* TODO(sergey): This is actually a copy of pack_leaf(), + * but this chunk of code only knows actual data and has + * no idea about BVHNode. + * + * Would be nice to de-duplicate code, but trying to make + * making code more general ends up in much nastier code + * in my opinion so far. + * + * Same applies to the inner nodes case below. + */ + float4 leaf_data[BVH_QNODE_LEAF_SIZE]; + leaf_data[0].x = __int_as_float(c.x); + leaf_data[0].y = __int_as_float(c.y); + leaf_data[0].z = __uint_as_float(visibility); + leaf_data[0].w = __uint_as_float(c.w); + memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4) * BVH_QNODE_LEAF_SIZE); + } + else { + int4 *data = &pack.nodes[idx]; + bool is_unaligned = (data[0].x & PATH_RAY_NODE_UNALIGNED) != 0; + int4 c; + if (is_unaligned) { + c = data[13]; + } + else { + c = data[7]; + } + /* Refit inner node, set bbox from children. */ + BoundBox child_bbox[4] = {BoundBox::empty, BoundBox::empty, BoundBox::empty, BoundBox::empty}; + uint child_visibility[4] = {0}; + int num_nodes = 0; + + for (int i = 0; i < 4; ++i) { + if (c[i] != 0) { + refit_node((c[i] < 0) ? -c[i] - 1 : c[i], (c[i] < 0), child_bbox[i], child_visibility[i]); + ++num_nodes; + bbox.grow(child_bbox[i]); + visibility |= child_visibility[i]; + } + } + + if (is_unaligned) { + Transform aligned_space[4] = { + transform_identity(), transform_identity(), transform_identity(), transform_identity()}; + pack_unaligned_node( + idx, aligned_space, child_bbox, &c[0], visibility, 0.0f, 1.0f, num_nodes); + } + else { + pack_aligned_node(idx, child_bbox, &c[0], visibility, 0.0f, 1.0f, num_nodes); + } + } } CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh4.h b/intern/cycles/bvh/bvh4.h index caa0e2c8182..38b0961d3df 100644 --- a/intern/cycles/bvh/bvh4.h +++ b/intern/cycles/bvh/bvh4.h @@ -34,8 +34,8 @@ class LeafNode; class Object; class Progress; -#define BVH_QNODE_SIZE 8 -#define BVH_QNODE_LEAF_SIZE 1 +#define BVH_QNODE_SIZE 8 +#define BVH_QNODE_LEAF_SIZE 1 #define BVH_UNALIGNED_QNODE_SIZE 14 /* BVH4 @@ -43,48 +43,44 @@ class Progress; * Quad BVH, with each node having four children, to use with SIMD instructions. */ class BVH4 : public BVH { -protected: - /* constructor */ - friend class BVH; - BVH4(const BVHParams& params, const vector<Object*>& objects); + protected: + /* constructor */ + friend class BVH; + BVH4(const BVHParams ¶ms, const vector<Object *> &objects); - /* Building process. */ - virtual BVHNode *widen_children_nodes(const BVHNode *root) override; + /* Building process. */ + virtual BVHNode *widen_children_nodes(const BVHNode *root) override; - /* pack */ - void pack_nodes(const BVHNode *root) override; + /* pack */ + void pack_nodes(const BVHNode *root) override; - void pack_leaf(const BVHStackEntry& e, const LeafNode *leaf); - void pack_inner(const BVHStackEntry& e, const BVHStackEntry *en, int num); + void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf); + void pack_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num); - void pack_aligned_inner(const BVHStackEntry& e, - const BVHStackEntry *en, - int num); - void pack_aligned_node(int idx, - const BoundBox *bounds, - const int *child, - const uint visibility, - const float time_from, - const float time_to, - const int num); + void pack_aligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num); + void pack_aligned_node(int idx, + const BoundBox *bounds, + const int *child, + const uint visibility, + const float time_from, + const float time_to, + const int num); - void pack_unaligned_inner(const BVHStackEntry& e, - const BVHStackEntry *en, - int num); - void pack_unaligned_node(int idx, - const Transform *aligned_space, - const BoundBox *bounds, - const int *child, - const uint visibility, - const float time_from, - const float time_to, - const int num); + void pack_unaligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num); + void pack_unaligned_node(int idx, + const Transform *aligned_space, + const BoundBox *bounds, + const int *child, + const uint visibility, + const float time_from, + const float time_to, + const int num); - /* refit */ - void refit_nodes() override; - void refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility); + /* refit */ + void refit_nodes() override; + void refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility); }; CCL_NAMESPACE_END -#endif /* __BVH4_H__ */ +#endif /* __BVH4_H__ */ diff --git a/intern/cycles/bvh/bvh8.cpp b/intern/cycles/bvh/bvh8.cpp index af930b2f2df..e812d806b94 100644 --- a/intern/cycles/bvh/bvh8.cpp +++ b/intern/cycles/bvh/bvh8.cpp @@ -36,8 +36,7 @@ CCL_NAMESPACE_BEGIN -BVH8::BVH8(const BVHParams& params_, const vector<Object*>& objects_) -: BVH(params_, objects_) +BVH8::BVH8(const BVHParams ¶ms_, const vector<Object *> &objects_) : BVH(params_, objects_) { } @@ -45,159 +44,148 @@ namespace { BVHNode *bvh_node_merge_children_recursively(const BVHNode *node) { - if(node->is_leaf()) { - return new LeafNode(*reinterpret_cast<const LeafNode *>(node)); - } - /* Collect nodes of two layer deeper, allowing us to have more childrem in - * an inner layer. */ - assert(node->num_children() <= 2); - const BVHNode *children[8]; - const BVHNode *child0 = node->get_child(0); - const BVHNode *child1 = node->get_child(1); - int num_children = 0; - if(child0->is_leaf()) { - children[num_children++] = child0; - } - else { - const BVHNode *child00 = child0->get_child(0), - *child01 = child0->get_child(1); - if(child00->is_leaf()) { - children[num_children++] = child00; - } - else { - children[num_children++] = child00->get_child(0); - children[num_children++] = child00->get_child(1); - } - if(child01->is_leaf()) { - children[num_children++] = child01; - } - else { - children[num_children++] = child01->get_child(0); - children[num_children++] = child01->get_child(1); - } - } - if(child1->is_leaf()) { - children[num_children++] = child1; - } - else { - const BVHNode *child10 = child1->get_child(0), - *child11 = child1->get_child(1); - if(child10->is_leaf()) { - children[num_children++] = child10; - } - else { - children[num_children++] = child10->get_child(0); - children[num_children++] = child10->get_child(1); - } - if(child11->is_leaf()) { - children[num_children++] = child11; - } - else { - children[num_children++] = child11->get_child(0); - children[num_children++] = child11->get_child(1); - } - } - /* Merge children in subtrees. */ - BVHNode *children4[8]; - for(int i = 0; i < num_children; ++i) { - children4[i] = bvh_node_merge_children_recursively(children[i]); - } - /* Allocate new node. */ - BVHNode *node8 = new InnerNode(node->bounds, children4, num_children); - /* TODO(sergey): Consider doing this from the InnerNode() constructor. - * But in order to do this nicely need to think of how to pass all the - * parameters there. */ - if(node->is_unaligned) { - node8->is_unaligned = true; - node8->aligned_space = new Transform(); - *node8->aligned_space = *node->aligned_space; - } - return node8; + if (node->is_leaf()) { + return new LeafNode(*reinterpret_cast<const LeafNode *>(node)); + } + /* Collect nodes of two layer deeper, allowing us to have more childrem in + * an inner layer. */ + assert(node->num_children() <= 2); + const BVHNode *children[8]; + const BVHNode *child0 = node->get_child(0); + const BVHNode *child1 = node->get_child(1); + int num_children = 0; + if (child0->is_leaf()) { + children[num_children++] = child0; + } + else { + const BVHNode *child00 = child0->get_child(0), *child01 = child0->get_child(1); + if (child00->is_leaf()) { + children[num_children++] = child00; + } + else { + children[num_children++] = child00->get_child(0); + children[num_children++] = child00->get_child(1); + } + if (child01->is_leaf()) { + children[num_children++] = child01; + } + else { + children[num_children++] = child01->get_child(0); + children[num_children++] = child01->get_child(1); + } + } + if (child1->is_leaf()) { + children[num_children++] = child1; + } + else { + const BVHNode *child10 = child1->get_child(0), *child11 = child1->get_child(1); + if (child10->is_leaf()) { + children[num_children++] = child10; + } + else { + children[num_children++] = child10->get_child(0); + children[num_children++] = child10->get_child(1); + } + if (child11->is_leaf()) { + children[num_children++] = child11; + } + else { + children[num_children++] = child11->get_child(0); + children[num_children++] = child11->get_child(1); + } + } + /* Merge children in subtrees. */ + BVHNode *children4[8]; + for (int i = 0; i < num_children; ++i) { + children4[i] = bvh_node_merge_children_recursively(children[i]); + } + /* Allocate new node. */ + BVHNode *node8 = new InnerNode(node->bounds, children4, num_children); + /* TODO(sergey): Consider doing this from the InnerNode() constructor. + * But in order to do this nicely need to think of how to pass all the + * parameters there. */ + if (node->is_unaligned) { + node8->is_unaligned = true; + node8->aligned_space = new Transform(); + *node8->aligned_space = *node->aligned_space; + } + return node8; } } // namespace BVHNode *BVH8::widen_children_nodes(const BVHNode *root) { - if(root == NULL) { - return NULL; - } - if(root->is_leaf()) { - return const_cast<BVHNode *>(root); - } - BVHNode *root8 = bvh_node_merge_children_recursively(root); - /* TODO(sergey): Pack children nodes to parents which has less that 4 - * children. */ - return root8; + if (root == NULL) { + return NULL; + } + if (root->is_leaf()) { + return const_cast<BVHNode *>(root); + } + BVHNode *root8 = bvh_node_merge_children_recursively(root); + /* TODO(sergey): Pack children nodes to parents which has less that 4 + * children. */ + return root8; } -void BVH8::pack_leaf(const BVHStackEntry& e, const LeafNode *leaf) +void BVH8::pack_leaf(const BVHStackEntry &e, const LeafNode *leaf) { - float4 data[BVH_ONODE_LEAF_SIZE]; - memset(data, 0, sizeof(data)); - if(leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) { - /* object */ - data[0].x = __int_as_float(~(leaf->lo)); - data[0].y = __int_as_float(0); - } - else { - /* triangle */ - data[0].x = __int_as_float(leaf->lo); - data[0].y = __int_as_float(leaf->hi); - } - data[0].z = __uint_as_float(leaf->visibility); - if(leaf->num_triangles() != 0) { - data[0].w = __uint_as_float(pack.prim_type[leaf->lo]); - } - - memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4)*BVH_ONODE_LEAF_SIZE); + float4 data[BVH_ONODE_LEAF_SIZE]; + memset(data, 0, sizeof(data)); + if (leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) { + /* object */ + data[0].x = __int_as_float(~(leaf->lo)); + data[0].y = __int_as_float(0); + } + else { + /* triangle */ + data[0].x = __int_as_float(leaf->lo); + data[0].y = __int_as_float(leaf->hi); + } + data[0].z = __uint_as_float(leaf->visibility); + if (leaf->num_triangles() != 0) { + data[0].w = __uint_as_float(pack.prim_type[leaf->lo]); + } + + memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4) * BVH_ONODE_LEAF_SIZE); } -void BVH8::pack_inner(const BVHStackEntry& e, - const BVHStackEntry *en, - int num) +void BVH8::pack_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num) { - bool has_unaligned = false; - /* Check whether we have to create unaligned node or all nodes are aligned - * and we can cut some corner here. - */ - if(params.use_unaligned_nodes) { - for(int i = 0; i < num; i++) { - if(en[i].node->is_unaligned) { - has_unaligned = true; - break; - } - } - } - if(has_unaligned) { - /* There's no unaligned children, pack into AABB node. */ - pack_unaligned_inner(e, en, num); - } - else { - /* Create unaligned node with orientation transform for each of the - * children. - */ - pack_aligned_inner(e, en, num); - } + bool has_unaligned = false; + /* Check whether we have to create unaligned node or all nodes are aligned + * and we can cut some corner here. + */ + if (params.use_unaligned_nodes) { + for (int i = 0; i < num; i++) { + if (en[i].node->is_unaligned) { + has_unaligned = true; + break; + } + } + } + if (has_unaligned) { + /* There's no unaligned children, pack into AABB node. */ + pack_unaligned_inner(e, en, num); + } + else { + /* Create unaligned node with orientation transform for each of the + * children. + */ + pack_aligned_inner(e, en, num); + } } -void BVH8::pack_aligned_inner(const BVHStackEntry& e, - const BVHStackEntry *en, - int num) +void BVH8::pack_aligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num) { - BoundBox bounds[8]; - int child[8]; - for(int i = 0; i < num; ++i) { - bounds[i] = en[i].node->bounds; - child[i] = en[i].encodeIdx(); - } - pack_aligned_node(e.idx, - bounds, - child, - e.node->visibility, - e.node->time_from, - e.node->time_to, - num); + BoundBox bounds[8]; + int child[8]; + for (int i = 0; i < num; ++i) { + bounds[i] = en[i].node->bounds; + child[i] = en[i].encodeIdx(); + } + pack_aligned_node( + e.idx, bounds, child, e.node->visibility, e.node->time_from, e.node->time_to, num); } void BVH8::pack_aligned_node(int idx, @@ -208,66 +196,64 @@ void BVH8::pack_aligned_node(int idx, const float time_to, const int num) { - float8 data[8]; - memset(data, 0, sizeof(data)); + float8 data[8]; + memset(data, 0, sizeof(data)); - data[0].a = __uint_as_float(visibility & ~PATH_RAY_NODE_UNALIGNED); - data[0].b = time_from; - data[0].c = time_to; + data[0].a = __uint_as_float(visibility & ~PATH_RAY_NODE_UNALIGNED); + data[0].b = time_from; + data[0].c = time_to; - for(int i = 0; i < num; i++) { - float3 bb_min = bounds[i].min; - float3 bb_max = bounds[i].max; + for (int i = 0; i < num; i++) { + float3 bb_min = bounds[i].min; + float3 bb_max = bounds[i].max; - data[1][i] = bb_min.x; - data[2][i] = bb_max.x; - data[3][i] = bb_min.y; - data[4][i] = bb_max.y; - data[5][i] = bb_min.z; - data[6][i] = bb_max.z; + data[1][i] = bb_min.x; + data[2][i] = bb_max.x; + data[3][i] = bb_min.y; + data[4][i] = bb_max.y; + data[5][i] = bb_min.z; + data[6][i] = bb_max.z; - data[7][i] = __int_as_float(child[i]); - } + data[7][i] = __int_as_float(child[i]); + } - for(int i = num; i < 8; i++) { - /* We store BB which would never be recorded as intersection - * so kernel might safely assume there are always 4 child nodes. - */ - data[1][i] = FLT_MAX; - data[2][i] = -FLT_MAX; + for (int i = num; i < 8; i++) { + /* We store BB which would never be recorded as intersection + * so kernel might safely assume there are always 4 child nodes. + */ + data[1][i] = FLT_MAX; + data[2][i] = -FLT_MAX; - data[3][i] = FLT_MAX; - data[4][i] = -FLT_MAX; + data[3][i] = FLT_MAX; + data[4][i] = -FLT_MAX; - data[5][i] = FLT_MAX; - data[6][i] = -FLT_MAX; + data[5][i] = FLT_MAX; + data[6][i] = -FLT_MAX; - data[7][i] = __int_as_float(0); - } + data[7][i] = __int_as_float(0); + } - memcpy(&pack.nodes[idx], data, sizeof(float4)*BVH_ONODE_SIZE); + memcpy(&pack.nodes[idx], data, sizeof(float4) * BVH_ONODE_SIZE); } -void BVH8::pack_unaligned_inner(const BVHStackEntry& e, - const BVHStackEntry *en, - int num) +void BVH8::pack_unaligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num) { - Transform aligned_space[8]; - BoundBox bounds[8]; - int child[8]; - for(int i = 0; i < num; ++i) { - aligned_space[i] = en[i].node->get_aligned_space(); - bounds[i] = en[i].node->bounds; - child[i] = en[i].encodeIdx(); - } - pack_unaligned_node(e.idx, - aligned_space, - bounds, - child, - e.node->visibility, - e.node->time_from, - e.node->time_to, - num); + Transform aligned_space[8]; + BoundBox bounds[8]; + int child[8]; + for (int i = 0; i < num; ++i) { + aligned_space[i] = en[i].node->get_aligned_space(); + bounds[i] = en[i].node->bounds; + child[i] = en[i].encodeIdx(); + } + pack_unaligned_node(e.idx, + aligned_space, + bounds, + child, + e.node->visibility, + e.node->time_from, + e.node->time_to, + num); } void BVH8::pack_unaligned_node(int idx, @@ -279,283 +265,275 @@ void BVH8::pack_unaligned_node(int idx, const float time_to, const int num) { - float8 data[BVH_UNALIGNED_ONODE_SIZE]; - memset(data, 0, sizeof(data)); + float8 data[BVH_UNALIGNED_ONODE_SIZE]; + memset(data, 0, sizeof(data)); - data[0].a = __uint_as_float(visibility | PATH_RAY_NODE_UNALIGNED); - data[0].b = time_from; - data[0].c = time_to; + data[0].a = __uint_as_float(visibility | PATH_RAY_NODE_UNALIGNED); + data[0].b = time_from; + data[0].c = time_to; - for(int i = 0; i < num; i++) { - Transform space = BVHUnaligned::compute_node_transform( - bounds[i], - aligned_space[i]); + for (int i = 0; i < num; i++) { + Transform space = BVHUnaligned::compute_node_transform(bounds[i], aligned_space[i]); - data[1][i] = space.x.x; - data[2][i] = space.x.y; - data[3][i] = space.x.z; + data[1][i] = space.x.x; + data[2][i] = space.x.y; + data[3][i] = space.x.z; - data[4][i] = space.y.x; - data[5][i] = space.y.y; - data[6][i] = space.y.z; + data[4][i] = space.y.x; + data[5][i] = space.y.y; + data[6][i] = space.y.z; - data[7][i] = space.z.x; - data[8][i] = space.z.y; - data[9][i] = space.z.z; + data[7][i] = space.z.x; + data[8][i] = space.z.y; + data[9][i] = space.z.z; - data[10][i] = space.x.w; - data[11][i] = space.y.w; - data[12][i] = space.z.w; + data[10][i] = space.x.w; + data[11][i] = space.y.w; + data[12][i] = space.z.w; - data[13][i] = __int_as_float(child[i]); - } + data[13][i] = __int_as_float(child[i]); + } - for(int i = num; i < 8; i++) { - /* We store BB which would never be recorded as intersection - * so kernel might safely assume there are always 4 child nodes. - */ + for (int i = num; i < 8; i++) { + /* We store BB which would never be recorded as intersection + * so kernel might safely assume there are always 4 child nodes. + */ - data[1][i] = NAN; - data[2][i] = NAN; - data[3][i] = NAN; + data[1][i] = NAN; + data[2][i] = NAN; + data[3][i] = NAN; - data[4][i] = NAN; - data[5][i] = NAN; - data[6][i] = NAN; + data[4][i] = NAN; + data[5][i] = NAN; + data[6][i] = NAN; - data[7][i] = NAN; - data[8][i] = NAN; - data[9][i] = NAN; + data[7][i] = NAN; + data[8][i] = NAN; + data[9][i] = NAN; - data[10][i] = NAN; - data[11][i] = NAN; - data[12][i] = NAN; + data[10][i] = NAN; + data[11][i] = NAN; + data[12][i] = NAN; - data[13][i] = __int_as_float(0); - } + data[13][i] = __int_as_float(0); + } - memcpy(&pack.nodes[idx], data, sizeof(float4)*BVH_UNALIGNED_ONODE_SIZE); + memcpy(&pack.nodes[idx], data, sizeof(float4) * BVH_UNALIGNED_ONODE_SIZE); } /* Quad SIMD Nodes */ void BVH8::pack_nodes(const BVHNode *root) { - /* Calculate size of the arrays required. */ - 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_ONODE_SIZE) + - (num_inner_nodes - num_unaligned_nodes) * BVH_ONODE_SIZE; - } - else { - node_size = num_inner_nodes * BVH_ONODE_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_ONODE_LEAF_SIZE); - } - else { - pack.nodes.resize(node_size); - pack.leaf_nodes.resize(num_leaf_nodes*BVH_ONODE_LEAF_SIZE); - } - - int nextNodeIdx = 0, 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_ONODE_SIZE - : BVH_ONODE_SIZE; - } - - while(stack.size()) { - 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. */ - /* Collect nodes. */ - const BVHNode *children[8]; - int num_children = e.node->num_children(); - /* Push entries on the stack. */ - for(int i = 0; i < num_children; ++i) { - int idx; - children[i] = e.node->get_child(i); - if(children[i]->is_leaf()) { - idx = nextLeafNodeIdx++; - } - else { - idx = nextNodeIdx; - nextNodeIdx += children[i]->has_unaligned() - ? BVH_UNALIGNED_ONODE_SIZE - : BVH_ONODE_SIZE; - } - stack.push_back(BVHStackEntry(children[i], idx)); - } - /* Set node. */ - pack_inner(e, &stack[stack.size() - num_children], num_children); - } - } - - 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; + /* Calculate size of the arrays required. */ + 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_ONODE_SIZE) + + (num_inner_nodes - num_unaligned_nodes) * BVH_ONODE_SIZE; + } + else { + node_size = num_inner_nodes * BVH_ONODE_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_ONODE_LEAF_SIZE); + } + else { + pack.nodes.resize(node_size); + pack.leaf_nodes.resize(num_leaf_nodes * BVH_ONODE_LEAF_SIZE); + } + + int nextNodeIdx = 0, 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_ONODE_SIZE : BVH_ONODE_SIZE; + } + + while (stack.size()) { + 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. */ + /* Collect nodes. */ + const BVHNode *children[8]; + int num_children = e.node->num_children(); + /* Push entries on the stack. */ + for (int i = 0; i < num_children; ++i) { + int idx; + children[i] = e.node->get_child(i); + if (children[i]->is_leaf()) { + idx = nextLeafNodeIdx++; + } + else { + idx = nextNodeIdx; + nextNodeIdx += children[i]->has_unaligned() ? BVH_UNALIGNED_ONODE_SIZE : BVH_ONODE_SIZE; + } + stack.push_back(BVHStackEntry(children[i], idx)); + } + /* Set node. */ + pack_inner(e, &stack[stack.size() - num_children], num_children); + } + } + + 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 BVH8::refit_nodes() { - assert(!params.top_level); + assert(!params.top_level); - BoundBox bbox = BoundBox::empty; - uint visibility = 0; - refit_node(0, (pack.root_index == -1)? true: false, bbox, visibility); + BoundBox bbox = BoundBox::empty; + uint visibility = 0; + refit_node(0, (pack.root_index == -1) ? true : false, bbox, visibility); } -void BVH8::refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility) +void BVH8::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility) { - if(leaf) { - int4 *data = &pack.leaf_nodes[idx]; - int4 c = data[0]; - /* Refit leaf node. */ - for(int prim = c.x; prim < c.y; prim++) { - int pidx = pack.prim_index[prim]; - int tob = pack.prim_object[prim]; - Object *ob = objects[tob]; - - if(pidx == -1) { - /* Object instance. */ - bbox.grow(ob->bounds); - } - else { - /* Primitives. */ - const Mesh *mesh = ob->mesh; - - if(pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) { - /* Curves. */ - int str_offset = (params.top_level) ? mesh->curve_offset : 0; - Mesh::Curve curve = mesh->get_curve(pidx - str_offset); - int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]); - - curve.bounds_grow(k, &mesh->curve_keys[0], &mesh->curve_radius[0], bbox); - - visibility |= PATH_RAY_CURVE; - - /* Motion curves. */ - if(mesh->use_motion_blur) { - Attribute *attr = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - if(attr) { - size_t mesh_size = mesh->curve_keys.size(); - size_t steps = mesh->motion_steps - 1; - float3 *key_steps = attr->data_float3(); - - for(size_t i = 0; i < steps; i++) { - curve.bounds_grow(k, key_steps + i*mesh_size, &mesh->curve_radius[0], bbox); - } - } - } - } - else { - /* Triangles. */ - int tri_offset = (params.top_level) ? mesh->tri_offset : 0; - Mesh::Triangle triangle = mesh->get_triangle(pidx - tri_offset); - const float3 *vpos = &mesh->verts[0]; - - triangle.bounds_grow(vpos, bbox); - - /* Motion triangles. */ - if(mesh->use_motion_blur) { - Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - if(attr) { - size_t mesh_size = mesh->verts.size(); - size_t steps = mesh->motion_steps - 1; - float3 *vert_steps = attr->data_float3(); - - for(size_t i = 0; i < steps; i++) { - triangle.bounds_grow(vert_steps + i*mesh_size, bbox); - } - } - } - } - } - - visibility |= ob->visibility; - } - - float4 leaf_data[BVH_ONODE_LEAF_SIZE]; - leaf_data[0].x = __int_as_float(c.x); - leaf_data[0].y = __int_as_float(c.y); - leaf_data[0].z = __uint_as_float(visibility); - leaf_data[0].w = __uint_as_float(c.w); - memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4)*BVH_ONODE_LEAF_SIZE); - } - else { - float8 *data = (float8*)&pack.nodes[idx]; - bool is_unaligned = (__float_as_uint(data[0].a) & PATH_RAY_NODE_UNALIGNED) != 0; - /* Refit inner node, set bbox from children. */ - BoundBox child_bbox[8] = { BoundBox::empty, BoundBox::empty, - BoundBox::empty, BoundBox::empty, - BoundBox::empty, BoundBox::empty, - BoundBox::empty, BoundBox::empty }; - int child[8]; - uint child_visibility[8] = { 0 }; - int num_nodes = 0; - - for(int i = 0; i < 8; ++i) { - child[i] = __float_as_int(data[(is_unaligned) ? 13: 7][i]); - - if(child[i] != 0) { - refit_node((child[i] < 0)? -child[i]-1: child[i], (child[i] < 0), - child_bbox[i], child_visibility[i]); - ++num_nodes; - bbox.grow(child_bbox[i]); - visibility |= child_visibility[i]; - } - } - - if(is_unaligned) { - Transform aligned_space[8] = { transform_identity(), transform_identity(), - transform_identity(), transform_identity(), - transform_identity(), transform_identity(), - transform_identity(), transform_identity()}; - pack_unaligned_node(idx, - aligned_space, - child_bbox, - child, - visibility, - 0.0f, - 1.0f, - num_nodes); - } - else { - pack_aligned_node(idx, - child_bbox, - child, - visibility, - 0.0f, - 1.0f, - num_nodes); - } - } + if (leaf) { + int4 *data = &pack.leaf_nodes[idx]; + int4 c = data[0]; + /* Refit leaf node. */ + for (int prim = c.x; prim < c.y; prim++) { + int pidx = pack.prim_index[prim]; + int tob = pack.prim_object[prim]; + Object *ob = objects[tob]; + + if (pidx == -1) { + /* Object instance. */ + bbox.grow(ob->bounds); + } + else { + /* Primitives. */ + const Mesh *mesh = ob->mesh; + + if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) { + /* Curves. */ + int str_offset = (params.top_level) ? mesh->curve_offset : 0; + Mesh::Curve curve = mesh->get_curve(pidx - str_offset); + int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]); + + curve.bounds_grow(k, &mesh->curve_keys[0], &mesh->curve_radius[0], bbox); + + visibility |= PATH_RAY_CURVE; + + /* Motion curves. */ + if (mesh->use_motion_blur) { + Attribute *attr = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + + if (attr) { + size_t mesh_size = mesh->curve_keys.size(); + size_t steps = mesh->motion_steps - 1; + float3 *key_steps = attr->data_float3(); + + for (size_t i = 0; i < steps; i++) { + curve.bounds_grow(k, key_steps + i * mesh_size, &mesh->curve_radius[0], bbox); + } + } + } + } + else { + /* Triangles. */ + int tri_offset = (params.top_level) ? mesh->tri_offset : 0; + Mesh::Triangle triangle = mesh->get_triangle(pidx - tri_offset); + const float3 *vpos = &mesh->verts[0]; + + triangle.bounds_grow(vpos, bbox); + + /* Motion triangles. */ + if (mesh->use_motion_blur) { + Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + + if (attr) { + size_t mesh_size = mesh->verts.size(); + size_t steps = mesh->motion_steps - 1; + float3 *vert_steps = attr->data_float3(); + + for (size_t i = 0; i < steps; i++) { + triangle.bounds_grow(vert_steps + i * mesh_size, bbox); + } + } + } + } + } + + visibility |= ob->visibility; + } + + float4 leaf_data[BVH_ONODE_LEAF_SIZE]; + leaf_data[0].x = __int_as_float(c.x); + leaf_data[0].y = __int_as_float(c.y); + leaf_data[0].z = __uint_as_float(visibility); + leaf_data[0].w = __uint_as_float(c.w); + memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4) * BVH_ONODE_LEAF_SIZE); + } + else { + float8 *data = (float8 *)&pack.nodes[idx]; + bool is_unaligned = (__float_as_uint(data[0].a) & PATH_RAY_NODE_UNALIGNED) != 0; + /* Refit inner node, set bbox from children. */ + BoundBox child_bbox[8] = {BoundBox::empty, + BoundBox::empty, + BoundBox::empty, + BoundBox::empty, + BoundBox::empty, + BoundBox::empty, + BoundBox::empty, + BoundBox::empty}; + int child[8]; + uint child_visibility[8] = {0}; + int num_nodes = 0; + + for (int i = 0; i < 8; ++i) { + child[i] = __float_as_int(data[(is_unaligned) ? 13 : 7][i]); + + if (child[i] != 0) { + refit_node((child[i] < 0) ? -child[i] - 1 : child[i], + (child[i] < 0), + child_bbox[i], + child_visibility[i]); + ++num_nodes; + bbox.grow(child_bbox[i]); + visibility |= child_visibility[i]; + } + } + + if (is_unaligned) { + Transform aligned_space[8] = {transform_identity(), + transform_identity(), + transform_identity(), + transform_identity(), + transform_identity(), + transform_identity(), + transform_identity(), + transform_identity()}; + pack_unaligned_node( + idx, aligned_space, child_bbox, child, visibility, 0.0f, 1.0f, num_nodes); + } + else { + pack_aligned_node(idx, child_bbox, child, visibility, 0.0f, 1.0f, num_nodes); + } + } } CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh8.h b/intern/cycles/bvh/bvh8.h index 277e2f2d653..fc07eadcada 100644 --- a/intern/cycles/bvh/bvh8.h +++ b/intern/cycles/bvh/bvh8.h @@ -45,8 +45,8 @@ class LeafNode; class Object; class Progress; -#define BVH_ONODE_SIZE 16 -#define BVH_ONODE_LEAF_SIZE 1 +#define BVH_ONODE_SIZE 16 +#define BVH_ONODE_LEAF_SIZE 1 #define BVH_UNALIGNED_ONODE_SIZE 28 /* BVH8 @@ -54,48 +54,44 @@ class Progress; * Octo BVH, with each node having eight children, to use with SIMD instructions. */ class BVH8 : public BVH { -protected: - /* constructor */ - friend class BVH; - BVH8(const BVHParams& params, const vector<Object*>& objects); + protected: + /* constructor */ + friend class BVH; + BVH8(const BVHParams ¶ms, const vector<Object *> &objects); - /* Building process. */ - virtual BVHNode *widen_children_nodes(const BVHNode *root) override; + /* Building process. */ + virtual BVHNode *widen_children_nodes(const BVHNode *root) override; - /* pack */ - void pack_nodes(const BVHNode *root) override; + /* pack */ + void pack_nodes(const BVHNode *root) override; - void pack_leaf(const BVHStackEntry& e, const LeafNode *leaf); - void pack_inner(const BVHStackEntry& e, const BVHStackEntry *en, int num); + void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf); + void pack_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num); - void pack_aligned_inner(const BVHStackEntry& e, - const BVHStackEntry *en, - int num); - void pack_aligned_node(int idx, - const BoundBox *bounds, - const int *child, - const uint visibility, - const float time_from, - const float time_to, - const int num); + void pack_aligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num); + void pack_aligned_node(int idx, + const BoundBox *bounds, + const int *child, + const uint visibility, + const float time_from, + const float time_to, + const int num); - void pack_unaligned_inner(const BVHStackEntry& e, - const BVHStackEntry *en, - int num); - void pack_unaligned_node(int idx, - const Transform *aligned_space, - const BoundBox *bounds, - const int *child, - const uint visibility, - const float time_from, - const float time_to, - const int num); + void pack_unaligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num); + void pack_unaligned_node(int idx, + const Transform *aligned_space, + const BoundBox *bounds, + const int *child, + const uint visibility, + const float time_from, + const float time_to, + const int num); - /* refit */ - void refit_nodes() override; - void refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility); + /* refit */ + void refit_nodes() override; + void refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility); }; CCL_NAMESPACE_END -#endif /* __BVH8_H__ */ +#endif /* __BVH8_H__ */ diff --git a/intern/cycles/bvh/bvh_binning.cpp b/intern/cycles/bvh/bvh_binning.cpp index f574f31b358..d51143c578e 100644 --- a/intern/cycles/bvh/bvh_binning.cpp +++ b/intern/cycles/bvh/bvh_binning.cpp @@ -29,225 +29,265 @@ 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*/) { } +__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; } +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) +__forceinline int get_best_dimension(const float4 &bestSAH) { - // return (int)__bsf(movemask(reduce_min(bestSAH) == bestSAH)); + // return (int)__bsf(movemask(reduce_min(bestSAH) == bestSAH)); - float minSAH = min(bestSAH.x, min(bestSAH.y, bestSAH.z)); + float minSAH = min(bestSAH.x, min(bestSAH.y, bestSAH.z)); - if(bestSAH.x == minSAH) return 0; - else if(bestSAH.y == minSAH) return 1; - else return 2; + if (bestSAH.x == minSAH) + return 0; + else if (bestSAH.y == minSAH) + return 1; + else + return 2; } /* BVH Object Binning */ -BVHObjectBinning::BVHObjectBinning(const BVHRange& job, +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) + : BVHRange(job), + splitSAH(FLT_MAX), + dim(0), + pos(0), + unaligned_heuristic_(unaligned_heuristic), + aligned_space_(aligned_space) { - if(aligned_space_ == NULL) { - 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 = rcp(cent_bounds_.size()) * make_float3((float)num_bins); - - /* 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 */ - { - ssize_t i; - - for(i = 0; i < ssize_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]; - - BoundBox bounds0 = get_prim_bounds(prim0); - BoundBox bounds1 = get_prim_bounds(prim1); - - int4 bin0 = get_bin(bounds0); - int4 bin1 = get_bin(bounds1); - - /* increase bounds for bins for even primitive */ - int b00 = (int)extract<0>(bin0); bin_count[b00][0]++; bin_bounds[b00][0].grow(bounds0); - int b01 = (int)extract<1>(bin0); bin_count[b01][1]++; bin_bounds[b01][1].grow(bounds0); - int b02 = (int)extract<2>(bin0); bin_count[b02][2]++; bin_bounds[b02][2].grow(bounds0); - - /* increase bounds of bins for odd primitive */ - int b10 = (int)extract<0>(bin1); bin_count[b10][0]++; bin_bounds[b10][0].grow(bounds1); - int b11 = (int)extract<1>(bin1); bin_count[b11][1]++; bin_bounds[b11][1].grow(bounds1); - int b12 = (int)extract<2>(bin1); bin_count[b12][2]++; bin_bounds[b12][2].grow(bounds1); - } - - /* for uneven number of primitives */ - if(i < ssize_t(size())) { - /* map primitive to bin */ - const BVHReference& prim0 = prims[start() + i]; - BoundBox bounds0 = get_prim_bounds(prim0); - int4 bin0 = get_bin(bounds0); - - /* increase bounds of bins */ - int b00 = (int)extract<0>(bin0); bin_count[b00][0]++; bin_bounds[b00][0].grow(bounds0); - int b01 = (int)extract<1>(bin0); bin_count[b01][1]++; bin_bounds[b01][1].grow(bounds0); - 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]); float Ax = bx.half_area(); - by = merge(by,bin_bounds[i-1][1]); float Ay = by.half_area(); - bz = merge(bz,bin_bounds[i-1][2]); float Az = bz.half_area(); - - float4 lCount = blocks(count); - float4 lArea = make_float4(Ax,Ay,Az,Az); - float4 sah = lArea*lCount + r_area[i]*r_count[i]; - - bestSplit = select(sah < bestSAH,ii,bestSplit); - bestSAH = min(sah,bestSAH); - } - - int4 mask = float3_to_float4(cent_bounds_.size()) <= make_float4(0.0f); - 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()); + if (aligned_space_ == NULL) { + 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 = rcp(cent_bounds_.size()) * make_float3((float)num_bins); + + /* 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 */ + { + ssize_t i; + + for (i = 0; i < ssize_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]; + + BoundBox bounds0 = get_prim_bounds(prim0); + BoundBox bounds1 = get_prim_bounds(prim1); + + int4 bin0 = get_bin(bounds0); + int4 bin1 = get_bin(bounds1); + + /* increase bounds for bins for even primitive */ + int b00 = (int)extract<0>(bin0); + bin_count[b00][0]++; + bin_bounds[b00][0].grow(bounds0); + int b01 = (int)extract<1>(bin0); + bin_count[b01][1]++; + bin_bounds[b01][1].grow(bounds0); + int b02 = (int)extract<2>(bin0); + bin_count[b02][2]++; + bin_bounds[b02][2].grow(bounds0); + + /* increase bounds of bins for odd primitive */ + int b10 = (int)extract<0>(bin1); + bin_count[b10][0]++; + bin_bounds[b10][0].grow(bounds1); + int b11 = (int)extract<1>(bin1); + bin_count[b11][1]++; + bin_bounds[b11][1].grow(bounds1); + int b12 = (int)extract<2>(bin1); + bin_count[b12][2]++; + bin_bounds[b12][2].grow(bounds1); + } + + /* for uneven number of primitives */ + if (i < ssize_t(size())) { + /* map primitive to bin */ + const BVHReference &prim0 = prims[start() + i]; + BoundBox bounds0 = get_prim_bounds(prim0); + int4 bin0 = get_bin(bounds0); + + /* increase bounds of bins */ + int b00 = (int)extract<0>(bin0); + bin_count[b00][0]++; + bin_bounds[b00][0].grow(bounds0); + int b01 = (int)extract<1>(bin0); + bin_count[b01][1]++; + bin_bounds[b01][1].grow(bounds0); + 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]); + float Ax = bx.half_area(); + by = merge(by, bin_bounds[i - 1][1]); + float Ay = by.half_area(); + bz = merge(bz, bin_bounds[i - 1][2]); + float Az = bz.half_area(); + + float4 lCount = blocks(count); + float4 lArea = make_float4(Ax, Ay, Az, Az); + float4 sah = lArea * lCount + r_area[i] * r_count[i]; + + bestSplit = select(sah < bestSAH, ii, bestSplit); + bestSAH = min(sah, bestSAH); + } + + int4 mask = float3_to_float4(cent_bounds_.size()) <= make_float4(0.0f); + 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 +void BVHObjectBinning::split(BVHReference *prims, + BVHObjectBinning &left_o, + BVHObjectBinning &right_o) 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; - - ssize_t l = 0, r = N-1; - - while(l <= r) { - prefetch_L2(&prims[start() + l + 8]); - prefetch_L2(&prims[start() + r - 8]); - - BVHReference prim = prims[start() + l]; - BoundBox unaligned_bounds = get_prim_bounds(prim); - float3 unaligned_center = unaligned_bounds.center2(); - 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); + size_t N = size(); + + BoundBox lgeom_bounds = BoundBox::empty; + BoundBox rgeom_bounds = BoundBox::empty; + BoundBox lcent_bounds = BoundBox::empty; + BoundBox rcent_bounds = BoundBox::empty; + + ssize_t l = 0, r = N - 1; + + while (l <= r) { + prefetch_L2(&prims[start() + l + 8]); + prefetch_L2(&prims[start() + r - 8]); + + BVHReference prim = prims[start() + l]; + BoundBox unaligned_bounds = get_prim_bounds(prim); + float3 unaligned_center = unaligned_bounds.center2(); + 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 diff --git a/intern/cycles/bvh/bvh_binning.h b/intern/cycles/bvh/bvh_binning.h index c2e259b1696..ae6dba2805d 100644 --- a/intern/cycles/bvh/bvh_binning.h +++ b/intern/cycles/bvh/bvh_binning.h @@ -34,81 +34,82 @@ class BVHBuild; * 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 = NULL, - const Transform *aligned_space = NULL); - - 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 - { - int4 a = make_int4((box.center2() - cent_bounds_.min)*scale - make_float3(0.5f)); - int4 mn = make_int4(0); - 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(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_ == NULL) { - return prim.bounds(); - } - else { - return unaligned_heuristic_->compute_aligned_prim_boundbox( - prim, *aligned_space_); - } - } +class BVHObjectBinning : public BVHRange { + public: + __forceinline BVHObjectBinning() : leafSAH(FLT_MAX) + { + } + + BVHObjectBinning(const BVHRange &job, + BVHReference *prims, + const BVHUnaligned *unaligned_heuristic = NULL, + const Transform *aligned_space = NULL); + + 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 + { + int4 a = make_int4((box.center2() - cent_bounds_.min) * scale - make_float3(0.5f)); + int4 mn = make_int4(0); + 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(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_ == NULL) { + return prim.bounds(); + } + else { + return unaligned_heuristic_->compute_aligned_prim_boundbox(prim, *aligned_space_); + } + } }; CCL_NAMESPACE_END -#endif /* __BVH_BINNING_H__ */ +#endif /* __BVH_BINNING_H__ */ diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp index c0b3d683e37..1d9b006e8cb 100644 --- a/intern/cycles/bvh/bvh_build.cpp +++ b/intern/cycles/bvh/bvh_build.cpp @@ -41,72 +41,65 @@ CCL_NAMESPACE_BEGIN /* BVH Build Task */ class BVHBuildTask : public Task { -public: - BVHBuildTask(BVHBuild *build, - InnerNode *node, - int child, - const BVHObjectBinning& range, - int level) - : range_(range) - { - run = function_bind(&BVHBuild::thread_build_node, - build, - node, - child, - &range_, - level); - } -private: - BVHObjectBinning range_; + public: + BVHBuildTask( + BVHBuild *build, InnerNode *node, int child, const BVHObjectBinning &range, int level) + : range_(range) + { + run = function_bind(&BVHBuild::thread_build_node, build, node, child, &range_, level); + } + + private: + BVHObjectBinning range_; }; class BVHSpatialSplitBuildTask : public Task { -public: - 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); - } -private: - BVHRange range_; - vector<BVHReference> references_; + public: + 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); + } + + private: + BVHRange range_; + vector<BVHReference> references_; }; /* Constructor / Destructor */ -BVHBuild::BVHBuild(const vector<Object*>& objects_, - array<int>& prim_type_, - array<int>& prim_index_, - array<int>& prim_object_, - array<float2>& prim_time_, - const BVHParams& params_, - Progress& progress_) - : objects(objects_), - prim_type(prim_type_), - prim_index(prim_index_), - prim_object(prim_object_), - prim_time(prim_time_), - params(params_), - progress(progress_), - progress_start_time(0.0), - unaligned_heuristic(objects_) +BVHBuild::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_) + : objects(objects_), + prim_type(prim_type_), + prim_index(prim_index_), + prim_object(prim_object_), + prim_time(prim_time_), + params(params_), + progress(progress_), + progress_start_time(0.0), + unaligned_heuristic(objects_) { - spatial_min_overlap = 0.0f; + spatial_min_overlap = 0.0f; } BVHBuild::~BVHBuild() @@ -115,467 +108,440 @@ BVHBuild::~BVHBuild() /* Adding References */ -void BVHBuild::add_reference_triangles(BoundBox& root, BoundBox& center, Mesh *mesh, int i) +void BVHBuild::add_reference_triangles(BoundBox &root, BoundBox ¢er, Mesh *mesh, int i) { - const Attribute *attr_mP = NULL; - if(mesh->has_motion_blur()) { - attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - } - const size_t num_triangles = mesh->num_triangles(); - for(uint j = 0; j < num_triangles; j++) { - Mesh::Triangle t = mesh->get_triangle(j); - const float3 *verts = &mesh->verts[0]; - if(attr_mP == NULL) { - BoundBox bounds = BoundBox::empty; - t.bounds_grow(verts, bounds); - if(bounds.valid() && t.valid(verts)) { - references.push_back(BVHReference(bounds, - j, - i, - PRIMITIVE_TRIANGLE)); - root.grow(bounds); - center.grow(bounds.center2()); - } - } - else if(params.num_motion_triangle_steps == 0 || params.use_spatial_split) { - /* Motion triangles, simple case: single node for the whole - * primitive. Lowest memory footprint and faster BVH build but - * least optimal ray-tracing. - */ - /* TODO(sergey): Support motion steps for spatially split BVH. */ - const size_t num_verts = mesh->verts.size(); - const size_t num_steps = mesh->motion_steps; - const float3 *vert_steps = attr_mP->data_float3(); - BoundBox bounds = BoundBox::empty; - t.bounds_grow(verts, bounds); - for(size_t step = 0; step < num_steps - 1; step++) { - t.bounds_grow(vert_steps + step*num_verts, bounds); - } - if(bounds.valid()) { - references.push_back( - BVHReference(bounds, - j, - i, - PRIMITIVE_MOTION_TRIANGLE)); - root.grow(bounds); - center.grow(bounds.center2()); - } - } - else { - /* Motion triangles, trace optimized case: we split triangle - * primitives into separate nodes for each of the time steps. - * This way we minimize overlap of neighbor curve primitives. - */ - const int num_bvh_steps = params.num_motion_curve_steps * 2 + 1; - const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1); - const size_t num_verts = mesh->verts.size(); - const size_t num_steps = mesh->motion_steps; - const float3 *vert_steps = attr_mP->data_float3(); - /* Calculate bounding box of the previous time step. - * Will be reused later to avoid duplicated work on - * calculating BVH time step boundbox. - */ - float3 prev_verts[3]; - t.motion_verts(verts, - vert_steps, - num_verts, - num_steps, - 0.0f, - prev_verts); - BoundBox prev_bounds = BoundBox::empty; - prev_bounds.grow(prev_verts[0]); - prev_bounds.grow(prev_verts[1]); - prev_bounds.grow(prev_verts[2]); - /* Create all primitive time steps, */ - for(int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) { - const float curr_time = (float)(bvh_step) * num_bvh_steps_inv_1; - float3 curr_verts[3]; - t.motion_verts(verts, - vert_steps, - num_verts, - num_steps, - curr_time, - curr_verts); - BoundBox curr_bounds = BoundBox::empty; - curr_bounds.grow(curr_verts[0]); - curr_bounds.grow(curr_verts[1]); - curr_bounds.grow(curr_verts[2]); - BoundBox bounds = prev_bounds; - bounds.grow(curr_bounds); - if(bounds.valid()) { - const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1; - references.push_back( - BVHReference(bounds, - j, - i, - PRIMITIVE_MOTION_TRIANGLE, - prev_time, - curr_time)); - root.grow(bounds); - center.grow(bounds.center2()); - } - /* Current time boundbox becomes previous one for the - * next time step. - */ - prev_bounds = curr_bounds; - } - } - } + const Attribute *attr_mP = NULL; + if (mesh->has_motion_blur()) { + attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + } + const size_t num_triangles = mesh->num_triangles(); + for (uint j = 0; j < num_triangles; j++) { + Mesh::Triangle t = mesh->get_triangle(j); + const float3 *verts = &mesh->verts[0]; + if (attr_mP == NULL) { + BoundBox bounds = BoundBox::empty; + t.bounds_grow(verts, bounds); + if (bounds.valid() && t.valid(verts)) { + references.push_back(BVHReference(bounds, j, i, PRIMITIVE_TRIANGLE)); + root.grow(bounds); + center.grow(bounds.center2()); + } + } + else if (params.num_motion_triangle_steps == 0 || params.use_spatial_split) { + /* Motion triangles, simple case: single node for the whole + * primitive. Lowest memory footprint and faster BVH build but + * least optimal ray-tracing. + */ + /* TODO(sergey): Support motion steps for spatially split BVH. */ + const size_t num_verts = mesh->verts.size(); + const size_t num_steps = mesh->motion_steps; + const float3 *vert_steps = attr_mP->data_float3(); + BoundBox bounds = BoundBox::empty; + t.bounds_grow(verts, bounds); + for (size_t step = 0; step < num_steps - 1; step++) { + t.bounds_grow(vert_steps + step * num_verts, bounds); + } + if (bounds.valid()) { + references.push_back(BVHReference(bounds, j, i, PRIMITIVE_MOTION_TRIANGLE)); + root.grow(bounds); + center.grow(bounds.center2()); + } + } + else { + /* Motion triangles, trace optimized case: we split triangle + * primitives into separate nodes for each of the time steps. + * This way we minimize overlap of neighbor curve primitives. + */ + const int num_bvh_steps = params.num_motion_curve_steps * 2 + 1; + const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1); + const size_t num_verts = mesh->verts.size(); + const size_t num_steps = mesh->motion_steps; + const float3 *vert_steps = attr_mP->data_float3(); + /* Calculate bounding box of the previous time step. + * Will be reused later to avoid duplicated work on + * calculating BVH time step boundbox. + */ + float3 prev_verts[3]; + t.motion_verts(verts, vert_steps, num_verts, num_steps, 0.0f, prev_verts); + BoundBox prev_bounds = BoundBox::empty; + prev_bounds.grow(prev_verts[0]); + prev_bounds.grow(prev_verts[1]); + prev_bounds.grow(prev_verts[2]); + /* Create all primitive time steps, */ + for (int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) { + const float curr_time = (float)(bvh_step)*num_bvh_steps_inv_1; + float3 curr_verts[3]; + t.motion_verts(verts, vert_steps, num_verts, num_steps, curr_time, curr_verts); + BoundBox curr_bounds = BoundBox::empty; + curr_bounds.grow(curr_verts[0]); + curr_bounds.grow(curr_verts[1]); + curr_bounds.grow(curr_verts[2]); + BoundBox bounds = prev_bounds; + bounds.grow(curr_bounds); + if (bounds.valid()) { + const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1; + references.push_back( + BVHReference(bounds, j, i, PRIMITIVE_MOTION_TRIANGLE, prev_time, curr_time)); + root.grow(bounds); + center.grow(bounds.center2()); + } + /* Current time boundbox becomes previous one for the + * next time step. + */ + prev_bounds = curr_bounds; + } + } + } } -void BVHBuild::add_reference_curves(BoundBox& root, BoundBox& center, Mesh *mesh, int i) +void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Mesh *mesh, int i) { - const Attribute *curve_attr_mP = NULL; - if(mesh->has_motion_blur()) { - curve_attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - } - const size_t num_curves = mesh->num_curves(); - for(uint j = 0; j < num_curves; j++) { - const Mesh::Curve curve = mesh->get_curve(j); - const float *curve_radius = &mesh->curve_radius[0]; - for(int k = 0; k < curve.num_keys - 1; k++) { - if(curve_attr_mP == NULL) { - /* Really simple logic for static hair. */ - BoundBox bounds = BoundBox::empty; - curve.bounds_grow(k, &mesh->curve_keys[0], curve_radius, bounds); - if(bounds.valid()) { - int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_CURVE, k); - references.push_back(BVHReference(bounds, j, i, packed_type)); - root.grow(bounds); - center.grow(bounds.center2()); - } - } - else if(params.num_motion_curve_steps == 0 || params.use_spatial_split) { - /* Simple case of motion curves: single node for the while - * shutter time. Lowest memory usage but less optimal - * rendering. - */ - /* TODO(sergey): Support motion steps for spatially split BVH. */ - BoundBox bounds = BoundBox::empty; - curve.bounds_grow(k, &mesh->curve_keys[0], curve_radius, bounds); - const size_t num_keys = mesh->curve_keys.size(); - const size_t num_steps = mesh->motion_steps; - const float3 *key_steps = curve_attr_mP->data_float3(); - for(size_t step = 0; step < num_steps - 1; step++) { - curve.bounds_grow(k, - key_steps + step*num_keys, - curve_radius, - bounds); - } - if(bounds.valid()) { - int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_MOTION_CURVE, k); - references.push_back(BVHReference(bounds, - j, - i, - packed_type)); - root.grow(bounds); - center.grow(bounds.center2()); - } - } - else { - /* Motion curves, trace optimized case: we split curve keys - * primitives into separate nodes for each of the time steps. - * This way we minimize overlap of neighbor curve primitives. - */ - const int num_bvh_steps = params.num_motion_curve_steps * 2 + 1; - const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1); - const size_t num_steps = mesh->motion_steps; - const float3 *curve_keys = &mesh->curve_keys[0]; - const float3 *key_steps = curve_attr_mP->data_float3(); - const size_t num_keys = mesh->curve_keys.size(); - /* Calculate bounding box of the previous time step. - * Will be reused later to avoid duplicated work on - * calculating BVH time step boundbox. - */ - float4 prev_keys[4]; - curve.cardinal_motion_keys(curve_keys, - curve_radius, - key_steps, - num_keys, - num_steps, - 0.0f, - k - 1, k, k + 1, k + 2, - prev_keys); - BoundBox prev_bounds = BoundBox::empty; - curve.bounds_grow(prev_keys, prev_bounds); - /* Create all primitive time steps, */ - for(int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) { - const float curr_time = (float)(bvh_step) * num_bvh_steps_inv_1; - float4 curr_keys[4]; - curve.cardinal_motion_keys(curve_keys, - curve_radius, - key_steps, - num_keys, - num_steps, - curr_time, - k - 1, k, k + 1, k + 2, - curr_keys); - BoundBox curr_bounds = BoundBox::empty; - curve.bounds_grow(curr_keys, curr_bounds); - BoundBox bounds = prev_bounds; - bounds.grow(curr_bounds); - if(bounds.valid()) { - const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1; - int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_MOTION_CURVE, k); - references.push_back(BVHReference(bounds, - j, - i, - packed_type, - prev_time, - curr_time)); - root.grow(bounds); - center.grow(bounds.center2()); - } - /* Current time boundbox becomes previous one for the - * next time step. - */ - prev_bounds = curr_bounds; - } - } - } - } + const Attribute *curve_attr_mP = NULL; + if (mesh->has_motion_blur()) { + curve_attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + } + const size_t num_curves = mesh->num_curves(); + for (uint j = 0; j < num_curves; j++) { + const Mesh::Curve curve = mesh->get_curve(j); + const float *curve_radius = &mesh->curve_radius[0]; + for (int k = 0; k < curve.num_keys - 1; k++) { + if (curve_attr_mP == NULL) { + /* Really simple logic for static hair. */ + BoundBox bounds = BoundBox::empty; + curve.bounds_grow(k, &mesh->curve_keys[0], curve_radius, bounds); + if (bounds.valid()) { + int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_CURVE, k); + references.push_back(BVHReference(bounds, j, i, packed_type)); + root.grow(bounds); + center.grow(bounds.center2()); + } + } + else if (params.num_motion_curve_steps == 0 || params.use_spatial_split) { + /* Simple case of motion curves: single node for the while + * shutter time. Lowest memory usage but less optimal + * rendering. + */ + /* TODO(sergey): Support motion steps for spatially split BVH. */ + BoundBox bounds = BoundBox::empty; + curve.bounds_grow(k, &mesh->curve_keys[0], curve_radius, bounds); + const size_t num_keys = mesh->curve_keys.size(); + const size_t num_steps = mesh->motion_steps; + const float3 *key_steps = curve_attr_mP->data_float3(); + for (size_t step = 0; step < num_steps - 1; step++) { + curve.bounds_grow(k, key_steps + step * num_keys, curve_radius, bounds); + } + if (bounds.valid()) { + int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_MOTION_CURVE, k); + references.push_back(BVHReference(bounds, j, i, packed_type)); + root.grow(bounds); + center.grow(bounds.center2()); + } + } + else { + /* Motion curves, trace optimized case: we split curve keys + * primitives into separate nodes for each of the time steps. + * This way we minimize overlap of neighbor curve primitives. + */ + const int num_bvh_steps = params.num_motion_curve_steps * 2 + 1; + const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1); + const size_t num_steps = mesh->motion_steps; + const float3 *curve_keys = &mesh->curve_keys[0]; + const float3 *key_steps = curve_attr_mP->data_float3(); + const size_t num_keys = mesh->curve_keys.size(); + /* Calculate bounding box of the previous time step. + * Will be reused later to avoid duplicated work on + * calculating BVH time step boundbox. + */ + float4 prev_keys[4]; + curve.cardinal_motion_keys(curve_keys, + curve_radius, + key_steps, + num_keys, + num_steps, + 0.0f, + k - 1, + k, + k + 1, + k + 2, + prev_keys); + BoundBox prev_bounds = BoundBox::empty; + curve.bounds_grow(prev_keys, prev_bounds); + /* Create all primitive time steps, */ + for (int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) { + const float curr_time = (float)(bvh_step)*num_bvh_steps_inv_1; + float4 curr_keys[4]; + curve.cardinal_motion_keys(curve_keys, + curve_radius, + key_steps, + num_keys, + num_steps, + curr_time, + k - 1, + k, + k + 1, + k + 2, + curr_keys); + BoundBox curr_bounds = BoundBox::empty; + curve.bounds_grow(curr_keys, curr_bounds); + BoundBox bounds = prev_bounds; + bounds.grow(curr_bounds); + if (bounds.valid()) { + const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1; + int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_MOTION_CURVE, k); + references.push_back(BVHReference(bounds, j, i, packed_type, prev_time, curr_time)); + root.grow(bounds); + center.grow(bounds.center2()); + } + /* Current time boundbox becomes previous one for the + * next time step. + */ + prev_bounds = curr_bounds; + } + } + } + } } -void BVHBuild::add_reference_mesh(BoundBox& root, BoundBox& center, Mesh *mesh, int i) +void BVHBuild::add_reference_mesh(BoundBox &root, BoundBox ¢er, Mesh *mesh, int i) { - if(params.primitive_mask & PRIMITIVE_ALL_TRIANGLE) { - add_reference_triangles(root, center, mesh, i); - } - if(params.primitive_mask & PRIMITIVE_ALL_CURVE) { - add_reference_curves(root, center, mesh, i); - } + if (params.primitive_mask & PRIMITIVE_ALL_TRIANGLE) { + add_reference_triangles(root, center, mesh, i); + } + if (params.primitive_mask & PRIMITIVE_ALL_CURVE) { + add_reference_curves(root, center, mesh, i); + } } -void BVHBuild::add_reference_object(BoundBox& root, BoundBox& center, Object *ob, int i) +void BVHBuild::add_reference_object(BoundBox &root, BoundBox ¢er, Object *ob, int i) { - references.push_back(BVHReference(ob->bounds, -1, i, 0)); - root.grow(ob->bounds); - center.grow(ob->bounds.center2()); + references.push_back(BVHReference(ob->bounds, -1, i, 0)); + root.grow(ob->bounds); + center.grow(ob->bounds.center2()); } static size_t count_curve_segments(Mesh *mesh) { - size_t num = 0, num_curves = mesh->num_curves(); + size_t num = 0, num_curves = mesh->num_curves(); - for(size_t i = 0; i < num_curves; i++) - num += mesh->get_curve(i).num_keys - 1; + for (size_t i = 0; i < num_curves; i++) + num += mesh->get_curve(i).num_keys - 1; - return num; + return num; } -void BVHBuild::add_references(BVHRange& root) +void BVHBuild::add_references(BVHRange &root) { - /* reserve space for references */ - size_t num_alloc_references = 0; - - foreach(Object *ob, objects) { - if(params.top_level) { - if(!ob->is_traceable()) { - continue; - } - if(!ob->mesh->is_instanced()) { - if(params.primitive_mask & PRIMITIVE_ALL_TRIANGLE) { - num_alloc_references += ob->mesh->num_triangles(); - } - if(params.primitive_mask & PRIMITIVE_ALL_CURVE) { - num_alloc_references += count_curve_segments(ob->mesh); - } - } - else - num_alloc_references++; - } - else { - if(params.primitive_mask & PRIMITIVE_ALL_TRIANGLE) { - num_alloc_references += ob->mesh->num_triangles(); - } - if(params.primitive_mask & PRIMITIVE_ALL_CURVE) { - num_alloc_references += count_curve_segments(ob->mesh); - } - } - } - - references.reserve(num_alloc_references); - - /* add references from objects */ - BoundBox bounds = BoundBox::empty, center = BoundBox::empty; - int i = 0; - - foreach(Object *ob, objects) { - if(params.top_level) { - if(!ob->is_traceable()) { - ++i; - continue; - } - if(!ob->mesh->is_instanced()) - add_reference_mesh(bounds, center, ob->mesh, i); - else - add_reference_object(bounds, center, ob, i); - } - else - add_reference_mesh(bounds, center, ob->mesh, i); - - i++; - - if(progress.get_cancel()) return; - } - - /* happens mostly on empty meshes */ - if(!bounds.valid()) - bounds.grow(make_float3(0.0f, 0.0f, 0.0f)); - - root = BVHRange(bounds, center, 0, references.size()); + /* reserve space for references */ + size_t num_alloc_references = 0; + + foreach (Object *ob, objects) { + if (params.top_level) { + if (!ob->is_traceable()) { + continue; + } + if (!ob->mesh->is_instanced()) { + if (params.primitive_mask & PRIMITIVE_ALL_TRIANGLE) { + num_alloc_references += ob->mesh->num_triangles(); + } + if (params.primitive_mask & PRIMITIVE_ALL_CURVE) { + num_alloc_references += count_curve_segments(ob->mesh); + } + } + else + num_alloc_references++; + } + else { + if (params.primitive_mask & PRIMITIVE_ALL_TRIANGLE) { + num_alloc_references += ob->mesh->num_triangles(); + } + if (params.primitive_mask & PRIMITIVE_ALL_CURVE) { + num_alloc_references += count_curve_segments(ob->mesh); + } + } + } + + references.reserve(num_alloc_references); + + /* add references from objects */ + BoundBox bounds = BoundBox::empty, center = BoundBox::empty; + int i = 0; + + foreach (Object *ob, objects) { + if (params.top_level) { + if (!ob->is_traceable()) { + ++i; + continue; + } + if (!ob->mesh->is_instanced()) + add_reference_mesh(bounds, center, ob->mesh, i); + else + add_reference_object(bounds, center, ob, i); + } + else + add_reference_mesh(bounds, center, ob->mesh, i); + + i++; + + if (progress.get_cancel()) + return; + } + + /* happens mostly on empty meshes */ + if (!bounds.valid()) + bounds.grow(make_float3(0.0f, 0.0f, 0.0f)); + + root = BVHRange(bounds, center, 0, references.size()); } /* Build */ -BVHNode* BVHBuild::run() +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); - } - spatial_free_index = 0; - - need_prim_time = params.num_motion_curve_steps > 0 || - params.num_motion_triangle_steps > 0; - - /* init progress updates */ - double build_start_time; - build_start_time = progress_start_time = time_dt(); - progress_count = 0; - progress_total = references.size(); - progress_original_total = progress_total; - - prim_type.resize(references.size()); - prim_index.resize(references.size()); - prim_object.resize(references.size()); - if(need_prim_time) { - prim_time.resize(references.size()); - } - else { - prim_time.resize(0); - } - - /* build recursively */ - BVHNode *rootnode; - - if(params.use_spatial_split) { - /* Perform multithreaded spatial split build. */ - rootnode = build_node(root, &references, 0, 0); - task_pool.wait_work(); - } - else { - /* Perform 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; - VLOG(1) << "BVH build cancelled."; - } - else { - /*rotate(rootnode, 4, 5);*/ - rootnode->update_visibility(); - rootnode->update_time(); - } - if(rootnode != NULL) { - VLOG(1) << "BVH build statistics:\n" - << " Build time: " << time_dt() - build_start_time << "\n" - << " Total number of nodes: " - << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_NODE_COUNT)) << "\n" - << " Number of inner nodes: " - << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_INNER_COUNT)) << "\n" - << " Number of leaf nodes: " - << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_LEAF_COUNT)) << "\n" - << " Number of unaligned nodes: " - << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_UNALIGNED_COUNT)) << "\n" - << " Allocation slop factor: " - << ((prim_type.capacity() != 0) - ? (float)prim_type.size() / prim_type.capacity() - : 1.0f) << "\n" - << " Maximum depth: " - << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_DEPTH)) << "\n"; - } - } - - - return rootnode; + 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); + } + spatial_free_index = 0; + + need_prim_time = params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0; + + /* init progress updates */ + double build_start_time; + build_start_time = progress_start_time = time_dt(); + progress_count = 0; + progress_total = references.size(); + progress_original_total = progress_total; + + prim_type.resize(references.size()); + prim_index.resize(references.size()); + prim_object.resize(references.size()); + if (need_prim_time) { + prim_time.resize(references.size()); + } + else { + prim_time.resize(0); + } + + /* build recursively */ + BVHNode *rootnode; + + if (params.use_spatial_split) { + /* Perform multithreaded spatial split build. */ + rootnode = build_node(root, &references, 0, 0); + task_pool.wait_work(); + } + else { + /* Perform 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; + VLOG(1) << "BVH build cancelled."; + } + else { + /*rotate(rootnode, 4, 5);*/ + rootnode->update_visibility(); + rootnode->update_time(); + } + if (rootnode != NULL) { + VLOG(1) << "BVH build statistics:\n" + << " Build time: " << time_dt() - build_start_time << "\n" + << " Total number of nodes: " + << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_NODE_COUNT)) + << "\n" + << " Number of inner nodes: " + << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_INNER_COUNT)) + << "\n" + << " Number of leaf nodes: " + << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_LEAF_COUNT)) + << "\n" + << " Number of unaligned nodes: " + << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_UNALIGNED_COUNT)) + << "\n" + << " Allocation slop factor: " + << ((prim_type.capacity() != 0) ? (float)prim_type.size() / prim_type.capacity() : + 1.0f) + << "\n" + << " Maximum depth: " + << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_DEPTH)) << "\n"; + } + } + + return rootnode; } void BVHBuild::progress_update() { - if(time_dt() - progress_start_time < 0.25) - return; + if (time_dt() - progress_start_time < 0.25) + return; - double progress_start = (double)progress_count/(double)progress_total; - double duplicates = (double)(progress_total - progress_original_total)/(double)progress_total; + double progress_start = (double)progress_count / (double)progress_total; + double duplicates = (double)(progress_total - progress_original_total) / (double)progress_total; - string msg = string_printf("Building BVH %.0f%%, duplicates %.0f%%", - progress_start * 100.0, duplicates * 100.0); + string msg = string_printf( + "Building BVH %.0f%%, duplicates %.0f%%", progress_start * 100.0, duplicates * 100.0); - progress.set_substatus(msg); - progress_start_time = time_dt(); + progress.set_substatus(msg); + progress_start_time = time_dt(); } -void BVHBuild::thread_build_node(InnerNode *inner, - int child, - BVHObjectBinning *range, - int level) +void BVHBuild::thread_build_node(InnerNode *inner, int child, BVHObjectBinning *range, int level) { - if(progress.get_cancel()) - return; + if (progress.get_cancel()) + return; - /* build nodes */ - BVHNode *node = build_node(*range, level); + /* build nodes */ + BVHNode *node = build_node(*range, level); - /* set child in inner node */ - inner->children[child] = node; + /* set child in inner node */ + inner->children[child] = node; - /* update progress */ - if(range->size() < THREAD_TASK_SIZE) { - /*rotate(node, INT_MAX, 5);*/ + /* update progress */ + if (range->size() < THREAD_TASK_SIZE) { + /*rotate(node, INT_MAX, 5);*/ - thread_scoped_lock lock(build_mutex); + thread_scoped_lock lock(build_mutex); - progress_count += range->size(); - progress_update(); - } + progress_count += range->size(); + progress_update(); + } } void BVHBuild::thread_build_spatial_split_node(InnerNode *inner, @@ -585,567 +551,528 @@ void BVHBuild::thread_build_spatial_split_node(InnerNode *inner, int level, int thread_id) { - if(progress.get_cancel()) { - return; - } + if (progress.get_cancel()) { + return; + } - /* build nodes */ - BVHNode *node = build_node(*range, references, level, thread_id); + /* build nodes */ + BVHNode *node = build_node(*range, references, level, thread_id); - /* set child in inner node */ - inner->children[child] = node; + /* set child in inner node */ + inner->children[child] = node; } -bool BVHBuild::range_within_max_leaf_size(const BVHRange& range, - const vector<BVHReference>& references) const +bool BVHBuild::range_within_max_leaf_size(const BVHRange &range, + const vector<BVHReference> &references) const { - 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_motion_triangles = 0; - size_t num_curves = 0; - size_t num_motion_curves = 0; - - for(int i = 0; i < size; i++) { - const BVHReference& ref = references[range.start() + i]; - - if(ref.prim_type() & PRIMITIVE_CURVE) - num_curves++; - if(ref.prim_type() & PRIMITIVE_MOTION_CURVE) - num_motion_curves++; - else if(ref.prim_type() & PRIMITIVE_TRIANGLE) - num_triangles++; - else if(ref.prim_type() & PRIMITIVE_MOTION_TRIANGLE) - num_motion_triangles++; - } - - return (num_triangles <= params.max_triangle_leaf_size) && - (num_motion_triangles <= params.max_motion_triangle_leaf_size) && - (num_curves <= params.max_curve_leaf_size) && - (num_motion_curves <= params.max_motion_curve_leaf_size); + 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_motion_triangles = 0; + size_t num_curves = 0; + size_t num_motion_curves = 0; + + for (int i = 0; i < size; i++) { + const BVHReference &ref = references[range.start() + i]; + + if (ref.prim_type() & PRIMITIVE_CURVE) + num_curves++; + if (ref.prim_type() & PRIMITIVE_MOTION_CURVE) + num_motion_curves++; + else if (ref.prim_type() & PRIMITIVE_TRIANGLE) + num_triangles++; + else if (ref.prim_type() & PRIMITIVE_MOTION_TRIANGLE) + num_motion_triangles++; + } + + return (num_triangles <= params.max_triangle_leaf_size) && + (num_motion_triangles <= params.max_motion_triangle_leaf_size) && + (num_curves <= params.max_curve_leaf_size) && + (num_motion_curves <= params.max_motion_curve_leaf_size); } /* multithreaded binning builder */ -BVHNode* BVHBuild::build_node(const BVHObjectBinning& range, int level) +BVHNode *BVHBuild::build_node(const BVHObjectBinning &range, int level) { - size_t size = range.size(); - float leafSAH = params.sah_primitive_cost * range.leafSAH; - float splitSAH = params.sah_node_cost * range.bounds().half_area() + params.sah_primitive_cost * range.splitSAH; - - /* Have at least one inner node on top level, for performance and correct - * visibility tests, since object instances do not check visibility flag. - */ - if(!(range.size() > 0 && params.top_level && level == 0)) { - /* Make leaf node when threshold reached or SAH tells us. */ - if((params.small_enough_for_leaf(size, level)) || - (range_within_max_leaf_size(range, references) && leafSAH < splitSAH)) - { - return create_leaf_node(range, references); - } - } - - BVHObjectBinning unaligned_range; - float unalignedSplitSAH = FLT_MAX; - float unalignedLeafSAH = FLT_MAX; - Transform aligned_space; - bool do_unalinged_split = false; - if(params.use_unaligned_nodes && - splitSAH > params.unaligned_split_threshold*leafSAH) - { - aligned_space = unaligned_heuristic.compute_aligned_space( - range, &references[0]); - unaligned_range = BVHObjectBinning(range, - &references[0], - &unaligned_heuristic, - &aligned_space); - unalignedSplitSAH = params.sah_node_cost * unaligned_range.unaligned_bounds().half_area() + - params.sah_primitive_cost * unaligned_range.splitSAH; - unalignedLeafSAH = params.sah_primitive_cost * unaligned_range.leafSAH; - if(!(range.size() > 0 && params.top_level && level == 0)) { - if(unalignedLeafSAH < unalignedSplitSAH && unalignedSplitSAH < splitSAH && - range_within_max_leaf_size(range, references)) - { - return create_leaf_node(range, references); - } - } - /* Check whether unaligned split is better than the regular one. */ - if(unalignedSplitSAH < splitSAH) { - do_unalinged_split = true; - } - } - - /* Perform split. */ - BVHObjectBinning left, right; - if(do_unalinged_split) { - unaligned_range.split(&references[0], left, right); - } - else { - range.split(&references[0], left, right); - } - - BoundBox bounds; - if(do_unalinged_split) { - bounds = unaligned_heuristic.compute_aligned_boundbox( - range, &references[0], aligned_space); - } - else { - bounds = range.bounds(); - } - - /* Create inner node. */ - InnerNode *inner; - if(range.size() < THREAD_TASK_SIZE) { - /* local build */ - BVHNode *leftnode = build_node(left, level + 1); - BVHNode *rightnode = build_node(right, level + 1); - - inner = new InnerNode(bounds, leftnode, rightnode); - } - else { - /* Threaded build */ - inner = new InnerNode(bounds); - - task_pool.push(new BVHBuildTask(this, inner, 0, left, level + 1), true); - task_pool.push(new BVHBuildTask(this, inner, 1, right, level + 1), true); - } - - if(do_unalinged_split) { - inner->set_aligned_space(aligned_space); - } - - return inner; + size_t size = range.size(); + float leafSAH = params.sah_primitive_cost * range.leafSAH; + float splitSAH = params.sah_node_cost * range.bounds().half_area() + + params.sah_primitive_cost * range.splitSAH; + + /* Have at least one inner node on top level, for performance and correct + * visibility tests, since object instances do not check visibility flag. + */ + if (!(range.size() > 0 && params.top_level && level == 0)) { + /* Make leaf node when threshold reached or SAH tells us. */ + if ((params.small_enough_for_leaf(size, level)) || + (range_within_max_leaf_size(range, references) && leafSAH < splitSAH)) { + return create_leaf_node(range, references); + } + } + + BVHObjectBinning unaligned_range; + float unalignedSplitSAH = FLT_MAX; + float unalignedLeafSAH = FLT_MAX; + Transform aligned_space; + bool do_unalinged_split = false; + if (params.use_unaligned_nodes && splitSAH > params.unaligned_split_threshold * leafSAH) { + aligned_space = unaligned_heuristic.compute_aligned_space(range, &references[0]); + unaligned_range = BVHObjectBinning( + range, &references[0], &unaligned_heuristic, &aligned_space); + unalignedSplitSAH = params.sah_node_cost * unaligned_range.unaligned_bounds().half_area() + + params.sah_primitive_cost * unaligned_range.splitSAH; + unalignedLeafSAH = params.sah_primitive_cost * unaligned_range.leafSAH; + if (!(range.size() > 0 && params.top_level && level == 0)) { + if (unalignedLeafSAH < unalignedSplitSAH && unalignedSplitSAH < splitSAH && + range_within_max_leaf_size(range, references)) { + return create_leaf_node(range, references); + } + } + /* Check whether unaligned split is better than the regular one. */ + if (unalignedSplitSAH < splitSAH) { + do_unalinged_split = true; + } + } + + /* Perform split. */ + BVHObjectBinning left, right; + if (do_unalinged_split) { + unaligned_range.split(&references[0], left, right); + } + else { + range.split(&references[0], left, right); + } + + BoundBox bounds; + if (do_unalinged_split) { + bounds = unaligned_heuristic.compute_aligned_boundbox(range, &references[0], aligned_space); + } + else { + bounds = range.bounds(); + } + + /* Create inner node. */ + InnerNode *inner; + if (range.size() < THREAD_TASK_SIZE) { + /* local build */ + BVHNode *leftnode = build_node(left, level + 1); + BVHNode *rightnode = build_node(right, level + 1); + + inner = new InnerNode(bounds, leftnode, rightnode); + } + else { + /* Threaded build */ + inner = new InnerNode(bounds); + + task_pool.push(new BVHBuildTask(this, inner, 0, left, level + 1), true); + task_pool.push(new BVHBuildTask(this, inner, 1, right, level + 1), true); + } + + if (do_unalinged_split) { + inner->set_aligned_space(aligned_space); + } + + return inner; } /* multithreaded spatial split builder */ -BVHNode* BVHBuild::build_node(const BVHRange& range, +BVHNode *BVHBuild::build_node(const BVHRange &range, vector<BVHReference> *references, int level, int thread_id) { - /* Update progress. - * - * TODO(sergey): Currently it matches old behavior, but we can move it to the - * task thread (which will mimic non=split builder) and save some CPU ticks - * on checking cancel status. - */ - progress_update(); - if(progress.get_cancel()) { - return NULL; - } - - /* Small enough or too deep => create leaf. */ - if(!(range.size() > 0 && params.top_level && level == 0)) { - if(params.small_enough_for_leaf(range.size(), level)) { - progress_count += range.size(); - return create_leaf_node(range, *references); - } - } - - /* Perform splitting test. */ - BVHSpatialStorage *storage = &spatial_storage[thread_id]; - BVHMixedSplit split(this, storage, range, references, level); - - if(!(range.size() > 0 && params.top_level && level == 0)) { - if(split.no_split) { - progress_count += range.size(); - return create_leaf_node(range, *references); - } - } - float leafSAH = params.sah_primitive_cost * split.leafSAH; - float splitSAH = params.sah_node_cost * range.bounds().half_area() + - params.sah_primitive_cost * split.nodeSAH; - - BVHMixedSplit unaligned_split; - float unalignedSplitSAH = FLT_MAX; - /* float unalignedLeafSAH = FLT_MAX; */ - Transform aligned_space; - bool do_unalinged_split = false; - if(params.use_unaligned_nodes && - splitSAH > params.unaligned_split_threshold*leafSAH) - { - aligned_space = - unaligned_heuristic.compute_aligned_space(range, &references->at(0)); - unaligned_split = BVHMixedSplit(this, - storage, - range, - references, - level, - &unaligned_heuristic, - &aligned_space); - /* unalignedLeafSAH = params.sah_primitive_cost * split.leafSAH; */ - unalignedSplitSAH = params.sah_node_cost * unaligned_split.bounds.half_area() + - params.sah_primitive_cost * unaligned_split.nodeSAH; - /* TOOD(sergey): Check we can create leaf already. */ - /* Check whether unaligned split is better than the regulat one. */ - if(unalignedSplitSAH < splitSAH) { - do_unalinged_split = true; - } - } - - /* Do split. */ - BVHRange left, right; - if(do_unalinged_split) { - unaligned_split.split(this, left, right, range); - } - else { - split.split(this, left, right, range); - } - - progress_total += left.size() + right.size() - range.size(); - - BoundBox bounds; - if(do_unalinged_split) { - bounds = unaligned_heuristic.compute_aligned_boundbox( - range, &references->at(0), aligned_space); - } - else { - bounds = range.bounds(); - } - - /* Create inner node. */ - InnerNode *inner; - if(range.size() < THREAD_TASK_SIZE) { - /* Local build. */ - - /* Build left node. */ - vector<BVHReference> copy(references->begin() + right.start(), - references->begin() + right.end()); - right.set_start(0); - - BVHNode *leftnode = build_node(left, references, level + 1, thread_id); - - /* Build right node. */ - BVHNode *rightnode = build_node(right, ©, level + 1, thread_id); - - inner = new InnerNode(bounds, leftnode, rightnode); - } - else { - /* Threaded build. */ - inner = new InnerNode(bounds); - task_pool.push(new BVHSpatialSplitBuildTask(this, - inner, - 0, - left, - *references, - level + 1), - true); - task_pool.push(new BVHSpatialSplitBuildTask(this, - inner, - 1, - right, - *references, - level + 1), - true); - } - - if(do_unalinged_split) { - inner->set_aligned_space(aligned_space); - } - - return inner; + /* Update progress. + * + * TODO(sergey): Currently it matches old behavior, but we can move it to the + * task thread (which will mimic non=split builder) and save some CPU ticks + * on checking cancel status. + */ + progress_update(); + if (progress.get_cancel()) { + return NULL; + } + + /* Small enough or too deep => create leaf. */ + if (!(range.size() > 0 && params.top_level && level == 0)) { + if (params.small_enough_for_leaf(range.size(), level)) { + progress_count += range.size(); + return create_leaf_node(range, *references); + } + } + + /* Perform splitting test. */ + BVHSpatialStorage *storage = &spatial_storage[thread_id]; + BVHMixedSplit split(this, storage, range, references, level); + + if (!(range.size() > 0 && params.top_level && level == 0)) { + if (split.no_split) { + progress_count += range.size(); + return create_leaf_node(range, *references); + } + } + float leafSAH = params.sah_primitive_cost * split.leafSAH; + float splitSAH = params.sah_node_cost * range.bounds().half_area() + + params.sah_primitive_cost * split.nodeSAH; + + BVHMixedSplit unaligned_split; + float unalignedSplitSAH = FLT_MAX; + /* float unalignedLeafSAH = FLT_MAX; */ + Transform aligned_space; + bool do_unalinged_split = false; + if (params.use_unaligned_nodes && splitSAH > params.unaligned_split_threshold * leafSAH) { + aligned_space = unaligned_heuristic.compute_aligned_space(range, &references->at(0)); + unaligned_split = BVHMixedSplit( + this, storage, range, references, level, &unaligned_heuristic, &aligned_space); + /* unalignedLeafSAH = params.sah_primitive_cost * split.leafSAH; */ + unalignedSplitSAH = params.sah_node_cost * unaligned_split.bounds.half_area() + + params.sah_primitive_cost * unaligned_split.nodeSAH; + /* TOOD(sergey): Check we can create leaf already. */ + /* Check whether unaligned split is better than the regulat one. */ + if (unalignedSplitSAH < splitSAH) { + do_unalinged_split = true; + } + } + + /* Do split. */ + BVHRange left, right; + if (do_unalinged_split) { + unaligned_split.split(this, left, right, range); + } + else { + split.split(this, left, right, range); + } + + progress_total += left.size() + right.size() - range.size(); + + BoundBox bounds; + if (do_unalinged_split) { + bounds = unaligned_heuristic.compute_aligned_boundbox( + range, &references->at(0), aligned_space); + } + else { + bounds = range.bounds(); + } + + /* Create inner node. */ + InnerNode *inner; + if (range.size() < THREAD_TASK_SIZE) { + /* Local build. */ + + /* Build left node. */ + vector<BVHReference> copy(references->begin() + right.start(), + references->begin() + right.end()); + right.set_start(0); + + BVHNode *leftnode = build_node(left, references, level + 1, thread_id); + + /* Build right node. */ + BVHNode *rightnode = build_node(right, ©, level + 1, thread_id); + + inner = new InnerNode(bounds, leftnode, rightnode); + } + else { + /* Threaded build. */ + inner = new InnerNode(bounds); + task_pool.push(new BVHSpatialSplitBuildTask(this, inner, 0, left, *references, level + 1), + true); + task_pool.push(new BVHSpatialSplitBuildTask(this, inner, 1, right, *references, level + 1), + true); + } + + if (do_unalinged_split) { + inner->set_aligned_space(aligned_space); + } + + return inner; } /* Create Nodes */ BVHNode *BVHBuild::create_object_leaf_nodes(const BVHReference *ref, int start, int num) { - if(num == 0) { - BoundBox bounds = BoundBox::empty; - return new LeafNode(bounds, 0, 0, 0); - } - else if(num == 1) { - assert(start < prim_type.size()); - prim_type[start] = ref->prim_type(); - prim_index[start] = ref->prim_index(); - prim_object[start] = ref->prim_object(); - if(need_prim_time) { - prim_time[start] = make_float2(ref->time_from(), ref->time_to()); - } - - const uint visibility = objects[ref->prim_object()]->visibility_for_tracing(); - BVHNode *leaf_node = new LeafNode(ref->bounds(), visibility, start, start+1); - leaf_node->time_from = ref->time_from(); - leaf_node->time_to = ref->time_to(); - return leaf_node; - } - else { - int mid = num/2; - BVHNode *leaf0 = create_object_leaf_nodes(ref, start, mid); - BVHNode *leaf1 = create_object_leaf_nodes(ref+mid, start+mid, num-mid); - - BoundBox bounds = BoundBox::empty; - bounds.grow(leaf0->bounds); - bounds.grow(leaf1->bounds); - - BVHNode *inner_node = new InnerNode(bounds, leaf0, leaf1); - inner_node->time_from = min(leaf0->time_from, leaf1->time_from); - inner_node->time_to = max(leaf0->time_to, leaf1->time_to); - return inner_node; - } + if (num == 0) { + BoundBox bounds = BoundBox::empty; + return new LeafNode(bounds, 0, 0, 0); + } + else if (num == 1) { + assert(start < prim_type.size()); + prim_type[start] = ref->prim_type(); + prim_index[start] = ref->prim_index(); + prim_object[start] = ref->prim_object(); + if (need_prim_time) { + prim_time[start] = make_float2(ref->time_from(), ref->time_to()); + } + + const uint visibility = objects[ref->prim_object()]->visibility_for_tracing(); + BVHNode *leaf_node = new LeafNode(ref->bounds(), visibility, start, start + 1); + leaf_node->time_from = ref->time_from(); + leaf_node->time_to = ref->time_to(); + return leaf_node; + } + else { + int mid = num / 2; + BVHNode *leaf0 = create_object_leaf_nodes(ref, start, mid); + BVHNode *leaf1 = create_object_leaf_nodes(ref + mid, start + mid, num - mid); + + BoundBox bounds = BoundBox::empty; + bounds.grow(leaf0->bounds); + bounds.grow(leaf1->bounds); + + BVHNode *inner_node = new InnerNode(bounds, leaf0, leaf1); + inner_node->time_from = min(leaf0->time_from, leaf1->time_from); + inner_node->time_to = max(leaf0->time_to, leaf1->time_to); + return inner_node; + } } -BVHNode* BVHBuild::create_leaf_node(const BVHRange& range, - const vector<BVHReference>& references) +BVHNode *BVHBuild::create_leaf_node(const BVHRange &range, const vector<BVHReference> &references) { - /* This is a bit overallocating here (considering leaf size into account), - * but chunk-based re-allocation in vector makes it difficult to use small - * size of stack storage here. Some tweaks are possible tho. - * - * NOTES: - * - If the size is too big, we'll have inefficient stack usage, - * and lots of cache misses. - * - If the size is too small, then we can run out of memory - * allowed to be used by vector. - * In practice it wouldn't mean crash, just allocator will fallback - * to heap which is slower. - * - Optimistic re-allocation in STL could jump us out of stack usage - * because re-allocation happens in chunks and size of those chunks we - * can not control. - */ - typedef StackAllocator<256, int> LeafStackAllocator; - typedef StackAllocator<256, float2> LeafTimeStackAllocator; - typedef StackAllocator<256, BVHReference> LeafReferenceStackAllocator; - - vector<int, LeafStackAllocator> p_type[PRIMITIVE_NUM_TOTAL]; - vector<int, LeafStackAllocator> p_index[PRIMITIVE_NUM_TOTAL]; - vector<int, LeafStackAllocator> p_object[PRIMITIVE_NUM_TOTAL]; - vector<float2, LeafTimeStackAllocator> p_time[PRIMITIVE_NUM_TOTAL]; - vector<BVHReference, LeafReferenceStackAllocator> p_ref[PRIMITIVE_NUM_TOTAL]; - - /* TODO(sergey): In theory we should be able to store references. */ - vector<BVHReference, LeafReferenceStackAllocator> object_references; - - uint visibility[PRIMITIVE_NUM_TOTAL] = {0}; - /* NOTE: Keep initializtion in sync with actual number of primitives. */ - BoundBox bounds[PRIMITIVE_NUM_TOTAL] = {BoundBox::empty, - BoundBox::empty, - BoundBox::empty, - BoundBox::empty}; - int ob_num = 0; - int num_new_prims = 0; - /* Fill in per-type type/index array. */ - for(int i = 0; i < range.size(); i++) { - const BVHReference& ref = references[range.start() + i]; - if(ref.prim_index() != -1) { - int type_index = bitscan(ref.prim_type() & PRIMITIVE_ALL); - p_ref[type_index].push_back(ref); - p_type[type_index].push_back(ref.prim_type()); - p_index[type_index].push_back(ref.prim_index()); - p_object[type_index].push_back(ref.prim_object()); - p_time[type_index].push_back(make_float2(ref.time_from(), - ref.time_to())); - - bounds[type_index].grow(ref.bounds()); - visibility[type_index] |= objects[ref.prim_object()]->visibility_for_tracing(); - if(ref.prim_type() & PRIMITIVE_ALL_CURVE) { - visibility[type_index] |= PATH_RAY_CURVE; - } - ++num_new_prims; - } - else { - object_references.push_back(ref); - ++ob_num; - } - } - - /* Create leaf nodes for every existing primitive. - * - * Here we write primitive types, indices and objects to a temporary array. - * This way we keep all the heavy memory allocation code outside of the - * thread lock in the case of spatial split building. - * - * TODO(sergey): With some pointer trickery we can write directly to the - * destination buffers for the non-spatial split BVH. - */ - BVHNode *leaves[PRIMITIVE_NUM_TOTAL + 1] = {NULL}; - int num_leaves = 0; - size_t start_index = 0; - vector<int, LeafStackAllocator> local_prim_type, - local_prim_index, - local_prim_object; - vector<float2, LeafTimeStackAllocator> local_prim_time; - local_prim_type.resize(num_new_prims); - local_prim_index.resize(num_new_prims); - local_prim_object.resize(num_new_prims); - if(need_prim_time) { - local_prim_time.resize(num_new_prims); - } - for(int i = 0; i < PRIMITIVE_NUM_TOTAL; ++i) { - int num = (int)p_type[i].size(); - if(num != 0) { - assert(p_type[i].size() == p_index[i].size()); - assert(p_type[i].size() == p_object[i].size()); - Transform aligned_space; - bool alignment_found = false; - for(int j = 0; j < num; ++j) { - const int index = start_index + j; - local_prim_type[index] = p_type[i][j]; - local_prim_index[index] = p_index[i][j]; - local_prim_object[index] = p_object[i][j]; - if(need_prim_time) { - local_prim_time[index] = p_time[i][j]; - } - if(params.use_unaligned_nodes && !alignment_found) { - alignment_found = - unaligned_heuristic.compute_aligned_space(p_ref[i][j], - &aligned_space); - } - } - LeafNode *leaf_node = new LeafNode(bounds[i], - visibility[i], - start_index, - start_index + num); - if(true) { - float time_from = 1.0f, time_to = 0.0f; - for(int j = 0; j < num; ++j) { - const BVHReference &ref = p_ref[i][j]; - time_from = min(time_from, ref.time_from()); - time_to = max(time_to, ref.time_to()); - } - leaf_node->time_from = time_from; - leaf_node->time_to = time_to; - } - if(alignment_found) { - /* Need to recalculate leaf bounds with new alignment. */ - leaf_node->bounds = BoundBox::empty; - for(int j = 0; j < num; ++j) { - const BVHReference &ref = p_ref[i][j]; - BoundBox ref_bounds = - unaligned_heuristic.compute_aligned_prim_boundbox( - ref, - aligned_space); - leaf_node->bounds.grow(ref_bounds); - } - /* Set alignment space. */ - leaf_node->set_aligned_space(aligned_space); - } - leaves[num_leaves++] = leaf_node; - start_index += num; - } - } - /* Get size of new data to be copied to the packed arrays. */ - const int num_new_leaf_data = start_index; - const size_t new_leaf_data_size = sizeof(int) * num_new_leaf_data; - /* Copy actual data to the packed array. */ - if(params.use_spatial_split) { - spatial_spin_lock.lock(); - /* We use first free index in the packed arrays and mode pointer to the - * end of the current range. - * - * This doesn't give deterministic packed arrays, but it shouldn't really - * matter because order of children in BVH is deterministic. - */ - start_index = spatial_free_index; - spatial_free_index += range.size(); - /* Extend an array when needed. */ - const size_t range_end = start_index + range.size(); - if(prim_type.size() < range_end) { - /* Avoid extra re-allocations by pre-allocating bigger array in an - * advance. - */ - if(range_end >= prim_type.capacity()) { - float progress = (float)progress_count/(float)progress_total; - float factor = (1.0f - progress); - const size_t reserve = (size_t)(range_end + (float)range_end*factor); - prim_type.reserve(reserve); - prim_index.reserve(reserve); - prim_object.reserve(reserve); - if(need_prim_time) { - prim_time.reserve(reserve); - } - } - - prim_type.resize(range_end); - prim_index.resize(range_end); - prim_object.resize(range_end); - if(need_prim_time) { - prim_time.resize(range_end); - } - } - /* Perform actual data copy. */ - if(new_leaf_data_size > 0) { - memcpy(&prim_type[start_index], &local_prim_type[0], new_leaf_data_size); - memcpy(&prim_index[start_index], &local_prim_index[0], new_leaf_data_size); - memcpy(&prim_object[start_index], &local_prim_object[0], new_leaf_data_size); - if(need_prim_time) { - memcpy(&prim_time[start_index], &local_prim_time[0], sizeof(float2)*num_new_leaf_data); - } - } - spatial_spin_lock.unlock(); - } - else { - /* For the regular BVH builder we simply copy new data starting at the - * range start. This is totally thread-safe, all threads are living - * inside of their own range. - */ - start_index = range.start(); - if(new_leaf_data_size > 0) { - memcpy(&prim_type[start_index], &local_prim_type[0], new_leaf_data_size); - memcpy(&prim_index[start_index], &local_prim_index[0], new_leaf_data_size); - memcpy(&prim_object[start_index], &local_prim_object[0], new_leaf_data_size); - if(need_prim_time) { - memcpy(&prim_time[start_index], &local_prim_time[0], sizeof(float2)*num_new_leaf_data); - } - } - } - - /* So far leaves were created with the zero-based index in an arrays, - * here we modify the indices to correspond to actual packed array start - * index. - */ - for(int i = 0; i < num_leaves; ++i) { - LeafNode *leaf = (LeafNode *)leaves[i]; - leaf->lo += start_index; - leaf->hi += start_index; - } - - /* Create leaf node for object. */ - if(num_leaves == 0 || ob_num) { - /* Only create object leaf nodes if there are objects or no other - * nodes created. - */ - const BVHReference *ref = (ob_num)? &object_references[0]: NULL; - leaves[num_leaves] = create_object_leaf_nodes(ref, - start_index + num_new_leaf_data, - ob_num); - ++num_leaves; - } - - /* TODO(sergey): Need to take care of alignment when number of leaves - * is more than 1. - */ - if(num_leaves == 1) { - /* Simplest case: single leaf, just return it. - * In all the rest cases we'll be creating intermediate inner node with - * an appropriate bounding box. - */ - return leaves[0]; - } - else if(num_leaves == 2) { - return new InnerNode(range.bounds(), leaves[0], leaves[1]); - } - else if(num_leaves == 3) { - BoundBox inner_bounds = merge(leaves[1]->bounds, leaves[2]->bounds); - BVHNode *inner = new InnerNode(inner_bounds, leaves[1], leaves[2]); - return new InnerNode(range.bounds(), leaves[0], inner); - } else { - /* Should be doing more branches if more primitive types added. */ - assert(num_leaves <= 5); - BoundBox inner_bounds_a = merge(leaves[0]->bounds, leaves[1]->bounds); - BoundBox inner_bounds_b = merge(leaves[2]->bounds, leaves[3]->bounds); - BVHNode *inner_a = new InnerNode(inner_bounds_a, leaves[0], leaves[1]); - BVHNode *inner_b = new InnerNode(inner_bounds_b, leaves[2], leaves[3]); - BoundBox inner_bounds_c = merge(inner_a->bounds, inner_b->bounds); - BVHNode *inner_c = new InnerNode(inner_bounds_c, inner_a, inner_b); - if(num_leaves == 5) { - return new InnerNode(range.bounds(), inner_c, leaves[4]); - } - return inner_c; - } + /* This is a bit overallocating here (considering leaf size into account), + * but chunk-based re-allocation in vector makes it difficult to use small + * size of stack storage here. Some tweaks are possible tho. + * + * NOTES: + * - If the size is too big, we'll have inefficient stack usage, + * and lots of cache misses. + * - If the size is too small, then we can run out of memory + * allowed to be used by vector. + * In practice it wouldn't mean crash, just allocator will fallback + * to heap which is slower. + * - Optimistic re-allocation in STL could jump us out of stack usage + * because re-allocation happens in chunks and size of those chunks we + * can not control. + */ + typedef StackAllocator<256, int> LeafStackAllocator; + typedef StackAllocator<256, float2> LeafTimeStackAllocator; + typedef StackAllocator<256, BVHReference> LeafReferenceStackAllocator; + + vector<int, LeafStackAllocator> p_type[PRIMITIVE_NUM_TOTAL]; + vector<int, LeafStackAllocator> p_index[PRIMITIVE_NUM_TOTAL]; + vector<int, LeafStackAllocator> p_object[PRIMITIVE_NUM_TOTAL]; + vector<float2, LeafTimeStackAllocator> p_time[PRIMITIVE_NUM_TOTAL]; + vector<BVHReference, LeafReferenceStackAllocator> p_ref[PRIMITIVE_NUM_TOTAL]; + + /* TODO(sergey): In theory we should be able to store references. */ + vector<BVHReference, LeafReferenceStackAllocator> object_references; + + uint visibility[PRIMITIVE_NUM_TOTAL] = {0}; + /* NOTE: Keep initializtion in sync with actual number of primitives. */ + BoundBox bounds[PRIMITIVE_NUM_TOTAL] = { + BoundBox::empty, BoundBox::empty, BoundBox::empty, BoundBox::empty}; + int ob_num = 0; + int num_new_prims = 0; + /* Fill in per-type type/index array. */ + for (int i = 0; i < range.size(); i++) { + const BVHReference &ref = references[range.start() + i]; + if (ref.prim_index() != -1) { + int type_index = bitscan(ref.prim_type() & PRIMITIVE_ALL); + p_ref[type_index].push_back(ref); + p_type[type_index].push_back(ref.prim_type()); + p_index[type_index].push_back(ref.prim_index()); + p_object[type_index].push_back(ref.prim_object()); + p_time[type_index].push_back(make_float2(ref.time_from(), ref.time_to())); + + bounds[type_index].grow(ref.bounds()); + visibility[type_index] |= objects[ref.prim_object()]->visibility_for_tracing(); + if (ref.prim_type() & PRIMITIVE_ALL_CURVE) { + visibility[type_index] |= PATH_RAY_CURVE; + } + ++num_new_prims; + } + else { + object_references.push_back(ref); + ++ob_num; + } + } + + /* Create leaf nodes for every existing primitive. + * + * Here we write primitive types, indices and objects to a temporary array. + * This way we keep all the heavy memory allocation code outside of the + * thread lock in the case of spatial split building. + * + * TODO(sergey): With some pointer trickery we can write directly to the + * destination buffers for the non-spatial split BVH. + */ + BVHNode *leaves[PRIMITIVE_NUM_TOTAL + 1] = {NULL}; + int num_leaves = 0; + size_t start_index = 0; + vector<int, LeafStackAllocator> local_prim_type, local_prim_index, local_prim_object; + vector<float2, LeafTimeStackAllocator> local_prim_time; + local_prim_type.resize(num_new_prims); + local_prim_index.resize(num_new_prims); + local_prim_object.resize(num_new_prims); + if (need_prim_time) { + local_prim_time.resize(num_new_prims); + } + for (int i = 0; i < PRIMITIVE_NUM_TOTAL; ++i) { + int num = (int)p_type[i].size(); + if (num != 0) { + assert(p_type[i].size() == p_index[i].size()); + assert(p_type[i].size() == p_object[i].size()); + Transform aligned_space; + bool alignment_found = false; + for (int j = 0; j < num; ++j) { + const int index = start_index + j; + local_prim_type[index] = p_type[i][j]; + local_prim_index[index] = p_index[i][j]; + local_prim_object[index] = p_object[i][j]; + if (need_prim_time) { + local_prim_time[index] = p_time[i][j]; + } + if (params.use_unaligned_nodes && !alignment_found) { + alignment_found = unaligned_heuristic.compute_aligned_space(p_ref[i][j], &aligned_space); + } + } + LeafNode *leaf_node = new LeafNode(bounds[i], visibility[i], start_index, start_index + num); + if (true) { + float time_from = 1.0f, time_to = 0.0f; + for (int j = 0; j < num; ++j) { + const BVHReference &ref = p_ref[i][j]; + time_from = min(time_from, ref.time_from()); + time_to = max(time_to, ref.time_to()); + } + leaf_node->time_from = time_from; + leaf_node->time_to = time_to; + } + if (alignment_found) { + /* Need to recalculate leaf bounds with new alignment. */ + leaf_node->bounds = BoundBox::empty; + for (int j = 0; j < num; ++j) { + const BVHReference &ref = p_ref[i][j]; + BoundBox ref_bounds = unaligned_heuristic.compute_aligned_prim_boundbox(ref, + aligned_space); + leaf_node->bounds.grow(ref_bounds); + } + /* Set alignment space. */ + leaf_node->set_aligned_space(aligned_space); + } + leaves[num_leaves++] = leaf_node; + start_index += num; + } + } + /* Get size of new data to be copied to the packed arrays. */ + const int num_new_leaf_data = start_index; + const size_t new_leaf_data_size = sizeof(int) * num_new_leaf_data; + /* Copy actual data to the packed array. */ + if (params.use_spatial_split) { + spatial_spin_lock.lock(); + /* We use first free index in the packed arrays and mode pointer to the + * end of the current range. + * + * This doesn't give deterministic packed arrays, but it shouldn't really + * matter because order of children in BVH is deterministic. + */ + start_index = spatial_free_index; + spatial_free_index += range.size(); + /* Extend an array when needed. */ + const size_t range_end = start_index + range.size(); + if (prim_type.size() < range_end) { + /* Avoid extra re-allocations by pre-allocating bigger array in an + * advance. + */ + if (range_end >= prim_type.capacity()) { + float progress = (float)progress_count / (float)progress_total; + float factor = (1.0f - progress); + const size_t reserve = (size_t)(range_end + (float)range_end * factor); + prim_type.reserve(reserve); + prim_index.reserve(reserve); + prim_object.reserve(reserve); + if (need_prim_time) { + prim_time.reserve(reserve); + } + } + + prim_type.resize(range_end); + prim_index.resize(range_end); + prim_object.resize(range_end); + if (need_prim_time) { + prim_time.resize(range_end); + } + } + /* Perform actual data copy. */ + if (new_leaf_data_size > 0) { + memcpy(&prim_type[start_index], &local_prim_type[0], new_leaf_data_size); + memcpy(&prim_index[start_index], &local_prim_index[0], new_leaf_data_size); + memcpy(&prim_object[start_index], &local_prim_object[0], new_leaf_data_size); + if (need_prim_time) { + memcpy(&prim_time[start_index], &local_prim_time[0], sizeof(float2) * num_new_leaf_data); + } + } + spatial_spin_lock.unlock(); + } + else { + /* For the regular BVH builder we simply copy new data starting at the + * range start. This is totally thread-safe, all threads are living + * inside of their own range. + */ + start_index = range.start(); + if (new_leaf_data_size > 0) { + memcpy(&prim_type[start_index], &local_prim_type[0], new_leaf_data_size); + memcpy(&prim_index[start_index], &local_prim_index[0], new_leaf_data_size); + memcpy(&prim_object[start_index], &local_prim_object[0], new_leaf_data_size); + if (need_prim_time) { + memcpy(&prim_time[start_index], &local_prim_time[0], sizeof(float2) * num_new_leaf_data); + } + } + } + + /* So far leaves were created with the zero-based index in an arrays, + * here we modify the indices to correspond to actual packed array start + * index. + */ + for (int i = 0; i < num_leaves; ++i) { + LeafNode *leaf = (LeafNode *)leaves[i]; + leaf->lo += start_index; + leaf->hi += start_index; + } + + /* Create leaf node for object. */ + if (num_leaves == 0 || ob_num) { + /* Only create object leaf nodes if there are objects or no other + * nodes created. + */ + const BVHReference *ref = (ob_num) ? &object_references[0] : NULL; + leaves[num_leaves] = create_object_leaf_nodes(ref, start_index + num_new_leaf_data, ob_num); + ++num_leaves; + } + + /* TODO(sergey): Need to take care of alignment when number of leaves + * is more than 1. + */ + if (num_leaves == 1) { + /* Simplest case: single leaf, just return it. + * In all the rest cases we'll be creating intermediate inner node with + * an appropriate bounding box. + */ + return leaves[0]; + } + else if (num_leaves == 2) { + return new InnerNode(range.bounds(), leaves[0], leaves[1]); + } + else if (num_leaves == 3) { + BoundBox inner_bounds = merge(leaves[1]->bounds, leaves[2]->bounds); + BVHNode *inner = new InnerNode(inner_bounds, leaves[1], leaves[2]); + return new InnerNode(range.bounds(), leaves[0], inner); + } + else { + /* Should be doing more branches if more primitive types added. */ + assert(num_leaves <= 5); + BoundBox inner_bounds_a = merge(leaves[0]->bounds, leaves[1]->bounds); + BoundBox inner_bounds_b = merge(leaves[2]->bounds, leaves[3]->bounds); + BVHNode *inner_a = new InnerNode(inner_bounds_a, leaves[0], leaves[1]); + BVHNode *inner_b = new InnerNode(inner_bounds_b, leaves[2], leaves[3]); + BoundBox inner_bounds_c = merge(inner_a->bounds, inner_b->bounds); + BVHNode *inner_c = new InnerNode(inner_bounds_c, inner_a, inner_b); + if (num_leaves == 5) { + return new InnerNode(range.bounds(), inner_c, leaves[4]); + } + return inner_c; + } #undef MAX_ITEMS_PER_LEAF } @@ -1154,81 +1081,81 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range, void BVHBuild::rotate(BVHNode *node, int max_depth, int iterations) { - /* in tested scenes, this resulted in slightly slower raytracing, so disabled - * it for now. could be implementation bug, or depend on the scene */ - if(node) - for(int i = 0; i < iterations; i++) - rotate(node, max_depth); + /* in tested scenes, this resulted in slightly slower raytracing, so disabled + * it for now. could be implementation bug, or depend on the scene */ + if (node) + for (int i = 0; i < iterations; i++) + rotate(node, max_depth); } void BVHBuild::rotate(BVHNode *node, int max_depth) { - /* nothing to rotate if we reached a leaf node. */ - if(node->is_leaf() || max_depth < 0) - return; - - InnerNode *parent = (InnerNode*)node; - - /* rotate all children first */ - for(size_t c = 0; c < 2; c++) - rotate(parent->children[c], max_depth-1); - - /* compute current area of all children */ - BoundBox bounds0 = parent->children[0]->bounds; - BoundBox bounds1 = parent->children[1]->bounds; - - float area0 = bounds0.half_area(); - float area1 = bounds1.half_area(); - float4 child_area = make_float4(area0, area1, 0.0f, 0.0f); - - /* find best rotation. we pick a target child of a first child, and swap - * this with an other child. we perform the best such swap. */ - float best_cost = FLT_MAX; - int best_child = -1, best_target = -1, best_other = -1; - - for(size_t c = 0; c < 2; c++) { - /* ignore leaf nodes as we cannot descent into */ - if(parent->children[c]->is_leaf()) - continue; - - InnerNode *child = (InnerNode*)parent->children[c]; - BoundBox& other = (c == 0)? bounds1: bounds0; - - /* transpose child bounds */ - BoundBox target0 = child->children[0]->bounds; - BoundBox target1 = child->children[1]->bounds; - - /* compute cost for both possible swaps */ - float cost0 = merge(other, target1).half_area() - child_area[c]; - float cost1 = merge(target0, other).half_area() - child_area[c]; - - if(min(cost0,cost1) < best_cost) { - best_child = (int)c; - best_other = (int)(1-c); - - if(cost0 < cost1) { - best_cost = cost0; - best_target = 0; - } - else { - best_cost = cost0; - best_target = 1; - } - } - } - - /* if we did not find a swap that improves the SAH then do nothing */ - if(best_cost >= 0) - return; - - assert(best_child == 0 || best_child == 1); - assert(best_target != -1); - - /* perform the best found tree rotation */ - InnerNode *child = (InnerNode*)parent->children[best_child]; - - swap(parent->children[best_other], child->children[best_target]); - child->bounds = merge(child->children[0]->bounds, child->children[1]->bounds); + /* nothing to rotate if we reached a leaf node. */ + if (node->is_leaf() || max_depth < 0) + return; + + InnerNode *parent = (InnerNode *)node; + + /* rotate all children first */ + for (size_t c = 0; c < 2; c++) + rotate(parent->children[c], max_depth - 1); + + /* compute current area of all children */ + BoundBox bounds0 = parent->children[0]->bounds; + BoundBox bounds1 = parent->children[1]->bounds; + + float area0 = bounds0.half_area(); + float area1 = bounds1.half_area(); + float4 child_area = make_float4(area0, area1, 0.0f, 0.0f); + + /* find best rotation. we pick a target child of a first child, and swap + * this with an other child. we perform the best such swap. */ + float best_cost = FLT_MAX; + int best_child = -1, best_target = -1, best_other = -1; + + for (size_t c = 0; c < 2; c++) { + /* ignore leaf nodes as we cannot descent into */ + if (parent->children[c]->is_leaf()) + continue; + + InnerNode *child = (InnerNode *)parent->children[c]; + BoundBox &other = (c == 0) ? bounds1 : bounds0; + + /* transpose child bounds */ + BoundBox target0 = child->children[0]->bounds; + BoundBox target1 = child->children[1]->bounds; + + /* compute cost for both possible swaps */ + float cost0 = merge(other, target1).half_area() - child_area[c]; + float cost1 = merge(target0, other).half_area() - child_area[c]; + + if (min(cost0, cost1) < best_cost) { + best_child = (int)c; + best_other = (int)(1 - c); + + if (cost0 < cost1) { + best_cost = cost0; + best_target = 0; + } + else { + best_cost = cost0; + best_target = 1; + } + } + } + + /* if we did not find a swap that improves the SAH then do nothing */ + if (best_cost >= 0) + return; + + assert(best_child == 0 || best_child == 1); + assert(best_target != -1); + + /* perform the best found tree rotation */ + InnerNode *child = (InnerNode *)parent->children[best_child]; + + swap(parent->children[best_other], child->children[best_target]); + child->bounds = merge(child->children[0]->bounds, child->children[1]->bounds); } CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh_build.h b/intern/cycles/bvh/bvh_build.h index dd95a5cc0e8..9685e26cfac 100644 --- a/intern/cycles/bvh/bvh_build.h +++ b/intern/cycles/bvh/bvh_build.h @@ -41,106 +41,101 @@ 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& params, - Progress& progress); - ~BVHBuild(); - - 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& center, Mesh *mesh, int i); - void add_reference_curves(BoundBox& root, BoundBox& center, Mesh *mesh, int i); - void add_reference_mesh(BoundBox& root, BoundBox& center, Mesh *mesh, int i); - void add_reference_object(BoundBox& root, BoundBox& center, Object *ob, int i); - void add_references(BVHRange& root); - - /* Building. */ - BVHNode *build_node(const BVHRange& range, - vector<BVHReference> *references, - int level, - int thread_id); - BVHNode *build_node(const BVHObjectBinning& range, int level); - BVHNode *create_leaf_node(const BVHRange& range, - const vector<BVHReference>& references); - BVHNode *create_object_leaf_nodes(const BVHReference *ref, int start, 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 *node, - int child, - BVHObjectBinning *range, - int level); - void thread_build_spatial_split_node(InnerNode *node, - int child, - BVHRange *range, - vector<BVHReference> *references, - int level, - int thread_id); - thread_mutex build_mutex; - - /* Progress. */ - void progress_update(); - - /* Tree rotations. */ - void rotate(BVHNode *node, int max_depth); - void rotate(BVHNode *node, int max_depth, 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; - vector<BVHSpatialStorage> spatial_storage; - size_t spatial_free_index; - thread_spin_lock spatial_spin_lock; - - /* Threads. */ - TaskPool task_pool; - - /* Unaligned building. */ - BVHUnaligned unaligned_heuristic; +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(); + + 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, int i); + void add_reference_curves(BoundBox &root, BoundBox ¢er, Mesh *mesh, int i); + void add_reference_mesh(BoundBox &root, BoundBox ¢er, Mesh *mesh, int i); + void add_reference_object(BoundBox &root, BoundBox ¢er, Object *ob, int i); + void add_references(BVHRange &root); + + /* Building. */ + BVHNode *build_node(const BVHRange &range, + vector<BVHReference> *references, + int level, + int thread_id); + BVHNode *build_node(const BVHObjectBinning &range, int level); + BVHNode *create_leaf_node(const BVHRange &range, const vector<BVHReference> &references); + BVHNode *create_object_leaf_nodes(const BVHReference *ref, int start, 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 *node, int child, BVHObjectBinning *range, int level); + void thread_build_spatial_split_node(InnerNode *node, + int child, + BVHRange *range, + vector<BVHReference> *references, + int level, + int thread_id); + thread_mutex build_mutex; + + /* Progress. */ + void progress_update(); + + /* Tree rotations. */ + void rotate(BVHNode *node, int max_depth); + void rotate(BVHNode *node, int max_depth, 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; + vector<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 -#endif /* __BVH_BUILD_H__ */ +#endif /* __BVH_BUILD_H__ */ diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index fa9993d8ac3..5ef9622aba2 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -34,28 +34,28 @@ #ifdef WITH_EMBREE -#include <pmmintrin.h> -#include <xmmintrin.h> -#include <embree3/rtcore_geometry.h> +# include <pmmintrin.h> +# include <xmmintrin.h> +# include <embree3/rtcore_geometry.h> -#include "bvh/bvh_embree.h" +# include "bvh/bvh_embree.h" /* Kernel includes are necessary so that the filter function for Embree can access the packed BVH. */ -#include "kernel/bvh/bvh_embree.h" -#include "kernel/kernel_compat_cpu.h" -#include "kernel/split/kernel_split_data_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_random.h" - -#include "render/mesh.h" -#include "render/object.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_progress.h" +# include "kernel/bvh/bvh_embree.h" +# include "kernel/kernel_compat_cpu.h" +# include "kernel/split/kernel_split_data_types.h" +# include "kernel/kernel_globals.h" +# include "kernel/kernel_random.h" + +# include "render/mesh.h" +# include "render/object.h" +# include "util/util_foreach.h" +# include "util/util_logging.h" +# include "util/util_progress.h" CCL_NAMESPACE_BEGIN -#define IS_HAIR(x) (x & 1) +# define IS_HAIR(x) (x & 1) /* This gets called by Embree at every valid ray/object intersection. * Things like recording subsurface or shadow hits for later evaluation @@ -64,215 +64,217 @@ CCL_NAMESPACE_BEGIN */ static void rtc_filter_func(const RTCFilterFunctionNArguments *args) { - /* Current implementation in Cycles assumes only single-ray intersection queries. */ - assert(args->N == 1); - - const RTCRay *ray = (RTCRay*)args->ray; - const RTCHit *hit = (RTCHit*)args->hit; - CCLIntersectContext *ctx = ((IntersectContext*)args->context)->userRayExt; - KernelGlobals *kg = ctx->kg; - - /* Check if there is backfacing hair to ignore. */ - if(IS_HAIR(hit->geomID) && (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) - && !(kernel_data.curve.curveflags & CURVE_KN_BACKFACING) - && !(kernel_data.curve.curveflags & CURVE_KN_RIBBONS)) { - if(dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z), make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) { - *args->valid = 0; - return; - } - } + /* Current implementation in Cycles assumes only single-ray intersection queries. */ + assert(args->N == 1); + + const RTCRay *ray = (RTCRay *)args->ray; + const RTCHit *hit = (RTCHit *)args->hit; + CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt; + KernelGlobals *kg = ctx->kg; + + /* Check if there is backfacing hair to ignore. */ + if (IS_HAIR(hit->geomID) && (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) && + !(kernel_data.curve.curveflags & CURVE_KN_BACKFACING) && + !(kernel_data.curve.curveflags & CURVE_KN_RIBBONS)) { + if (dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z), + make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) { + *args->valid = 0; + return; + } + } } -static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments* args) +static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) { - assert(args->N == 1); - - const RTCRay *ray = (RTCRay*)args->ray; - RTCHit *hit = (RTCHit*)args->hit; - CCLIntersectContext *ctx = ((IntersectContext*)args->context)->userRayExt; - KernelGlobals *kg = ctx->kg; - - /* For all ray types: Check if there is backfacing hair to ignore */ - if(IS_HAIR(hit->geomID) && (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) - && !(kernel_data.curve.curveflags & CURVE_KN_BACKFACING) - && !(kernel_data.curve.curveflags & CURVE_KN_RIBBONS)) { - if(dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z), make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) { - *args->valid = 0; - return; - } - } - - switch(ctx->type) { - case CCLIntersectContext::RAY_SHADOW_ALL: { - /* Append the intersection to the end of the array. */ - if(ctx->num_hits < ctx->max_hits) { - Intersection current_isect; - kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); - for(size_t i = 0; i < ctx->max_hits; ++i) { - if(current_isect.object == ctx->isect_s[i].object && - current_isect.prim == ctx->isect_s[i].prim && - current_isect.t == ctx->isect_s[i].t) { - /* This intersection was already recorded, skip it. */ - *args->valid = 0; - break; - } - } - Intersection *isect = &ctx->isect_s[ctx->num_hits]; - ++ctx->num_hits; - *isect = current_isect; - int prim = kernel_tex_fetch(__prim_index, isect->prim); - int shader = 0; - if(kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) { - shader = kernel_tex_fetch(__tri_shader, prim); - } - else { - float4 str = kernel_tex_fetch(__curves, prim); - shader = __float_as_int(str.z); - } - int flag = kernel_tex_fetch(__shaders, shader & SHADER_MASK).flags; - /* If no transparent shadows, all light is blocked. */ - if(flag & (SD_HAS_TRANSPARENT_SHADOW)) { - /* This tells Embree to continue tracing. */ - *args->valid = 0; - } - } - else { - /* Increase the number of hits beyond ray.max_hits - * so that the caller can detect this as opaque. */ - ++ctx->num_hits; - } - break; - } - case CCLIntersectContext::RAY_SSS: { - /* No intersection information requested, just return a hit. */ - if(ctx->max_hits == 0) { - break; - } - - /* Ignore curves. */ - if(hit->geomID & 1) { - /* This tells Embree to continue tracing. */ - *args->valid = 0; - break; - } - - /* See triangle_intersect_subsurface() for the native equivalent. */ - for(int i = min(ctx->max_hits, ctx->ss_isect->num_hits) - 1; i >= 0; --i) { - if(ctx->ss_isect->hits[i].t == ray->tfar) { - /* This tells Embree to continue tracing. */ - *args->valid = 0; - break; - } - } - - ++ctx->ss_isect->num_hits; - int hit_idx; - - if(ctx->ss_isect->num_hits <= ctx->max_hits) { - hit_idx = ctx->ss_isect->num_hits - 1; - } - else { - /* reservoir sampling: if we are at the maximum number of - * hits, randomly replace element or skip it */ - hit_idx = lcg_step_uint(ctx->lcg_state) % ctx->ss_isect->num_hits; - - if(hit_idx >= ctx->max_hits) { - /* This tells Embree to continue tracing. */ - *args->valid = 0; - break; - } - } - /* record intersection */ - kernel_embree_convert_local_hit(kg, ray, hit, &ctx->ss_isect->hits[hit_idx], ctx->sss_object_id); - ctx->ss_isect->Ng[hit_idx].x = hit->Ng_x; - ctx->ss_isect->Ng[hit_idx].y = hit->Ng_y; - ctx->ss_isect->Ng[hit_idx].z = hit->Ng_z; - ctx->ss_isect->Ng[hit_idx] = normalize(ctx->ss_isect->Ng[hit_idx]); - /* This tells Embree to continue tracing .*/ - *args->valid = 0; - break; - } - case CCLIntersectContext::RAY_VOLUME_ALL: { - /* Append the intersection to the end of the array. */ - if(ctx->num_hits < ctx->max_hits) { - Intersection current_isect; - kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); - for(size_t i = 0; i < ctx->max_hits; ++i) { - if(current_isect.object == ctx->isect_s[i].object && - current_isect.prim == ctx->isect_s[i].prim && - current_isect.t == ctx->isect_s[i].t) { - /* This intersection was already recorded, skip it. */ - *args->valid = 0; - break; - } - } - Intersection *isect = &ctx->isect_s[ctx->num_hits]; - ++ctx->num_hits; - *isect = current_isect; - /* Only primitives from volume object. */ - uint tri_object = (isect->object == OBJECT_NONE) ? - kernel_tex_fetch(__prim_object, isect->prim) : isect->object; - int object_flag = kernel_tex_fetch(__object_flag, tri_object); - if((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { - --ctx->num_hits; - } - /* This tells Embree to continue tracing. */ - *args->valid = 0; - break; - } - } - case CCLIntersectContext::RAY_REGULAR: - default: - /* Nothing to do here. */ - break; - } + assert(args->N == 1); + + const RTCRay *ray = (RTCRay *)args->ray; + RTCHit *hit = (RTCHit *)args->hit; + CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt; + KernelGlobals *kg = ctx->kg; + + /* For all ray types: Check if there is backfacing hair to ignore */ + if (IS_HAIR(hit->geomID) && (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) && + !(kernel_data.curve.curveflags & CURVE_KN_BACKFACING) && + !(kernel_data.curve.curveflags & CURVE_KN_RIBBONS)) { + if (dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z), + make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) { + *args->valid = 0; + return; + } + } + + switch (ctx->type) { + case CCLIntersectContext::RAY_SHADOW_ALL: { + /* Append the intersection to the end of the array. */ + if (ctx->num_hits < ctx->max_hits) { + Intersection current_isect; + kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); + for (size_t i = 0; i < ctx->max_hits; ++i) { + if (current_isect.object == ctx->isect_s[i].object && + current_isect.prim == ctx->isect_s[i].prim && current_isect.t == ctx->isect_s[i].t) { + /* This intersection was already recorded, skip it. */ + *args->valid = 0; + break; + } + } + Intersection *isect = &ctx->isect_s[ctx->num_hits]; + ++ctx->num_hits; + *isect = current_isect; + int prim = kernel_tex_fetch(__prim_index, isect->prim); + int shader = 0; + if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) { + shader = kernel_tex_fetch(__tri_shader, prim); + } + else { + float4 str = kernel_tex_fetch(__curves, prim); + shader = __float_as_int(str.z); + } + int flag = kernel_tex_fetch(__shaders, shader & SHADER_MASK).flags; + /* If no transparent shadows, all light is blocked. */ + if (flag & (SD_HAS_TRANSPARENT_SHADOW)) { + /* This tells Embree to continue tracing. */ + *args->valid = 0; + } + } + else { + /* Increase the number of hits beyond ray.max_hits + * so that the caller can detect this as opaque. */ + ++ctx->num_hits; + } + break; + } + case CCLIntersectContext::RAY_SSS: { + /* No intersection information requested, just return a hit. */ + if (ctx->max_hits == 0) { + break; + } + + /* Ignore curves. */ + if (hit->geomID & 1) { + /* This tells Embree to continue tracing. */ + *args->valid = 0; + break; + } + + /* See triangle_intersect_subsurface() for the native equivalent. */ + for (int i = min(ctx->max_hits, ctx->ss_isect->num_hits) - 1; i >= 0; --i) { + if (ctx->ss_isect->hits[i].t == ray->tfar) { + /* This tells Embree to continue tracing. */ + *args->valid = 0; + break; + } + } + + ++ctx->ss_isect->num_hits; + int hit_idx; + + if (ctx->ss_isect->num_hits <= ctx->max_hits) { + hit_idx = ctx->ss_isect->num_hits - 1; + } + else { + /* reservoir sampling: if we are at the maximum number of + * hits, randomly replace element or skip it */ + hit_idx = lcg_step_uint(ctx->lcg_state) % ctx->ss_isect->num_hits; + + if (hit_idx >= ctx->max_hits) { + /* This tells Embree to continue tracing. */ + *args->valid = 0; + break; + } + } + /* record intersection */ + kernel_embree_convert_local_hit( + kg, ray, hit, &ctx->ss_isect->hits[hit_idx], ctx->sss_object_id); + ctx->ss_isect->Ng[hit_idx].x = hit->Ng_x; + ctx->ss_isect->Ng[hit_idx].y = hit->Ng_y; + ctx->ss_isect->Ng[hit_idx].z = hit->Ng_z; + ctx->ss_isect->Ng[hit_idx] = normalize(ctx->ss_isect->Ng[hit_idx]); + /* This tells Embree to continue tracing .*/ + *args->valid = 0; + break; + } + case CCLIntersectContext::RAY_VOLUME_ALL: { + /* Append the intersection to the end of the array. */ + if (ctx->num_hits < ctx->max_hits) { + Intersection current_isect; + kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); + for (size_t i = 0; i < ctx->max_hits; ++i) { + if (current_isect.object == ctx->isect_s[i].object && + current_isect.prim == ctx->isect_s[i].prim && current_isect.t == ctx->isect_s[i].t) { + /* This intersection was already recorded, skip it. */ + *args->valid = 0; + break; + } + } + Intersection *isect = &ctx->isect_s[ctx->num_hits]; + ++ctx->num_hits; + *isect = current_isect; + /* Only primitives from volume object. */ + uint tri_object = (isect->object == OBJECT_NONE) ? + kernel_tex_fetch(__prim_object, isect->prim) : + isect->object; + int object_flag = kernel_tex_fetch(__object_flag, tri_object); + if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { + --ctx->num_hits; + } + /* This tells Embree to continue tracing. */ + *args->valid = 0; + break; + } + } + case CCLIntersectContext::RAY_REGULAR: + default: + /* Nothing to do here. */ + break; + } } static size_t unaccounted_mem = 0; -static bool rtc_memory_monitor_func(void* userPtr, const ssize_t bytes, const bool) +static bool rtc_memory_monitor_func(void *userPtr, const ssize_t bytes, const bool) { - 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; + 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*, enum RTCError, const char* str) +static void rtc_error_func(void *, enum RTCError, const char *str) { - VLOG(1) << str; + VLOG(1) << str; } static double progress_start_time = 0.0f; -static bool rtc_progress_func(void* user_ptr, const double n) +static bool rtc_progress_func(void *user_ptr, const double n) { - Progress *progress = (Progress*)user_ptr; + Progress *progress = (Progress *)user_ptr; - if(time_dt() - progress_start_time < 0.25) { - return true; - } + if (time_dt() - progress_start_time < 0.25) { + return true; + } - string msg = string_printf("Building BVH %.0f%%", n * 100.0); - progress->set_substatus(msg); - progress_start_time = time_dt(); + string msg = string_printf("Building BVH %.0f%%", n * 100.0); + progress->set_substatus(msg); + progress_start_time = time_dt(); - return !progress->get_cancel(); + return !progress->get_cancel(); } /* This is to have a shared device between all BVH instances. @@ -281,665 +283,679 @@ RTCDevice BVHEmbree::rtc_shared_device = NULL; int BVHEmbree::rtc_shared_users = 0; thread_mutex BVHEmbree::rtc_shared_mutex; -BVHEmbree::BVHEmbree(const BVHParams& params_, const vector<Object*>& objects_) -: BVH(params_, objects_), scene(NULL), mem_used(0), top_level(NULL), stats(NULL), - curve_subdivisions(params.curve_subdivisions), build_quality(RTC_BUILD_QUALITY_REFIT), - use_curves(params_.curve_flags & CURVE_KN_INTERPOLATE), - use_ribbons(params.curve_flags & CURVE_KN_RIBBONS), dynamic_scene(true) +BVHEmbree::BVHEmbree(const BVHParams ¶ms_, const vector<Object *> &objects_) + : BVH(params_, objects_), + scene(NULL), + mem_used(0), + top_level(NULL), + stats(NULL), + curve_subdivisions(params.curve_subdivisions), + build_quality(RTC_BUILD_QUALITY_REFIT), + use_curves(params_.curve_flags & CURVE_KN_INTERPOLATE), + use_ribbons(params.curve_flags & CURVE_KN_RIBBONS), + dynamic_scene(true) { - _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); - _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); - thread_scoped_lock lock(rtc_shared_mutex); - if(rtc_shared_users == 0) { - rtc_shared_device = rtcNewDevice("verbose=0"); - /* Check here if Embree was built with the correct flags. */ - ssize_t ret = rtcGetDeviceProperty (rtc_shared_device,RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED); - if(ret != 1) { - assert(0); - VLOG(1) << "Embree is compiled without the RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED flag."\ - "Ray visiblity will not work."; - } - ret = rtcGetDeviceProperty (rtc_shared_device,RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED); - if(ret != 1) { - assert(0); - VLOG(1) << "Embree is compiled without the RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED flag."\ - "Renders may not look as expected."; - } - ret = rtcGetDeviceProperty (rtc_shared_device,RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED); - if(ret != 1) { - assert(0); - VLOG(1) << "Embree is compiled without the RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED flag. "\ - "Line primitives will not be rendered."; - } - ret = rtcGetDeviceProperty (rtc_shared_device,RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED); - if(ret != 1) { - assert(0); - VLOG(1) << "Embree is compiled without the RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED flag. "\ - "Triangle primitives will not be rendered."; - } - ret = rtcGetDeviceProperty (rtc_shared_device,RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED); - if(ret != 0) { - assert(0); - VLOG(1) << "Embree is compiled with the RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED flag. "\ - "Renders may not look as expected."; - } - } - ++rtc_shared_users; - - rtcSetDeviceErrorFunction(rtc_shared_device, rtc_error_func, NULL); - - pack.root_index = -1; + _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); + _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); + thread_scoped_lock lock(rtc_shared_mutex); + if (rtc_shared_users == 0) { + rtc_shared_device = rtcNewDevice("verbose=0"); + /* Check here if Embree was built with the correct flags. */ + ssize_t ret = rtcGetDeviceProperty(rtc_shared_device, RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED); + if (ret != 1) { + assert(0); + VLOG(1) << "Embree is compiled without the RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED flag." + "Ray visiblity will not work."; + } + ret = rtcGetDeviceProperty(rtc_shared_device, RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED); + if (ret != 1) { + assert(0); + VLOG(1) + << "Embree is compiled without the RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED flag." + "Renders may not look as expected."; + } + ret = rtcGetDeviceProperty(rtc_shared_device, RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED); + if (ret != 1) { + assert(0); + VLOG(1) + << "Embree is compiled without the RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED flag. " + "Line primitives will not be rendered."; + } + ret = rtcGetDeviceProperty(rtc_shared_device, RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED); + if (ret != 1) { + assert(0); + VLOG(1) << "Embree is compiled without the RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED " + "flag. " + "Triangle primitives will not be rendered."; + } + ret = rtcGetDeviceProperty(rtc_shared_device, RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED); + if (ret != 0) { + assert(0); + VLOG(1) << "Embree is compiled with the RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED flag. " + "Renders may not look as expected."; + } + } + ++rtc_shared_users; + + rtcSetDeviceErrorFunction(rtc_shared_device, rtc_error_func, NULL); + + pack.root_index = -1; } BVHEmbree::~BVHEmbree() { - if(!params.top_level) { - destroy(scene); - } + if (!params.top_level) { + destroy(scene); + } } void BVHEmbree::destroy(RTCScene scene) { - if(scene) { - rtcReleaseScene(scene); - scene = NULL; - } - thread_scoped_lock lock(rtc_shared_mutex); - --rtc_shared_users; - if(rtc_shared_users == 0) { - rtcReleaseDevice (rtc_shared_device); - rtc_shared_device = NULL; - } + if (scene) { + rtcReleaseScene(scene); + scene = NULL; + } + thread_scoped_lock lock(rtc_shared_mutex); + --rtc_shared_users; + if (rtc_shared_users == 0) { + rtcReleaseDevice(rtc_shared_device); + rtc_shared_device = NULL; + } } void BVHEmbree::delete_rtcScene() { - if(scene) { - /* When this BVH is used as an instance in a top level BVH, don't delete now - * Let the top_level BVH know that it should delete it later. */ - if(top_level) { - top_level->add_delayed_delete_scene(scene); - } - else { - rtcReleaseScene(scene); - if(delayed_delete_scenes.size()) { - foreach(RTCScene s, delayed_delete_scenes) { - rtcReleaseScene(s); - } - } - delayed_delete_scenes.clear(); - } - scene = NULL; - } + if (scene) { + /* When this BVH is used as an instance in a top level BVH, don't delete now + * Let the top_level BVH know that it should delete it later. */ + if (top_level) { + top_level->add_delayed_delete_scene(scene); + } + else { + rtcReleaseScene(scene); + if (delayed_delete_scenes.size()) { + foreach (RTCScene s, delayed_delete_scenes) { + rtcReleaseScene(s); + } + } + delayed_delete_scenes.clear(); + } + scene = NULL; + } } -void BVHEmbree::build(Progress& progress, Stats *stats_) +void BVHEmbree::build(Progress &progress, Stats *stats_) { - assert(rtc_shared_device); - stats = stats_; - rtcSetDeviceMemoryMonitorFunction(rtc_shared_device, rtc_memory_monitor_func, stats); - - progress.set_substatus("Building BVH"); - - if(scene) { - rtcReleaseScene(scene); - scene = NULL; - } - - const bool dynamic = params.bvh_type == SceneParams::BVH_DYNAMIC; - - scene = rtcNewScene(rtc_shared_device); - const RTCSceneFlags scene_flags = (dynamic ? RTC_SCENE_FLAG_DYNAMIC : RTC_SCENE_FLAG_NONE) | - RTC_SCENE_FLAG_COMPACT | RTC_SCENE_FLAG_ROBUST; - rtcSetSceneFlags(scene, scene_flags); - build_quality = dynamic ? RTC_BUILD_QUALITY_LOW : - (params.use_spatial_split ? RTC_BUILD_QUALITY_HIGH : RTC_BUILD_QUALITY_MEDIUM); - rtcSetSceneBuildQuality(scene, build_quality); - - /* Count triangles and curves first, reserve arrays once. */ - size_t prim_count = 0; - - foreach(Object *ob, objects) { - if (params.top_level) { - if (!ob->is_traceable()) { - continue; - } - if (!ob->mesh->is_instanced()) { - if(params.primitive_mask & PRIMITIVE_ALL_TRIANGLE) { - prim_count += ob->mesh->num_triangles(); - } - if (params.primitive_mask & PRIMITIVE_ALL_CURVE) { - for (size_t j = 0; j < ob->mesh->num_curves(); ++j) { - prim_count += ob->mesh->get_curve(j).num_segments(); - } - } - } - else { - ++prim_count; - } - } - else { - if (params.primitive_mask & PRIMITIVE_ALL_TRIANGLE && ob->mesh->num_triangles() > 0) { - prim_count += ob->mesh->num_triangles(); - } - if (params.primitive_mask & PRIMITIVE_ALL_CURVE) { - for (size_t j = 0; j < ob->mesh->num_curves(); ++j) { - prim_count += ob->mesh->get_curve(j).num_segments(); - } - } - } - } - - pack.prim_object.reserve(prim_count); - pack.prim_type.reserve(prim_count); - pack.prim_index.reserve(prim_count); - pack.prim_tri_index.reserve(prim_count); - - int i = 0; - - pack.object_node.clear(); - - foreach(Object *ob, objects) { - if(params.top_level) { - if(!ob->is_traceable()) { - ++i; - continue; - } - if(!ob->mesh->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()) { - delete_rtcScene(); - stats = NULL; - return; - } - - rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress); - rtcCommitScene(scene); - - pack_primitives(); - - if(progress.get_cancel()) { - delete_rtcScene(); - stats = NULL; - return; - } - - progress.set_substatus("Packing geometry"); - pack_nodes(NULL); - - stats = NULL; + assert(rtc_shared_device); + stats = stats_; + rtcSetDeviceMemoryMonitorFunction(rtc_shared_device, rtc_memory_monitor_func, stats); + + progress.set_substatus("Building BVH"); + + if (scene) { + rtcReleaseScene(scene); + scene = NULL; + } + + const bool dynamic = params.bvh_type == SceneParams::BVH_DYNAMIC; + + scene = rtcNewScene(rtc_shared_device); + const RTCSceneFlags scene_flags = (dynamic ? RTC_SCENE_FLAG_DYNAMIC : RTC_SCENE_FLAG_NONE) | + RTC_SCENE_FLAG_COMPACT | RTC_SCENE_FLAG_ROBUST; + rtcSetSceneFlags(scene, scene_flags); + build_quality = dynamic ? RTC_BUILD_QUALITY_LOW : + (params.use_spatial_split ? RTC_BUILD_QUALITY_HIGH : + RTC_BUILD_QUALITY_MEDIUM); + rtcSetSceneBuildQuality(scene, build_quality); + + /* Count triangles and curves first, reserve arrays once. */ + size_t prim_count = 0; + + foreach (Object *ob, objects) { + if (params.top_level) { + if (!ob->is_traceable()) { + continue; + } + if (!ob->mesh->is_instanced()) { + if (params.primitive_mask & PRIMITIVE_ALL_TRIANGLE) { + prim_count += ob->mesh->num_triangles(); + } + if (params.primitive_mask & PRIMITIVE_ALL_CURVE) { + for (size_t j = 0; j < ob->mesh->num_curves(); ++j) { + prim_count += ob->mesh->get_curve(j).num_segments(); + } + } + } + else { + ++prim_count; + } + } + else { + if (params.primitive_mask & PRIMITIVE_ALL_TRIANGLE && ob->mesh->num_triangles() > 0) { + prim_count += ob->mesh->num_triangles(); + } + if (params.primitive_mask & PRIMITIVE_ALL_CURVE) { + for (size_t j = 0; j < ob->mesh->num_curves(); ++j) { + prim_count += ob->mesh->get_curve(j).num_segments(); + } + } + } + } + + pack.prim_object.reserve(prim_count); + pack.prim_type.reserve(prim_count); + pack.prim_index.reserve(prim_count); + pack.prim_tri_index.reserve(prim_count); + + int i = 0; + + pack.object_node.clear(); + + foreach (Object *ob, objects) { + if (params.top_level) { + if (!ob->is_traceable()) { + ++i; + continue; + } + if (!ob->mesh->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()) { + delete_rtcScene(); + stats = NULL; + return; + } + + rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress); + rtcCommitScene(scene); + + pack_primitives(); + + if (progress.get_cancel()) { + delete_rtcScene(); + stats = NULL; + return; + } + + progress.set_substatus("Packing geometry"); + pack_nodes(NULL); + + stats = NULL; } BVHNode *BVHEmbree::widen_children_nodes(const BVHNode * /*root*/) { - assert(!"Must not be called."); - return NULL; + assert(!"Must not be called."); + return NULL; } void BVHEmbree::add_object(Object *ob, int i) { - Mesh *mesh = ob->mesh; - if(params.primitive_mask & PRIMITIVE_ALL_TRIANGLE && mesh->num_triangles() > 0) { - add_triangles(ob, i); - } - if(params.primitive_mask & PRIMITIVE_ALL_CURVE && mesh->num_curves() > 0) { - add_curves(ob, i); - } + Mesh *mesh = ob->mesh; + if (params.primitive_mask & PRIMITIVE_ALL_TRIANGLE && mesh->num_triangles() > 0) { + add_triangles(ob, i); + } + if (params.primitive_mask & PRIMITIVE_ALL_CURVE && mesh->num_curves() > 0) { + add_curves(ob, i); + } } void BVHEmbree::add_instance(Object *ob, int i) { - if(!ob || !ob->mesh) { - assert(0); - return; - } - BVHEmbree *instance_bvh = (BVHEmbree*)(ob->mesh->bvh); - - if(instance_bvh->top_level != this) { - instance_bvh->top_level = this; - } - - const size_t num_motion_steps = ob->use_motion() ? ob->motion.size() : 1; - RTCGeometry geom_id = rtcNewGeometry(rtc_shared_device, RTC_GEOMETRY_TYPE_INSTANCE); - rtcSetGeometryInstancedScene(geom_id, instance_bvh->scene); - rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); - - if(ob->use_motion()) { - for(size_t step = 0; step < num_motion_steps; ++step) { - rtcSetGeometryTransform(geom_id, step, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float*)&ob->motion[step]); - } - } - else { - rtcSetGeometryTransform(geom_id, 0, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float*)&ob->tfm); - } - - pack.prim_index.push_back_slow(-1); - pack.prim_object.push_back_slow(i); - pack.prim_type.push_back_slow(PRIMITIVE_NONE); - pack.prim_tri_index.push_back_slow(-1); - - rtcSetGeometryUserData(geom_id, (void*) instance_bvh->scene); - rtcSetGeometryMask(geom_id, ob->visibility); - - rtcCommitGeometry(geom_id); - rtcAttachGeometryByID(scene, geom_id, i*2); - rtcReleaseGeometry(geom_id); + if (!ob || !ob->mesh) { + assert(0); + return; + } + BVHEmbree *instance_bvh = (BVHEmbree *)(ob->mesh->bvh); + + if (instance_bvh->top_level != this) { + instance_bvh->top_level = this; + } + + const size_t num_motion_steps = ob->use_motion() ? ob->motion.size() : 1; + RTCGeometry geom_id = rtcNewGeometry(rtc_shared_device, RTC_GEOMETRY_TYPE_INSTANCE); + rtcSetGeometryInstancedScene(geom_id, instance_bvh->scene); + rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); + + if (ob->use_motion()) { + for (size_t step = 0; step < num_motion_steps; ++step) { + rtcSetGeometryTransform( + geom_id, step, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float *)&ob->motion[step]); + } + } + else { + rtcSetGeometryTransform(geom_id, 0, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float *)&ob->tfm); + } + + pack.prim_index.push_back_slow(-1); + pack.prim_object.push_back_slow(i); + pack.prim_type.push_back_slow(PRIMITIVE_NONE); + pack.prim_tri_index.push_back_slow(-1); + + rtcSetGeometryUserData(geom_id, (void *)instance_bvh->scene); + rtcSetGeometryMask(geom_id, ob->visibility); + + rtcCommitGeometry(geom_id); + rtcAttachGeometryByID(scene, geom_id, i * 2); + rtcReleaseGeometry(geom_id); } void BVHEmbree::add_triangles(Object *ob, int i) { - size_t prim_offset = pack.prim_index.size(); - Mesh *mesh = ob->mesh; - const Attribute *attr_mP = NULL; - size_t num_motion_steps = 1; - if(mesh->has_motion_blur()) { - attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - if(attr_mP) { - num_motion_steps = mesh->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_triangles = mesh->num_triangles(); - RTCGeometry geom_id = rtcNewGeometry(rtc_shared_device, RTC_GEOMETRY_TYPE_TRIANGLE); - rtcSetGeometryBuildQuality(geom_id, build_quality); - rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); - - unsigned *rtc_indices = (unsigned*)rtcSetNewGeometryBuffer(geom_id, RTC_BUFFER_TYPE_INDEX, 0, - RTC_FORMAT_UINT3, sizeof (int) * 3, num_triangles); - assert(rtc_indices); - if(!rtc_indices) { - VLOG(1) << "Embree could not create new geometry buffer for mesh " << mesh->name.c_str() << ".\n"; - return; - } - for(size_t j = 0; j < num_triangles; ++j) { - Mesh::Triangle t = mesh->get_triangle(j); - rtc_indices[j*3] = t.v[0]; - rtc_indices[j*3+1] = t.v[1]; - rtc_indices[j*3+2] = t.v[2]; - } - - update_tri_vertex_buffer(geom_id, mesh); - - size_t prim_object_size = pack.prim_object.size(); - pack.prim_object.resize(prim_object_size + num_triangles); - size_t prim_type_size = pack.prim_type.size(); - pack.prim_type.resize(prim_type_size + num_triangles); - size_t prim_index_size = pack.prim_index.size(); - pack.prim_index.resize(prim_index_size + num_triangles); - pack.prim_tri_index.resize(prim_index_size + num_triangles); - int prim_type = (num_motion_steps > 1 ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE); - - for(size_t j = 0; j < num_triangles; ++j) { - pack.prim_object[prim_object_size + j] = i; - pack.prim_type[prim_type_size + j] = prim_type; - pack.prim_index[prim_index_size + j] = j; - pack.prim_tri_index[prim_index_size + j] = j; - } - - rtcSetGeometryUserData(geom_id, (void*) prim_offset); - rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func); - rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func); - rtcSetGeometryMask(geom_id, ob->visibility); - - rtcCommitGeometry(geom_id); - rtcAttachGeometryByID(scene, geom_id, i*2); - rtcReleaseGeometry(geom_id); + size_t prim_offset = pack.prim_index.size(); + Mesh *mesh = ob->mesh; + const Attribute *attr_mP = NULL; + size_t num_motion_steps = 1; + if (mesh->has_motion_blur()) { + attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + if (attr_mP) { + num_motion_steps = mesh->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_triangles = mesh->num_triangles(); + RTCGeometry geom_id = rtcNewGeometry(rtc_shared_device, RTC_GEOMETRY_TYPE_TRIANGLE); + rtcSetGeometryBuildQuality(geom_id, build_quality); + rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); + + unsigned *rtc_indices = (unsigned *)rtcSetNewGeometryBuffer( + geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(int) * 3, num_triangles); + assert(rtc_indices); + if (!rtc_indices) { + VLOG(1) << "Embree could not create new geometry buffer for mesh " << mesh->name.c_str() + << ".\n"; + return; + } + for (size_t j = 0; j < num_triangles; ++j) { + Mesh::Triangle t = mesh->get_triangle(j); + rtc_indices[j * 3] = t.v[0]; + rtc_indices[j * 3 + 1] = t.v[1]; + rtc_indices[j * 3 + 2] = t.v[2]; + } + + update_tri_vertex_buffer(geom_id, mesh); + + size_t prim_object_size = pack.prim_object.size(); + pack.prim_object.resize(prim_object_size + num_triangles); + size_t prim_type_size = pack.prim_type.size(); + pack.prim_type.resize(prim_type_size + num_triangles); + size_t prim_index_size = pack.prim_index.size(); + pack.prim_index.resize(prim_index_size + num_triangles); + pack.prim_tri_index.resize(prim_index_size + num_triangles); + int prim_type = (num_motion_steps > 1 ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE); + + for (size_t j = 0; j < num_triangles; ++j) { + pack.prim_object[prim_object_size + j] = i; + pack.prim_type[prim_type_size + j] = prim_type; + pack.prim_index[prim_index_size + j] = j; + pack.prim_tri_index[prim_index_size + j] = j; + } + + rtcSetGeometryUserData(geom_id, (void *)prim_offset); + rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func); + rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func); + rtcSetGeometryMask(geom_id, ob->visibility); + + rtcCommitGeometry(geom_id); + rtcAttachGeometryByID(scene, geom_id, i * 2); + rtcReleaseGeometry(geom_id); } -void BVHEmbree::update_tri_vertex_buffer(RTCGeometry geom_id, const Mesh* mesh) +void BVHEmbree::update_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh) { - const Attribute *attr_mP = NULL; - size_t num_motion_steps = 1; - int t_mid = 0; - if(mesh->has_motion_blur()) { - attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - if(attr_mP) { - num_motion_steps = mesh->motion_steps; - t_mid = (num_motion_steps - 1) / 2; - 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->verts.size(); - - for(int t = 0; t < num_motion_steps; ++t) { - const float3 *verts; - if(t == t_mid) { - verts = &mesh->verts[0]; - } - else { - int t_ = (t > t_mid) ? (t - 1) : t; - verts = &attr_mP->data_float3()[t_ * num_verts]; - } - - float *rtc_verts = (float*) rtcSetNewGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t, - RTC_FORMAT_FLOAT3, sizeof(float) * 3, num_verts + 1); - assert(rtc_verts); - if(rtc_verts) { - for(size_t j = 0; j < num_verts; ++j) { - rtc_verts[0] = verts[j].x; - rtc_verts[1] = verts[j].y; - rtc_verts[2] = verts[j].z; - rtc_verts += 3; - } - } - } + const Attribute *attr_mP = NULL; + size_t num_motion_steps = 1; + int t_mid = 0; + if (mesh->has_motion_blur()) { + attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + if (attr_mP) { + num_motion_steps = mesh->motion_steps; + t_mid = (num_motion_steps - 1) / 2; + 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->verts.size(); + + for (int t = 0; t < num_motion_steps; ++t) { + const float3 *verts; + if (t == t_mid) { + verts = &mesh->verts[0]; + } + else { + int t_ = (t > t_mid) ? (t - 1) : t; + verts = &attr_mP->data_float3()[t_ * num_verts]; + } + + float *rtc_verts = (float *)rtcSetNewGeometryBuffer( + geom_id, RTC_BUFFER_TYPE_VERTEX, t, RTC_FORMAT_FLOAT3, sizeof(float) * 3, num_verts + 1); + assert(rtc_verts); + if (rtc_verts) { + for (size_t j = 0; j < num_verts; ++j) { + rtc_verts[0] = verts[j].x; + rtc_verts[1] = verts[j].y; + rtc_verts[2] = verts[j].z; + rtc_verts += 3; + } + } + } } -void BVHEmbree::update_curve_vertex_buffer(RTCGeometry geom_id, const Mesh* mesh) +void BVHEmbree::update_curve_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh) { - const Attribute *attr_mP = NULL; - size_t num_motion_steps = 1; - if(mesh->has_motion_blur()) { - attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - if(attr_mP) { - num_motion_steps = mesh->motion_steps; - } - } - - const size_t num_curves = mesh->num_curves(); - size_t num_keys = 0; - for(size_t j = 0; j < num_curves; ++j) { - const Mesh::Curve c = mesh->get_curve(j); - num_keys += c.num_keys; - } - - /* Copy the CV data to Embree */ - const int t_mid = (num_motion_steps - 1) / 2; - const float *curve_radius = &mesh->curve_radius[0]; - for(int t = 0; t < num_motion_steps; ++t) { - const float3 *verts; - if(t == t_mid || attr_mP == NULL) { - verts = &mesh->curve_keys[0]; - } - else { - int t_ = (t > t_mid) ? (t - 1) : t; - verts = &attr_mP->data_float3()[t_ * num_keys]; - } - - float4 *rtc_verts = (float4*)rtcSetNewGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t, - RTC_FORMAT_FLOAT4, sizeof (float) * 4, num_keys); - float4 *rtc_tangents = NULL; - if(use_curves) { - rtc_tangents = (float4*)rtcSetNewGeometryBuffer(geom_id, RTC_BUFFER_TYPE_TANGENT, t, - RTC_FORMAT_FLOAT4, sizeof (float) * 4, num_keys); - assert(rtc_tangents); - } - assert(rtc_verts); - if(rtc_verts) { - if(use_curves && rtc_tangents) { - const size_t num_curves = mesh->num_curves(); - for(size_t j = 0; j < num_curves; ++j) { - Mesh::Curve c = mesh->get_curve(j); - int fk = c.first_key; - rtc_verts[0] = float3_to_float4(verts[fk]); - rtc_verts[0].w = curve_radius[fk]; - rtc_tangents[0] = float3_to_float4(verts[fk + 1] - verts[fk]); - rtc_tangents[0].w = curve_radius[fk + 1] - curve_radius[fk]; - ++fk; - int k = 1; - for(;k < c.num_segments(); ++k, ++fk) { - rtc_verts[k] = float3_to_float4(verts[fk]); - rtc_verts[k].w = curve_radius[fk]; - rtc_tangents[k] = float3_to_float4((verts[fk + 1] - verts[fk - 1]) * 0.5f); - rtc_tangents[k].w = (curve_radius[fk + 1] - curve_radius[fk - 1]) * 0.5f; - } - rtc_verts[k] = float3_to_float4(verts[fk]); - rtc_verts[k].w = curve_radius[fk]; - rtc_tangents[k] = float3_to_float4(verts[fk] - verts[fk - 1]); - rtc_tangents[k].w = curve_radius[fk] - curve_radius[fk - 1]; - rtc_verts += c.num_keys; - rtc_tangents += c.num_keys; - } - } - else { - for(size_t j = 0; j < num_keys; ++j) { - rtc_verts[j] = float3_to_float4(verts[j]); - rtc_verts[j].w = curve_radius[j]; - } - } - } - } + const Attribute *attr_mP = NULL; + size_t num_motion_steps = 1; + if (mesh->has_motion_blur()) { + attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + if (attr_mP) { + num_motion_steps = mesh->motion_steps; + } + } + + const size_t num_curves = mesh->num_curves(); + size_t num_keys = 0; + for (size_t j = 0; j < num_curves; ++j) { + const Mesh::Curve c = mesh->get_curve(j); + num_keys += c.num_keys; + } + + /* Copy the CV data to Embree */ + const int t_mid = (num_motion_steps - 1) / 2; + const float *curve_radius = &mesh->curve_radius[0]; + for (int t = 0; t < num_motion_steps; ++t) { + const float3 *verts; + if (t == t_mid || attr_mP == NULL) { + verts = &mesh->curve_keys[0]; + } + else { + int t_ = (t > t_mid) ? (t - 1) : t; + verts = &attr_mP->data_float3()[t_ * num_keys]; + } + + float4 *rtc_verts = (float4 *)rtcSetNewGeometryBuffer( + geom_id, RTC_BUFFER_TYPE_VERTEX, t, RTC_FORMAT_FLOAT4, sizeof(float) * 4, num_keys); + float4 *rtc_tangents = NULL; + if (use_curves) { + rtc_tangents = (float4 *)rtcSetNewGeometryBuffer( + geom_id, RTC_BUFFER_TYPE_TANGENT, t, RTC_FORMAT_FLOAT4, sizeof(float) * 4, num_keys); + assert(rtc_tangents); + } + assert(rtc_verts); + if (rtc_verts) { + if (use_curves && rtc_tangents) { + const size_t num_curves = mesh->num_curves(); + for (size_t j = 0; j < num_curves; ++j) { + Mesh::Curve c = mesh->get_curve(j); + int fk = c.first_key; + rtc_verts[0] = float3_to_float4(verts[fk]); + rtc_verts[0].w = curve_radius[fk]; + rtc_tangents[0] = float3_to_float4(verts[fk + 1] - verts[fk]); + rtc_tangents[0].w = curve_radius[fk + 1] - curve_radius[fk]; + ++fk; + int k = 1; + for (; k < c.num_segments(); ++k, ++fk) { + rtc_verts[k] = float3_to_float4(verts[fk]); + rtc_verts[k].w = curve_radius[fk]; + rtc_tangents[k] = float3_to_float4((verts[fk + 1] - verts[fk - 1]) * 0.5f); + rtc_tangents[k].w = (curve_radius[fk + 1] - curve_radius[fk - 1]) * 0.5f; + } + rtc_verts[k] = float3_to_float4(verts[fk]); + rtc_verts[k].w = curve_radius[fk]; + rtc_tangents[k] = float3_to_float4(verts[fk] - verts[fk - 1]); + rtc_tangents[k].w = curve_radius[fk] - curve_radius[fk - 1]; + rtc_verts += c.num_keys; + rtc_tangents += c.num_keys; + } + } + else { + for (size_t j = 0; j < num_keys; ++j) { + rtc_verts[j] = float3_to_float4(verts[j]); + rtc_verts[j].w = curve_radius[j]; + } + } + } + } } void BVHEmbree::add_curves(Object *ob, int i) { - size_t prim_offset = pack.prim_index.size(); - const Mesh *mesh = ob->mesh; - const Attribute *attr_mP = NULL; - size_t num_motion_steps = 1; - if(mesh->has_motion_blur()) { - attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - if(attr_mP) { - num_motion_steps = mesh->motion_steps; - } - } - - const size_t num_curves = mesh->num_curves(); - size_t num_segments = 0; - for(size_t j = 0; j < num_curves; ++j) { - Mesh::Curve c = mesh->get_curve(j); - assert(c.num_segments() > 0); - num_segments += c.num_segments(); - } - - /* Make room for Cycles specific data. */ - size_t prim_object_size = pack.prim_object.size(); - pack.prim_object.resize(prim_object_size + num_segments); - size_t prim_type_size = pack.prim_type.size(); - pack.prim_type.resize(prim_type_size + num_segments); - size_t prim_index_size = pack.prim_index.size(); - pack.prim_index.resize(prim_index_size + num_segments); - size_t prim_tri_index_size = pack.prim_index.size(); - pack.prim_tri_index.resize(prim_tri_index_size + num_segments); - - enum RTCGeometryType type = (!use_curves) ? RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE : - (use_ribbons ? RTC_GEOMETRY_TYPE_FLAT_HERMITE_CURVE : - RTC_GEOMETRY_TYPE_ROUND_HERMITE_CURVE); - - RTCGeometry geom_id = rtcNewGeometry(rtc_shared_device, type); - rtcSetGeometryTessellationRate(geom_id, curve_subdivisions); - unsigned *rtc_indices = (unsigned*) rtcSetNewGeometryBuffer(geom_id, RTC_BUFFER_TYPE_INDEX, 0, - RTC_FORMAT_UINT, sizeof (int), num_segments); - size_t rtc_index = 0; - for(size_t j = 0; j < num_curves; ++j) { - Mesh::Curve c = mesh->get_curve(j); - for(size_t k = 0; k < c.num_segments(); ++k) { - rtc_indices[rtc_index] = c.first_key + k; - /* Cycles specific data. */ - pack.prim_object[prim_object_size + rtc_index] = i; - pack.prim_type[prim_type_size + rtc_index] = (PRIMITIVE_PACK_SEGMENT(num_motion_steps > 1 ? - PRIMITIVE_MOTION_CURVE : PRIMITIVE_CURVE, k)); - pack.prim_index[prim_index_size + rtc_index] = j; - pack.prim_tri_index[prim_tri_index_size + rtc_index] = rtc_index; - - ++rtc_index; - } - } - - rtcSetGeometryBuildQuality(geom_id, build_quality); - rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); - - update_curve_vertex_buffer(geom_id, mesh); - - rtcSetGeometryUserData(geom_id, (void*) prim_offset); - rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func); - rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func); - rtcSetGeometryMask(geom_id, ob->visibility); - - rtcCommitGeometry(geom_id); - rtcAttachGeometryByID(scene, geom_id, i * 2 + 1); - rtcReleaseGeometry(geom_id); + size_t prim_offset = pack.prim_index.size(); + const Mesh *mesh = ob->mesh; + const Attribute *attr_mP = NULL; + size_t num_motion_steps = 1; + if (mesh->has_motion_blur()) { + attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + if (attr_mP) { + num_motion_steps = mesh->motion_steps; + } + } + + const size_t num_curves = mesh->num_curves(); + size_t num_segments = 0; + for (size_t j = 0; j < num_curves; ++j) { + Mesh::Curve c = mesh->get_curve(j); + assert(c.num_segments() > 0); + num_segments += c.num_segments(); + } + + /* Make room for Cycles specific data. */ + size_t prim_object_size = pack.prim_object.size(); + pack.prim_object.resize(prim_object_size + num_segments); + size_t prim_type_size = pack.prim_type.size(); + pack.prim_type.resize(prim_type_size + num_segments); + size_t prim_index_size = pack.prim_index.size(); + pack.prim_index.resize(prim_index_size + num_segments); + size_t prim_tri_index_size = pack.prim_index.size(); + pack.prim_tri_index.resize(prim_tri_index_size + num_segments); + + enum RTCGeometryType type = (!use_curves) ? + RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE : + (use_ribbons ? RTC_GEOMETRY_TYPE_FLAT_HERMITE_CURVE : + RTC_GEOMETRY_TYPE_ROUND_HERMITE_CURVE); + + RTCGeometry geom_id = rtcNewGeometry(rtc_shared_device, type); + rtcSetGeometryTessellationRate(geom_id, curve_subdivisions); + unsigned *rtc_indices = (unsigned *)rtcSetNewGeometryBuffer( + geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof(int), num_segments); + size_t rtc_index = 0; + for (size_t j = 0; j < num_curves; ++j) { + Mesh::Curve c = mesh->get_curve(j); + for (size_t k = 0; k < c.num_segments(); ++k) { + rtc_indices[rtc_index] = c.first_key + k; + /* Cycles specific data. */ + pack.prim_object[prim_object_size + rtc_index] = i; + pack.prim_type[prim_type_size + rtc_index] = (PRIMITIVE_PACK_SEGMENT( + num_motion_steps > 1 ? PRIMITIVE_MOTION_CURVE : PRIMITIVE_CURVE, k)); + pack.prim_index[prim_index_size + rtc_index] = j; + pack.prim_tri_index[prim_tri_index_size + rtc_index] = rtc_index; + + ++rtc_index; + } + } + + rtcSetGeometryBuildQuality(geom_id, build_quality); + rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); + + update_curve_vertex_buffer(geom_id, mesh); + + rtcSetGeometryUserData(geom_id, (void *)prim_offset); + rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func); + rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func); + rtcSetGeometryMask(geom_id, ob->visibility); + + rtcCommitGeometry(geom_id); + rtcAttachGeometryByID(scene, geom_id, i * 2 + 1); + rtcReleaseGeometry(geom_id); } void BVHEmbree::pack_nodes(const BVHNode *) { - /* Quite a bit of this code is for compatibility with Cycles' native BVH. */ - if(!params.top_level) { - return; - } - - for(size_t i = 0; i < pack.prim_index.size(); ++i) { - if(pack.prim_index[i] != -1) { - if(pack.prim_type[i] & PRIMITIVE_ALL_CURVE) - pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->curve_offset; - else - pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->tri_offset; - } - } - - size_t prim_offset = pack.prim_index.size(); - - /* reserve */ - size_t prim_index_size = pack.prim_index.size(); - size_t prim_tri_verts_size = pack.prim_tri_verts.size(); - - size_t pack_prim_index_offset = prim_index_size; - size_t pack_prim_tri_verts_offset = prim_tri_verts_size; - size_t object_offset = 0; - - map<Mesh*, int> mesh_map; - - foreach(Object *ob, objects) { - Mesh *mesh = ob->mesh; - BVH *bvh = mesh->bvh; - - if(mesh->need_build_bvh()) { - if(mesh_map.find(mesh) == mesh_map.end()) { - prim_index_size += bvh->pack.prim_index.size(); - prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); - mesh_map[mesh] = 1; - } - } - } - - mesh_map.clear(); - - pack.prim_index.resize(prim_index_size); - pack.prim_type.resize(prim_index_size); - pack.prim_object.resize(prim_index_size); - pack.prim_visibility.clear(); - pack.prim_tri_verts.resize(prim_tri_verts_size); - pack.prim_tri_index.resize(prim_index_size); - pack.object_node.resize(objects.size()); - - int *pack_prim_index = (pack.prim_index.size())? &pack.prim_index[0]: NULL; - int *pack_prim_type = (pack.prim_type.size())? &pack.prim_type[0]: NULL; - int *pack_prim_object = (pack.prim_object.size())? &pack.prim_object[0]: NULL; - float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size())? &pack.prim_tri_verts[0]: NULL; - uint *pack_prim_tri_index = (pack.prim_tri_index.size())? &pack.prim_tri_index[0]: NULL; - - /* merge */ - foreach(Object *ob, objects) { - Mesh *mesh = ob->mesh; - - /* 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(!mesh->need_build_bvh()) { - pack.object_node[object_offset++] = prim_offset; - continue; - } - - /* if mesh already added once, don't add it again, but used set - * node offset for this object */ - map<Mesh*, int>::iterator it = mesh_map.find(mesh); - - if(mesh_map.find(mesh) != mesh_map.end()) { - int noffset = it->second; - pack.object_node[object_offset++] = noffset; - continue; - } - - BVHEmbree *bvh = (BVHEmbree*)mesh->bvh; - - rtc_memory_monitor_func(stats, unaccounted_mem, true); - unaccounted_mem = 0; - - int mesh_tri_offset = mesh->tri_offset; - int mesh_curve_offset = mesh->curve_offset; - - /* fill in node indexes for instances */ - pack.object_node[object_offset++] = prim_offset; - - mesh_map[mesh] = pack.object_node[object_offset-1]; - - /* merge primitive, object and triangle indexes */ - if(bvh->pack.prim_index.size()) { - size_t bvh_prim_index_size = bvh->pack.prim_index.size(); - int *bvh_prim_index = &bvh->pack.prim_index[0]; - int *bvh_prim_type = &bvh->pack.prim_type[0]; - uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0]; - - for(size_t i = 0; i < bvh_prim_index_size; ++i) { - if(bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { - pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_curve_offset; - pack_prim_tri_index[pack_prim_index_offset] = -1; - } - else { - pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_tri_offset; - pack_prim_tri_index[pack_prim_index_offset] = - bvh_prim_tri_index[i] + pack_prim_tri_verts_offset; - } - - pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i]; - pack_prim_object[pack_prim_index_offset] = 0; - - ++pack_prim_index_offset; - } - } - - /* Merge triangle vertices data. */ - if(bvh->pack.prim_tri_verts.size()) { - const size_t prim_tri_size = bvh->pack.prim_tri_verts.size(); - memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset, - &bvh->pack.prim_tri_verts[0], - prim_tri_size*sizeof(float4)); - pack_prim_tri_verts_offset += prim_tri_size; - } - - prim_offset += bvh->pack.prim_index.size(); - } + /* Quite a bit of this code is for compatibility with Cycles' native BVH. */ + if (!params.top_level) { + return; + } + + for (size_t i = 0; i < pack.prim_index.size(); ++i) { + if (pack.prim_index[i] != -1) { + if (pack.prim_type[i] & PRIMITIVE_ALL_CURVE) + pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->curve_offset; + else + pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->tri_offset; + } + } + + size_t prim_offset = pack.prim_index.size(); + + /* reserve */ + size_t prim_index_size = pack.prim_index.size(); + size_t prim_tri_verts_size = pack.prim_tri_verts.size(); + + size_t pack_prim_index_offset = prim_index_size; + size_t pack_prim_tri_verts_offset = prim_tri_verts_size; + size_t object_offset = 0; + + map<Mesh *, int> mesh_map; + + foreach (Object *ob, objects) { + Mesh *mesh = ob->mesh; + BVH *bvh = mesh->bvh; + + if (mesh->need_build_bvh()) { + if (mesh_map.find(mesh) == mesh_map.end()) { + prim_index_size += bvh->pack.prim_index.size(); + prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); + mesh_map[mesh] = 1; + } + } + } + + mesh_map.clear(); + + pack.prim_index.resize(prim_index_size); + pack.prim_type.resize(prim_index_size); + pack.prim_object.resize(prim_index_size); + pack.prim_visibility.clear(); + pack.prim_tri_verts.resize(prim_tri_verts_size); + pack.prim_tri_index.resize(prim_index_size); + pack.object_node.resize(objects.size()); + + int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL; + int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL; + int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL; + float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL; + uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL; + + /* merge */ + foreach (Object *ob, objects) { + Mesh *mesh = ob->mesh; + + /* 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 (!mesh->need_build_bvh()) { + pack.object_node[object_offset++] = prim_offset; + continue; + } + + /* if mesh already added once, don't add it again, but used set + * node offset for this object */ + map<Mesh *, int>::iterator it = mesh_map.find(mesh); + + if (mesh_map.find(mesh) != mesh_map.end()) { + int noffset = it->second; + pack.object_node[object_offset++] = noffset; + continue; + } + + BVHEmbree *bvh = (BVHEmbree *)mesh->bvh; + + rtc_memory_monitor_func(stats, unaccounted_mem, true); + unaccounted_mem = 0; + + int mesh_tri_offset = mesh->tri_offset; + int mesh_curve_offset = mesh->curve_offset; + + /* fill in node indexes for instances */ + pack.object_node[object_offset++] = prim_offset; + + mesh_map[mesh] = pack.object_node[object_offset - 1]; + + /* merge primitive, object and triangle indexes */ + if (bvh->pack.prim_index.size()) { + size_t bvh_prim_index_size = bvh->pack.prim_index.size(); + int *bvh_prim_index = &bvh->pack.prim_index[0]; + int *bvh_prim_type = &bvh->pack.prim_type[0]; + uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0]; + + for (size_t i = 0; i < bvh_prim_index_size; ++i) { + if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { + pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_curve_offset; + pack_prim_tri_index[pack_prim_index_offset] = -1; + } + else { + pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_tri_offset; + pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] + + pack_prim_tri_verts_offset; + } + + pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i]; + pack_prim_object[pack_prim_index_offset] = 0; + + ++pack_prim_index_offset; + } + } + + /* Merge triangle vertices data. */ + if (bvh->pack.prim_tri_verts.size()) { + const size_t prim_tri_size = bvh->pack.prim_tri_verts.size(); + memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset, + &bvh->pack.prim_tri_verts[0], + prim_tri_size * sizeof(float4)); + pack_prim_tri_verts_offset += prim_tri_size; + } + + prim_offset += bvh->pack.prim_index.size(); + } } void BVHEmbree::refit_nodes() { - /* Update all vertex buffers, then tell Embree to rebuild/-fit the BVHs. */ - unsigned geom_id = 0; - foreach(Object *ob, objects) { - if(!params.top_level || (ob->is_traceable() && !ob->mesh->is_instanced())) { - if(params.primitive_mask & PRIMITIVE_ALL_TRIANGLE && ob->mesh->num_triangles() > 0) { - update_tri_vertex_buffer(rtcGetGeometry(scene, geom_id), ob->mesh); - rtcCommitGeometry(rtcGetGeometry(scene,geom_id)); - } - - if(params.primitive_mask & PRIMITIVE_ALL_CURVE && ob->mesh->num_curves() > 0) { - update_curve_vertex_buffer(rtcGetGeometry(scene, geom_id+1), ob->mesh); - rtcCommitGeometry(rtcGetGeometry(scene,geom_id+1)); - } - } - geom_id += 2; - } - rtcCommitScene(scene); + /* Update all vertex buffers, then tell Embree to rebuild/-fit the BVHs. */ + unsigned geom_id = 0; + foreach (Object *ob, objects) { + if (!params.top_level || (ob->is_traceable() && !ob->mesh->is_instanced())) { + if (params.primitive_mask & PRIMITIVE_ALL_TRIANGLE && ob->mesh->num_triangles() > 0) { + update_tri_vertex_buffer(rtcGetGeometry(scene, geom_id), ob->mesh); + rtcCommitGeometry(rtcGetGeometry(scene, geom_id)); + } + + if (params.primitive_mask & PRIMITIVE_ALL_CURVE && ob->mesh->num_curves() > 0) { + update_curve_vertex_buffer(rtcGetGeometry(scene, geom_id + 1), ob->mesh); + rtcCommitGeometry(rtcGetGeometry(scene, geom_id + 1)); + } + } + geom_id += 2; + } + rtcCommitScene(scene); } CCL_NAMESPACE_END -#endif /* WITH_EMBREE */ +#endif /* WITH_EMBREE */ diff --git a/intern/cycles/bvh/bvh_embree.h b/intern/cycles/bvh/bvh_embree.h index 983b6dc07da..60702713583 100644 --- a/intern/cycles/bvh/bvh_embree.h +++ b/intern/cycles/bvh/bvh_embree.h @@ -19,65 +19,68 @@ #ifdef WITH_EMBREE -#include <embree3/rtcore.h> -#include <embree3/rtcore_scene.h> +# include <embree3/rtcore.h> +# include <embree3/rtcore_scene.h> -#include "bvh/bvh.h" -#include "bvh/bvh_params.h" +# include "bvh/bvh.h" +# include "bvh/bvh_params.h" -#include "util/util_thread.h" -#include "util/util_types.h" -#include "util/util_vector.h" +# include "util/util_thread.h" +# include "util/util_types.h" +# include "util/util_vector.h" CCL_NAMESPACE_BEGIN class Mesh; -class BVHEmbree : public BVH -{ -public: - virtual void build(Progress& progress, Stats *stats) override; - virtual ~BVHEmbree(); - RTCScene scene; - static void destroy(RTCScene); - - /* Building process. */ - virtual BVHNode *widen_children_nodes(const BVHNode *root) override; - -protected: - friend class BVH; - BVHEmbree(const BVHParams& params, const vector<Object*>& objects); - - virtual void pack_nodes(const BVHNode*) override; - virtual void refit_nodes() override; - - void add_object(Object *ob, int i); - void add_instance(Object *ob, int i); - void add_curves(Object *ob, int i); - void add_triangles(Object *ob, int i); - - ssize_t mem_used; - - void add_delayed_delete_scene(RTCScene scene) { delayed_delete_scenes.push_back(scene); } - BVHEmbree *top_level; -private: - void delete_rtcScene(); - void update_tri_vertex_buffer(RTCGeometry geom_id, const Mesh* mesh); - void update_curve_vertex_buffer(RTCGeometry geom_id, const Mesh* mesh); - - static RTCDevice rtc_shared_device; - static int rtc_shared_users; - static thread_mutex rtc_shared_mutex; - - Stats *stats; - vector<RTCScene> delayed_delete_scenes; - int curve_subdivisions; - enum RTCBuildQuality build_quality; - bool use_curves, use_ribbons, dynamic_scene; +class BVHEmbree : public BVH { + public: + virtual void build(Progress &progress, Stats *stats) override; + virtual ~BVHEmbree(); + RTCScene scene; + static void destroy(RTCScene); + + /* Building process. */ + virtual BVHNode *widen_children_nodes(const BVHNode *root) override; + + protected: + friend class BVH; + BVHEmbree(const BVHParams ¶ms, const vector<Object *> &objects); + + virtual void pack_nodes(const BVHNode *) override; + virtual void refit_nodes() override; + + void add_object(Object *ob, int i); + void add_instance(Object *ob, int i); + void add_curves(Object *ob, int i); + void add_triangles(Object *ob, int i); + + ssize_t mem_used; + + void add_delayed_delete_scene(RTCScene scene) + { + delayed_delete_scenes.push_back(scene); + } + BVHEmbree *top_level; + + private: + void delete_rtcScene(); + void update_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh); + void update_curve_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh); + + static RTCDevice rtc_shared_device; + static int rtc_shared_users; + static thread_mutex rtc_shared_mutex; + + Stats *stats; + vector<RTCScene> delayed_delete_scenes; + int curve_subdivisions; + enum RTCBuildQuality build_quality; + bool use_curves, use_ribbons, dynamic_scene; }; CCL_NAMESPACE_END -#endif /* WITH_EMBREE */ +#endif /* WITH_EMBREE */ -#endif /* __BVH_EMBREE_H__ */ +#endif /* __BVH_EMBREE_H__ */ diff --git a/intern/cycles/bvh/bvh_node.cpp b/intern/cycles/bvh/bvh_node.cpp index 614fb6be88a..38b554acfbf 100644 --- a/intern/cycles/bvh/bvh_node.cpp +++ b/intern/cycles/bvh/bvh_node.cpp @@ -28,199 +28,197 @@ CCL_NAMESPACE_BEGIN 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; + 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; } void BVHNode::deleteSubtree() { - for(int i = 0; i < num_children(); i++) - if(get_child(i)) - get_child(i)->deleteSubtree(); + for (int i = 0; i < num_children(); i++) + if (get_child(i)) + get_child(i)->deleteSubtree(); - delete this; + delete this; } -float BVHNode::computeSubtreeSAHCost(const BVHParams& p, float probability) const +float BVHNode::computeSubtreeSAHCost(const BVHParams &p, float probability) const { - float SAH = probability * p.cost(num_children(), num_triangles()); + 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()); - } + 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; + return SAH; } uint BVHNode::update_visibility() { - if(!is_leaf() && visibility == 0) { - InnerNode *inner = (InnerNode*)this; - BVHNode *child0 = inner->children[0]; - BVHNode *child1 = inner->children[1]; + if (!is_leaf() && visibility == 0) { + InnerNode *inner = (InnerNode *)this; + BVHNode *child0 = inner->children[0]; + BVHNode *child1 = inner->children[1]; - visibility = child0->update_visibility()|child1->update_visibility(); - } + visibility = child0->update_visibility() | child1->update_visibility(); + } - return visibility; + return visibility; } void BVHNode::update_time() { - if(!is_leaf()) { - InnerNode *inner = (InnerNode*)this; - BVHNode *child0 = inner->children[0]; - BVHNode *child1 = inner->children[1]; - child0->update_time(); - child1->update_time(); - time_from = min(child0->time_from, child1->time_from); - time_to = max(child0->time_to, child1->time_to); - } + if (!is_leaf()) { + InnerNode *inner = (InnerNode *)this; + BVHNode *child0 = inner->children[0]; + BVHNode *child1 = inner->children[1]; + 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 wile where writing is happening. */ - FILE *stream; - /* Unique identifier of the node current. */ - int id; + /* Descriptor of wile 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 = NULL) +void dump_subtree(DumpTraversalContext *context, const BVHNode *node, const BVHNode *parent = NULL) { - 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 != NULL) { - 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); - } + 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 != NULL) { + 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 == NULL) { - return; - } - context.id = 0; - fprintf(context.stream, "digraph BVH {\n"); - dump_subtree(&context, this); - fprintf(context.stream, "}\n"); - fclose(context.stream); + DumpTraversalContext context; + context.stream = fopen(filename, "w"); + if (context.stream == NULL) { + 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(int depth) const { - for(int i = 0; i < depth; i++) - printf(" "); + for (int i = 0; i < depth; i++) + printf(" "); - printf("inner node %p\n", (void*)this); + printf("inner node %p\n", (void *)this); - if(children[0]) - children[0]->print(depth+1); - if(children[1]) - children[1]->print(depth+1); + if (children[0]) + children[0]->print(depth + 1); + if (children[1]) + children[1]->print(depth + 1); } void LeafNode::print(int depth) const { - for(int i = 0; i < depth; i++) - printf(" "); + for (int i = 0; i < depth; i++) + printf(" "); - printf("leaf node %d to %d\n", lo, hi); + printf("leaf node %d to %d\n", lo, hi); } CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh_node.h b/intern/cycles/bvh/bvh_node.h index d9105d69739..797dd5b694e 100644 --- a/intern/cycles/bvh/bvh_node.h +++ b/intern/cycles/bvh/bvh_node.h @@ -24,227 +24,232 @@ 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, + 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() - { - delete aligned_space; - } - - virtual bool is_leaf() const = 0; - virtual int num_children() const = 0; - virtual BVHNode *get_child(int i) const = 0; - virtual int num_triangles() const { return 0; } - virtual void print(int depth = 0) const = 0; - - inline void set_aligned_space(const Transform& aligned_space) - { - is_unaligned = true; - if(this->aligned_space == NULL) { - this->aligned_space = new Transform(aligned_space); - } - else { - *this->aligned_space = aligned_space; - } - } - - inline Transform get_aligned_space() const - { - if(aligned_space == NULL) { - return transform_identity(); - } - return *aligned_space; - } - - inline 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, float probability = 1.0f) const; - void deleteSubtree(); - - 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; - - bool is_unaligned; - - /* TODO(sergey): Can be stored as 3x3 matrix, but better to have some - * utilities and type defines in util_transform first. - */ - Transform *aligned_space; - - float time_from, time_to; - -protected: - explicit BVHNode(const BoundBox& bounds) - : bounds(bounds), - visibility(0), - is_unaligned(false), - aligned_space(NULL), - time_from(0.0f), - time_to(1.0f) - { - } - - explicit BVHNode(const BVHNode& other) - : bounds(other.bounds), - visibility(other.visibility), - is_unaligned(other.is_unaligned), - aligned_space(NULL), - time_from(other.time_from), - time_to(other.time_to) - { - if(other.aligned_space != NULL) { - assert(other.is_unaligned); - aligned_space = new Transform(); - *aligned_space = *other.aligned_space; - } - else { - assert(!other.is_unaligned); - } - } +class BVHNode { + public: + virtual ~BVHNode() + { + delete aligned_space; + } + + virtual bool is_leaf() const = 0; + virtual int num_children() const = 0; + virtual BVHNode *get_child(int i) const = 0; + virtual int num_triangles() const + { + return 0; + } + virtual void print(int depth = 0) const = 0; + + inline void set_aligned_space(const Transform &aligned_space) + { + is_unaligned = true; + if (this->aligned_space == NULL) { + this->aligned_space = new Transform(aligned_space); + } + else { + *this->aligned_space = aligned_space; + } + } + + inline Transform get_aligned_space() const + { + if (aligned_space == NULL) { + return transform_identity(); + } + return *aligned_space; + } + + inline 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, float probability = 1.0f) const; + void deleteSubtree(); + + 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; + + bool is_unaligned; + + /* TODO(sergey): Can be stored as 3x3 matrix, but better to have some + * utilities and type defines in util_transform first. + */ + Transform *aligned_space; + + float time_from, time_to; + + protected: + explicit BVHNode(const BoundBox &bounds) + : bounds(bounds), + visibility(0), + is_unaligned(false), + aligned_space(NULL), + time_from(0.0f), + time_to(1.0f) + { + } + + explicit BVHNode(const BVHNode &other) + : bounds(other.bounds), + visibility(other.visibility), + is_unaligned(other.is_unaligned), + aligned_space(NULL), + time_from(other.time_from), + time_to(other.time_to) + { + if (other.aligned_space != NULL) { + assert(other.is_unaligned); + aligned_space = new Transform(); + *aligned_space = *other.aligned_space; + } + else { + assert(!other.is_unaligned); + } + } }; -class InnerNode : public BVHNode -{ -public: - static constexpr int kNumMaxChildren = 8; - - InnerNode(const BoundBox& bounds, - BVHNode* child0, - BVHNode* child1) - : BVHNode(bounds), - num_children_(2) - { - children[0] = child0; - children[1] = child1; - reset_unused_children(); - - if(child0 && child1) { - visibility = child0->visibility | child1->visibility; - } - else { - /* Happens on build cancel. */ - visibility = 0; - } - } - - InnerNode(const BoundBox& bounds, - BVHNode** children, - const int num_children) - : BVHNode(bounds), - num_children_(num_children) - { - visibility = 0; - time_from = FLT_MAX; - time_to = -FLT_MAX; - for(int i = 0; i < num_children; ++i) { - assert(children[i] != NULL); - visibility |= children[i]->visibility; - this->children[i] = children[i]; - time_from = min(time_from, children[i]->time_from); - time_to = max(time_to, children[i]->time_to); - } - reset_unused_children(); - } - - /* NOTE: This function is only used during binary BVH builder, and it - * supposed to be configured to have 2 children which will be filled in in a - * bit. But this is important to have children reset to NULL. */ - explicit InnerNode(const BoundBox& bounds) - : BVHNode(bounds), - num_children_(0) - { - reset_unused_children(); - visibility = 0; - num_children_ = 2; - } - - bool is_leaf() const { return false; } - int num_children() const { return num_children_; } - BVHNode *get_child(int i) const - { - assert(i >= 0 && i < num_children_); - return children[i]; - } - void print(int depth) const; - - int num_children_; - BVHNode *children[kNumMaxChildren]; - -protected: - void reset_unused_children() - { - for(int i = num_children_; i < kNumMaxChildren; ++i) { - children[i] = NULL; - } - } +class InnerNode : public BVHNode { + public: + static constexpr int kNumMaxChildren = 8; + + InnerNode(const BoundBox &bounds, BVHNode *child0, BVHNode *child1) + : BVHNode(bounds), num_children_(2) + { + children[0] = child0; + children[1] = child1; + reset_unused_children(); + + if (child0 && child1) { + visibility = child0->visibility | child1->visibility; + } + else { + /* Happens on build cancel. */ + visibility = 0; + } + } + + InnerNode(const BoundBox &bounds, BVHNode **children, const int num_children) + : BVHNode(bounds), num_children_(num_children) + { + visibility = 0; + time_from = FLT_MAX; + time_to = -FLT_MAX; + for (int i = 0; i < num_children; ++i) { + assert(children[i] != NULL); + visibility |= children[i]->visibility; + this->children[i] = children[i]; + time_from = min(time_from, children[i]->time_from); + time_to = max(time_to, children[i]->time_to); + } + reset_unused_children(); + } + + /* NOTE: This function is only used during binary BVH builder, and it + * supposed to be configured to have 2 children which will be filled in in a + * bit. But this is important to have children reset to NULL. */ + explicit InnerNode(const BoundBox &bounds) : BVHNode(bounds), num_children_(0) + { + reset_unused_children(); + visibility = 0; + num_children_ = 2; + } + + bool is_leaf() const + { + return false; + } + int num_children() const + { + return num_children_; + } + BVHNode *get_child(int i) const + { + assert(i >= 0 && i < num_children_); + return children[i]; + } + void print(int depth) const; + + int num_children_; + BVHNode *children[kNumMaxChildren]; + + protected: + void reset_unused_children() + { + for (int i = num_children_; i < kNumMaxChildren; ++i) { + children[i] = NULL; + } + } }; -class LeafNode : public BVHNode -{ -public: - LeafNode(const BoundBox& bounds, uint visibility, int lo, int hi) - : BVHNode(bounds), - lo(lo), - hi(hi) - { - this->bounds = bounds; - this->visibility = visibility; - } - - LeafNode(const LeafNode& other) - : BVHNode(other), - lo(other.lo), - hi(other.hi) - { - } - - bool is_leaf() const { return true; } - int num_children() const { return 0; } - BVHNode *get_child(int) const { return NULL; } - int num_triangles() const { return hi - lo; } - void print(int depth) const; - - int lo; - int hi; +class LeafNode : public BVHNode { + public: + LeafNode(const BoundBox &bounds, uint visibility, int lo, int hi) + : BVHNode(bounds), lo(lo), hi(hi) + { + this->bounds = bounds; + this->visibility = visibility; + } + + LeafNode(const LeafNode &other) : BVHNode(other), lo(other.lo), hi(other.hi) + { + } + + bool is_leaf() const + { + return true; + } + int num_children() const + { + return 0; + } + BVHNode *get_child(int) const + { + return NULL; + } + int num_triangles() const + { + return hi - lo; + } + void print(int depth) const; + + int lo; + int hi; }; CCL_NAMESPACE_END -#endif /* __BVH_NODE_H__ */ +#endif /* __BVH_NODE_H__ */ diff --git a/intern/cycles/bvh/bvh_params.h b/intern/cycles/bvh/bvh_params.h index 6408d56da80..2731662a39d 100644 --- a/intern/cycles/bvh/bvh_params.h +++ b/intern/cycles/bvh/bvh_params.h @@ -43,120 +43,121 @@ 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; - - /* object or mesh level bvh */ - bool top_level; - - /* BVH layout to be built. */ - BVHLayout bvh_layout; - - /* Mask of primitives to be included into the BVH. */ - int primitive_mask; - - /* Use unaligned bounding boxes. - * Only used for curves BVH. - */ - bool use_unaligned_nodes; - - /* Split time range to this number of steps and create leaf node for each - * of this time steps. - * - * Speeds up rendering of motion curve primitives in the cost of higher - * memory usage. - */ - int num_motion_curve_steps; - - /* Same as above, but for triangle primitives. */ - int num_motion_triangle_steps; - - /* Same as in SceneParams. */ - int bvh_type; - - /* These are needed for Embree. */ - int curve_flags; - 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; - - top_level = false; - bvh_layout = BVH_LAYOUT_BVH2; - use_unaligned_nodes = false; - - primitive_mask = PRIMITIVE_ALL; - - num_motion_curve_steps = 0; - num_motion_triangle_steps = 0; - - bvh_type = 0; - - curve_flags = 0; - curve_subdivisions = 4; - } - - /* SAH costs */ - __forceinline float cost(int num_nodes, int num_primitives) const - { return node_cost(num_nodes) + primitive_cost(num_primitives); } - - __forceinline float primitive_cost(int n) const - { return n*sah_primitive_cost; } - - __forceinline float node_cost(int n) const - { return n*sah_node_cost; } - - __forceinline bool small_enough_for_leaf(int size, int level) - { return (size <= min_leaf_size || level >= MAX_DEPTH); } - - /* 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); +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; + + /* object or mesh level bvh */ + bool top_level; + + /* BVH layout to be built. */ + BVHLayout bvh_layout; + + /* Mask of primitives to be included into the BVH. */ + int primitive_mask; + + /* Use unaligned bounding boxes. + * Only used for curves BVH. + */ + bool use_unaligned_nodes; + + /* Split time range to this number of steps and create leaf node for each + * of this time steps. + * + * Speeds up rendering of motion curve primitives in the cost of higher + * memory usage. + */ + int num_motion_curve_steps; + + /* Same as above, but for triangle primitives. */ + int num_motion_triangle_steps; + + /* Same as in SceneParams. */ + int bvh_type; + + /* These are needed for Embree. */ + int curve_flags; + 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; + + top_level = false; + bvh_layout = BVH_LAYOUT_BVH2; + use_unaligned_nodes = false; + + primitive_mask = PRIMITIVE_ALL; + + num_motion_curve_steps = 0; + num_motion_triangle_steps = 0; + + bvh_type = 0; + + curve_flags = 0; + curve_subdivisions = 4; + } + + /* SAH costs */ + __forceinline float cost(int num_nodes, int num_primitives) const + { + return node_cost(num_nodes) + primitive_cost(num_primitives); + } + + __forceinline float primitive_cost(int n) const + { + return n * sah_primitive_cost; + } + + __forceinline float node_cost(int n) const + { + return n * sah_node_cost; + } + + __forceinline bool small_enough_for_leaf(int size, int level) + { + return (size <= min_leaf_size || level >= MAX_DEPTH); + } + + /* 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 @@ -164,49 +165,65 @@ public: * 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() {} - - __forceinline BVHReference(const BoundBox& bounds_, - int prim_index_, - int prim_object_, - 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) { - if(&arg != this) { - /* TODO(sergey): Check if it is still faster to memcpy() with - * modern compilers. - */ - memcpy((void *)this, &arg, sizeof(BVHReference)); - } - return *this; - } - - -protected: - BoundBox rbounds; - uint type; - float time_from_, time_to_; +class BVHReference { + public: + __forceinline BVHReference() + { + } + + __forceinline BVHReference(const BoundBox &bounds_, + int prim_index_, + int prim_object_, + 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) + { + if (&arg != this) { + /* TODO(sergey): Check if it is still faster to memcpy() with + * modern compilers. + */ + memcpy((void *)this, &arg, sizeof(BVHReference)); + } + return *this; + } + + protected: + BoundBox rbounds; + uint type; + float time_from_, time_to_; }; /* BVH Range @@ -215,53 +232,68 @@ protected: * 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(int start_) { rbounds.min.w = __int_as_float(start_); } - - __forceinline const BoundBox& bounds() const { return rbounds; } - __forceinline const BoundBox& cent_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; +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(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; +struct BVHSpatialBin { + BoundBox bounds; + int enter; + int exit; - __forceinline BVHSpatialBin() - { - } + __forceinline BVHSpatialBin() + { + } }; /* BVH Spatial Storage @@ -272,18 +304,18 @@ struct BVHSpatialBin */ struct BVHSpatialStorage { - /* Accumulated bounds when sweeping from right to left. */ - vector<BoundBox> right_bounds; + /* 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]; + /* 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; + /* 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 -#endif /* __BVH_PARAMS_H__ */ +#endif /* __BVH_PARAMS_H__ */ diff --git a/intern/cycles/bvh/bvh_sort.cpp b/intern/cycles/bvh/bvh_sort.cpp index f4f73ab2f2f..4498a759c08 100644 --- a/intern/cycles/bvh/bvh_sort.cpp +++ b/intern/cycles/bvh/bvh_sort.cpp @@ -27,79 +27,77 @@ CCL_NAMESPACE_BEGIN static const int BVH_SORT_THRESHOLD = 4096; struct BVHReferenceCompare { -public: - int dim; - const BVHUnaligned *unaligned_heuristic; - const Transform *aligned_space; - - BVHReferenceCompare(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 != NULL) - ? 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), - rb_bounds = get_prim_bounds(rb); - float ca = ra_bounds.min[dim] + ra_bounds.max[dim]; - float cb = rb_bounds.min[dim] + rb_bounds.max[dim]; - - if(ca < cb) return -1; - else if(ca > cb) return 1; - else if(ra.prim_object() < rb.prim_object()) return -1; - else if(ra.prim_object() > rb.prim_object()) return 1; - else if(ra.prim_index() < rb.prim_index()) return -1; - else if(ra.prim_index() > rb.prim_index()) return 1; - else if(ra.prim_type() < rb.prim_type()) return -1; - else if(ra.prim_type() > rb.prim_type()) return 1; - - return 0; - } - - bool operator()(const BVHReference& ra, const BVHReference& rb) - { - return (compare(ra, rb) < 0); - } + public: + int dim; + const BVHUnaligned *unaligned_heuristic; + const Transform *aligned_space; + + BVHReferenceCompare(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 != NULL) ? + 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), rb_bounds = get_prim_bounds(rb); + float ca = ra_bounds.min[dim] + ra_bounds.max[dim]; + float cb = rb_bounds.min[dim] + rb_bounds.max[dim]; + + if (ca < cb) + return -1; + else if (ca > cb) + return 1; + else if (ra.prim_object() < rb.prim_object()) + return -1; + else if (ra.prim_object() > rb.prim_object()) + return 1; + else if (ra.prim_index() < rb.prim_index()) + return -1; + else if (ra.prim_index() > rb.prim_index()) + return 1; + else if (ra.prim_type() < rb.prim_type()) + return -1; + else 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); + const BVHReferenceCompare &compare); class BVHSortTask : public Task { -public: - BVHSortTask(TaskPool *task_pool, - BVHReference *data, - const int job_start, - const int job_end, - const BVHReferenceCompare& compare) - { - run = function_bind(bvh_reference_sort_threaded, - task_pool, - data, - job_start, - job_end, - compare); - } + public: + BVHSortTask(TaskPool *task_pool, + BVHReference *data, + const int job_start, + const int job_end, + const BVHReferenceCompare &compare) + { + run = function_bind(bvh_reference_sort_threaded, task_pool, data, job_start, job_end, compare); + } }; /* Multi-threaded reference sort. */ @@ -107,74 +105,71 @@ static void bvh_reference_sort_threaded(TaskPool *task_pool, BVHReference *data, const int job_start, const int job_end, - const BVHReferenceCompare& compare) + const BVHReferenceCompare &compare) { - int start = job_start, 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, right = end; - 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]); - 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(new BVHSortTask(task_pool, - data, - left, end, - compare), true); - } - else { - start = left; - have_work = true; - } - } - if(start < right) { - end = right; - have_work = true; - } - } + int start = job_start, 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, right = end; + 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]); + 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(new BVHSortTask(task_pool, data, left, end, compare), true); + } + else { + start = left; + have_work = true; + } + } + if (start < right) { + end = right; + have_work = true; + } + } } void bvh_reference_sort(int start, @@ -184,20 +179,20 @@ void bvh_reference_sort(int start, const BVHUnaligned *unaligned_heuristic, const Transform *aligned_space) { - const int count = end - start; - 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(); - } + const int count = end - start; + 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 diff --git a/intern/cycles/bvh/bvh_sort.h b/intern/cycles/bvh/bvh_sort.h index 6910cc1e9b4..936401d8607 100644 --- a/intern/cycles/bvh/bvh_sort.h +++ b/intern/cycles/bvh/bvh_sort.h @@ -35,4 +35,4 @@ void bvh_reference_sort(int start, CCL_NAMESPACE_END -#endif /* __BVH_SORT_H__ */ +#endif /* __BVH_SORT_H__ */ diff --git a/intern/cycles/bvh/bvh_split.cpp b/intern/cycles/bvh/bvh_split.cpp index c55ba40b565..bd261c10d55 100644 --- a/intern/cycles/bvh/bvh_split.cpp +++ b/intern/cycles/bvh/bvh_split.cpp @@ -31,322 +31,314 @@ CCL_NAMESPACE_BEGIN BVHObjectSplit::BVHObjectSplit(BVHBuild *builder, BVHSpatialStorage *storage, - const BVHRange& range, + const BVHRange &range, vector<BVHReference> *references, 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) + : 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; - - storage_->right_bounds.resize(range.size()); - - for(int dim = 0; dim < 3; dim++) { - /* Sort references. */ - bvh_reference_sort(range.start(), - range.end(), - &references_->at(0), - dim, - unaligned_heuristic_, - aligned_space_); - - /* sweep right to left and determine bounds. */ - BoundBox right_bounds = BoundBox::empty; - for(int i = range.size() - 1; i > 0; i--) { - 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++) { - BoundBox prim_bounds = get_prim_bounds(ref_ptr[i - 1]); - left_bounds.grow(prim_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; - } - } - } + const BVHReference *ref_ptr = &references_->at(range.start()); + float min_sah = FLT_MAX; + + storage_->right_bounds.resize(range.size()); + + for (int dim = 0; dim < 3; dim++) { + /* Sort references. */ + bvh_reference_sort(range.start(), + range.end(), + &references_->at(0), + dim, + unaligned_heuristic_, + aligned_space_); + + /* sweep right to left and determine bounds. */ + BoundBox right_bounds = BoundBox::empty; + for (int i = range.size() - 1; i > 0; i--) { + 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++) { + BoundBox prim_bounds = get_prim_bounds(ref_ptr[i - 1]); + left_bounds.grow(prim_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; + } + } + } } -void BVHObjectSplit::split(BVHRange& left, - BVHRange& right, - const BVHRange& range) +void BVHObjectSplit::split(BVHRange &left, BVHRange &right, const BVHRange &range) { - assert(references_->size() > 0); - /* 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, effective_right_bounds; - const int num_right = range.size() - this->num_left; - if(aligned_space_ == NULL) { - 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) { - BoundBox prim_boundbox = references_->at(range.start() + i).bounds(); - effective_left_bounds.grow(prim_boundbox); - } - for(int i = 0; i < num_right; ++i) { - 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); + assert(references_->size() > 0); + /* 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, effective_right_bounds; + const int num_right = range.size() - this->num_left; + if (aligned_space_ == NULL) { + 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) { + BoundBox prim_boundbox = references_->at(range.start() + i).bounds(); + effective_left_bounds.grow(prim_boundbox); + } + for (int i = 0; i < num_right; ++i) { + 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, +BVHSpatialSplit::BVHSpatialSplit(const BVHBuild &builder, BVHSpatialStorage *storage, - const BVHRange& range, + const BVHRange &range, vector<BVHReference> *references, 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) + : 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 == NULL) { - 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); - 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); - BoundBox prim_bounds = get_prim_bounds(ref); - float3 firstBinf = (prim_bounds.min - origin) * invBinSize; - 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, 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; - - 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; - } - } - } + /* initialize bins. */ + BoundBox range_bounds; + if (aligned_space == NULL) { + 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); + 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); + BoundBox prim_bounds = get_prim_bounds(ref); + float3 firstBinf = (prim_bounds.min - origin) * invBinSize; + 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, 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; + + 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) + 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++) { - 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. */ - 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, 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()); - - 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()); - } - if(aligned_space_ != NULL) { - left_bounds = right_bounds = BoundBox::empty; - for(int i = left_start; i < left_end - left_start; ++i) { - BoundBox prim_boundbox = references_->at(i).bounds(); - left_bounds.grow(prim_boundbox); - } - for(int i = right_start; i < right_end - right_start; ++i) { - 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); + /* 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++) { + 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. */ + 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, 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()); + + 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()); + } + if (aligned_space_ != NULL) { + left_bounds = right_bounds = BoundBox::empty; + for (int i = left_start; i < left_end - left_start; ++i) { + BoundBox prim_boundbox = references_->at(i).bounds(); + left_bounds.grow(prim_boundbox); + } + for (int i = right_start; i < right_end - right_start; ++i) { + 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, @@ -354,36 +346,36 @@ void BVHSpatialSplit::split_triangle_primitive(const Mesh *mesh, int prim_index, int dim, float pos, - BoundBox& left_bounds, - BoundBox& right_bounds) + BoundBox &left_bounds, + BoundBox &right_bounds) { - Mesh::Triangle t = mesh->get_triangle(prim_index); - const float3 *verts = &mesh->verts[0]; - float3 v1 = tfm ? transform_point(tfm, verts[t.v[2]]) : verts[t.v[2]]; - v1 = get_unaligned_point(v1); - - for(int i = 0; i < 3; i++) { - float3 v0 = v1; - int vindex = t.v[i]; - v1 = tfm ? transform_point(tfm, verts[vindex]) : verts[vindex]; - v1 = get_unaligned_point(v1); - float v0p = v0[dim]; - 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)) { - float3 t = lerp(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f)); - left_bounds.grow(t); - right_bounds.grow(t); - } - } + Mesh::Triangle t = mesh->get_triangle(prim_index); + const float3 *verts = &mesh->verts[0]; + float3 v1 = tfm ? transform_point(tfm, verts[t.v[2]]) : verts[t.v[2]]; + v1 = get_unaligned_point(v1); + + for (int i = 0; i < 3; i++) { + float3 v0 = v1; + int vindex = t.v[i]; + v1 = tfm ? transform_point(tfm, verts[vindex]) : verts[vindex]; + v1 = get_unaligned_point(v1); + float v0p = v0[dim]; + 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)) { + float3 t = lerp(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 Mesh *mesh, @@ -392,163 +384,125 @@ void BVHSpatialSplit::split_curve_primitive(const Mesh *mesh, int segment_index, int dim, float pos, - BoundBox& left_bounds, - BoundBox& right_bounds) + BoundBox &left_bounds, + BoundBox &right_bounds) { - /* curve split: NOTE - Currently ignores curve width and needs to be fixed.*/ - Mesh::Curve curve = mesh->get_curve(prim_index); - const int k0 = curve.first_key + segment_index; - const int k1 = k0 + 1; - float3 v0 = mesh->curve_keys[k0]; - float3 v1 = mesh->curve_keys[k1]; - - if(tfm != NULL) { - v0 = transform_point(tfm, v0); - v1 = transform_point(tfm, v1); - } - v0 = get_unaligned_point(v0); - v1 = get_unaligned_point(v1); - - float v0p = v0[dim]; - 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)) { - float3 t = lerp(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f)); - left_bounds.grow(t); - right_bounds.grow(t); - } + /* curve split: NOTE - Currently ignores curve width and needs to be fixed.*/ + Mesh::Curve curve = mesh->get_curve(prim_index); + const int k0 = curve.first_key + segment_index; + const int k1 = k0 + 1; + float3 v0 = mesh->curve_keys[k0]; + float3 v1 = mesh->curve_keys[k1]; + + if (tfm != NULL) { + v0 = transform_point(tfm, v0); + v1 = transform_point(tfm, v1); + } + v0 = get_unaligned_point(v0); + v1 = get_unaligned_point(v1); + + float v0p = v0[dim]; + 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)) { + float3 t = lerp(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f)); + left_bounds.grow(t); + right_bounds.grow(t); + } } -void BVHSpatialSplit::split_triangle_reference(const BVHReference& ref, +void BVHSpatialSplit::split_triangle_reference(const BVHReference &ref, const Mesh *mesh, int dim, float pos, - BoundBox& left_bounds, - BoundBox& right_bounds) + BoundBox &left_bounds, + BoundBox &right_bounds) { - split_triangle_primitive(mesh, - NULL, - ref.prim_index(), - dim, - pos, - left_bounds, - right_bounds); + split_triangle_primitive(mesh, NULL, ref.prim_index(), dim, pos, left_bounds, right_bounds); } -void BVHSpatialSplit::split_curve_reference(const BVHReference& ref, +void BVHSpatialSplit::split_curve_reference(const BVHReference &ref, const Mesh *mesh, int dim, float pos, - BoundBox& left_bounds, - BoundBox& right_bounds) + BoundBox &left_bounds, + BoundBox &right_bounds) { - split_curve_primitive(mesh, - NULL, - ref.prim_index(), - PRIMITIVE_UNPACK_SEGMENT(ref.prim_type()), - dim, - pos, - left_bounds, - right_bounds); + split_curve_primitive(mesh, + NULL, + ref.prim_index(), + PRIMITIVE_UNPACK_SEGMENT(ref.prim_type()), + dim, + pos, + left_bounds, + right_bounds); } -void BVHSpatialSplit::split_object_reference(const Object *object, - int dim, - float pos, - BoundBox& left_bounds, - BoundBox& right_bounds) +void BVHSpatialSplit::split_object_reference( + const Object *object, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds) { - Mesh *mesh = object->mesh; - for(int tri_idx = 0; tri_idx < mesh->num_triangles(); ++tri_idx) { - split_triangle_primitive(mesh, - &object->tfm, - tri_idx, - dim, - pos, - left_bounds, - right_bounds); - } - for(int curve_idx = 0; curve_idx < mesh->num_curves(); ++curve_idx) { - Mesh::Curve curve = mesh->get_curve(curve_idx); - for(int segment_idx = 0; - segment_idx < curve.num_keys - 1; - ++segment_idx) - { - split_curve_primitive(mesh, - &object->tfm, - curve_idx, - segment_idx, - dim, - pos, - left_bounds, - right_bounds); - } - } + Mesh *mesh = object->mesh; + for (int tri_idx = 0; tri_idx < mesh->num_triangles(); ++tri_idx) { + split_triangle_primitive(mesh, &object->tfm, tri_idx, dim, pos, left_bounds, right_bounds); + } + for (int curve_idx = 0; curve_idx < mesh->num_curves(); ++curve_idx) { + Mesh::Curve curve = mesh->get_curve(curve_idx); + for (int segment_idx = 0; segment_idx < curve.num_keys - 1; ++segment_idx) { + split_curve_primitive( + mesh, &object->tfm, curve_idx, segment_idx, dim, pos, left_bounds, right_bounds); + } + } } -void BVHSpatialSplit::split_reference(const BVHBuild& builder, - BVHReference& left, - BVHReference& right, - const BVHReference& ref, +void BVHSpatialSplit::split_reference(const BVHBuild &builder, + BVHReference &left, + BVHReference &right, + const BVHReference &ref, int dim, float pos) { - /* initialize boundboxes */ - BoundBox left_bounds = BoundBox::empty; - BoundBox right_bounds = BoundBox::empty; - - /* loop over vertices/edges. */ - const Object *ob = builder.objects[ref.prim_object()]; - const Mesh *mesh = ob->mesh; - - if(ref.prim_type() & PRIMITIVE_ALL_TRIANGLE) { - split_triangle_reference(ref, - mesh, - dim, - pos, - left_bounds, - right_bounds); - } - else if(ref.prim_type() & PRIMITIVE_ALL_CURVE) { - split_curve_reference(ref, - mesh, - 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()); + /* initialize boundboxes */ + BoundBox left_bounds = BoundBox::empty; + BoundBox right_bounds = BoundBox::empty; + + /* loop over vertices/edges. */ + const Object *ob = builder.objects[ref.prim_object()]; + const Mesh *mesh = ob->mesh; + + if (ref.prim_type() & PRIMITIVE_ALL_TRIANGLE) { + split_triangle_reference(ref, mesh, dim, pos, left_bounds, right_bounds); + } + else if (ref.prim_type() & PRIMITIVE_ALL_CURVE) { + split_curve_reference(ref, mesh, 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 diff --git a/intern/cycles/bvh/bvh_split.h b/intern/cycles/bvh/bvh_split.h index cb47deab211..eddd1c27f49 100644 --- a/intern/cycles/bvh/bvh_split.h +++ b/intern/cycles/bvh/bvh_split.h @@ -28,235 +28,211 @@ struct Transform; /* Object Split */ -class BVHObjectSplit -{ -public: - float sah; - int dim; - int num_left; - BoundBox left_bounds; - BoundBox right_bounds; - - BVHObjectSplit() {} - BVHObjectSplit(BVHBuild *builder, - BVHSpatialStorage *storage, - const BVHRange& range, - vector<BVHReference> *references, - float nodeSAH, - const BVHUnaligned *unaligned_heuristic = NULL, - const Transform *aligned_space = NULL); - - 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_ == NULL) { - return prim.bounds(); - } - else { - return unaligned_heuristic_->compute_aligned_prim_boundbox( - prim, *aligned_space_); - } - } +class BVHObjectSplit { + public: + float sah; + int dim; + int num_left; + BoundBox left_bounds; + BoundBox right_bounds; + + BVHObjectSplit() + { + } + BVHObjectSplit(BVHBuild *builder, + BVHSpatialStorage *storage, + const BVHRange &range, + vector<BVHReference> *references, + float nodeSAH, + const BVHUnaligned *unaligned_heuristic = NULL, + const Transform *aligned_space = NULL); + + 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_ == NULL) { + return prim.bounds(); + } + else { + 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_(NULL), - references_(NULL) {} - BVHSpatialSplit(const BVHBuild& builder, - BVHSpatialStorage *storage, - const BVHRange& range, - vector<BVHReference> *references, - float nodeSAH, - const BVHUnaligned *unaligned_heuristic = NULL, - const Transform *aligned_space = NULL); - - void split(BVHBuild *builder, - BVHRange& left, - BVHRange& right, - const BVHRange& range); - - void split_reference(const BVHBuild& builder, - BVHReference& left, - BVHReference& right, - const BVHReference& ref, - 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 it's index, reused by higher - * level splitting functions. - */ - void split_triangle_primitive(const Mesh *mesh, - const Transform *tfm, - int prim_index, - int dim, - float pos, - BoundBox& left_bounds, - BoundBox& right_bounds); - void split_curve_primitive(const Mesh *mesh, - const Transform *tfm, - int prim_index, - int segment_index, - int dim, - 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, - int dim, - float pos, - BoundBox& left_bounds, - BoundBox& right_bounds); - void split_curve_reference(const BVHReference& ref, - const Mesh *mesh, - int dim, - float pos, - BoundBox& left_bounds, - BoundBox& right_bounds); - void split_object_reference(const Object *object, - int dim, - float pos, - BoundBox& left_bounds, - BoundBox& right_bounds); - - __forceinline BoundBox get_prim_bounds(const BVHReference& prim) const - { - if(aligned_space_ == NULL) { - return prim.bounds(); - } - else { - return unaligned_heuristic_->compute_aligned_prim_boundbox( - prim, *aligned_space_); - } - } - - __forceinline float3 get_unaligned_point(const float3& point) const - { - if(aligned_space_ == NULL) { - return point; - } - else { - return transform_point(aligned_space_, point); - } - } +class BVHSpatialSplit { + public: + float sah; + int dim; + float pos; + + BVHSpatialSplit() : sah(FLT_MAX), dim(0), pos(0.0f), storage_(NULL), references_(NULL) + { + } + BVHSpatialSplit(const BVHBuild &builder, + BVHSpatialStorage *storage, + const BVHRange &range, + vector<BVHReference> *references, + float nodeSAH, + const BVHUnaligned *unaligned_heuristic = NULL, + const Transform *aligned_space = NULL); + + void split(BVHBuild *builder, BVHRange &left, BVHRange &right, const BVHRange &range); + + void split_reference(const BVHBuild &builder, + BVHReference &left, + BVHReference &right, + const BVHReference &ref, + 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 it's index, reused by higher + * level splitting functions. + */ + void split_triangle_primitive(const Mesh *mesh, + const Transform *tfm, + int prim_index, + int dim, + float pos, + BoundBox &left_bounds, + BoundBox &right_bounds); + void split_curve_primitive(const Mesh *mesh, + const Transform *tfm, + int prim_index, + int segment_index, + int dim, + 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, + int dim, + float pos, + BoundBox &left_bounds, + BoundBox &right_bounds); + void split_curve_reference(const BVHReference &ref, + const Mesh *mesh, + int dim, + float pos, + BoundBox &left_bounds, + BoundBox &right_bounds); + void split_object_reference( + const Object *object, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds); + + __forceinline BoundBox get_prim_bounds(const BVHReference &prim) const + { + if (aligned_space_ == NULL) { + return prim.bounds(); + } + else { + return unaligned_heuristic_->compute_aligned_prim_boundbox(prim, *aligned_space_); + } + } + + __forceinline float3 get_unaligned_point(const float3 &point) const + { + if (aligned_space_ == NULL) { + return point; + } + else { + 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() {} - - __forceinline BVHMixedSplit(BVHBuild *builder, - BVHSpatialStorage *storage, - const BVHRange& range, - vector<BVHReference> *references, - int level, - const BVHUnaligned *unaligned_heuristic = NULL, - const Transform *aligned_space = NULL) - { - if(aligned_space == NULL) { - bounds = range.bounds(); - } - else { - bounds = unaligned_heuristic->compute_aligned_boundbox( - range, - &references->at(0), - *aligned_space); - } - /* find split candidates. */ - 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); - } +class BVHMixedSplit { + public: + BVHObjectSplit object; + BVHSpatialSplit spatial; + + float leafSAH; + float nodeSAH; + float minSAH; + + bool no_split; + + BoundBox bounds; + + BVHMixedSplit() + { + } + + __forceinline BVHMixedSplit(BVHBuild *builder, + BVHSpatialStorage *storage, + const BVHRange &range, + vector<BVHReference> *references, + int level, + const BVHUnaligned *unaligned_heuristic = NULL, + const Transform *aligned_space = NULL) + { + if (aligned_space == NULL) { + bounds = range.bounds(); + } + else { + bounds = unaligned_heuristic->compute_aligned_boundbox( + range, &references->at(0), *aligned_space); + } + /* find split candidates. */ + 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 -#endif /* __BVH_SPLIT_H__ */ +#endif /* __BVH_SPLIT_H__ */ diff --git a/intern/cycles/bvh/bvh_unaligned.cpp b/intern/cycles/bvh/bvh_unaligned.cpp index 910f82137c5..1843ca403a5 100644 --- a/intern/cycles/bvh/bvh_unaligned.cpp +++ b/intern/cycles/bvh/bvh_unaligned.cpp @@ -27,150 +27,137 @@ CCL_NAMESPACE_BEGIN - -BVHUnaligned::BVHUnaligned(const vector<Object*>& objects) - : objects_(objects) +BVHUnaligned::BVHUnaligned(const vector<Object *> &objects) : objects_(objects) { } -Transform BVHUnaligned::compute_aligned_space( - const BVHObjectBinning& range, - const BVHReference *references) const +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(); + 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 +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(); + 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 +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); - if(type & PRIMITIVE_CURVE) { - const int curve_index = ref.prim_index(); - const int segment = PRIMITIVE_UNPACK_SEGMENT(packed_type); - const Mesh *mesh = object->mesh; - const Mesh::Curve& curve = mesh->get_curve(curve_index); - const int key = curve.first_key + segment; - const float3 v1 = mesh->curve_keys[key], - v2 = mesh->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; + const Object *object = objects_[ref.prim_object()]; + const int packed_type = ref.prim_type(); + const int type = (packed_type & PRIMITIVE_ALL); + if (type & PRIMITIVE_CURVE) { + const int curve_index = ref.prim_index(); + const int segment = PRIMITIVE_UNPACK_SEGMENT(packed_type); + const Mesh *mesh = object->mesh; + const Mesh::Curve &curve = mesh->get_curve(curve_index); + const int key = curve.first_key + segment; + const float3 v1 = mesh->curve_keys[key], v2 = mesh->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 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); - if(type & PRIMITIVE_CURVE) { - const int curve_index = prim.prim_index(); - const int segment = PRIMITIVE_UNPACK_SEGMENT(packed_type); - const Mesh *mesh = object->mesh; - const Mesh::Curve& curve = mesh->get_curve(curve_index); - curve.bounds_grow(segment, - &mesh->curve_keys[0], - &mesh->curve_radius[0], - aligned_space, - bounds); - } - else { - bounds = prim.bounds().transformed(&aligned_space); - } - return bounds; + 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); + if (type & PRIMITIVE_CURVE) { + const int curve_index = prim.prim_index(); + const int segment = PRIMITIVE_UNPACK_SEGMENT(packed_type); + const Mesh *mesh = object->mesh; + const Mesh::Curve &curve = mesh->get_curve(curve_index); + curve.bounds_grow( + segment, &mesh->curve_keys[0], &mesh->curve_radius[0], 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 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 != NULL) { - *cent_bounds = BoundBox::empty; - } - for(int i = range.start(); i < range.end(); ++i) { - const BVHReference& ref = references[i]; - BoundBox ref_bounds = compute_aligned_prim_boundbox(ref, aligned_space); - bounds.grow(ref_bounds); - if(cent_bounds != NULL) { - cent_bounds->grow(ref_bounds.center2()); - } - } - return bounds; + BoundBox bounds = BoundBox::empty; + if (cent_bounds != NULL) { + *cent_bounds = BoundBox::empty; + } + for (int i = range.start(); i < range.end(); ++i) { + const BVHReference &ref = references[i]; + BoundBox ref_bounds = compute_aligned_prim_boundbox(ref, aligned_space); + bounds.grow(ref_bounds); + if (cent_bounds != NULL) { + 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 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 != NULL) { - *cent_bounds = BoundBox::empty; - } - for(int i = range.start(); i < range.end(); ++i) { - const BVHReference& ref = references[i]; - BoundBox ref_bounds = compute_aligned_prim_boundbox(ref, aligned_space); - bounds.grow(ref_bounds); - if(cent_bounds != NULL) { - cent_bounds->grow(ref_bounds.center2()); - } - } - return bounds; + BoundBox bounds = BoundBox::empty; + if (cent_bounds != NULL) { + *cent_bounds = BoundBox::empty; + } + for (int i = range.start(); i < range.end(); ++i) { + const BVHReference &ref = references[i]; + BoundBox ref_bounds = compute_aligned_prim_boundbox(ref, aligned_space); + bounds.grow(ref_bounds); + if (cent_bounds != NULL) { + cent_bounds->grow(ref_bounds.center2()); + } + } + return bounds; } -Transform BVHUnaligned::compute_node_transform( - const BoundBox& bounds, - const Transform& aligned_space) +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; - 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; + Transform space = aligned_space; + space.x.w -= bounds.min.x; + space.y.w -= bounds.min.y; + space.z.w -= bounds.min.z; + 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 diff --git a/intern/cycles/bvh/bvh_unaligned.h b/intern/cycles/bvh/bvh_unaligned.h index bcfb6ed68da..e8a9a25daa8 100644 --- a/intern/cycles/bvh/bvh_unaligned.h +++ b/intern/cycles/bvh/bvh_unaligned.h @@ -30,51 +30,44 @@ class Object; /* Helper class to perform calculations needed for unaligned nodes. */ class BVHUnaligned { -public: - BVHUnaligned(const vector<Object*>& objects); + 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 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 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 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 = NULL) const; - BoundBox compute_aligned_boundbox( - const BVHRange& range, - const BVHReference *references, - const Transform& aligned_space, - BoundBox *cent_bounds = NULL) const; + /* Calculate bounding box in given space. */ + BoundBox compute_aligned_boundbox(const BVHObjectBinning &range, + const BVHReference *references, + const Transform &aligned_space, + BoundBox *cent_bounds = NULL) const; + BoundBox compute_aligned_boundbox(const BVHRange &range, + const BVHReference *references, + const Transform &aligned_space, + BoundBox *cent_bounds = NULL) 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_; + /* 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 -#endif /* __BVH_UNALIGNED_H__ */ +#endif /* __BVH_UNALIGNED_H__ */ |