Cycles: Add CPU+GPU rendering support with OptiX

Adds support for building multiple BVH types in order to support using both CPU and OptiX
devices for rendering simultaneously. Primitive packing for Embree and OptiX is now
standalone, so it only needs to be run once and can be shared between the two. Additionally,
BVH building was made a device call, so that each device backend can decide how to
perform the building. The multi-device for instance creates a special multi-BVH that holds
references to several sub-BVHs, one for each sub-device.

Reviewed By: brecht, kevindietrich

Differential Revision: https://developer.blender.org/D9718
This commit is contained in:
Patrick Mours 2020-12-10 14:18:25 +01:00
parent d72ec16e70
commit bfb6fce659
30 changed files with 1089 additions and 1332 deletions

@ -1570,7 +1570,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
elif entry.type == 'CPU': elif entry.type == 'CPU':
cpu_devices.append(entry) cpu_devices.append(entry)
# Extend all GPU devices with CPU. # 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) devices.extend(cpu_devices)
return devices return devices

@ -90,8 +90,7 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
mask |= DEVICE_MASK_CUDA; mask |= DEVICE_MASK_CUDA;
} }
else if (compute_device == COMPUTE_DEVICE_OPTIX) { 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) { else if (compute_device == COMPUTE_DEVICE_OPENCL) {
mask |= DEVICE_MASK_OPENCL; mask |= DEVICE_MASK_OPENCL;

@ -25,6 +25,7 @@ set(SRC
bvh_binning.cpp bvh_binning.cpp
bvh_build.cpp bvh_build.cpp
bvh_embree.cpp bvh_embree.cpp
bvh_multi.cpp
bvh_node.cpp bvh_node.cpp
bvh_optix.cpp bvh_optix.cpp
bvh_sort.cpp bvh_sort.cpp
@ -38,6 +39,7 @@ set(SRC_HEADERS
bvh_binning.h bvh_binning.h
bvh_build.h bvh_build.h
bvh_embree.h bvh_embree.h
bvh_multi.h
bvh_node.h bvh_node.h
bvh_optix.h bvh_optix.h
bvh_params.h bvh_params.h

@ -17,17 +17,11 @@
#include "bvh/bvh.h" #include "bvh/bvh.h"
#include "render/hair.h"
#include "render/mesh.h"
#include "render/object.h"
#include "bvh/bvh2.h" #include "bvh/bvh2.h"
#include "bvh/bvh_build.h"
#include "bvh/bvh_embree.h" #include "bvh/bvh_embree.h"
#include "bvh/bvh_node.h" #include "bvh/bvh_multi.h"
#include "bvh/bvh_optix.h" #include "bvh/bvh_optix.h"
#include "util/util_foreach.h"
#include "util/util_logging.h" #include "util/util_logging.h"
#include "util/util_progress.h" #include "util/util_progress.h"
@ -38,14 +32,17 @@ CCL_NAMESPACE_BEGIN
const char *bvh_layout_name(BVHLayout layout) const char *bvh_layout_name(BVHLayout layout)
{ {
switch (layout) { switch (layout) {
case BVH_LAYOUT_BVH2:
return "BVH2";
case BVH_LAYOUT_NONE: case BVH_LAYOUT_NONE:
return "NONE"; return "NONE";
case BVH_LAYOUT_BVH2:
return "BVH2";
case BVH_LAYOUT_EMBREE: case BVH_LAYOUT_EMBREE:
return "EMBREE"; return "EMBREE";
case BVH_LAYOUT_OPTIX: case BVH_LAYOUT_OPTIX:
return "OPTIX"; return "OPTIX";
case BVH_LAYOUT_MULTI_OPTIX:
case BVH_LAYOUT_MULTI_OPTIX_EMBREE:
return "MULTI";
case BVH_LAYOUT_ALL: case BVH_LAYOUT_ALL:
return "ALL"; return "ALL";
} }
@ -76,17 +73,6 @@ BVHLayout BVHParams::best_bvh_layout(BVHLayout requested_layout, BVHLayoutMask s
return (BVHLayout)(1 << widest_allowed_layout_mask); 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::BVH(const BVHParams &params_, BVH::BVH(const BVHParams &params_,
@ -99,24 +85,27 @@ BVH::BVH(const BVHParams &params_,
BVH *BVH::create(const BVHParams &params, BVH *BVH::create(const BVHParams &params,
const vector<Geometry *> &geometry, const vector<Geometry *> &geometry,
const vector<Object *> &objects, const vector<Object *> &objects,
const Device *device) Device *device)
{ {
switch (params.bvh_layout) { switch (params.bvh_layout) {
case BVH_LAYOUT_BVH2: case BVH_LAYOUT_BVH2:
return new BVH2(params, geometry, objects); return new BVH2(params, geometry, objects);
case BVH_LAYOUT_EMBREE: case BVH_LAYOUT_EMBREE:
#ifdef WITH_EMBREE #ifdef WITH_EMBREE
return new BVHEmbree(params, geometry, objects, device); return new BVHEmbree(params, geometry, objects);
#else #else
(void)device;
break; break;
#endif #endif
case BVH_LAYOUT_OPTIX: case BVH_LAYOUT_OPTIX:
#ifdef WITH_OPTIX #ifdef WITH_OPTIX
return new BVHOptiX(params, geometry, objects); return new BVHOptiX(params, geometry, objects, device);
#else #else
(void)device;
break; break;
#endif #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_NONE:
case BVH_LAYOUT_ALL: case BVH_LAYOUT_ALL:
break; break;
@ -125,399 +114,4 @@ BVH *BVH::create(const BVHParams &params,
return NULL; 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<const Hair *>(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<const Mesh *>(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<const Mesh *>(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 *, int> 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<Geometry *, int>::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 CCL_NAMESPACE_END

@ -25,17 +25,16 @@
CCL_NAMESPACE_BEGIN CCL_NAMESPACE_BEGIN
class Stats; class BoundBox;
class BVHNode;
class BVHParams;
class Device; class Device;
class DeviceScene; class DeviceScene;
class BVHNode;
struct BVHStackEntry;
class BVHParams;
class BoundBox;
class LeafNode;
class Geometry; class Geometry;
class LeafNode;
class Object; class Object;
class Progress; class Progress;
class Stats;
#define BVH_ALIGN 4096 #define BVH_ALIGN 4096
#define TRI_NODE_SIZE 3 #define TRI_NODE_SIZE 3
@ -76,13 +75,10 @@ struct PackedBVH {
} }
}; };
enum BVH_TYPE { bvh2 };
/* BVH */ /* BVH */
class BVH { class BVH {
public: public:
PackedBVH pack;
BVHParams params; BVHParams params;
vector<Geometry *> geometry; vector<Geometry *> geometry;
vector<Object *> objects; vector<Object *> objects;
@ -90,47 +86,15 @@ class BVH {
static BVH *create(const BVHParams &params, static BVH *create(const BVHParams &params,
const vector<Geometry *> &geometry, const vector<Geometry *> &geometry,
const vector<Object *> &objects, const vector<Object *> &objects,
const Device *device); Device *device);
virtual ~BVH() virtual ~BVH()
{ {
} }
virtual void build(Progress &progress, Stats *stats = NULL);
virtual void copy_to_device(Progress & /*progress*/, DeviceScene * /*dscene*/)
{
}
void refit(Progress &progress);
protected: protected:
BVH(const BVHParams &params, BVH(const BVHParams &params,
const vector<Geometry *> &geometry, const vector<Geometry *> &geometry,
const vector<Object *> &objects); const vector<Object *> &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 CCL_NAMESPACE_END

@ -17,14 +17,28 @@
#include "bvh/bvh2.h" #include "bvh/bvh2.h"
#include "render/hair.h"
#include "render/mesh.h" #include "render/mesh.h"
#include "render/object.h" #include "render/object.h"
#include "bvh/bvh_build.h"
#include "bvh/bvh_node.h" #include "bvh/bvh_node.h"
#include "bvh/bvh_unaligned.h" #include "bvh/bvh_unaligned.h"
#include "util/util_foreach.h"
#include "util/util_progress.h"
CCL_NAMESPACE_BEGIN 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 &params_, BVH2::BVH2(const BVHParams &params_,
const vector<Geometry *> &geometry_, const vector<Geometry *> &geometry_,
const vector<Object *> &objects_) const vector<Object *> &objects_)
@ -32,6 +46,70 @@ BVH2::BVH2(const BVHParams &params_,
{ {
} }
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) BVHNode *BVH2::widen_children_nodes(const BVHNode *root)
{ {
return const_cast<BVHNode *>(root); return const_cast<BVHNode *>(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 c0 = data[0].x;
const int c1 = data[0].y; 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(). */ /* TODO(sergey): De-duplicate with pack_leaf(). */
float4 leaf_data[BVH_NODE_LEAF_SIZE]; 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<const Hair *>(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<const Mesh *>(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<const Mesh *>(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<BVH2 *>(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 *, int> 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<Geometry *, int>::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<BVH2 *>(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 CCL_NAMESPACE_END

@ -26,23 +26,30 @@
CCL_NAMESPACE_BEGIN 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_SIZE 4
#define BVH_NODE_LEAF_SIZE 1 #define BVH_NODE_LEAF_SIZE 1
#define BVH_UNALIGNED_NODE_SIZE 7 #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 /* BVH2
* *
* Typical BVH with each node having two children. * Typical BVH with each node having two children.
*/ */
class BVH2 : public BVH { class BVH2 : public BVH {
public:
void build(Progress &progress, Stats *stats);
void refit(Progress &progress);
PackedBVH pack;
protected: protected:
/* constructor */ /* constructor */
friend class BVH; friend class BVH;
@ -51,10 +58,10 @@ class BVH2 : public BVH {
const vector<Object *> &objects); const vector<Object *> &objects);
/* Building process. */ /* Building process. */
virtual BVHNode *widen_children_nodes(const BVHNode *root) override; virtual BVHNode *widen_children_nodes(const BVHNode *root);
/* pack */ /* 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_leaf(const BVHStackEntry &e, const LeafNode *leaf);
void pack_inner(const BVHStackEntry &e, const BVHStackEntry &e0, const BVHStackEntry &e1); void pack_inner(const BVHStackEntry &e, const BVHStackEntry &e0, const BVHStackEntry &e1);
@ -84,8 +91,18 @@ class BVH2 : public BVH {
uint visibility1); uint visibility1);
/* refit */ /* refit */
void refit_nodes() override; void refit_nodes();
void refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility); 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 CCL_NAMESPACE_END

@ -298,82 +298,31 @@ static bool rtc_progress_func(void *user_ptr, const double n)
return !progress->get_cancel(); 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<Mesh *>(geom);
return mesh->num_triangles();
}
else if (geom->geometry_type == Geometry::HAIR) {
Hair *hair = static_cast<Hair *>(geom);
return hair->num_segments();
}
return 0;
}
BVHEmbree::BVHEmbree(const BVHParams &params_, BVHEmbree::BVHEmbree(const BVHParams &params_,
const vector<Geometry *> &geometry_, const vector<Geometry *> &geometry_,
const vector<Object *> &objects_, const vector<Object *> &objects_)
const Device *device)
: BVH(params_, geometry_, objects_), : BVH(params_, geometry_, objects_),
scene(NULL), scene(NULL),
mem_used(0), rtc_device(NULL),
top_level(NULL), build_quality(RTC_BUILD_QUALITY_REFIT)
rtc_device((RTCDevice)device->bvh_device()),
stats(NULL),
curve_subdivisions(params.curve_subdivisions),
build_quality(RTC_BUILD_QUALITY_REFIT),
dynamic_scene(true)
{ {
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
pack.root_index = -1;
} }
BVHEmbree::~BVHEmbree() BVHEmbree::~BVHEmbree()
{
if (!params.top_level) {
destroy(scene);
}
}
void BVHEmbree::destroy(RTCScene scene)
{ {
if (scene) { if (scene) {
rtcReleaseScene(scene); rtcReleaseScene(scene);
scene = NULL;
} }
} }
void BVHEmbree::delete_rtcScene() void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_)
{
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_)
{ {
rtc_device = rtc_device_;
assert(rtc_device); assert(rtc_device);
stats = stats_;
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
rtcSetDeviceMemoryMonitorFunction(rtc_device, rtc_memory_monitor_func, stats); rtcSetDeviceMemoryMonitorFunction(rtc_device, rtc_memory_monitor_func, stats);
progress.set_substatus("Building BVH"); progress.set_substatus("Building BVH");
@ -394,35 +343,7 @@ void BVHEmbree::build(Progress &progress, Stats *stats_)
RTC_BUILD_QUALITY_MEDIUM); RTC_BUILD_QUALITY_MEDIUM);
rtcSetSceneBuildQuality(scene, build_quality); 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; int i = 0;
pack.object_node.clear();
foreach (Object *ob, objects) { foreach (Object *ob, objects) {
if (params.top_level) { if (params.top_level) {
if (!ob->is_traceable()) { if (!ob->is_traceable()) {
@ -445,37 +366,11 @@ void BVHEmbree::build(Progress &progress, Stats *stats_)
} }
if (progress.get_cancel()) { if (progress.get_cancel()) {
delete_rtcScene();
stats = NULL;
return; return;
} }
rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress); rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress);
rtcCommitScene(scene); 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) 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) void BVHEmbree::add_instance(Object *ob, int i)
{ {
if (!ob || !ob->get_geometry()) {
assert(0);
return;
}
BVHEmbree *instance_bvh = (BVHEmbree *)(ob->get_geometry()->bvh); BVHEmbree *instance_bvh = (BVHEmbree *)(ob->get_geometry()->bvh);
assert(instance_bvh != NULL);
if (instance_bvh->top_level != this) {
instance_bvh->top_level = this;
}
const size_t num_object_motion_steps = ob->use_motion() ? ob->get_motion().size() : 1; 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); 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()); 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); rtcSetGeometryUserData(geom_id, (void *)instance_bvh->scene);
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing()); 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) 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; const Attribute *attr_mP = NULL;
size_t num_geometry_motion_steps = 1; size_t num_motion_steps = 1;
if (mesh->has_motion_blur()) { if (mesh->has_motion_blur()) {
attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mP) { 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_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
assert(num_geometry_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(); const size_t num_triangles = mesh->num_triangles();
RTCGeometry geom_id = rtcNewGeometry(rtc_device, RTC_GEOMETRY_TYPE_TRIANGLE); RTCGeometry geom_id = rtcNewGeometry(rtc_device, RTC_GEOMETRY_TYPE_TRIANGLE);
rtcSetGeometryBuildQuality(geom_id, build_quality); rtcSetGeometryBuildQuality(geom_id, build_quality);
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); 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); 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); rtcSetGeometryUserData(geom_id, (void *)prim_offset);
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func); rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing()); 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) { for (int t = 0; t < num_motion_steps; ++t) {
const float3 *verts; const float3 *verts;
if (t == t_mid) { if (t == t_mid) {
verts = &mesh->verts[0]; verts = mesh->get_verts().data();
} }
else { else {
int t_ = (t > t_mid) ? (t - 1) : t; 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) 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; const Attribute *attr_mP = NULL;
size_t num_geometry_motion_steps = 1; size_t num_motion_steps = 1;
if (hair->has_motion_blur()) { if (hair->has_motion_blur()) {
attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mP) { 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); assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
const PrimitiveType primitive_type = num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT);
(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);
const size_t num_curves = hair->num_curves(); const size_t num_curves = hair->num_curves();
size_t num_segments = 0; 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(); 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 ? enum RTCGeometryType type = (hair->curve_shape == CURVE_RIBBON ?
RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE : RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE :
RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE); RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE);
RTCGeometry geom_id = rtcNewGeometry(rtc_device, type); 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( unsigned *rtc_indices = (unsigned *)rtcSetNewGeometryBuffer(
geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof(int), num_segments); geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof(int), num_segments);
size_t rtc_index = 0; 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; rtc_indices[rtc_index] = c.first_key + k;
/* Room for extra CVs at Catmull-Rom splines. */ /* Room for extra CVs at Catmull-Rom splines. */
rtc_indices[rtc_index] += j * 2; 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; ++rtc_index;
} }
@ -818,134 +667,10 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
rtcReleaseGeometry(geom_id); 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. */ progress.set_substatus("Refitting BVH nodes");
if (!params.top_level) {
return;
}
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 *, int> 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<Geometry *, int>::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. */ /* Update all vertex buffers, then tell Embree to rebuild/-fit the BVHs. */
unsigned geom_id = 0; unsigned geom_id = 0;
foreach (Object *ob, objects) { foreach (Object *ob, objects) {
@ -971,8 +696,10 @@ void BVHEmbree::refit_nodes()
} }
geom_id += 2; geom_id += 2;
} }
rtcCommitScene(scene); rtcCommitScene(scene);
} }
CCL_NAMESPACE_END CCL_NAMESPACE_END
#endif /* WITH_EMBREE */ #endif /* WITH_EMBREE */

@ -31,56 +31,34 @@
CCL_NAMESPACE_BEGIN CCL_NAMESPACE_BEGIN
class Geometry;
class Hair; class Hair;
class Mesh; class Mesh;
class BVHEmbree : public BVH { class BVHEmbree : public BVH {
public: public:
virtual void build(Progress &progress, Stats *stats) override; void build(Progress &progress, Stats *stats, RTCDevice rtc_device);
virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override; void refit(Progress &progress);
virtual ~BVHEmbree();
RTCScene scene;
static void destroy(RTCScene);
/* Building process. */ RTCScene scene;
virtual BVHNode *widen_children_nodes(const BVHNode *root) override;
protected: protected:
friend class BVH; friend class BVH;
BVHEmbree(const BVHParams &params, BVHEmbree(const BVHParams &params,
const vector<Geometry *> &geometry, const vector<Geometry *> &geometry,
const vector<Object *> &objects, const vector<Object *> &objects);
const Device *device); virtual ~BVHEmbree();
virtual void pack_nodes(const BVHNode *) override;
virtual void refit_nodes() override;
void add_object(Object *ob, int i); void add_object(Object *ob, int i);
void add_instance(Object *ob, int i); void add_instance(Object *ob, int i);
void add_curves(const Object *ob, const Hair *hair, int i); void add_curves(const Object *ob, const Hair *hair, int i);
void add_triangles(const Object *ob, const Mesh *mesh, 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: private:
void delete_rtcScene();
void set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, const bool update); void set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, const bool update);
void set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, const bool update); void set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, const bool update);
RTCDevice rtc_device; RTCDevice rtc_device;
Stats *stats;
vector<RTCScene> delayed_delete_scenes;
int curve_subdivisions;
enum RTCBuildQuality build_quality; enum RTCBuildQuality build_quality;
bool dynamic_scene;
}; };
CCL_NAMESPACE_END CCL_NAMESPACE_END

@ -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 &params_,
const vector<Geometry *> &geometry_,
const vector<Object *> &objects_)
: BVH(params_, geometry_, objects_)
{
}
BVHMulti::~BVHMulti()
{
foreach (BVH *bvh, sub_bvhs) {
delete bvh;
}
}
CCL_NAMESPACE_END

@ -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<BVH *> sub_bvhs;
protected:
friend class BVH;
BVHMulti(const BVHParams &params,
const vector<Geometry *> &geometry,
const vector<Object *> &objects);
virtual ~BVHMulti();
};
CCL_NAMESPACE_END
#endif /* __BVH_MULTI_H__ */

@ -19,212 +19,22 @@
# include "bvh/bvh_optix.h" # 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 CCL_NAMESPACE_BEGIN
BVHOptiX::BVHOptiX(const BVHParams &params_, BVHOptiX::BVHOptiX(const BVHParams &params_,
const vector<Geometry *> &geometry_, const vector<Geometry *> &geometry_,
const vector<Object *> &objects_) const vector<Object *> &objects_,
: BVH(params_, geometry_, 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() BVHOptiX::~BVHOptiX()
{ {
} // Acceleration structure memory is freed via the 'as_data' destructor
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<Hair *const>(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<Mesh *const>(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;
} }
CCL_NAMESPACE_END CCL_NAMESPACE_END

@ -26,33 +26,19 @@
CCL_NAMESPACE_BEGIN CCL_NAMESPACE_BEGIN
class Geometry;
class Optix;
class BVHOptiX : public BVH { class BVHOptiX : public BVH {
friend class BVH;
public: public:
uint64_t optix_handle; uint64_t traversable_handle;
uint64_t optix_data_handle; device_only_memory<char> as_data;
bool do_refit; device_only_memory<char> motion_transform_data;
protected:
friend class BVH;
BVHOptiX(const BVHParams &params, BVHOptiX(const BVHParams &params,
const vector<Geometry *> &geometry, const vector<Geometry *> &geometry,
const vector<Object *> &objects); const vector<Object *> &objects,
Device *device);
virtual ~BVHOptiX(); 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 CCL_NAMESPACE_END

@ -71,6 +71,7 @@ class CUDADevice : public Device {
}; };
typedef map<device_memory *, CUDAMem> CUDAMemMap; typedef map<device_memory *, CUDAMem> CUDAMemMap;
CUDAMemMap cuda_mem_map; CUDAMemMap cuda_mem_map;
thread_mutex cuda_mem_map_mutex;
struct PixelMem { struct PixelMem {
GLuint cuPBO; GLuint cuPBO;

@ -718,8 +718,10 @@ void CUDADevice::init_host_memory()
void CUDADevice::load_texture_info() void CUDADevice::load_texture_info()
{ {
if (need_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; 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."); assert(!"mem_alloc not supported for global memory.");
} }
else { else {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_alloc(mem); generic_alloc(mem);
} }
} }
@ -1006,10 +1009,10 @@ void CUDADevice::mem_copy_to(device_memory &mem)
tex_alloc((device_texture &)mem); tex_alloc((device_texture &)mem);
} }
else { else {
thread_scoped_lock lock(cuda_mem_map_mutex);
if (!mem.device_pointer) { if (!mem.device_pointer) {
generic_alloc(mem); generic_alloc(mem);
} }
generic_copy_to(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 /* 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. */ * 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) { if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) {
const CUDAContextScope scope(this); const CUDAContextScope scope(this);
cuda_assert(cuMemsetD8((CUdeviceptr)mem.device_pointer, 0, mem.memory_size())); 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); tex_free((device_texture &)mem);
} }
else { else {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_free(mem); 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) void CUDADevice::global_alloc(device_memory &mem)
{ {
if (mem.is_resident(this)) { if (mem.is_resident(this)) {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_alloc(mem); generic_alloc(mem);
generic_copy_to(mem); generic_copy_to(mem);
} }
@ -1102,6 +1108,7 @@ void CUDADevice::global_alloc(device_memory &mem)
void CUDADevice::global_free(device_memory &mem) void CUDADevice::global_free(device_memory &mem)
{ {
if (mem.is_resident(this) && mem.device_pointer) { if (mem.is_resident(this) && mem.device_pointer) {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_free(mem); 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 src_pitch = mem.data_width * dsize * mem.data_elements;
size_t dst_pitch = src_pitch; size_t dst_pitch = src_pitch;
thread_scoped_lock lock(cuda_mem_map_mutex);
if (!mem.is_resident(this)) { if (!mem.is_resident(this)) {
cmem = &cuda_mem_map[&mem]; cmem = &cuda_mem_map[&mem];
cmem->texobject = 0; cmem->texobject = 0;
@ -1257,6 +1266,9 @@ void CUDADevice::tex_alloc(device_texture &mem)
cuda_assert(cuMemcpyHtoD(mem.device_pointer, mem.host_pointer, size)); 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 */ /* Resize once */
const uint slot = mem.slot; const uint slot = mem.slot;
if (slot >= texture_info.size()) { if (slot >= texture_info.size()) {
@ -1305,6 +1317,11 @@ void CUDADevice::tex_alloc(device_texture &mem)
texDesc.filterMode = filter_mode; texDesc.filterMode = filter_mode;
texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES; 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)); cuda_assert(cuTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL));
texture_info[slot].data = (uint64_t)cmem->texobject; texture_info[slot].data = (uint64_t)cmem->texobject;
@ -1318,6 +1335,7 @@ void CUDADevice::tex_free(device_texture &mem)
{ {
if (mem.device_pointer) { if (mem.device_pointer) {
CUDAContextScope scope(this); CUDAContextScope scope(this);
thread_scoped_lock lock(cuda_mem_map_mutex);
const CUDAMem &cmem = cuda_mem_map[&mem]; const CUDAMem &cmem = cuda_mem_map[&mem];
if (cmem.texobject) { if (cmem.texobject) {

@ -17,6 +17,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "bvh/bvh2.h"
#include "device/device.h" #include "device/device.h"
#include "device/device_intern.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<BVH2 *>(bvh);
if (refit) {
bvh2->refit(progress);
}
else {
bvh2->build(progress, &stats);
}
}
Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background)
{ {
#ifdef WITH_MULTI #ifdef WITH_MULTI

@ -373,12 +373,6 @@ class Device {
return NULL; 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 */ /* load/compile kernels, must be called before adding tasks */
virtual bool load_kernels(const DeviceRequestedFeatures & /*requested_features*/) virtual bool load_kernels(const DeviceRequestedFeatures & /*requested_features*/)
{ {
@ -427,10 +421,7 @@ class Device {
const DeviceDrawParams &draw_params); const DeviceDrawParams &draw_params);
/* acceleration structure building */ /* acceleration structure building */
virtual bool build_optix_bvh(BVH *) virtual void build_bvh(BVH *bvh, Progress &progress, bool refit);
{
return false;
}
#ifdef WITH_NETWORK #ifdef WITH_NETWORK
/* networking */ /* networking */

@ -47,6 +47,8 @@
#include "kernel/osl/osl_globals.h" #include "kernel/osl/osl_globals.h"
// clang-format on // clang-format on
#include "bvh/bvh_embree.h"
#include "render/buffers.h" #include "render/buffers.h"
#include "render/coverage.h" #include "render/coverage.h"
@ -188,6 +190,7 @@ class CPUDevice : public Device {
#endif #endif
thread_spin_lock oidn_task_lock; thread_spin_lock oidn_task_lock;
#ifdef WITH_EMBREE #ifdef WITH_EMBREE
RTCScene embree_scene = NULL;
RTCDevice embree_device; RTCDevice embree_device;
#endif #endif
@ -472,6 +475,15 @@ class CPUDevice : public Device {
virtual void const_copy_to(const char *name, void *host, size_t size) override 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); kernel_const_copy(&kernel_globals, name, host, size);
} }
@ -537,13 +549,26 @@ class CPUDevice : public Device {
#endif #endif
} }
void *bvh_device() const override void build_bvh(BVH *bvh, Progress &progress, bool refit) override
{ {
#ifdef WITH_EMBREE #ifdef WITH_EMBREE
return embree_device; if (bvh->params.bvh_layout == BVH_LAYOUT_EMBREE ||
#else bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) {
return NULL; BVHEmbree *const bvh_embree = static_cast<BVHEmbree *>(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 #endif
Device::build_bvh(bvh, progress, refit);
} }
void thread_run(DeviceTask &task) void thread_run(DeviceTask &task)

@ -17,11 +17,14 @@
#include <sstream> #include <sstream>
#include <stdlib.h> #include <stdlib.h>
#include "bvh/bvh_multi.h"
#include "device/device.h" #include "device/device.h"
#include "device/device_intern.h" #include "device/device_intern.h"
#include "device/device_network.h" #include "device/device_network.h"
#include "render/buffers.h" #include "render/buffers.h"
#include "render/geometry.h"
#include "util/util_foreach.h" #include "util/util_foreach.h"
#include "util/util_list.h" #include "util/util_list.h"
@ -164,9 +167,24 @@ class MultiDevice : public Device {
virtual BVHLayoutMask get_bvh_layout_mask() const virtual BVHLayoutMask get_bvh_layout_mask() const
{ {
BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL; BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL;
BVHLayoutMask bvh_layout_mask_all = BVH_LAYOUT_NONE;
foreach (const SubDevice &sub_device, devices) { 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; return bvh_layout_mask;
} }
@ -227,21 +245,58 @@ class MultiDevice : public Device {
return result; 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 */ /* Try to build and share a single acceleration structure, if possible */
foreach (SubDevice &sub, devices) { if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2) {
if (!sub.device->build_optix_bvh(bvh)) devices.back().device->build_bvh(bvh, progress, refit);
return false; return;
}
return true;
} }
virtual void *bvh_device() const BVHMulti *const bvh_multi = static_cast<BVHMulti *>(bvh);
{ bvh_multi->sub_bvhs.resize(devices.size());
/* 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. */ vector<BVHMulti *> geom_bvhs;
return devices.back().device->bvh_device(); geom_bvhs.reserve(bvh->geometry.size());
foreach (Geometry *geom, bvh->geometry) {
geom_bvhs.push_back(static_cast<BVHMulti *>(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() virtual void *osl_memory()

@ -31,6 +31,7 @@
# include "util/util_logging.h" # include "util/util_logging.h"
# include "util/util_md5.h" # include "util/util_md5.h"
# include "util/util_path.h" # include "util/util_path.h"
# include "util/util_progress.h"
# include "util/util_time.h" # include "util/util_time.h"
# ifdef WITH_CUDA_DYNLOAD # ifdef WITH_CUDA_DYNLOAD
@ -186,7 +187,6 @@ class OptiXDevice : public CUDADevice {
bool motion_blur = false; bool motion_blur = false;
device_vector<SbtRecord> sbt_data; device_vector<SbtRecord> sbt_data;
device_only_memory<KernelParams> launch_params; device_only_memory<KernelParams> launch_params;
vector<CUdeviceptr> as_mem;
OptixTraversableHandle tlas_handle = 0; OptixTraversableHandle tlas_handle = 0;
OptixDenoiser denoiser = NULL; OptixDenoiser denoiser = NULL;
@ -258,11 +258,6 @@ class OptiXDevice : public CUDADevice {
// Make CUDA context current // Make CUDA context current
const CUDAContextScope scope(cuContext); const CUDAContextScope scope(cuContext);
// Free all acceleration structures
for (CUdeviceptr mem : as_mem) {
cuMemFree(mem);
}
sbt_data.free(); sbt_data.free();
texture_info.free(); texture_info.free();
launch_params.free(); launch_params.free();
@ -1136,11 +1131,10 @@ class OptiXDevice : public CUDADevice {
} }
} }
bool build_optix_bvh(const OptixBuildInput &build_input, bool build_optix_bvh(BVHOptiX *bvh,
uint16_t num_motion_steps, OptixBuildOperation operation,
OptixTraversableHandle &out_handle, const OptixBuildInput &build_input,
CUdeviceptr &out_data, uint16_t num_motion_steps)
OptixBuildOperation operation)
{ {
const CUDAContextScope scope(cuContext); const CUDAContextScope scope(cuContext);
@ -1166,24 +1160,21 @@ class OptiXDevice : public CUDADevice {
optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes)); optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes));
// Allocate required output buffers // Allocate required output buffers
device_only_memory<char> temp_mem(this, "temp_build_mem"); device_only_memory<char> temp_mem(this, "optix temp as build mem");
temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8); temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8);
if (!temp_mem.device_pointer) if (!temp_mem.device_pointer)
return false; // Make sure temporary memory allocation succeeded return false; // Make sure temporary memory allocation succeeded
// Move textures to host memory if there is not enough room device_only_memory<char> &out_data = bvh->as_data;
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);
}
if (operation == OPTIX_BUILD_OPERATION_BUILD) { 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 // Finally build the acceleration structure
OptixAccelEmitDesc compacted_size_prop; OptixAccelEmitDesc compacted_size_prop;
@ -1192,6 +1183,7 @@ class OptiXDevice : public CUDADevice {
// Make sure this pointer is 8-byte aligned // Make sure this pointer is 8-byte aligned
compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8); compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8);
OptixTraversableHandle out_handle = 0;
check_result_optix_ret(optixAccelBuild(context, check_result_optix_ret(optixAccelBuild(context,
NULL, NULL,
&options, &options,
@ -1199,11 +1191,12 @@ class OptiXDevice : public CUDADevice {
1, 1,
temp_mem.device_pointer, temp_mem.device_pointer,
sizes.tempSizeInBytes, sizes.tempSizeInBytes,
out_data, out_data.device_pointer,
sizes.outputSizeInBytes, sizes.outputSizeInBytes,
&out_handle, &out_handle,
background ? &compacted_size_prop : NULL, background ? &compacted_size_prop : NULL,
background ? 1 : 0)); background ? 1 : 0));
bvh->traversable_handle = static_cast<uint64_t>(out_handle);
// Wait for all operations to finish // Wait for all operations to finish
check_result_cuda_ret(cuStreamSynchronize(NULL)); 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 // There is no point compacting if the size does not change
if (compacted_size < sizes.outputSizeInBytes) { if (compacted_size < sizes.outputSizeInBytes) {
CUdeviceptr compacted_data = 0; device_only_memory<char> compacted_data(this, "optix compacted as");
if (cuMemAlloc(&compacted_data, compacted_size) != CUDA_SUCCESS) compacted_data.alloc_to_device(compacted_size);
if (!compacted_data.device_pointer)
// Do not compact if memory allocation for compacted acceleration structure fails // Do not compact if memory allocation for compacted acceleration structure fails
// Can just use the uncompacted one then, so succeed here regardless // Can just use the uncompacted one then, so succeed here regardless
return true; return true;
as_mem.push_back(compacted_data);
check_result_optix_ret(optixAccelCompact( check_result_optix_ret(optixAccelCompact(context,
context, NULL, out_handle, compacted_data, compacted_size, &out_handle)); NULL,
out_handle,
compacted_data.device_pointer,
compacted_size,
&out_handle));
bvh->traversable_handle = static_cast<uint64_t>(out_handle);
// Wait for compaction to finish // Wait for compaction to finish
check_result_cuda_ret(cuStreamSynchronize(NULL)); check_result_cuda_ret(cuStreamSynchronize(NULL));
// Free uncompacted acceleration structure std::swap(out_data.device_size, compacted_data.device_size);
cuMemFree(out_data); std::swap(out_data.device_pointer, compacted_data.device_pointer);
as_mem.erase(as_mem.end() - 2); // Remove 'out_data' from 'as_mem' array
} }
} }
return true; 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<BVHOptiX *>(bvh);
unsigned int num_instances = 0; progress.set_substatus("Building OptiX acceleration structure");
unordered_map<Geometry *, OptixTraversableHandle> geometry;
geometry.reserve(bvh->geometry.size());
// Free all previous acceleration structures which can not be refit if (!bvh->params.top_level) {
std::set<CUdeviceptr> refit_mem; assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1);
for (Geometry *geom : bvh->geometry) { // Refit is only possible in viewport for now (because AS is built with
if (static_cast<BVHOptiX *>(geom->bvh)->do_refit) { // OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above)
refit_mem.insert(static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle); OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD;
} if (refit && !background) {
} assert(bvh_optix->traversable_handle != 0);
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<BVHOptiX *>(geom->bvh)->do_refit && !background) {
out_data = static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle;
handle = static_cast<BVHOptiX *>(geom->bvh)->optix_handle;
operation = OPTIX_BUILD_OPERATION_UPDATE; operation = OPTIX_BUILD_OPERATION_UPDATE;
} }
else { else {
out_data = 0; bvh_optix->as_data.free();
handle = 0; bvh_optix->traversable_handle = 0;
operation = OPTIX_BUILD_OPERATION_BUILD;
} }
// Build bottom level acceleration structures (BLAS)
Geometry *const geom = bvh->geometry[0];
if (geom->geometry_type == Geometry::HAIR) { if (geom->geometry_type == Geometry::HAIR) {
// Build BLAS for curve primitives // Build BLAS for curve primitives
Hair *const hair = static_cast<Hair *const>(ob->get_geometry()); Hair *const hair = static_cast<Hair *const>(geom);
if (hair->num_curves() == 0) { if (hair->num_curves() == 0) {
continue; return;
} }
const size_t num_segments = hair->num_segments(); const size_t num_segments = hair->num_segments();
@ -1304,10 +1276,10 @@ class OptiXDevice : public CUDADevice {
num_motion_steps = hair->get_motion_steps(); num_motion_steps = hair->get_motion_steps();
} }
device_vector<OptixAabb> aabb_data(this, "temp_aabb_data", MEM_READ_ONLY); device_vector<OptixAabb> aabb_data(this, "optix temp aabb data", MEM_READ_ONLY);
# if OPTIX_ABI_VERSION >= 36 # if OPTIX_ABI_VERSION >= 36
device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY); device_vector<int> index_data(this, "optix temp index data", MEM_READ_ONLY);
device_vector<float4> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY); device_vector<float4> vertex_data(this, "optix temp vertex data", MEM_READ_ONLY);
// Four control points for each curve segment // Four control points for each curve segment
const size_t num_vertices = num_segments * 4; const size_t num_vertices = num_segments * 4;
if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { 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; size_t center_step = (num_motion_steps - 1) / 2;
if (step != center_step) { if (step != center_step) {
size_t attr_offset = (step > center_step) ? step - 1 : 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(); keys = motion_keys->data_float3() + attr_offset * hair->get_curve_keys().size();
} }
@ -1452,22 +1424,15 @@ class OptiXDevice : public CUDADevice {
# endif # endif
} }
// Allocate memory for new BLAS and build it if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) { progress.set_error("Failed to build OptiX acceleration structure");
geometry.insert({ob->get_geometry(), handle});
static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle = out_data;
static_cast<BVHOptiX *>(geom->bvh)->optix_handle = handle;
static_cast<BVHOptiX *>(geom->bvh)->do_refit = false;
}
else {
return false;
} }
} }
else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
// Build BLAS for triangle primitives // Build BLAS for triangle primitives
Mesh *const mesh = static_cast<Mesh *const>(ob->get_geometry()); Mesh *const mesh = static_cast<Mesh *const>(geom);
if (mesh->num_triangles() == 0) { if (mesh->num_triangles() == 0) {
continue; return;
} }
const size_t num_verts = mesh->get_verts().size(); const size_t num_verts = mesh->get_verts().size();
@ -1478,12 +1443,12 @@ class OptiXDevice : public CUDADevice {
num_motion_steps = mesh->get_motion_steps(); num_motion_steps = mesh->get_motion_steps();
} }
device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY); device_vector<int> index_data(this, "optix temp index data", MEM_READ_ONLY);
index_data.alloc(mesh->get_triangles().size()); index_data.alloc(mesh->get_triangles().size());
memcpy(index_data.data(), memcpy(index_data.data(),
mesh->get_triangles().data(), mesh->get_triangles().data(),
mesh->get_triangles().size() * sizeof(int)); mesh->get_triangles().size() * sizeof(int));
device_vector<float3> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY); device_vector<float3> vertex_data(this, "optix temp vertex data", MEM_READ_ONLY);
vertex_data.alloc(num_verts * num_motion_steps); vertex_data.alloc(num_verts * num_motion_steps);
for (size_t step = 0; step < num_motion_steps; ++step) { for (size_t step = 0; step < num_motion_steps; ++step) {
@ -1528,38 +1493,52 @@ class OptiXDevice : public CUDADevice {
build_input.triangleArray.numSbtRecords = 1; build_input.triangleArray.numSbtRecords = 1;
build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset; build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset;
// Allocate memory for new BLAS and build it if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) { progress.set_error("Failed to build OptiX acceleration structure");
geometry.insert({ob->get_geometry(), handle}); }
static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle = out_data; }
static_cast<BVHOptiX *>(geom->bvh)->optix_handle = handle;
static_cast<BVHOptiX *>(geom->bvh)->do_refit = false;
} }
else { else {
return false; unsigned int num_instances = 0;
}
} bvh_optix->as_data.free();
} bvh_optix->traversable_handle = 0;
bvh_optix->motion_transform_data.free();
// Fill instance descriptions // Fill instance descriptions
# if OPTIX_ABI_VERSION < 41 # if OPTIX_ABI_VERSION < 41
device_vector<OptixAabb> aabbs(this, "tlas_aabbs", MEM_READ_ONLY); device_vector<OptixAabb> aabbs(this, "optix tlas aabbs", MEM_READ_ONLY);
aabbs.alloc(bvh->objects.size()); aabbs.alloc(bvh->objects.size());
# endif # endif
device_vector<OptixInstance> instances(this, "tlas_instances", MEM_READ_ONLY); device_vector<OptixInstance> instances(this, "optix tlas instances", MEM_READ_ONLY);
instances.alloc(bvh->objects.size()); instances.alloc(bvh->objects.size());
// 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);
}
}
assert(bvh_optix->motion_transform_data.device == this);
bvh_optix->motion_transform_data.alloc_to_device(total_motion_transform_size);
}
for (Object *ob : bvh->objects) { for (Object *ob : bvh->objects) {
// Skip non-traceable objects // Skip non-traceable objects
if (!ob->is_traceable()) if (!ob->is_traceable())
continue; continue;
// Create separate instance for triangle/curve meshes of an object BVHOptiX *const blas = static_cast<BVHOptiX *>(ob->get_geometry()->bvh);
const auto handle_it = geometry.find(ob->get_geometry()); OptixTraversableHandle handle = blas->traversable_handle;
if (handle_it == geometry.end()) {
continue;
}
OptixTraversableHandle handle = handle_it->second;
# if OPTIX_ABI_VERSION < 41 # if OPTIX_ABI_VERSION < 41
OptixAabb &aabb = aabbs[num_instances]; OptixAabb &aabb = aabbs[num_instances];
@ -1612,9 +1591,11 @@ class OptiXDevice : public CUDADevice {
const CUDAContextScope scope(cuContext); const CUDAContextScope scope(cuContext);
CUdeviceptr motion_transform_gpu = 0; motion_transform_offset = align_up(motion_transform_offset,
check_result_cuda_ret(cuMemAlloc(&motion_transform_gpu, motion_transform_size)); OPTIX_TRANSFORM_BYTE_ALIGNMENT);
as_mem.push_back(motion_transform_gpu); CUdeviceptr motion_transform_gpu = bvh_optix->motion_transform_data.device_pointer +
motion_transform_offset;
motion_transform_offset += motion_transform_size;
// Allocate host side memory for motion transform and fill it with transform data // Allocate host side memory for motion transform and fill it with transform data
OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>( OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>(
@ -1709,9 +1690,11 @@ class OptiXDevice : public CUDADevice {
build_input.instanceArray.instances = instances.device_pointer; build_input.instanceArray.instances = instances.device_pointer;
build_input.instanceArray.numInstances = num_instances; build_input.instanceArray.numInstances = num_instances;
CUdeviceptr out_data = 0; if (!build_optix_bvh(bvh_optix, OPTIX_BUILD_OPERATION_BUILD, build_input, 0)) {
tlas_handle = 0; progress.set_error("Failed to build OptiX acceleration structure");
return build_optix_bvh(build_input, 0, tlas_handle, out_data, OPTIX_BUILD_OPERATION_BUILD); }
tlas_handle = bvh_optix->traversable_handle;
}
} }
void const_copy_to(const char *name, void *host, size_t size) override 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) { if (strcmp(name, "__data") == 0) {
assert(size <= sizeof(KernelData)); 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; KernelData *const data = (KernelData *)host;
*(OptixTraversableHandle *)&data->bvh.scene = tlas_handle; *(OptixTraversableHandle *)&data->bvh.scene = tlas_handle;

@ -112,8 +112,7 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals *kg,
RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(
rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0])); rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0]));
isect->prim = hit->primID + isect->prim = hit->primID +
(intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID));
kernel_tex_fetch(__object_node, hit->instID[0] / 2);
isect->object = hit->instID[0] / 2; isect->object = hit->instID[0] / 2;
} }
else { else {
@ -137,8 +136,7 @@ ccl_device_inline void kernel_embree_convert_sss_hit(KernelGlobals *kg,
RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(
rtcGetGeometry(kernel_data.bvh.scene, local_object_id * 2)); rtcGetGeometry(kernel_data.bvh.scene, local_object_id * 2));
isect->prim = hit->primID + isect->prim = hit->primID +
(intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID));
kernel_tex_fetch(__object_node, local_object_id);
isect->object = local_object_id; isect->object = local_object_id;
isect->type = kernel_tex_fetch(__prim_type, isect->prim); isect->type = kernel_tex_fetch(__prim_type, isect->prim);
} }

@ -1397,10 +1397,12 @@ typedef enum KernelBVHLayout {
BVH_LAYOUT_BVH2 = (1 << 0), BVH_LAYOUT_BVH2 = (1 << 0),
BVH_LAYOUT_EMBREE = (1 << 1), BVH_LAYOUT_EMBREE = (1 << 1),
BVH_LAYOUT_OPTIX = (1 << 2), 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. */ /* Default BVH layout to use for CPU. */
BVH_LAYOUT_AUTO = BVH_LAYOUT_EMBREE, 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; } KernelBVHLayout;
typedef struct KernelBVH { typedef struct KernelBVH {

@ -15,8 +15,7 @@
*/ */
#include "bvh/bvh.h" #include "bvh/bvh.h"
#include "bvh/bvh_build.h" #include "bvh/bvh2.h"
#include "bvh/bvh_embree.h"
#include "device/device.h" #include "device/device.h"
@ -41,6 +40,7 @@
#include "util/util_foreach.h" #include "util/util_foreach.h"
#include "util/util_logging.h" #include "util/util_logging.h"
#include "util/util_progress.h" #include "util/util_progress.h"
#include "util/util_task.h"
CCL_NAMESPACE_BEGIN CCL_NAMESPACE_BEGIN
@ -162,7 +162,8 @@ int Geometry::motion_step(float time) const
bool Geometry::need_build_bvh(BVHLayout layout) 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 bool Geometry::is_instanced() const
@ -218,7 +219,7 @@ void Geometry::compute_bvh(
bvh->geometry = geometry; bvh->geometry = geometry;
bvh->objects = objects; bvh->objects = objects;
bvh->refit(*progress); device->build_bvh(bvh, *progress, true);
} }
else { else {
progress->set_status(msg, "Building BVH"); progress->set_status(msg, "Building BVH");
@ -235,7 +236,7 @@ void Geometry::compute_bvh(
delete bvh; delete bvh;
bvh = BVH::create(bparams, geometry, objects, device); 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."; VLOG(1) << "Using " << bvh_layout_name(bparams.bvh_layout) << " layout.";
BVH *bvh = BVH::create(bparams, scene->geometry, scene->objects, device); delete scene->bvh;
bvh->build(progress, &device->stats); BVH *bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
device->build_bvh(bvh, progress, false);
if (progress.get_cancel()) { 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; return;
} }
PackedBVH pack;
if (bparams.bvh_layout == BVH_LAYOUT_BVH2) {
pack = std::move(static_cast<BVH2 *>(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<Mesh *>(geom);
num_prims += mesh->num_triangles();
num_tri_verts += 3 * mesh->num_triangles();
}
else if (geom->is_hair()) {
Hair *hair = static_cast<Hair *>(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<const Geometry *, pair<int, uint>> 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<int, uint> &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<int, uint> &info = geometry_to_object_info[geom];
geom->pack_primitives(pack, info.first, info.second);
}
}
/* copy to device */ /* copy to device */
progress.set_status("Updating Scene BVH", "Copying BVH to device"); progress.set_status("Updating Scene BVH", "Copying BVH to device");
PackedBVH &pack = bvh->pack;
if (pack.nodes.size()) { if (pack.nodes.size()) {
dscene->bvh_nodes.steal_data(pack.nodes); dscene->bvh_nodes.steal_data(pack.nodes);
dscene->bvh_nodes.copy_to_device(); 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.bvh_layout = bparams.bvh_layout;
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0); dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions(); dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions();
/* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */
bvh->copy_to_device(progress, dscene); dscene->data.bvh.scene = NULL;
delete bvh;
} }
void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress) 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) 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_nodes.free();
dscene->bvh_leaf_nodes.free(); dscene->bvh_leaf_nodes.free();
dscene->object_node.free(); dscene->object_node.free();

@ -41,6 +41,7 @@ class Scene;
class SceneParams; class SceneParams;
class Shader; class Shader;
class Volume; class Volume;
struct PackedBVH;
/* Geometry /* Geometry
* *
@ -124,6 +125,8 @@ class Geometry : public Node {
int n, int n,
int total); int total);
virtual void pack_primitives(PackedBVH &pack, int object, uint visibility) = 0;
/* Check whether the geometry should have own BVH built separately. Briefly, /* Check whether the geometry should have own BVH built separately. Briefly,
* own BVH is needed for geometry, if: * own BVH is needed for geometry, if:
* *

@ -14,6 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
#include "bvh/bvh.h"
#include "render/hair.h" #include "render/hair.h"
#include "render/curves.h" #include "render/curves.h"
#include "render/scene.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 CCL_NAMESPACE_END

@ -145,6 +145,8 @@ class Hair : public Geometry {
/* BVH */ /* BVH */
void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset); 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 CCL_NAMESPACE_END

@ -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 CCL_NAMESPACE_END

@ -184,9 +184,8 @@ class Mesh : public Geometry {
unordered_multimap<int, int> unordered_multimap<int, int>
vert_stitching_map; /* stitching index -> multiple real vert indices */ vert_stitching_map; /* stitching index -> multiple real vert indices */
friend class BVH; friend class BVH2;
friend class BVHBuild; friend class BVHBuild;
friend class BVHEmbree;
friend class BVHSpatialSplit; friend class BVHSpatialSplit;
friend class DiagSplit; friend class DiagSplit;
friend class EdgeDice; friend class EdgeDice;
@ -233,6 +232,8 @@ class Mesh : public Geometry {
size_t tri_offset); size_t tri_offset);
void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_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); void tessellate(DiagSplit *split);
SubdFace get_subd_face(size_t index) const; SubdFace get_subd_face(size_t index) const;

@ -16,6 +16,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "bvh/bvh.h"
#include "device/device.h" #include "device/device.h"
#include "render/background.h" #include "render/background.h"
#include "render/bake.h" #include "render/bake.h"
@ -100,6 +101,7 @@ Scene::Scene(const SceneParams &params_, Device *device)
{ {
memset((void *)&dscene.data, 0, sizeof(dscene.data)); memset((void *)&dscene.data, 0, sizeof(dscene.data));
bvh = NULL;
camera = create_node<Camera>(); camera = create_node<Camera>();
dicing_camera = create_node<Camera>(); dicing_camera = create_node<Camera>();
lookup_tables = new LookupTables(); lookup_tables = new LookupTables();
@ -135,6 +137,9 @@ Scene::~Scene()
void Scene::free_memory(bool final) void Scene::free_memory(bool final)
{ {
delete bvh;
bvh = NULL;
foreach (Shader *s, shaders) foreach (Shader *s, shaders)
delete s; delete s;
foreach (Geometry *g, geometry) foreach (Geometry *g, geometry)

@ -38,6 +38,7 @@ CCL_NAMESPACE_BEGIN
class AttributeRequestSet; class AttributeRequestSet;
class Background; class Background;
class BVH;
class Camera; class Camera;
class Device; class Device;
class DeviceInfo; class DeviceInfo;
@ -220,6 +221,7 @@ class Scene : public NodeOwner {
string name; string name;
/* data */ /* data */
BVH *bvh;
Camera *camera; Camera *camera;
Camera *dicing_camera; Camera *dicing_camera;
LookupTables *lookup_tables; LookupTables *lookup_tables;