diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 1cb29fc6cb0..2f204b2c658 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1570,7 +1570,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): elif entry.type == 'CPU': cpu_devices.append(entry) # Extend all GPU devices with CPU. - if compute_device_type in {'CUDA', 'OPENCL'}: + if compute_device_type in {'CUDA', 'OPTIX', 'OPENCL'}: devices.extend(cpu_devices) return devices diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/blender_device.cpp index ffcaef0b2a9..977f8297de1 100644 --- a/intern/cycles/blender/blender_device.cpp +++ b/intern/cycles/blender/blender_device.cpp @@ -90,8 +90,7 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen mask |= DEVICE_MASK_CUDA; } else if (compute_device == COMPUTE_DEVICE_OPTIX) { - /* Cannot use CPU and OptiX device at the same time right now, so replace mask. */ - mask = DEVICE_MASK_OPTIX; + mask |= DEVICE_MASK_OPTIX; } else if (compute_device == COMPUTE_DEVICE_OPENCL) { mask |= DEVICE_MASK_OPENCL; diff --git a/intern/cycles/bvh/CMakeLists.txt b/intern/cycles/bvh/CMakeLists.txt index 703c69b1797..8cc72359757 100644 --- a/intern/cycles/bvh/CMakeLists.txt +++ b/intern/cycles/bvh/CMakeLists.txt @@ -25,6 +25,7 @@ set(SRC bvh_binning.cpp bvh_build.cpp bvh_embree.cpp + bvh_multi.cpp bvh_node.cpp bvh_optix.cpp bvh_sort.cpp @@ -38,6 +39,7 @@ set(SRC_HEADERS bvh_binning.h bvh_build.h bvh_embree.h + bvh_multi.h bvh_node.h bvh_optix.h bvh_params.h diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp index a51ac4cf4a9..256382e63ba 100644 --- a/intern/cycles/bvh/bvh.cpp +++ b/intern/cycles/bvh/bvh.cpp @@ -17,17 +17,11 @@ #include "bvh/bvh.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" - #include "bvh/bvh2.h" -#include "bvh/bvh_build.h" #include "bvh/bvh_embree.h" -#include "bvh/bvh_node.h" +#include "bvh/bvh_multi.h" #include "bvh/bvh_optix.h" -#include "util/util_foreach.h" #include "util/util_logging.h" #include "util/util_progress.h" @@ -38,14 +32,17 @@ CCL_NAMESPACE_BEGIN const char *bvh_layout_name(BVHLayout layout) { switch (layout) { - case BVH_LAYOUT_BVH2: - return "BVH2"; case BVH_LAYOUT_NONE: return "NONE"; + case BVH_LAYOUT_BVH2: + return "BVH2"; case BVH_LAYOUT_EMBREE: return "EMBREE"; case BVH_LAYOUT_OPTIX: return "OPTIX"; + case BVH_LAYOUT_MULTI_OPTIX: + case BVH_LAYOUT_MULTI_OPTIX_EMBREE: + return "MULTI"; case BVH_LAYOUT_ALL: return "ALL"; } @@ -76,17 +73,6 @@ BVHLayout BVHParams::best_bvh_layout(BVHLayout requested_layout, BVHLayoutMask s return (BVHLayout)(1 << widest_allowed_layout_mask); } -/* Pack Utility */ - -BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i) -{ -} - -int BVHStackEntry::encodeIdx() const -{ - return (node->is_leaf()) ? ~idx : idx; -} - /* BVH */ BVH::BVH(const BVHParams ¶ms_, @@ -99,24 +85,27 @@ BVH::BVH(const BVHParams ¶ms_, BVH *BVH::create(const BVHParams ¶ms, const vector &geometry, const vector &objects, - const Device *device) + Device *device) { switch (params.bvh_layout) { case BVH_LAYOUT_BVH2: return new BVH2(params, geometry, objects); case BVH_LAYOUT_EMBREE: #ifdef WITH_EMBREE - return new BVHEmbree(params, geometry, objects, device); + return new BVHEmbree(params, geometry, objects); #else - (void)device; break; #endif case BVH_LAYOUT_OPTIX: #ifdef WITH_OPTIX - return new BVHOptiX(params, geometry, objects); + return new BVHOptiX(params, geometry, objects, device); #else + (void)device; break; #endif + case BVH_LAYOUT_MULTI_OPTIX: + case BVH_LAYOUT_MULTI_OPTIX_EMBREE: + return new BVHMulti(params, geometry, objects); case BVH_LAYOUT_NONE: case BVH_LAYOUT_ALL: break; @@ -125,399 +114,4 @@ BVH *BVH::create(const BVHParams ¶ms, return NULL; } -/* Building */ - -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(); -} - -/* Refitting */ - -void BVH::refit(Progress &progress) -{ - progress.set_substatus("Packing BVH primitives"); - pack_primitives(); - - if (progress.get_cancel()) - return; - - progress.set_substatus("Refitting BVH nodes"); - refit_nodes(); -} - -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. */ - if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) { - /* Curves. */ - const Hair *hair = static_cast(ob->get_geometry()); - int prim_offset = (params.top_level) ? hair->prim_offset : 0; - Hair::Curve curve = hair->get_curve(pidx - prim_offset); - int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]); - - curve.bounds_grow(k, &hair->get_curve_keys()[0], &hair->get_curve_radius()[0], bbox); - - /* Motion curves. */ - if (hair->get_use_motion_blur()) { - Attribute *attr = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - if (attr) { - size_t hair_size = hair->get_curve_keys().size(); - size_t steps = hair->get_motion_steps() - 1; - float3 *key_steps = attr->data_float3(); - - for (size_t i = 0; i < steps; i++) - curve.bounds_grow(k, key_steps + i * hair_size, &hair->get_curve_radius()[0], bbox); - } - } - } - else { - /* Triangles. */ - const Mesh *mesh = static_cast(ob->get_geometry()); - int prim_offset = (params.top_level) ? mesh->prim_offset : 0; - Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_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 = static_cast(objects[tob]->get_geometry()); - - 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(); - } - 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) -{ - /* Adjust primitive index to point to the triangle in the global array, for - * geometry with transform applied and already in the top level BVH. - */ - for (size_t i = 0; i < pack.prim_index.size(); i++) { - if (pack.prim_index[i] != -1) { - pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset; - } - } - - /* track offsets of instanced BVH data in global array */ - size_t prim_offset = pack.prim_index.size(); - size_t nodes_offset = nodes_size; - size_t nodes_leaf_offset = leaf_nodes_size; - - /* clear array that gives the node indexes for instanced objects */ - pack.object_node.clear(); - - /* reserve */ - size_t prim_index_size = pack.prim_index.size(); - size_t 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; - - foreach (Geometry *geom, geometry) { - BVH *bvh = geom->bvh; - - if (geom->need_build_bvh(params.bvh_layout)) { - 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(); - } - } - - 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; - - map geometry_map; - - /* merge */ - foreach (Object *ob, objects) { - Geometry *geom = ob->get_geometry(); - - /* We assume that if mesh doesn't need own BVH it was already included - * into a top-level BVH and no packing here is needed. - */ - if (!geom->need_build_bvh(params.bvh_layout)) { - pack.object_node[object_offset++] = 0; - continue; - } - - /* if mesh already added once, don't add it again, but used set - * node offset for this object */ - map::iterator it = geometry_map.find(geom); - - if (geometry_map.find(geom) != geometry_map.end()) { - int noffset = it->second; - pack.object_node[object_offset++] = noffset; - continue; - } - - BVH *bvh = geom->bvh; - - int noffset = nodes_offset; - int noffset_leaf = nodes_leaf_offset; - int geom_prim_offset = geom->prim_offset; - - /* fill in node indexes for instances */ - if (bvh->pack.root_index == -1) - pack.object_node[object_offset++] = -noffset_leaf - 1; - else - pack.object_node[object_offset++] = noffset; - - geometry_map[geom] = pack.object_node[object_offset - 1]; - - /* merge primitive, object and triangle indexes */ - if (bvh->pack.prim_index.size()) { - 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] + geom_prim_offset; - pack_prim_tri_index[pack_prim_index_offset] = -1; - } - else { - pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_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) { - nsize = BVH_UNALIGNED_NODE_SIZE; - nsize_bbox = 0; - } - else { - nsize = BVH_NODE_SIZE; - nsize_bbox = 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]; - data.z += (data.z < 0) ? -noffset_leaf : noffset; - data.w += (data.w < 0) ? -noffset_leaf : noffset; - pack_nodes[pack_nodes_offset + nsize_bbox] = data; - - /* Usually this copies nothing, but we better - * be prepared for possible node size extension. - */ - 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 033b1fd8e04..94935c26f10 100644 --- a/intern/cycles/bvh/bvh.h +++ b/intern/cycles/bvh/bvh.h @@ -25,17 +25,16 @@ CCL_NAMESPACE_BEGIN -class Stats; +class BoundBox; +class BVHNode; +class BVHParams; class Device; class DeviceScene; -class BVHNode; -struct BVHStackEntry; -class BVHParams; -class BoundBox; -class LeafNode; class Geometry; +class LeafNode; class Object; class Progress; +class Stats; #define BVH_ALIGN 4096 #define TRI_NODE_SIZE 3 @@ -76,13 +75,10 @@ struct PackedBVH { } }; -enum BVH_TYPE { bvh2 }; - /* BVH */ class BVH { public: - PackedBVH pack; BVHParams params; vector geometry; vector objects; @@ -90,47 +86,15 @@ class BVH { static BVH *create(const BVHParams ¶ms, const vector &geometry, const vector &objects, - const Device *device); + Device *device); virtual ~BVH() { } - virtual void build(Progress &progress, Stats *stats = NULL); - virtual void copy_to_device(Progress & /*progress*/, DeviceScene * /*dscene*/) - { - } - - void refit(Progress &progress); - protected: BVH(const BVHParams ¶ms, const vector &geometry, const vector &objects); - - /* 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]); - - /* 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; - - virtual BVHNode *widen_children_nodes(const BVHNode *root) = 0; -}; - -/* Pack Utility */ -struct BVHStackEntry { - const BVHNode *node; - int idx; - - BVHStackEntry(const BVHNode *n = 0, int i = 0); - int encodeIdx() const; }; CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh2.cpp b/intern/cycles/bvh/bvh2.cpp index c903070429e..379ae9b25ff 100644 --- a/intern/cycles/bvh/bvh2.cpp +++ b/intern/cycles/bvh/bvh2.cpp @@ -17,14 +17,28 @@ #include "bvh/bvh2.h" +#include "render/hair.h" #include "render/mesh.h" #include "render/object.h" +#include "bvh/bvh_build.h" #include "bvh/bvh_node.h" #include "bvh/bvh_unaligned.h" +#include "util/util_foreach.h" +#include "util/util_progress.h" + CCL_NAMESPACE_BEGIN +BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i) +{ +} + +int BVHStackEntry::encodeIdx() const +{ + return (node->is_leaf()) ? ~idx : idx; +} + BVH2::BVH2(const BVHParams ¶ms_, const vector &geometry_, const vector &objects_) @@ -32,6 +46,70 @@ BVH2::BVH2(const BVHParams ¶ms_, { } +void BVH2::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(); +} + +void BVH2::refit(Progress &progress) +{ + progress.set_substatus("Packing BVH primitives"); + pack_primitives(); + + if (progress.get_cancel()) + return; + + progress.set_substatus("Refitting BVH nodes"); + refit_nodes(); +} + BVHNode *BVH2::widen_children_nodes(const BVHNode *root) { return const_cast(root); @@ -253,7 +331,7 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility) const int c0 = data[0].x; const int c1 = data[0].y; - BVH::refit_primitives(c0, c1, bbox, visibility); + refit_primitives(c0, c1, bbox, visibility); /* TODO(sergey): De-duplicate with pack_leaf(). */ float4 leaf_data[BVH_NODE_LEAF_SIZE]; @@ -292,4 +370,333 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility) } } +/* Refitting */ + +void BVH2::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. */ + if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) { + /* Curves. */ + const Hair *hair = static_cast(ob->get_geometry()); + int prim_offset = (params.top_level) ? hair->prim_offset : 0; + Hair::Curve curve = hair->get_curve(pidx - prim_offset); + int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]); + + curve.bounds_grow(k, &hair->get_curve_keys()[0], &hair->get_curve_radius()[0], bbox); + + /* Motion curves. */ + if (hair->get_use_motion_blur()) { + Attribute *attr = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + + if (attr) { + size_t hair_size = hair->get_curve_keys().size(); + size_t steps = hair->get_motion_steps() - 1; + float3 *key_steps = attr->data_float3(); + + for (size_t i = 0; i < steps; i++) + curve.bounds_grow(k, key_steps + i * hair_size, &hair->get_curve_radius()[0], bbox); + } + } + } + else { + /* Triangles. */ + const Mesh *mesh = static_cast(ob->get_geometry()); + int prim_offset = (params.top_level) ? mesh->prim_offset : 0; + Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_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 BVH2::pack_triangle(int idx, float4 tri_verts[3]) +{ + int tob = pack.prim_object[idx]; + assert(tob >= 0 && tob < objects.size()); + const Mesh *mesh = static_cast(objects[tob]->get_geometry()); + + 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 BVH2::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(); + } + else { + pack.prim_tri_index[i] = -1; + pack.prim_visibility[i] = 0; + } + } +} + +/* Pack Instances */ + +void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) +{ + /* Adjust primitive index to point to the triangle in the global array, for + * geometry with transform applied and already in the top level BVH. + */ + for (size_t i = 0; i < pack.prim_index.size(); i++) { + if (pack.prim_index[i] != -1) { + pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset; + } + } + + /* track offsets of instanced BVH data in global array */ + size_t prim_offset = pack.prim_index.size(); + size_t nodes_offset = nodes_size; + size_t nodes_leaf_offset = leaf_nodes_size; + + /* clear array that gives the node indexes for instanced objects */ + pack.object_node.clear(); + + /* reserve */ + size_t prim_index_size = pack.prim_index.size(); + size_t 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; + + foreach (Geometry *geom, geometry) { + BVH2 *bvh = static_cast(geom->bvh); + + if (geom->need_build_bvh(params.bvh_layout)) { + 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(); + } + } + + 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; + + unordered_map geometry_map; + + /* merge */ + foreach (Object *ob, objects) { + Geometry *geom = ob->get_geometry(); + + /* We assume that if mesh doesn't need own BVH it was already included + * into a top-level BVH and no packing here is needed. + */ + if (!geom->need_build_bvh(params.bvh_layout)) { + pack.object_node[object_offset++] = 0; + continue; + } + + /* if mesh already added once, don't add it again, but used set + * node offset for this object */ + unordered_map::iterator it = geometry_map.find(geom); + + if (geometry_map.find(geom) != geometry_map.end()) { + int noffset = it->second; + pack.object_node[object_offset++] = noffset; + continue; + } + + BVH2 *bvh = static_cast(geom->bvh); + + int noffset = nodes_offset; + int noffset_leaf = nodes_leaf_offset; + int geom_prim_offset = geom->prim_offset; + + /* fill in node indexes for instances */ + if (bvh->pack.root_index == -1) + pack.object_node[object_offset++] = -noffset_leaf - 1; + else + pack.object_node[object_offset++] = noffset; + + geometry_map[geom] = pack.object_node[object_offset - 1]; + + /* merge primitive, object and triangle indexes */ + if (bvh->pack.prim_index.size()) { + 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] + geom_prim_offset; + pack_prim_tri_index[pack_prim_index_offset] = -1; + } + else { + pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_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) { + nsize = BVH_UNALIGNED_NODE_SIZE; + nsize_bbox = 0; + } + else { + nsize = BVH_NODE_SIZE; + nsize_bbox = 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]; + data.z += (data.z < 0) ? -noffset_leaf : noffset; + data.w += (data.w < 0) ? -noffset_leaf : noffset; + pack_nodes[pack_nodes_offset + nsize_bbox] = data; + + /* Usually this copies nothing, but we better + * be prepared for possible node size extension. + */ + 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/bvh2.h b/intern/cycles/bvh/bvh2.h index fa3e45b72d2..1030a0f76c7 100644 --- a/intern/cycles/bvh/bvh2.h +++ b/intern/cycles/bvh/bvh2.h @@ -26,23 +26,30 @@ CCL_NAMESPACE_BEGIN -class BVHNode; -struct BVHStackEntry; -class BVHParams; -class BoundBox; -class LeafNode; -class Object; -class Progress; - #define BVH_NODE_SIZE 4 #define BVH_NODE_LEAF_SIZE 1 #define BVH_UNALIGNED_NODE_SIZE 7 +/* Pack Utility */ +struct BVHStackEntry { + const BVHNode *node; + int idx; + + BVHStackEntry(const BVHNode *n = 0, int i = 0); + int encodeIdx() const; +}; + /* BVH2 * * Typical BVH with each node having two children. */ class BVH2 : public BVH { + public: + void build(Progress &progress, Stats *stats); + void refit(Progress &progress); + + PackedBVH pack; + protected: /* constructor */ friend class BVH; @@ -51,10 +58,10 @@ class BVH2 : public BVH { const vector &objects); /* Building process. */ - virtual BVHNode *widen_children_nodes(const BVHNode *root) override; + virtual BVHNode *widen_children_nodes(const BVHNode *root); /* pack */ - void pack_nodes(const BVHNode *root) override; + void pack_nodes(const BVHNode *root); void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf); void pack_inner(const BVHStackEntry &e, const BVHStackEntry &e0, const BVHStackEntry &e1); @@ -84,8 +91,18 @@ class BVH2 : public BVH { uint visibility1); /* refit */ - void refit_nodes() override; + void refit_nodes(); void refit_node(int idx, bool leaf, 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]); + + /* merge instance BVH's */ + void pack_instances(size_t nodes_size, size_t leaf_nodes_size); }; CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index 910e3780b2e..b874bda7186 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -298,82 +298,31 @@ static bool rtc_progress_func(void *user_ptr, const double n) return !progress->get_cancel(); } -static size_t count_primitives(Geometry *geom) -{ - if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - Mesh *mesh = static_cast(geom); - return mesh->num_triangles(); - } - else if (geom->geometry_type == Geometry::HAIR) { - Hair *hair = static_cast(geom); - return hair->num_segments(); - } - - return 0; -} - BVHEmbree::BVHEmbree(const BVHParams ¶ms_, const vector &geometry_, - const vector &objects_, - const Device *device) + const vector &objects_) : BVH(params_, geometry_, objects_), scene(NULL), - mem_used(0), - top_level(NULL), - rtc_device((RTCDevice)device->bvh_device()), - stats(NULL), - curve_subdivisions(params.curve_subdivisions), - build_quality(RTC_BUILD_QUALITY_REFIT), - dynamic_scene(true) + rtc_device(NULL), + build_quality(RTC_BUILD_QUALITY_REFIT) { _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); - - rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL); - - pack.root_index = -1; } BVHEmbree::~BVHEmbree() -{ - if (!params.top_level) { - destroy(scene); - } -} - -void BVHEmbree::destroy(RTCScene scene) { if (scene) { rtcReleaseScene(scene); - scene = 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; - } -} - -void BVHEmbree::build(Progress &progress, Stats *stats_) +void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_) { + rtc_device = rtc_device_; assert(rtc_device); - stats = stats_; + + rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL); rtcSetDeviceMemoryMonitorFunction(rtc_device, rtc_memory_monitor_func, stats); progress.set_substatus("Building BVH"); @@ -394,35 +343,7 @@ void BVHEmbree::build(Progress &progress, Stats *stats_) 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->get_geometry()->is_instanced()) { - prim_count += count_primitives(ob->get_geometry()); - } - else { - ++prim_count; - } - } - else { - prim_count += count_primitives(ob->get_geometry()); - } - } - - 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()) { @@ -445,37 +366,11 @@ void BVHEmbree::build(Progress &progress, Stats *stats_) } 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; -} - -void BVHEmbree::copy_to_device(Progress & /*progress*/, DeviceScene *dscene) -{ - dscene->data.bvh.scene = scene; -} - -BVHNode *BVHEmbree::widen_children_nodes(const BVHNode * /*root*/) -{ - assert(!"Must not be called."); - return NULL; } void BVHEmbree::add_object(Object *ob, int i) @@ -498,15 +393,8 @@ void BVHEmbree::add_object(Object *ob, int i) void BVHEmbree::add_instance(Object *ob, int i) { - if (!ob || !ob->get_geometry()) { - assert(0); - return; - } BVHEmbree *instance_bvh = (BVHEmbree *)(ob->get_geometry()->bvh); - - if (instance_bvh->top_level != this) { - instance_bvh->top_level = this; - } + assert(instance_bvh != NULL); const size_t num_object_motion_steps = ob->use_motion() ? ob->get_motion().size() : 1; const size_t num_motion_steps = min(num_object_motion_steps, RTC_MAX_TIME_STEP_COUNT); @@ -538,11 +426,6 @@ void BVHEmbree::add_instance(Object *ob, int i) geom_id, 0, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float *)&ob->get_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_for_tracing()); @@ -553,20 +436,22 @@ void BVHEmbree::add_instance(Object *ob, int i) void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i) { - size_t prim_offset = pack.prim_index.size(); + size_t prim_offset = mesh->optix_prim_offset; + const Attribute *attr_mP = NULL; - size_t num_geometry_motion_steps = 1; + 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_geometry_motion_steps = mesh->get_motion_steps(); + num_motion_steps = mesh->get_motion_steps(); } } - const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT); - assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT); + assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT); + num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT); const size_t num_triangles = mesh->num_triangles(); + RTCGeometry geom_id = rtcNewGeometry(rtc_device, RTC_GEOMETRY_TYPE_TRIANGLE); rtcSetGeometryBuildQuality(geom_id, build_quality); rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); @@ -588,22 +473,6 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i) set_tri_vertex_buffer(geom_id, mesh, false); - 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); rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func); rtcSetGeometryMask(geom_id, ob->visibility_for_tracing()); @@ -629,12 +498,12 @@ void BVHEmbree::set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, con } } } - const size_t num_verts = mesh->verts.size(); + const size_t num_verts = mesh->get_verts().size(); for (int t = 0; t < num_motion_steps; ++t) { const float3 *verts; if (t == t_mid) { - verts = &mesh->verts[0]; + verts = mesh->get_verts().data(); } else { int t_ = (t > t_mid) ? (t - 1) : t; @@ -736,24 +605,19 @@ void BVHEmbree::set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, c void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i) { - size_t prim_offset = pack.prim_index.size(); + size_t prim_offset = hair->optix_prim_offset; + const Attribute *attr_mP = NULL; - size_t num_geometry_motion_steps = 1; + size_t num_motion_steps = 1; if (hair->has_motion_blur()) { attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); if (attr_mP) { - num_geometry_motion_steps = hair->get_motion_steps(); + num_motion_steps = hair->get_motion_steps(); } } - const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT); - const PrimitiveType primitive_type = - (num_motion_steps > 1) ? - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : - PRIMITIVE_MOTION_CURVE_THICK) : - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK); - - assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT); + assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT); + num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT); const size_t num_curves = hair->num_curves(); size_t num_segments = 0; @@ -763,22 +627,12 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i) 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 = (hair->curve_shape == CURVE_RIBBON ? RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE : RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE); RTCGeometry geom_id = rtcNewGeometry(rtc_device, type); - rtcSetGeometryTessellationRate(geom_id, curve_subdivisions + 1); + rtcSetGeometryTessellationRate(geom_id, params.curve_subdivisions + 1); unsigned *rtc_indices = (unsigned *)rtcSetNewGeometryBuffer( geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof(int), num_segments); size_t rtc_index = 0; @@ -788,11 +642,6 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i) rtc_indices[rtc_index] = c.first_key + k; /* Room for extra CVs at Catmull-Rom splines. */ rtc_indices[rtc_index] += j * 2; - /* Cycles specific data. */ - pack.prim_object[prim_object_size + rtc_index] = i; - pack.prim_type[prim_type_size + rtc_index] = (PRIMITIVE_PACK_SEGMENT(primitive_type, k)); - pack.prim_index[prim_index_size + rtc_index] = j; - pack.prim_tri_index[prim_tri_index_size + rtc_index] = rtc_index; ++rtc_index; } @@ -818,134 +667,10 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i) rtcReleaseGeometry(geom_id); } -void BVHEmbree::pack_nodes(const BVHNode *) +void BVHEmbree::refit(Progress &progress) { - /* Quite a bit of this code is for compatibility with Cycles' native BVH. */ - if (!params.top_level) { - return; - } + progress.set_substatus("Refitting BVH nodes"); - for (size_t i = 0; i < pack.prim_index.size(); ++i) { - if (pack.prim_index[i] != -1) { - pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset; - } - } - - 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 geometry_map; - - foreach (Object *ob, objects) { - Geometry *geom = ob->get_geometry(); - BVH *bvh = geom->bvh; - - if (geom->need_build_bvh(BVH_LAYOUT_EMBREE)) { - if (geometry_map.find(geom) == geometry_map.end()) { - prim_index_size += bvh->pack.prim_index.size(); - prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); - geometry_map[geom] = 1; - } - } - } - - geometry_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) { - Geometry *geom = ob->get_geometry(); - - /* We assume that if mesh doesn't need own BVH it was already included - * into a top-level BVH and no packing here is needed. - */ - if (!geom->need_build_bvh(BVH_LAYOUT_EMBREE)) { - pack.object_node[object_offset++] = prim_offset; - continue; - } - - /* if geom already added once, don't add it again, but used set - * node offset for this object */ - map::iterator it = geometry_map.find(geom); - - if (geometry_map.find(geom) != geometry_map.end()) { - int noffset = it->second; - pack.object_node[object_offset++] = noffset; - continue; - } - - BVHEmbree *bvh = (BVHEmbree *)geom->bvh; - - rtc_memory_monitor_func(stats, unaccounted_mem, true); - unaccounted_mem = 0; - - int geom_prim_offset = geom->prim_offset; - - /* fill in node indexes for instances */ - pack.object_node[object_offset++] = prim_offset; - - geometry_map[geom] = 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] + geom_prim_offset; - pack_prim_tri_index[pack_prim_index_offset] = -1; - } - else { - pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_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) { @@ -971,8 +696,10 @@ void BVHEmbree::refit_nodes() } geom_id += 2; } + rtcCommitScene(scene); } + CCL_NAMESPACE_END #endif /* WITH_EMBREE */ diff --git a/intern/cycles/bvh/bvh_embree.h b/intern/cycles/bvh/bvh_embree.h index 3e895e7b588..01636fbd1dc 100644 --- a/intern/cycles/bvh/bvh_embree.h +++ b/intern/cycles/bvh/bvh_embree.h @@ -31,56 +31,34 @@ CCL_NAMESPACE_BEGIN -class Geometry; class Hair; class Mesh; class BVHEmbree : public BVH { public: - virtual void build(Progress &progress, Stats *stats) override; - virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override; - virtual ~BVHEmbree(); - RTCScene scene; - static void destroy(RTCScene); + void build(Progress &progress, Stats *stats, RTCDevice rtc_device); + void refit(Progress &progress); - /* Building process. */ - virtual BVHNode *widen_children_nodes(const BVHNode *root) override; + RTCScene scene; protected: friend class BVH; BVHEmbree(const BVHParams ¶ms, const vector &geometry, - const vector &objects, - const Device *device); - - virtual void pack_nodes(const BVHNode *) override; - virtual void refit_nodes() override; + const vector &objects); + virtual ~BVHEmbree(); void add_object(Object *ob, int i); void add_instance(Object *ob, int i); void add_curves(const Object *ob, const Hair *hair, int i); void add_triangles(const Object *ob, const Mesh *mesh, 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 set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, const bool update); void set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, const bool update); RTCDevice rtc_device; - - Stats *stats; - vector delayed_delete_scenes; - int curve_subdivisions; enum RTCBuildQuality build_quality; - bool dynamic_scene; }; CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh_multi.cpp b/intern/cycles/bvh/bvh_multi.cpp new file mode 100644 index 00000000000..a9e771f20f1 --- /dev/null +++ b/intern/cycles/bvh/bvh_multi.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2020, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bvh/bvh_multi.h" + +#include "util/util_foreach.h" + +CCL_NAMESPACE_BEGIN + +BVHMulti::BVHMulti(const BVHParams ¶ms_, + const vector &geometry_, + const vector &objects_) + : BVH(params_, geometry_, objects_) +{ +} + +BVHMulti::~BVHMulti() +{ + foreach (BVH *bvh, sub_bvhs) { + delete bvh; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh_multi.h b/intern/cycles/bvh/bvh_multi.h new file mode 100644 index 00000000000..840438c5d0c --- /dev/null +++ b/intern/cycles/bvh/bvh_multi.h @@ -0,0 +1,39 @@ +/* + * Copyright 2020, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BVH_MULTI_H__ +#define __BVH_MULTI_H__ + +#include "bvh/bvh.h" +#include "bvh/bvh_params.h" + +CCL_NAMESPACE_BEGIN + +class BVHMulti : public BVH { + public: + vector sub_bvhs; + + protected: + friend class BVH; + BVHMulti(const BVHParams ¶ms, + const vector &geometry, + const vector &objects); + virtual ~BVHMulti(); +}; + +CCL_NAMESPACE_END + +#endif /* __BVH_MULTI_H__ */ diff --git a/intern/cycles/bvh/bvh_optix.cpp b/intern/cycles/bvh/bvh_optix.cpp index 52fc9c0a50d..e094f339ede 100644 --- a/intern/cycles/bvh/bvh_optix.cpp +++ b/intern/cycles/bvh/bvh_optix.cpp @@ -19,212 +19,22 @@ # include "bvh/bvh_optix.h" -# include "device/device.h" - -# include "render/geometry.h" -# include "render/hair.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 BVHOptiX::BVHOptiX(const BVHParams ¶ms_, const vector &geometry_, - const vector &objects_) - : BVH(params_, geometry_, objects_) + const vector &objects_, + Device *device) + : BVH(params_, geometry_, objects_), + traversable_handle(0), + as_data(device, params_.top_level ? "optix tlas" : "optix blas"), + motion_transform_data(device, "optix motion transform") { - optix_handle = 0; - optix_data_handle = 0; - do_refit = false; } BVHOptiX::~BVHOptiX() { -} - -void BVHOptiX::build(Progress &, Stats *) -{ - if (params.top_level) - pack_tlas(); - else - pack_blas(); -} - -void BVHOptiX::copy_to_device(Progress &progress, DeviceScene *dscene) -{ - progress.set_status("Updating Scene BVH", "Building OptiX acceleration structure"); - - Device *const device = dscene->bvh_nodes.device; - if (!device->build_optix_bvh(this)) - progress.set_error("Failed to build OptiX acceleration structure"); -} - -void BVHOptiX::pack_blas() -{ - // Bottom-level BVH can contain multiple primitive types, so merge them: - assert(geometry.size() == 1 && objects.size() == 1); // These are built per-mesh - Geometry *const geom = geometry[0]; - - if (geom->geometry_type == Geometry::HAIR) { - Hair *const hair = static_cast(geom); - if (hair->num_curves() > 0) { - const size_t num_curves = hair->num_curves(); - const size_t num_segments = hair->num_segments(); - pack.prim_type.reserve(pack.prim_type.size() + num_segments); - pack.prim_index.reserve(pack.prim_index.size() + num_segments); - pack.prim_object.reserve(pack.prim_object.size() + num_segments); - // 'pack.prim_time' is only used in geom_curve_intersect.h - // It is not needed because of OPTIX_MOTION_FLAG_[START|END]_VANISH - - uint type = (hair->get_use_motion_blur() && - hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) ? - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : - PRIMITIVE_MOTION_CURVE_THICK) : - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : - PRIMITIVE_CURVE_THICK); - - for (size_t j = 0; j < num_curves; ++j) { - const Hair::Curve curve = hair->get_curve(j); - for (size_t k = 0; k < curve.num_segments(); ++k) { - pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k)); - // Each curve segment points back to its curve index - pack.prim_index.push_back_reserved(j); - pack.prim_object.push_back_reserved(0); - } - } - } - } - else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - Mesh *const mesh = static_cast(geom); - if (mesh->num_triangles() > 0) { - const size_t num_triangles = mesh->num_triangles(); - pack.prim_type.reserve(pack.prim_type.size() + num_triangles); - pack.prim_index.reserve(pack.prim_index.size() + num_triangles); - pack.prim_object.reserve(pack.prim_object.size() + num_triangles); - - uint type = PRIMITIVE_TRIANGLE; - if (mesh->get_use_motion_blur() && mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) - type = PRIMITIVE_MOTION_TRIANGLE; - - for (size_t k = 0; k < num_triangles; ++k) { - pack.prim_type.push_back_reserved(type); - pack.prim_index.push_back_reserved(k); - pack.prim_object.push_back_reserved(0); - } - } - } - - // Initialize visibility to zero and later update it during top-level build - uint prev_visibility = objects[0]->get_visibility(); - objects[0]->set_visibility(0); - - // Update 'pack.prim_tri_index', 'pack.prim_tri_verts' and 'pack.prim_visibility' - pack_primitives(); - - // Reset visibility after packing - objects[0]->set_visibility(prev_visibility); -} - -void BVHOptiX::pack_tlas() -{ - // Calculate total packed size - size_t prim_index_size = 0; - size_t prim_tri_verts_size = 0; - foreach (Geometry *geom, geometry) { - BVH *const bvh = geom->bvh; - prim_index_size += bvh->pack.prim_index.size(); - prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); - } - - if (prim_index_size == 0) - return; // Abort right away if this is an empty BVH - - size_t pack_offset = 0; - size_t pack_verts_offset = 0; - - pack.prim_type.resize(prim_index_size); - int *pack_prim_type = pack.prim_type.data(); - pack.prim_index.resize(prim_index_size); - int *pack_prim_index = pack.prim_index.data(); - pack.prim_object.resize(prim_index_size); - int *pack_prim_object = pack.prim_object.data(); - pack.prim_visibility.resize(prim_index_size); - uint *pack_prim_visibility = pack.prim_visibility.data(); - pack.prim_tri_index.resize(prim_index_size); - uint *pack_prim_tri_index = pack.prim_tri_index.data(); - pack.prim_tri_verts.resize(prim_tri_verts_size); - float4 *pack_prim_tri_verts = pack.prim_tri_verts.data(); - - // Top-level BVH should only contain instances, see 'Geometry::need_build_bvh' - // Iterate over scene mesh list instead of objects, since the 'prim_offset' is calculated based - // on that list, which may be ordered differently from the object list. - foreach (Geometry *geom, geometry) { - PackedBVH &bvh_pack = geom->bvh->pack; - int geom_prim_offset = geom->prim_offset; - - // Merge visibility flags of all objects and fix object indices for non-instanced geometry - int object_index = 0; // Unused for instanced geometry - int object_visibility = 0; - foreach (Object *ob, objects) { - if (ob->get_geometry() == geom) { - object_visibility |= ob->visibility_for_tracing(); - if (!geom->is_instanced()) { - object_index = ob->get_device_index(); - break; - } - } - } - - // Merge primitive, object and triangle indexes - if (!bvh_pack.prim_index.empty()) { - int *bvh_prim_type = &bvh_pack.prim_type[0]; - int *bvh_prim_index = &bvh_pack.prim_index[0]; - uint *bvh_prim_tri_index = &bvh_pack.prim_tri_index[0]; - uint *bvh_prim_visibility = &bvh_pack.prim_visibility[0]; - - for (size_t i = 0; i < bvh_pack.prim_index.size(); i++, pack_offset++) { - if (bvh_pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { - pack_prim_index[pack_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_offset] = -1; - } - else { - pack_prim_index[pack_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_offset] = bvh_prim_tri_index[i] + pack_verts_offset; - } - - pack_prim_type[pack_offset] = bvh_prim_type[i]; - pack_prim_object[pack_offset] = object_index; - pack_prim_visibility[pack_offset] = bvh_prim_visibility[i] | object_visibility; - } - } - - // Merge triangle vertex data - if (!bvh_pack.prim_tri_verts.empty()) { - const size_t prim_tri_size = bvh_pack.prim_tri_verts.size(); - memcpy(pack_prim_tri_verts + pack_verts_offset, - bvh_pack.prim_tri_verts.data(), - prim_tri_size * sizeof(float4)); - pack_verts_offset += prim_tri_size; - } - } -} - -void BVHOptiX::pack_nodes(const BVHNode *) -{ -} - -void BVHOptiX::refit_nodes() -{ - do_refit = true; -} - -BVHNode *BVHOptiX::widen_children_nodes(const BVHNode *) -{ - return NULL; + // Acceleration structure memory is freed via the 'as_data' destructor } CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh_optix.h b/intern/cycles/bvh/bvh_optix.h index 663cba67260..aa514beae0d 100644 --- a/intern/cycles/bvh/bvh_optix.h +++ b/intern/cycles/bvh/bvh_optix.h @@ -26,33 +26,19 @@ CCL_NAMESPACE_BEGIN -class Geometry; -class Optix; - class BVHOptiX : public BVH { - friend class BVH; - public: - uint64_t optix_handle; - uint64_t optix_data_handle; - bool do_refit; + uint64_t traversable_handle; + device_only_memory as_data; + device_only_memory motion_transform_data; + protected: + friend class BVH; BVHOptiX(const BVHParams ¶ms, const vector &geometry, - const vector &objects); + const vector &objects, + Device *device); virtual ~BVHOptiX(); - - virtual void build(Progress &progress, Stats *) override; - virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override; - - private: - void pack_blas(); - void pack_tlas(); - - virtual void pack_nodes(const BVHNode *) override; - virtual void refit_nodes() override; - - virtual BVHNode *widen_children_nodes(const BVHNode *) override; }; CCL_NAMESPACE_END diff --git a/intern/cycles/device/cuda/device_cuda.h b/intern/cycles/device/cuda/device_cuda.h index e5e3e24165d..c3271c3cfcf 100644 --- a/intern/cycles/device/cuda/device_cuda.h +++ b/intern/cycles/device/cuda/device_cuda.h @@ -71,6 +71,7 @@ class CUDADevice : public Device { }; typedef map CUDAMemMap; CUDAMemMap cuda_mem_map; + thread_mutex cuda_mem_map_mutex; struct PixelMem { GLuint cuPBO; diff --git a/intern/cycles/device/cuda/device_cuda_impl.cpp b/intern/cycles/device/cuda/device_cuda_impl.cpp index 01adf10f252..6c26b247743 100644 --- a/intern/cycles/device/cuda/device_cuda_impl.cpp +++ b/intern/cycles/device/cuda/device_cuda_impl.cpp @@ -718,8 +718,10 @@ void CUDADevice::init_host_memory() void CUDADevice::load_texture_info() { if (need_texture_info) { - texture_info.copy_to_device(); + /* Unset flag before copying, so this does not loop indenfinetly if the copy below calls + * into 'move_textures_to_host' (which calls 'load_texture_info' again). */ need_texture_info = false; + texture_info.copy_to_device(); } } @@ -988,6 +990,7 @@ void CUDADevice::mem_alloc(device_memory &mem) assert(!"mem_alloc not supported for global memory."); } else { + thread_scoped_lock lock(cuda_mem_map_mutex); generic_alloc(mem); } } @@ -1006,10 +1009,10 @@ void CUDADevice::mem_copy_to(device_memory &mem) tex_alloc((device_texture &)mem); } else { + thread_scoped_lock lock(cuda_mem_map_mutex); if (!mem.device_pointer) { generic_alloc(mem); } - generic_copy_to(mem); } } @@ -1048,6 +1051,7 @@ void CUDADevice::mem_zero(device_memory &mem) /* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory * regardless of mem.host_pointer and mem.shared_pointer. */ + thread_scoped_lock lock(cuda_mem_map_mutex); if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { const CUDAContextScope scope(this); cuda_assert(cuMemsetD8((CUdeviceptr)mem.device_pointer, 0, mem.memory_size())); @@ -1069,6 +1073,7 @@ void CUDADevice::mem_free(device_memory &mem) tex_free((device_texture &)mem); } else { + thread_scoped_lock lock(cuda_mem_map_mutex); generic_free(mem); } } @@ -1092,6 +1097,7 @@ void CUDADevice::const_copy_to(const char *name, void *host, size_t size) void CUDADevice::global_alloc(device_memory &mem) { if (mem.is_resident(this)) { + thread_scoped_lock lock(cuda_mem_map_mutex); generic_alloc(mem); generic_copy_to(mem); } @@ -1102,6 +1108,7 @@ void CUDADevice::global_alloc(device_memory &mem) void CUDADevice::global_free(device_memory &mem) { if (mem.is_resident(this) && mem.device_pointer) { + thread_scoped_lock lock(cuda_mem_map_mutex); generic_free(mem); } } @@ -1170,6 +1177,8 @@ void CUDADevice::tex_alloc(device_texture &mem) size_t src_pitch = mem.data_width * dsize * mem.data_elements; size_t dst_pitch = src_pitch; + thread_scoped_lock lock(cuda_mem_map_mutex); + if (!mem.is_resident(this)) { cmem = &cuda_mem_map[&mem]; cmem->texobject = 0; @@ -1257,6 +1266,9 @@ void CUDADevice::tex_alloc(device_texture &mem) cuda_assert(cuMemcpyHtoD(mem.device_pointer, mem.host_pointer, size)); } + /* Unlock mutex before resizing texture info, since that may attempt to lock it again. */ + lock.unlock(); + /* Resize once */ const uint slot = mem.slot; if (slot >= texture_info.size()) { @@ -1305,6 +1317,11 @@ void CUDADevice::tex_alloc(device_texture &mem) texDesc.filterMode = filter_mode; texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES; + /* Lock again and refresh the data pointer (in case another thread modified the map in the + * meantime). */ + lock.lock(); + cmem = &cuda_mem_map[&mem]; + cuda_assert(cuTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL)); texture_info[slot].data = (uint64_t)cmem->texobject; @@ -1318,6 +1335,7 @@ void CUDADevice::tex_free(device_texture &mem) { if (mem.device_pointer) { CUDAContextScope scope(this); + thread_scoped_lock lock(cuda_mem_map_mutex); const CUDAMem &cmem = cuda_mem_map[&mem]; if (cmem.texobject) { diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index eb8fb8040e3..1efd628b79b 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -17,6 +17,8 @@ #include #include +#include "bvh/bvh2.h" + #include "device/device.h" #include "device/device_intern.h" @@ -364,6 +366,19 @@ void Device::draw_pixels(device_memory &rgba, } } +void Device::build_bvh(BVH *bvh, Progress &progress, bool refit) +{ + assert(bvh->params.bvh_layout == BVH_LAYOUT_BVH2); + + BVH2 *const bvh2 = static_cast(bvh); + if (refit) { + bvh2->refit(progress); + } + else { + bvh2->build(progress, &stats); + } +} + Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) { #ifdef WITH_MULTI diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index 2006db02ce7..e9b7cde7a16 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -373,12 +373,6 @@ class Device { return NULL; } - /* Device specific pointer for BVH creation. Currently only used by Embree. */ - virtual void *bvh_device() const - { - return NULL; - } - /* load/compile kernels, must be called before adding tasks */ virtual bool load_kernels(const DeviceRequestedFeatures & /*requested_features*/) { @@ -427,10 +421,7 @@ class Device { const DeviceDrawParams &draw_params); /* acceleration structure building */ - virtual bool build_optix_bvh(BVH *) - { - return false; - } + virtual void build_bvh(BVH *bvh, Progress &progress, bool refit); #ifdef WITH_NETWORK /* networking */ diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp index 6912ac1e638..fea4fc53d1f 100644 --- a/intern/cycles/device/device_cpu.cpp +++ b/intern/cycles/device/device_cpu.cpp @@ -47,6 +47,8 @@ #include "kernel/osl/osl_globals.h" // clang-format on +#include "bvh/bvh_embree.h" + #include "render/buffers.h" #include "render/coverage.h" @@ -188,6 +190,7 @@ class CPUDevice : public Device { #endif thread_spin_lock oidn_task_lock; #ifdef WITH_EMBREE + RTCScene embree_scene = NULL; RTCDevice embree_device; #endif @@ -472,6 +475,15 @@ class CPUDevice : public Device { virtual void const_copy_to(const char *name, void *host, size_t size) override { +#if WITH_EMBREE + if (strcmp(name, "__data") == 0) { + assert(size <= sizeof(KernelData)); + + // Update scene handle (since it is different for each device on multi devices) + KernelData *const data = (KernelData *)host; + data->bvh.scene = embree_scene; + } +#endif kernel_const_copy(&kernel_globals, name, host, size); } @@ -537,13 +549,26 @@ class CPUDevice : public Device { #endif } - void *bvh_device() const override + void build_bvh(BVH *bvh, Progress &progress, bool refit) override { #ifdef WITH_EMBREE - return embree_device; -#else - return NULL; + if (bvh->params.bvh_layout == BVH_LAYOUT_EMBREE || + bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) { + BVHEmbree *const bvh_embree = static_cast(bvh); + if (refit) { + bvh_embree->refit(progress); + } + else { + bvh_embree->build(progress, &stats, embree_device); + } + + if (bvh->params.top_level) { + embree_scene = bvh_embree->scene; + } + } + else #endif + Device::build_bvh(bvh, progress, refit); } void thread_run(DeviceTask &task) diff --git a/intern/cycles/device/device_multi.cpp b/intern/cycles/device/device_multi.cpp index 2e72a0b4393..ed6e764b995 100644 --- a/intern/cycles/device/device_multi.cpp +++ b/intern/cycles/device/device_multi.cpp @@ -17,11 +17,14 @@ #include #include +#include "bvh/bvh_multi.h" + #include "device/device.h" #include "device/device_intern.h" #include "device/device_network.h" #include "render/buffers.h" +#include "render/geometry.h" #include "util/util_foreach.h" #include "util/util_list.h" @@ -164,9 +167,24 @@ class MultiDevice : public Device { virtual BVHLayoutMask get_bvh_layout_mask() const { BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL; + BVHLayoutMask bvh_layout_mask_all = BVH_LAYOUT_NONE; foreach (const SubDevice &sub_device, devices) { - bvh_layout_mask &= sub_device.device->get_bvh_layout_mask(); + BVHLayoutMask device_bvh_layout_mask = sub_device.device->get_bvh_layout_mask(); + bvh_layout_mask &= device_bvh_layout_mask; + bvh_layout_mask_all |= device_bvh_layout_mask; } + + /* With multiple OptiX devices, every device needs its own acceleration structure */ + if (bvh_layout_mask == BVH_LAYOUT_OPTIX) { + return BVH_LAYOUT_MULTI_OPTIX; + } + + /* When devices do not share a common BVH layout, fall back to creating one for each */ + const BVHLayoutMask BVH_LAYOUT_OPTIX_EMBREE = (BVH_LAYOUT_OPTIX | BVH_LAYOUT_EMBREE); + if ((bvh_layout_mask_all & BVH_LAYOUT_OPTIX_EMBREE) == BVH_LAYOUT_OPTIX_EMBREE) { + return BVH_LAYOUT_MULTI_OPTIX_EMBREE; + } + return bvh_layout_mask; } @@ -227,21 +245,58 @@ class MultiDevice : public Device { return result; } - bool build_optix_bvh(BVH *bvh) + void build_bvh(BVH *bvh, Progress &progress, bool refit) override { - /* Broadcast acceleration structure build to all render devices */ - foreach (SubDevice &sub, devices) { - if (!sub.device->build_optix_bvh(bvh)) - return false; + /* Try to build and share a single acceleration structure, if possible */ + if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2) { + devices.back().device->build_bvh(bvh, progress, refit); + return; } - return true; - } - virtual void *bvh_device() const - { - /* CPU devices will always be at the back, so simply choose the last one. - There should only ever be one CPU device anyway and we need the Embree device for it. */ - return devices.back().device->bvh_device(); + BVHMulti *const bvh_multi = static_cast(bvh); + bvh_multi->sub_bvhs.resize(devices.size()); + + vector geom_bvhs; + geom_bvhs.reserve(bvh->geometry.size()); + foreach (Geometry *geom, bvh->geometry) { + geom_bvhs.push_back(static_cast(geom->bvh)); + } + + /* Broadcast acceleration structure build to all render devices */ + size_t i = 0; + foreach (SubDevice &sub, devices) { + /* Change geometry BVH pointers to the sub BVH */ + for (size_t k = 0; k < bvh->geometry.size(); ++k) { + bvh->geometry[k]->bvh = geom_bvhs[k]->sub_bvhs[i]; + } + + if (!bvh_multi->sub_bvhs[i]) { + BVHParams params = bvh->params; + if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX) + params.bvh_layout = BVH_LAYOUT_OPTIX; + else if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) + params.bvh_layout = sub.device->info.type == DEVICE_OPTIX ? BVH_LAYOUT_OPTIX : + BVH_LAYOUT_EMBREE; + + /* Skip building a bottom level acceleration structure for non-instanced geometry on Embree + * (since they are put into the top level directly, see bvh_embree.cpp) */ + if (!params.top_level && params.bvh_layout == BVH_LAYOUT_EMBREE && + !bvh->geometry[0]->is_instanced()) { + i++; + continue; + } + + bvh_multi->sub_bvhs[i] = BVH::create(params, bvh->geometry, bvh->objects, sub.device); + } + + sub.device->build_bvh(bvh_multi->sub_bvhs[i], progress, refit); + i++; + } + + /* Change geomtry BVH pointers back to the multi BVH */ + for (size_t k = 0; k < bvh->geometry.size(); ++k) { + bvh->geometry[k]->bvh = geom_bvhs[k]; + } } virtual void *osl_memory() diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index c6276c1e955..a721f426dfe 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -31,6 +31,7 @@ # include "util/util_logging.h" # include "util/util_md5.h" # include "util/util_path.h" +# include "util/util_progress.h" # include "util/util_time.h" # ifdef WITH_CUDA_DYNLOAD @@ -186,7 +187,6 @@ class OptiXDevice : public CUDADevice { bool motion_blur = false; device_vector sbt_data; device_only_memory launch_params; - vector as_mem; OptixTraversableHandle tlas_handle = 0; OptixDenoiser denoiser = NULL; @@ -258,11 +258,6 @@ class OptiXDevice : public CUDADevice { // Make CUDA context current const CUDAContextScope scope(cuContext); - // Free all acceleration structures - for (CUdeviceptr mem : as_mem) { - cuMemFree(mem); - } - sbt_data.free(); texture_info.free(); launch_params.free(); @@ -1136,11 +1131,10 @@ class OptiXDevice : public CUDADevice { } } - bool build_optix_bvh(const OptixBuildInput &build_input, - uint16_t num_motion_steps, - OptixTraversableHandle &out_handle, - CUdeviceptr &out_data, - OptixBuildOperation operation) + bool build_optix_bvh(BVHOptiX *bvh, + OptixBuildOperation operation, + const OptixBuildInput &build_input, + uint16_t num_motion_steps) { const CUDAContextScope scope(cuContext); @@ -1166,24 +1160,21 @@ class OptiXDevice : public CUDADevice { optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes)); // Allocate required output buffers - device_only_memory temp_mem(this, "temp_build_mem"); + device_only_memory temp_mem(this, "optix temp as build mem"); temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8); if (!temp_mem.device_pointer) return false; // Make sure temporary memory allocation succeeded - // Move textures to host memory if there is not enough room - size_t size = 0, free = 0; - cuMemGetInfo(&free, &size); - size = sizes.outputSizeInBytes + device_working_headroom; - if (size >= free && can_map_host) { - move_textures_to_host(size - free, false); - } - + device_only_memory &out_data = bvh->as_data; if (operation == OPTIX_BUILD_OPERATION_BUILD) { - check_result_cuda_ret(cuMemAlloc(&out_data, sizes.outputSizeInBytes)); + assert(out_data.device == this); + out_data.alloc_to_device(sizes.outputSizeInBytes); + if (!out_data.device_pointer) + return false; + } + else { + assert(out_data.device_pointer && out_data.device_size >= sizes.outputSizeInBytes); } - - as_mem.push_back(out_data); // Finally build the acceleration structure OptixAccelEmitDesc compacted_size_prop; @@ -1192,6 +1183,7 @@ class OptiXDevice : public CUDADevice { // Make sure this pointer is 8-byte aligned compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8); + OptixTraversableHandle out_handle = 0; check_result_optix_ret(optixAccelBuild(context, NULL, &options, @@ -1199,11 +1191,12 @@ class OptiXDevice : public CUDADevice { 1, temp_mem.device_pointer, sizes.tempSizeInBytes, - out_data, + out_data.device_pointer, sizes.outputSizeInBytes, &out_handle, background ? &compacted_size_prop : NULL, background ? 1 : 0)); + bvh->traversable_handle = static_cast(out_handle); // Wait for all operations to finish check_result_cuda_ret(cuStreamSynchronize(NULL)); @@ -1219,81 +1212,60 @@ class OptiXDevice : public CUDADevice { // There is no point compacting if the size does not change if (compacted_size < sizes.outputSizeInBytes) { - CUdeviceptr compacted_data = 0; - if (cuMemAlloc(&compacted_data, compacted_size) != CUDA_SUCCESS) + device_only_memory compacted_data(this, "optix compacted as"); + compacted_data.alloc_to_device(compacted_size); + if (!compacted_data.device_pointer) // Do not compact if memory allocation for compacted acceleration structure fails // Can just use the uncompacted one then, so succeed here regardless return true; - as_mem.push_back(compacted_data); - check_result_optix_ret(optixAccelCompact( - context, NULL, out_handle, compacted_data, compacted_size, &out_handle)); + check_result_optix_ret(optixAccelCompact(context, + NULL, + out_handle, + compacted_data.device_pointer, + compacted_size, + &out_handle)); + bvh->traversable_handle = static_cast(out_handle); // Wait for compaction to finish check_result_cuda_ret(cuStreamSynchronize(NULL)); - // Free uncompacted acceleration structure - cuMemFree(out_data); - as_mem.erase(as_mem.end() - 2); // Remove 'out_data' from 'as_mem' array + std::swap(out_data.device_size, compacted_data.device_size); + std::swap(out_data.device_pointer, compacted_data.device_pointer); } } return true; } - bool build_optix_bvh(BVH *bvh) override + void build_bvh(BVH *bvh, Progress &progress, bool refit) override { - assert(bvh->params.top_level); + BVHOptiX *const bvh_optix = static_cast(bvh); - unsigned int num_instances = 0; - unordered_map geometry; - geometry.reserve(bvh->geometry.size()); + progress.set_substatus("Building OptiX acceleration structure"); - // Free all previous acceleration structures which can not be refit - std::set refit_mem; + if (!bvh->params.top_level) { + assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1); - for (Geometry *geom : bvh->geometry) { - if (static_cast(geom->bvh)->do_refit) { - refit_mem.insert(static_cast(geom->bvh)->optix_data_handle); - } - } - - for (CUdeviceptr mem : as_mem) { - if (refit_mem.find(mem) == refit_mem.end()) { - cuMemFree(mem); - } - } - - as_mem.clear(); - - // Build bottom level acceleration structures (BLAS) - // Note: Always keep this logic in sync with bvh_optix.cpp! - for (Object *ob : bvh->objects) { - // Skip geometry for which acceleration structure already exists - Geometry *geom = ob->get_geometry(); - if (geometry.find(geom) != geometry.end()) - continue; - - OptixTraversableHandle handle; - OptixBuildOperation operation; - CUdeviceptr out_data; - // Refit is only possible in viewport for now. - if (static_cast(geom->bvh)->do_refit && !background) { - out_data = static_cast(geom->bvh)->optix_data_handle; - handle = static_cast(geom->bvh)->optix_handle; + // Refit is only possible in viewport for now (because AS is built with + // OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above) + OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD; + if (refit && !background) { + assert(bvh_optix->traversable_handle != 0); operation = OPTIX_BUILD_OPERATION_UPDATE; } else { - out_data = 0; - handle = 0; - operation = OPTIX_BUILD_OPERATION_BUILD; + bvh_optix->as_data.free(); + bvh_optix->traversable_handle = 0; } + // Build bottom level acceleration structures (BLAS) + Geometry *const geom = bvh->geometry[0]; if (geom->geometry_type == Geometry::HAIR) { // Build BLAS for curve primitives - Hair *const hair = static_cast(ob->get_geometry()); + Hair *const hair = static_cast(geom); if (hair->num_curves() == 0) { - continue; + return; } const size_t num_segments = hair->num_segments(); @@ -1304,10 +1276,10 @@ class OptiXDevice : public CUDADevice { num_motion_steps = hair->get_motion_steps(); } - device_vector aabb_data(this, "temp_aabb_data", MEM_READ_ONLY); + device_vector aabb_data(this, "optix temp aabb data", MEM_READ_ONLY); # if OPTIX_ABI_VERSION >= 36 - device_vector index_data(this, "temp_index_data", MEM_READ_ONLY); - device_vector vertex_data(this, "temp_vertex_data", MEM_READ_ONLY); + device_vector index_data(this, "optix temp index data", MEM_READ_ONLY); + device_vector vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); // Four control points for each curve segment const size_t num_vertices = num_segments * 4; if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { @@ -1325,7 +1297,7 @@ class OptiXDevice : public CUDADevice { size_t center_step = (num_motion_steps - 1) / 2; if (step != center_step) { size_t attr_offset = (step > center_step) ? step - 1 : step; - // Technically this is a float4 array, but sizeof(float3) is the same as sizeof(float4) + // Technically this is a float4 array, but sizeof(float3) == sizeof(float4) keys = motion_keys->data_float3() + attr_offset * hair->get_curve_keys().size(); } @@ -1452,22 +1424,15 @@ class OptiXDevice : public CUDADevice { # endif } - // Allocate memory for new BLAS and build it - if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) { - geometry.insert({ob->get_geometry(), handle}); - static_cast(geom->bvh)->optix_data_handle = out_data; - static_cast(geom->bvh)->optix_handle = handle; - static_cast(geom->bvh)->do_refit = false; - } - else { - return false; + if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { + progress.set_error("Failed to build OptiX acceleration structure"); } } else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { // Build BLAS for triangle primitives - Mesh *const mesh = static_cast(ob->get_geometry()); + Mesh *const mesh = static_cast(geom); if (mesh->num_triangles() == 0) { - continue; + return; } const size_t num_verts = mesh->get_verts().size(); @@ -1478,12 +1443,12 @@ class OptiXDevice : public CUDADevice { num_motion_steps = mesh->get_motion_steps(); } - device_vector index_data(this, "temp_index_data", MEM_READ_ONLY); + device_vector index_data(this, "optix temp index data", MEM_READ_ONLY); index_data.alloc(mesh->get_triangles().size()); memcpy(index_data.data(), mesh->get_triangles().data(), mesh->get_triangles().size() * sizeof(int)); - device_vector vertex_data(this, "temp_vertex_data", MEM_READ_ONLY); + device_vector vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); vertex_data.alloc(num_verts * num_motion_steps); for (size_t step = 0; step < num_motion_steps; ++step) { @@ -1528,190 +1493,208 @@ class OptiXDevice : public CUDADevice { build_input.triangleArray.numSbtRecords = 1; build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset; - // Allocate memory for new BLAS and build it - if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) { - geometry.insert({ob->get_geometry(), handle}); - static_cast(geom->bvh)->optix_data_handle = out_data; - static_cast(geom->bvh)->optix_handle = handle; - static_cast(geom->bvh)->do_refit = false; - } - else { - return false; + if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { + progress.set_error("Failed to build OptiX acceleration structure"); } } } + else { + unsigned int num_instances = 0; - // Fill instance descriptions + bvh_optix->as_data.free(); + bvh_optix->traversable_handle = 0; + bvh_optix->motion_transform_data.free(); + + // Fill instance descriptions # if OPTIX_ABI_VERSION < 41 - device_vector aabbs(this, "tlas_aabbs", MEM_READ_ONLY); - aabbs.alloc(bvh->objects.size()); + device_vector aabbs(this, "optix tlas aabbs", MEM_READ_ONLY); + aabbs.alloc(bvh->objects.size()); # endif - device_vector instances(this, "tlas_instances", MEM_READ_ONLY); - instances.alloc(bvh->objects.size()); + device_vector instances(this, "optix tlas instances", MEM_READ_ONLY); + instances.alloc(bvh->objects.size()); - for (Object *ob : bvh->objects) { - // Skip non-traceable objects - if (!ob->is_traceable()) - continue; + // Calculate total motion transform size and allocate memory for them + size_t motion_transform_offset = 0; + if (motion_blur) { + size_t total_motion_transform_size = 0; + for (Object *const ob : bvh->objects) { + if (ob->is_traceable() && ob->use_motion()) { + total_motion_transform_size = align_up(total_motion_transform_size, + OPTIX_TRANSFORM_BYTE_ALIGNMENT); + const size_t motion_keys = max(ob->get_motion().size(), 2) - 2; + total_motion_transform_size = total_motion_transform_size + + sizeof(OptixSRTMotionTransform) + + motion_keys * sizeof(OptixSRTData); + } + } - // Create separate instance for triangle/curve meshes of an object - const auto handle_it = geometry.find(ob->get_geometry()); - if (handle_it == geometry.end()) { - continue; - } - OptixTraversableHandle handle = handle_it->second; - -# if OPTIX_ABI_VERSION < 41 - OptixAabb &aabb = aabbs[num_instances]; - aabb.minX = ob->bounds.min.x; - aabb.minY = ob->bounds.min.y; - aabb.minZ = ob->bounds.min.z; - aabb.maxX = ob->bounds.max.x; - aabb.maxY = ob->bounds.max.y; - aabb.maxZ = ob->bounds.max.z; -# endif - - OptixInstance &instance = instances[num_instances++]; - memset(&instance, 0, sizeof(instance)); - - // Clear transform to identity matrix - instance.transform[0] = 1.0f; - instance.transform[5] = 1.0f; - instance.transform[10] = 1.0f; - - // Set user instance ID to object index - instance.instanceId = ob->get_device_index(); - - // Have to have at least one bit in the mask, or else instance would always be culled - instance.visibilityMask = 1; - - if (ob->get_geometry()->has_volume) { - // Volumes have a special bit set in the visibility mask so a trace can mask only volumes - instance.visibilityMask |= 2; + assert(bvh_optix->motion_transform_data.device == this); + bvh_optix->motion_transform_data.alloc_to_device(total_motion_transform_size); } - if (ob->get_geometry()->geometry_type == Geometry::HAIR) { - // Same applies to curves (so they can be skipped in local trace calls) - instance.visibilityMask |= 4; + for (Object *ob : bvh->objects) { + // Skip non-traceable objects + if (!ob->is_traceable()) + continue; + + BVHOptiX *const blas = static_cast(ob->get_geometry()->bvh); + OptixTraversableHandle handle = blas->traversable_handle; + +# if OPTIX_ABI_VERSION < 41 + OptixAabb &aabb = aabbs[num_instances]; + aabb.minX = ob->bounds.min.x; + aabb.minY = ob->bounds.min.y; + aabb.minZ = ob->bounds.min.z; + aabb.maxX = ob->bounds.max.x; + aabb.maxY = ob->bounds.max.y; + aabb.maxZ = ob->bounds.max.z; +# endif + + OptixInstance &instance = instances[num_instances++]; + memset(&instance, 0, sizeof(instance)); + + // Clear transform to identity matrix + instance.transform[0] = 1.0f; + instance.transform[5] = 1.0f; + instance.transform[10] = 1.0f; + + // Set user instance ID to object index + instance.instanceId = ob->get_device_index(); + + // Have to have at least one bit in the mask, or else instance would always be culled + instance.visibilityMask = 1; + + if (ob->get_geometry()->has_volume) { + // Volumes have a special bit set in the visibility mask so a trace can mask only volumes + instance.visibilityMask |= 2; + } + + if (ob->get_geometry()->geometry_type == Geometry::HAIR) { + // Same applies to curves (so they can be skipped in local trace calls) + instance.visibilityMask |= 4; # if OPTIX_ABI_VERSION >= 36 - if (motion_blur && ob->get_geometry()->has_motion_blur() && - DebugFlags().optix.curves_api && - static_cast(ob->get_geometry())->curve_shape == CURVE_THICK) { - // Select between motion blur and non-motion blur built-in intersection module - instance.sbtOffset = PG_HITD_MOTION - PG_HITD; - } + if (motion_blur && ob->get_geometry()->has_motion_blur() && + DebugFlags().optix.curves_api && + static_cast(ob->get_geometry())->curve_shape == CURVE_THICK) { + // Select between motion blur and non-motion blur built-in intersection module + instance.sbtOffset = PG_HITD_MOTION - PG_HITD; + } # endif - } - - // Insert motion traversable if object has motion - if (motion_blur && ob->use_motion()) { - size_t motion_keys = max(ob->get_motion().size(), 2) - 2; - size_t motion_transform_size = sizeof(OptixSRTMotionTransform) + - motion_keys * sizeof(OptixSRTData); - - const CUDAContextScope scope(cuContext); - - CUdeviceptr motion_transform_gpu = 0; - check_result_cuda_ret(cuMemAlloc(&motion_transform_gpu, motion_transform_size)); - as_mem.push_back(motion_transform_gpu); - - // Allocate host side memory for motion transform and fill it with transform data - OptixSRTMotionTransform &motion_transform = *reinterpret_cast( - new uint8_t[motion_transform_size]); - motion_transform.child = handle; - motion_transform.motionOptions.numKeys = ob->get_motion().size(); - motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE; - motion_transform.motionOptions.timeBegin = 0.0f; - motion_transform.motionOptions.timeEnd = 1.0f; - - OptixSRTData *const srt_data = motion_transform.srtData; - array decomp(ob->get_motion().size()); - transform_motion_decompose( - decomp.data(), ob->get_motion().data(), ob->get_motion().size()); - - for (size_t i = 0; i < ob->get_motion().size(); ++i) { - // Scale - srt_data[i].sx = decomp[i].y.w; // scale.x.x - srt_data[i].sy = decomp[i].z.w; // scale.y.y - srt_data[i].sz = decomp[i].w.w; // scale.z.z - - // Shear - srt_data[i].a = decomp[i].z.x; // scale.x.y - srt_data[i].b = decomp[i].z.y; // scale.x.z - srt_data[i].c = decomp[i].w.x; // scale.y.z - assert(decomp[i].z.z == 0.0f); // scale.y.x - assert(decomp[i].w.y == 0.0f); // scale.z.x - assert(decomp[i].w.z == 0.0f); // scale.z.y - - // Pivot point - srt_data[i].pvx = 0.0f; - srt_data[i].pvy = 0.0f; - srt_data[i].pvz = 0.0f; - - // Rotation - srt_data[i].qx = decomp[i].x.x; - srt_data[i].qy = decomp[i].x.y; - srt_data[i].qz = decomp[i].x.z; - srt_data[i].qw = decomp[i].x.w; - - // Translation - srt_data[i].tx = decomp[i].y.x; - srt_data[i].ty = decomp[i].y.y; - srt_data[i].tz = decomp[i].y.z; } - // Upload motion transform to GPU - cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size); - delete[] reinterpret_cast(&motion_transform); + // Insert motion traversable if object has motion + if (motion_blur && ob->use_motion()) { + size_t motion_keys = max(ob->get_motion().size(), 2) - 2; + size_t motion_transform_size = sizeof(OptixSRTMotionTransform) + + motion_keys * sizeof(OptixSRTData); - // Disable instance transform if object uses motion transform already - instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; + const CUDAContextScope scope(cuContext); - // Get traversable handle to motion transform - optixConvertPointerToTraversableHandle(context, - motion_transform_gpu, - OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM, - &instance.traversableHandle); - } - else { - instance.traversableHandle = handle; + motion_transform_offset = align_up(motion_transform_offset, + OPTIX_TRANSFORM_BYTE_ALIGNMENT); + CUdeviceptr motion_transform_gpu = bvh_optix->motion_transform_data.device_pointer + + motion_transform_offset; + motion_transform_offset += motion_transform_size; - if (ob->get_geometry()->is_instanced()) { - // Set transform matrix - memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform)); + // Allocate host side memory for motion transform and fill it with transform data + OptixSRTMotionTransform &motion_transform = *reinterpret_cast( + new uint8_t[motion_transform_size]); + motion_transform.child = handle; + motion_transform.motionOptions.numKeys = ob->get_motion().size(); + motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE; + motion_transform.motionOptions.timeBegin = 0.0f; + motion_transform.motionOptions.timeEnd = 1.0f; + + OptixSRTData *const srt_data = motion_transform.srtData; + array decomp(ob->get_motion().size()); + transform_motion_decompose( + decomp.data(), ob->get_motion().data(), ob->get_motion().size()); + + for (size_t i = 0; i < ob->get_motion().size(); ++i) { + // Scale + srt_data[i].sx = decomp[i].y.w; // scale.x.x + srt_data[i].sy = decomp[i].z.w; // scale.y.y + srt_data[i].sz = decomp[i].w.w; // scale.z.z + + // Shear + srt_data[i].a = decomp[i].z.x; // scale.x.y + srt_data[i].b = decomp[i].z.y; // scale.x.z + srt_data[i].c = decomp[i].w.x; // scale.y.z + assert(decomp[i].z.z == 0.0f); // scale.y.x + assert(decomp[i].w.y == 0.0f); // scale.z.x + assert(decomp[i].w.z == 0.0f); // scale.z.y + + // Pivot point + srt_data[i].pvx = 0.0f; + srt_data[i].pvy = 0.0f; + srt_data[i].pvz = 0.0f; + + // Rotation + srt_data[i].qx = decomp[i].x.x; + srt_data[i].qy = decomp[i].x.y; + srt_data[i].qz = decomp[i].x.z; + srt_data[i].qw = decomp[i].x.w; + + // Translation + srt_data[i].tx = decomp[i].y.x; + srt_data[i].ty = decomp[i].y.y; + srt_data[i].tz = decomp[i].y.z; + } + + // Upload motion transform to GPU + cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size); + delete[] reinterpret_cast(&motion_transform); + + // Disable instance transform if object uses motion transform already + instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; + + // Get traversable handle to motion transform + optixConvertPointerToTraversableHandle(context, + motion_transform_gpu, + OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM, + &instance.traversableHandle); } else { - // Disable instance transform if geometry already has it applied to vertex data - instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; - // Non-instanced objects read ID from prim_object, so - // distinguish them from instanced objects with high bit set - instance.instanceId |= 0x800000; + instance.traversableHandle = handle; + + if (ob->get_geometry()->is_instanced()) { + // Set transform matrix + memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform)); + } + else { + // Disable instance transform if geometry already has it applied to vertex data + instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; + // Non-instanced objects read ID from prim_object, so + // distinguish them from instanced objects with high bit set + instance.instanceId |= 0x800000; + } } } - } - // Upload instance descriptions + // Upload instance descriptions # if OPTIX_ABI_VERSION < 41 - aabbs.resize(num_instances); - aabbs.copy_to_device(); + aabbs.resize(num_instances); + aabbs.copy_to_device(); # endif - instances.resize(num_instances); - instances.copy_to_device(); + instances.resize(num_instances); + instances.copy_to_device(); - // Build top-level acceleration structure (TLAS) - OptixBuildInput build_input = {}; - build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES; + // Build top-level acceleration structure (TLAS) + OptixBuildInput build_input = {}; + build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES; # if OPTIX_ABI_VERSION < 41 // Instance AABBs no longer need to be set since OptiX 7.2 - build_input.instanceArray.aabbs = aabbs.device_pointer; - build_input.instanceArray.numAabbs = num_instances; + build_input.instanceArray.aabbs = aabbs.device_pointer; + build_input.instanceArray.numAabbs = num_instances; # endif - build_input.instanceArray.instances = instances.device_pointer; - build_input.instanceArray.numInstances = num_instances; + build_input.instanceArray.instances = instances.device_pointer; + build_input.instanceArray.numInstances = num_instances; - CUdeviceptr out_data = 0; - tlas_handle = 0; - return build_optix_bvh(build_input, 0, tlas_handle, out_data, OPTIX_BUILD_OPERATION_BUILD); + if (!build_optix_bvh(bvh_optix, OPTIX_BUILD_OPERATION_BUILD, build_input, 0)) { + progress.set_error("Failed to build OptiX acceleration structure"); + } + tlas_handle = bvh_optix->traversable_handle; + } } void const_copy_to(const char *name, void *host, size_t size) override @@ -1724,7 +1707,7 @@ class OptiXDevice : public CUDADevice { if (strcmp(name, "__data") == 0) { assert(size <= sizeof(KernelData)); - // Fix traversable handle on multi devices + // Update traversable handle (since it is different for each device on multi devices) KernelData *const data = (KernelData *)host; *(OptixTraversableHandle *)&data->bvh.scene = tlas_handle; diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/bvh_embree.h index ca637288bee..4605c3ea51d 100644 --- a/intern/cycles/kernel/bvh/bvh_embree.h +++ b/intern/cycles/kernel/bvh/bvh_embree.h @@ -112,8 +112,7 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals *kg, RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0])); isect->prim = hit->primID + - (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) + - kernel_tex_fetch(__object_node, hit->instID[0] / 2); + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); isect->object = hit->instID[0] / 2; } else { @@ -137,8 +136,7 @@ ccl_device_inline void kernel_embree_convert_sss_hit(KernelGlobals *kg, RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( rtcGetGeometry(kernel_data.bvh.scene, local_object_id * 2)); isect->prim = hit->primID + - (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) + - kernel_tex_fetch(__object_node, local_object_id); + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); isect->object = local_object_id; isect->type = kernel_tex_fetch(__prim_type, isect->prim); } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 6beabebb92f..9d00311f746 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1397,10 +1397,12 @@ typedef enum KernelBVHLayout { BVH_LAYOUT_BVH2 = (1 << 0), BVH_LAYOUT_EMBREE = (1 << 1), BVH_LAYOUT_OPTIX = (1 << 2), + BVH_LAYOUT_MULTI_OPTIX = (1 << 3), + BVH_LAYOUT_MULTI_OPTIX_EMBREE = (1 << 4), /* Default BVH layout to use for CPU. */ BVH_LAYOUT_AUTO = BVH_LAYOUT_EMBREE, - BVH_LAYOUT_ALL = (unsigned int)(~0u), + BVH_LAYOUT_ALL = BVH_LAYOUT_BVH2 | BVH_LAYOUT_EMBREE | BVH_LAYOUT_OPTIX, } KernelBVHLayout; typedef struct KernelBVH { diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 12f4eaf0b79..64b98a91853 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -15,8 +15,7 @@ */ #include "bvh/bvh.h" -#include "bvh/bvh_build.h" -#include "bvh/bvh_embree.h" +#include "bvh/bvh2.h" #include "device/device.h" @@ -41,6 +40,7 @@ #include "util/util_foreach.h" #include "util/util_logging.h" #include "util/util_progress.h" +#include "util/util_task.h" CCL_NAMESPACE_BEGIN @@ -162,7 +162,8 @@ int Geometry::motion_step(float time) const bool Geometry::need_build_bvh(BVHLayout layout) const { - return !transform_applied || has_surface_bssrdf || layout == BVH_LAYOUT_OPTIX; + return is_instanced() || layout == BVH_LAYOUT_OPTIX || layout == BVH_LAYOUT_MULTI_OPTIX || + layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE; } bool Geometry::is_instanced() const @@ -218,7 +219,7 @@ void Geometry::compute_bvh( bvh->geometry = geometry; bvh->objects = objects; - bvh->refit(*progress); + device->build_bvh(bvh, *progress, true); } else { progress->set_status(msg, "Building BVH"); @@ -235,7 +236,7 @@ void Geometry::compute_bvh( delete bvh; bvh = BVH::create(bparams, geometry, objects, device); - MEM_GUARDED_CALL(progress, bvh->build, *progress); + MEM_GUARDED_CALL(progress, device->build_bvh, bvh, *progress, false); } } @@ -1162,25 +1163,66 @@ void GeometryManager::device_update_bvh(Device *device, VLOG(1) << "Using " << bvh_layout_name(bparams.bvh_layout) << " layout."; - BVH *bvh = BVH::create(bparams, scene->geometry, scene->objects, device); - bvh->build(progress, &device->stats); + delete scene->bvh; + BVH *bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device); + device->build_bvh(bvh, progress, false); if (progress.get_cancel()) { -#ifdef WITH_EMBREE - if (dscene->data.bvh.scene) { - BVHEmbree::destroy(dscene->data.bvh.scene); - dscene->data.bvh.scene = NULL; - } -#endif - delete bvh; return; } + PackedBVH pack; + if (bparams.bvh_layout == BVH_LAYOUT_BVH2) { + pack = std::move(static_cast(bvh)->pack); + } + else { + progress.set_status("Updating Scene BVH", "Packing BVH primitives"); + + size_t num_prims = 0; + size_t num_tri_verts = 0; + foreach (Geometry *geom, scene->geometry) { + if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { + Mesh *mesh = static_cast(geom); + num_prims += mesh->num_triangles(); + num_tri_verts += 3 * mesh->num_triangles(); + } + else if (geom->is_hair()) { + Hair *hair = static_cast(geom); + num_prims += hair->num_segments(); + } + } + + pack.root_index = -1; + pack.prim_tri_index.reserve(num_prims); + pack.prim_tri_verts.reserve(num_tri_verts); + pack.prim_type.reserve(num_prims); + pack.prim_index.reserve(num_prims); + pack.prim_object.reserve(num_prims); + pack.prim_visibility.reserve(num_prims); + + // Merge visibility flags of all objects and find object index for non-instanced geometry + unordered_map> geometry_to_object_info; + geometry_to_object_info.reserve(scene->geometry.size()); + foreach (Object *ob, scene->objects) { + const Geometry *const geom = ob->get_geometry(); + pair &info = geometry_to_object_info[geom]; + info.second |= ob->visibility_for_tracing(); + if (!geom->is_instanced()) { + info.first = ob->get_device_index(); + } + } + + // Iterate over scene mesh list instead of objects, since 'optix_prim_offset' was calculated + // based on that list, which may be ordered differently from the object list. + foreach (Geometry *geom, scene->geometry) { + const pair &info = geometry_to_object_info[geom]; + geom->pack_primitives(pack, info.first, info.second); + } + } + /* copy to device */ progress.set_status("Updating Scene BVH", "Copying BVH to device"); - PackedBVH &pack = bvh->pack; - if (pack.nodes.size()) { dscene->bvh_nodes.steal_data(pack.nodes); dscene->bvh_nodes.copy_to_device(); @@ -1226,10 +1268,8 @@ void GeometryManager::device_update_bvh(Device *device, dscene->data.bvh.bvh_layout = bparams.bvh_layout; dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0); dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions(); - - bvh->copy_to_device(progress, dscene); - - delete bvh; + /* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */ + dscene->data.bvh.scene = NULL; } void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress) @@ -1653,14 +1693,6 @@ void GeometryManager::device_update(Device *device, void GeometryManager::device_free(Device *device, DeviceScene *dscene) { -#ifdef WITH_EMBREE - if (dscene->data.bvh.scene) { - if (dscene->data.bvh.bvh_layout == BVH_LAYOUT_EMBREE) - BVHEmbree::destroy(dscene->data.bvh.scene); - dscene->data.bvh.scene = NULL; - } -#endif - dscene->bvh_nodes.free(); dscene->bvh_leaf_nodes.free(); dscene->object_node.free(); diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h index 1c101540464..d3daf0cc809 100644 --- a/intern/cycles/render/geometry.h +++ b/intern/cycles/render/geometry.h @@ -41,6 +41,7 @@ class Scene; class SceneParams; class Shader; class Volume; +struct PackedBVH; /* Geometry * @@ -124,6 +125,8 @@ class Geometry : public Node { int n, int total); + virtual void pack_primitives(PackedBVH &pack, int object, uint visibility) = 0; + /* Check whether the geometry should have own BVH built separately. Briefly, * own BVH is needed for geometry, if: * diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp index d67bd209142..a64b8242337 100644 --- a/intern/cycles/render/hair.cpp +++ b/intern/cycles/render/hair.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "bvh/bvh.h" + #include "render/hair.h" #include "render/curves.h" #include "render/scene.h" @@ -492,4 +494,35 @@ void Hair::pack_curves(Scene *scene, } } +void Hair::pack_primitives(PackedBVH &pack, int object, uint visibility) +{ + if (curve_first_key.empty()) + return; + + const size_t num_prims = num_segments(); + pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims); + pack.prim_type.reserve(pack.prim_type.size() + num_prims); + pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims); + pack.prim_index.reserve(pack.prim_index.size() + num_prims); + pack.prim_object.reserve(pack.prim_object.size() + num_prims); + // 'pack.prim_time' is unused by Embree and OptiX + + uint type = has_motion_blur() ? + ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : + PRIMITIVE_MOTION_CURVE_THICK) : + ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK); + + for (size_t j = 0; j < num_curves(); ++j) { + Curve curve = get_curve(j); + for (size_t k = 0; k < curve.num_segments(); ++k) { + pack.prim_tri_index.push_back_reserved(-1); + pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k)); + pack.prim_visibility.push_back_reserved(visibility); + // Each curve segment points back to its curve index + pack.prim_index.push_back_reserved(j + prim_offset); + pack.prim_object.push_back_reserved(object); + } + } +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/hair.h b/intern/cycles/render/hair.h index 32c5b00e879..d86a34fb87b 100644 --- a/intern/cycles/render/hair.h +++ b/intern/cycles/render/hair.h @@ -145,6 +145,8 @@ class Hair : public Geometry { /* BVH */ void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset); + + void pack_primitives(PackedBVH &pack, int object, uint visibility); }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 43e664a686f..a0afdd3b841 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -805,4 +805,35 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui } } +void Mesh::pack_primitives(PackedBVH &pack, int object, uint visibility) +{ + if (triangles.empty()) + return; + + const size_t num_prims = num_triangles(); + pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims); + pack.prim_tri_verts.reserve(pack.prim_tri_verts.size() + num_prims * 3); + pack.prim_type.reserve(pack.prim_type.size() + num_prims); + pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims); + pack.prim_index.reserve(pack.prim_index.size() + num_prims); + pack.prim_object.reserve(pack.prim_object.size() + num_prims); + // 'pack.prim_time' is unused by Embree and OptiX + + uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE; + + for (size_t k = 0; k < num_prims; ++k) { + pack.prim_tri_index.push_back_reserved(pack.prim_tri_verts.size()); + + const Mesh::Triangle t = get_triangle(k); + pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[0]])); + pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[1]])); + pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[2]])); + + pack.prim_type.push_back_reserved(type); + pack.prim_visibility.push_back_reserved(visibility); + pack.prim_index.push_back_reserved(k + prim_offset); + pack.prim_object.push_back_reserved(object); + } +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index e2746e560da..b0a16fdfd8f 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -184,9 +184,8 @@ class Mesh : public Geometry { unordered_multimap vert_stitching_map; /* stitching index -> multiple real vert indices */ - friend class BVH; + friend class BVH2; friend class BVHBuild; - friend class BVHEmbree; friend class BVHSpatialSplit; friend class DiagSplit; friend class EdgeDice; @@ -233,6 +232,8 @@ class Mesh : public Geometry { size_t tri_offset); void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset); + void pack_primitives(PackedBVH &pack, int object, uint visibility) override; + void tessellate(DiagSplit *split); SubdFace get_subd_face(size_t index) const; diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index 98c256a43b5..b7720b7aa99 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -16,6 +16,7 @@ #include +#include "bvh/bvh.h" #include "device/device.h" #include "render/background.h" #include "render/bake.h" @@ -100,6 +101,7 @@ Scene::Scene(const SceneParams ¶ms_, Device *device) { memset((void *)&dscene.data, 0, sizeof(dscene.data)); + bvh = NULL; camera = create_node(); dicing_camera = create_node(); lookup_tables = new LookupTables(); @@ -135,6 +137,9 @@ Scene::~Scene() void Scene::free_memory(bool final) { + delete bvh; + bvh = NULL; + foreach (Shader *s, shaders) delete s; foreach (Geometry *g, geometry) diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 6686327dc49..27e9a131bbd 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -38,6 +38,7 @@ CCL_NAMESPACE_BEGIN class AttributeRequestSet; class Background; +class BVH; class Camera; class Device; class DeviceInfo; @@ -220,6 +221,7 @@ class Scene : public NodeOwner { string name; /* data */ + BVH *bvh; Camera *camera; Camera *dicing_camera; LookupTables *lookup_tables;