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
Notes: blender-bot 2023-02-14 06:17:17 +01:00
Referenced by issue #85089, Crash when rendering with CUDA / OptiX
Referenced by issue #84734, Denoising Data with large tiles causes instant crash and exit.
Referenced by issue #83915, Subdivision Surface modifier causes visual artifacts in Cycles rendered viewport - CPU and OptiX
Referenced by issue #69800, Cycles Optix feature completeness
30 changed files with 1089 additions and 1332 deletions

View File

@ -1570,7 +1570,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
elif entry.type == 'CPU':
cpu_devices.append(entry)
# Extend all GPU devices with CPU.
if compute_device_type in {'CUDA', 'OPENCL'}:
if compute_device_type in {'CUDA', 'OPTIX', 'OPENCL'}:
devices.extend(cpu_devices)
return devices

View File

@ -90,8 +90,7 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
mask |= DEVICE_MASK_CUDA;
}
else if (compute_device == COMPUTE_DEVICE_OPTIX) {
/* Cannot use CPU and OptiX device at the same time right now, so replace mask. */
mask = DEVICE_MASK_OPTIX;
mask |= DEVICE_MASK_OPTIX;
}
else if (compute_device == COMPUTE_DEVICE_OPENCL) {
mask |= DEVICE_MASK_OPENCL;

View File

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

View File

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

View File

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

View File

@ -17,14 +17,28 @@
#include "bvh/bvh2.h"
#include "render/hair.h"
#include "render/mesh.h"
#include "render/object.h"
#include "bvh/bvh_build.h"
#include "bvh/bvh_node.h"
#include "bvh/bvh_unaligned.h"
#include "util/util_foreach.h"
#include "util/util_progress.h"
CCL_NAMESPACE_BEGIN
BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i)
{
}
int BVHStackEntry::encodeIdx() const
{
return (node->is_leaf()) ? ~idx : idx;
}
BVH2::BVH2(const BVHParams &params_,
const vector<Geometry *> &geometry_,
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)
{
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 c1 = data[0].y;
BVH::refit_primitives(c0, c1, bbox, visibility);
refit_primitives(c0, c1, bbox, visibility);
/* TODO(sergey): De-duplicate with pack_leaf(). */
float4 leaf_data[BVH_NODE_LEAF_SIZE];
@ -292,4 +370,333 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility)
}
}
/* Refitting */
void BVH2::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility)
{
/* Refit range of primitives. */
for (int prim = start; prim < end; prim++) {
int pidx = pack.prim_index[prim];
int tob = pack.prim_object[prim];
Object *ob = objects[tob];
if (pidx == -1) {
/* Object instance. */
bbox.grow(ob->bounds);
}
else {
/* Primitives. */
if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) {
/* Curves. */
const Hair *hair = static_cast<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

View File

@ -26,23 +26,30 @@
CCL_NAMESPACE_BEGIN
class BVHNode;
struct BVHStackEntry;
class BVHParams;
class BoundBox;
class LeafNode;
class Object;
class Progress;
#define BVH_NODE_SIZE 4
#define BVH_NODE_LEAF_SIZE 1
#define BVH_UNALIGNED_NODE_SIZE 7
/* Pack Utility */
struct BVHStackEntry {
const BVHNode *node;
int idx;
BVHStackEntry(const BVHNode *n = 0, int i = 0);
int encodeIdx() const;
};
/* BVH2
*
* Typical BVH with each node having two children.
*/
class BVH2 : public BVH {
public:
void build(Progress &progress, Stats *stats);
void refit(Progress &progress);
PackedBVH pack;
protected:
/* constructor */
friend class BVH;
@ -51,10 +58,10 @@ class BVH2 : public BVH {
const vector<Object *> &objects);
/* Building process. */
virtual BVHNode *widen_children_nodes(const BVHNode *root) override;
virtual BVHNode *widen_children_nodes(const BVHNode *root);
/* pack */
void pack_nodes(const BVHNode *root) override;
void pack_nodes(const BVHNode *root);
void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf);
void pack_inner(const BVHStackEntry &e, const BVHStackEntry &e0, const BVHStackEntry &e1);
@ -84,8 +91,18 @@ class BVH2 : public BVH {
uint visibility1);
/* refit */
void refit_nodes() override;
void refit_nodes();
void refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility);
/* Refit range of primitives. */
void refit_primitives(int start, int end, BoundBox &bbox, uint &visibility);
/* triangles and strands */
void pack_primitives();
void pack_triangle(int idx, float4 storage[3]);
/* merge instance BVH's */
void pack_instances(size_t nodes_size, size_t leaf_nodes_size);
};
CCL_NAMESPACE_END

View File

@ -298,82 +298,31 @@ static bool rtc_progress_func(void *user_ptr, const double n)
return !progress->get_cancel();
}
static size_t count_primitives(Geometry *geom)
{
if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
Mesh *mesh = static_cast<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_,
const vector<Geometry *> &geometry_,
const vector<Object *> &objects_,
const Device *device)
const vector<Object *> &objects_)
: BVH(params_, geometry_, objects_),
scene(NULL),
mem_used(0),
top_level(NULL),
rtc_device((RTCDevice)device->bvh_device()),
stats(NULL),
curve_subdivisions(params.curve_subdivisions),
build_quality(RTC_BUILD_QUALITY_REFIT),
dynamic_scene(true)
rtc_device(NULL),
build_quality(RTC_BUILD_QUALITY_REFIT)
{
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
pack.root_index = -1;
}
BVHEmbree::~BVHEmbree()
{
if (!params.top_level) {
destroy(scene);
}
}
void BVHEmbree::destroy(RTCScene scene)
{
if (scene) {
rtcReleaseScene(scene);
scene = NULL;
}
}
void BVHEmbree::delete_rtcScene()
{
if (scene) {
/* When this BVH is used as an instance in a top level BVH, don't delete now
* Let the top_level BVH know that it should delete it later. */
if (top_level) {
top_level->add_delayed_delete_scene(scene);
}
else {
rtcReleaseScene(scene);
if (delayed_delete_scenes.size()) {
foreach (RTCScene s, delayed_delete_scenes) {
rtcReleaseScene(s);
}
}
delayed_delete_scenes.clear();
}
scene = NULL;
}
}
void BVHEmbree::build(Progress &progress, Stats *stats_)
void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_)
{
rtc_device = rtc_device_;
assert(rtc_device);
stats = stats_;
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
rtcSetDeviceMemoryMonitorFunction(rtc_device, rtc_memory_monitor_func, stats);
progress.set_substatus("Building BVH");
@ -394,35 +343,7 @@ void BVHEmbree::build(Progress &progress, Stats *stats_)
RTC_BUILD_QUALITY_MEDIUM);
rtcSetSceneBuildQuality(scene, build_quality);
/* Count triangles and curves first, reserve arrays once. */
size_t prim_count = 0;
foreach (Object *ob, objects) {
if (params.top_level) {
if (!ob->is_traceable()) {
continue;
}
if (!ob->get_geometry()->is_instanced()) {
prim_count += count_primitives(ob->get_geometry());
}
else {
++prim_count;
}
}
else {
prim_count += count_primitives(ob->get_geometry());
}
}
pack.prim_object.reserve(prim_count);
pack.prim_type.reserve(prim_count);
pack.prim_index.reserve(prim_count);
pack.prim_tri_index.reserve(prim_count);
int i = 0;
pack.object_node.clear();
foreach (Object *ob, objects) {
if (params.top_level) {
if (!ob->is_traceable()) {
@ -445,37 +366,11 @@ void BVHEmbree::build(Progress &progress, Stats *stats_)
}
if (progress.get_cancel()) {
delete_rtcScene();
stats = NULL;
return;
}
rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress);
rtcCommitScene(scene);
pack_primitives();
if (progress.get_cancel()) {
delete_rtcScene();
stats = NULL;
return;
}
progress.set_substatus("Packing geometry");
pack_nodes(NULL);
stats = NULL;
}
void BVHEmbree::copy_to_device(Progress & /*progress*/, DeviceScene *dscene)
{
dscene->data.bvh.scene = scene;
}
BVHNode *BVHEmbree::widen_children_nodes(const BVHNode * /*root*/)
{
assert(!"Must not be called.");
return NULL;
}
void BVHEmbree::add_object(Object *ob, int i)
@ -498,15 +393,8 @@ void BVHEmbree::add_object(Object *ob, int i)
void BVHEmbree::add_instance(Object *ob, int i)
{
if (!ob || !ob->get_geometry()) {
assert(0);
return;
}
BVHEmbree *instance_bvh = (BVHEmbree *)(ob->get_geometry()->bvh);
if (instance_bvh->top_level != this) {
instance_bvh->top_level = this;
}
assert(instance_bvh != NULL);
const size_t num_object_motion_steps = ob->use_motion() ? ob->get_motion().size() : 1;
const size_t num_motion_steps = min(num_object_motion_steps, RTC_MAX_TIME_STEP_COUNT);
@ -538,11 +426,6 @@ void BVHEmbree::add_instance(Object *ob, int i)
geom_id, 0, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float *)&ob->get_tfm());
}
pack.prim_index.push_back_slow(-1);
pack.prim_object.push_back_slow(i);
pack.prim_type.push_back_slow(PRIMITIVE_NONE);
pack.prim_tri_index.push_back_slow(-1);
rtcSetGeometryUserData(geom_id, (void *)instance_bvh->scene);
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
@ -553,20 +436,22 @@ void BVHEmbree::add_instance(Object *ob, int i)
void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
{
size_t prim_offset = pack.prim_index.size();
size_t prim_offset = mesh->optix_prim_offset;
const Attribute *attr_mP = NULL;
size_t num_geometry_motion_steps = 1;
size_t num_motion_steps = 1;
if (mesh->has_motion_blur()) {
attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mP) {
num_geometry_motion_steps = mesh->get_motion_steps();
num_motion_steps = mesh->get_motion_steps();
}
}
const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT);
assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT);
const size_t num_triangles = mesh->num_triangles();
RTCGeometry geom_id = rtcNewGeometry(rtc_device, RTC_GEOMETRY_TYPE_TRIANGLE);
rtcSetGeometryBuildQuality(geom_id, build_quality);
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
@ -588,22 +473,6 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
set_tri_vertex_buffer(geom_id, mesh, false);
size_t prim_object_size = pack.prim_object.size();
pack.prim_object.resize(prim_object_size + num_triangles);
size_t prim_type_size = pack.prim_type.size();
pack.prim_type.resize(prim_type_size + num_triangles);
size_t prim_index_size = pack.prim_index.size();
pack.prim_index.resize(prim_index_size + num_triangles);
pack.prim_tri_index.resize(prim_index_size + num_triangles);
int prim_type = (num_motion_steps > 1 ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE);
for (size_t j = 0; j < num_triangles; ++j) {
pack.prim_object[prim_object_size + j] = i;
pack.prim_type[prim_type_size + j] = prim_type;
pack.prim_index[prim_index_size + j] = j;
pack.prim_tri_index[prim_index_size + j] = j;
}
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
@ -629,12 +498,12 @@ void BVHEmbree::set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, con
}
}
}
const size_t num_verts = mesh->verts.size();
const size_t num_verts = mesh->get_verts().size();
for (int t = 0; t < num_motion_steps; ++t) {
const float3 *verts;
if (t == t_mid) {
verts = &mesh->verts[0];
verts = mesh->get_verts().data();
}
else {
int t_ = (t > t_mid) ? (t - 1) : t;
@ -736,24 +605,19 @@ void BVHEmbree::set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, c
void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
{
size_t prim_offset = pack.prim_index.size();
size_t prim_offset = hair->optix_prim_offset;
const Attribute *attr_mP = NULL;
size_t num_geometry_motion_steps = 1;
size_t num_motion_steps = 1;
if (hair->has_motion_blur()) {
attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mP) {
num_geometry_motion_steps = hair->get_motion_steps();
num_motion_steps = hair->get_motion_steps();
}
}
const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT);
const PrimitiveType primitive_type =
(num_motion_steps > 1) ?
((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
PRIMITIVE_MOTION_CURVE_THICK) :
((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT);
const size_t num_curves = hair->num_curves();
size_t num_segments = 0;
@ -763,22 +627,12 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
num_segments += c.num_segments();
}
/* Make room for Cycles specific data. */
size_t prim_object_size = pack.prim_object.size();
pack.prim_object.resize(prim_object_size + num_segments);
size_t prim_type_size = pack.prim_type.size();
pack.prim_type.resize(prim_type_size + num_segments);
size_t prim_index_size = pack.prim_index.size();
pack.prim_index.resize(prim_index_size + num_segments);
size_t prim_tri_index_size = pack.prim_index.size();
pack.prim_tri_index.resize(prim_tri_index_size + num_segments);
enum RTCGeometryType type = (hair->curve_shape == CURVE_RIBBON ?
RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE :
RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE);
RTCGeometry geom_id = rtcNewGeometry(rtc_device, type);
rtcSetGeometryTessellationRate(geom_id, curve_subdivisions + 1);
rtcSetGeometryTessellationRate(geom_id, params.curve_subdivisions + 1);
unsigned *rtc_indices = (unsigned *)rtcSetNewGeometryBuffer(
geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof(int), num_segments);
size_t rtc_index = 0;
@ -788,11 +642,6 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
rtc_indices[rtc_index] = c.first_key + k;
/* Room for extra CVs at Catmull-Rom splines. */
rtc_indices[rtc_index] += j * 2;
/* Cycles specific data. */
pack.prim_object[prim_object_size + rtc_index] = i;
pack.prim_type[prim_type_size + rtc_index] = (PRIMITIVE_PACK_SEGMENT(primitive_type, k));
pack.prim_index[prim_index_size + rtc_index] = j;
pack.prim_tri_index[prim_tri_index_size + rtc_index] = rtc_index;
++rtc_index;
}
@ -818,134 +667,10 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
rtcReleaseGeometry(geom_id);
}
void BVHEmbree::pack_nodes(const BVHNode *)
void BVHEmbree::refit(Progress &progress)
{
/* Quite a bit of this code is for compatibility with Cycles' native BVH. */
if (!params.top_level) {
return;
}
progress.set_substatus("Refitting BVH nodes");
for (size_t i = 0; i < pack.prim_index.size(); ++i) {
if (pack.prim_index[i] != -1) {
pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset;
}
}
size_t prim_offset = pack.prim_index.size();
/* reserve */
size_t prim_index_size = pack.prim_index.size();
size_t prim_tri_verts_size = pack.prim_tri_verts.size();
size_t pack_prim_index_offset = prim_index_size;
size_t pack_prim_tri_verts_offset = prim_tri_verts_size;
size_t object_offset = 0;
map<Geometry *, 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. */
unsigned geom_id = 0;
foreach (Object *ob, objects) {
@ -971,8 +696,10 @@ void BVHEmbree::refit_nodes()
}
geom_id += 2;
}
rtcCommitScene(scene);
}
CCL_NAMESPACE_END
#endif /* WITH_EMBREE */

View File

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

View File

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

View File

@ -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__ */

View File

@ -19,212 +19,22 @@
# include "bvh/bvh_optix.h"
# include "device/device.h"
# include "render/geometry.h"
# include "render/hair.h"
# include "render/mesh.h"
# include "render/object.h"
# include "util/util_foreach.h"
# include "util/util_logging.h"
# include "util/util_progress.h"
CCL_NAMESPACE_BEGIN
BVHOptiX::BVHOptiX(const BVHParams &params_,
const vector<Geometry *> &geometry_,
const vector<Object *> &objects_)
: BVH(params_, geometry_, objects_)
const vector<Object *> &objects_,
Device *device)
: BVH(params_, geometry_, objects_),
traversable_handle(0),
as_data(device, params_.top_level ? "optix tlas" : "optix blas"),
motion_transform_data(device, "optix motion transform")
{
optix_handle = 0;
optix_data_handle = 0;
do_refit = false;
}
BVHOptiX::~BVHOptiX()
{
}
void BVHOptiX::build(Progress &, Stats *)
{
if (params.top_level)
pack_tlas();
else
pack_blas();
}
void BVHOptiX::copy_to_device(Progress &progress, DeviceScene *dscene)
{
progress.set_status("Updating Scene BVH", "Building OptiX acceleration structure");
Device *const device = dscene->bvh_nodes.device;
if (!device->build_optix_bvh(this))
progress.set_error("Failed to build OptiX acceleration structure");
}
void BVHOptiX::pack_blas()
{
// Bottom-level BVH can contain multiple primitive types, so merge them:
assert(geometry.size() == 1 && objects.size() == 1); // These are built per-mesh
Geometry *const geom = geometry[0];
if (geom->geometry_type == Geometry::HAIR) {
Hair *const hair = static_cast<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;
// Acceleration structure memory is freed via the 'as_data' destructor
}
CCL_NAMESPACE_END

View File

@ -26,33 +26,19 @@
CCL_NAMESPACE_BEGIN
class Geometry;
class Optix;
class BVHOptiX : public BVH {
friend class BVH;
public:
uint64_t optix_handle;
uint64_t optix_data_handle;
bool do_refit;
uint64_t traversable_handle;
device_only_memory<char> as_data;
device_only_memory<char> motion_transform_data;
protected:
friend class BVH;
BVHOptiX(const BVHParams &params,
const vector<Geometry *> &geometry,
const vector<Object *> &objects);
const vector<Object *> &objects,
Device *device);
virtual ~BVHOptiX();
virtual void build(Progress &progress, Stats *) override;
virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override;
private:
void pack_blas();
void pack_tlas();
virtual void pack_nodes(const BVHNode *) override;
virtual void refit_nodes() override;
virtual BVHNode *widen_children_nodes(const BVHNode *) override;
};
CCL_NAMESPACE_END

View File

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

View File

@ -718,8 +718,10 @@ void CUDADevice::init_host_memory()
void CUDADevice::load_texture_info()
{
if (need_texture_info) {
texture_info.copy_to_device();
/* Unset flag before copying, so this does not loop indenfinetly if the copy below calls
* into 'move_textures_to_host' (which calls 'load_texture_info' again). */
need_texture_info = false;
texture_info.copy_to_device();
}
}
@ -988,6 +990,7 @@ void CUDADevice::mem_alloc(device_memory &mem)
assert(!"mem_alloc not supported for global memory.");
}
else {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_alloc(mem);
}
}
@ -1006,10 +1009,10 @@ void CUDADevice::mem_copy_to(device_memory &mem)
tex_alloc((device_texture &)mem);
}
else {
thread_scoped_lock lock(cuda_mem_map_mutex);
if (!mem.device_pointer) {
generic_alloc(mem);
}
generic_copy_to(mem);
}
}
@ -1048,6 +1051,7 @@ void CUDADevice::mem_zero(device_memory &mem)
/* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory
* regardless of mem.host_pointer and mem.shared_pointer. */
thread_scoped_lock lock(cuda_mem_map_mutex);
if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) {
const CUDAContextScope scope(this);
cuda_assert(cuMemsetD8((CUdeviceptr)mem.device_pointer, 0, mem.memory_size()));
@ -1069,6 +1073,7 @@ void CUDADevice::mem_free(device_memory &mem)
tex_free((device_texture &)mem);
}
else {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_free(mem);
}
}
@ -1092,6 +1097,7 @@ void CUDADevice::const_copy_to(const char *name, void *host, size_t size)
void CUDADevice::global_alloc(device_memory &mem)
{
if (mem.is_resident(this)) {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_alloc(mem);
generic_copy_to(mem);
}
@ -1102,6 +1108,7 @@ void CUDADevice::global_alloc(device_memory &mem)
void CUDADevice::global_free(device_memory &mem)
{
if (mem.is_resident(this) && mem.device_pointer) {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_free(mem);
}
}
@ -1170,6 +1177,8 @@ void CUDADevice::tex_alloc(device_texture &mem)
size_t src_pitch = mem.data_width * dsize * mem.data_elements;
size_t dst_pitch = src_pitch;
thread_scoped_lock lock(cuda_mem_map_mutex);
if (!mem.is_resident(this)) {
cmem = &cuda_mem_map[&mem];
cmem->texobject = 0;
@ -1257,6 +1266,9 @@ void CUDADevice::tex_alloc(device_texture &mem)
cuda_assert(cuMemcpyHtoD(mem.device_pointer, mem.host_pointer, size));
}
/* Unlock mutex before resizing texture info, since that may attempt to lock it again. */
lock.unlock();
/* Resize once */
const uint slot = mem.slot;
if (slot >= texture_info.size()) {
@ -1305,6 +1317,11 @@ void CUDADevice::tex_alloc(device_texture &mem)
texDesc.filterMode = filter_mode;
texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES;
/* Lock again and refresh the data pointer (in case another thread modified the map in the
* meantime). */
lock.lock();
cmem = &cuda_mem_map[&mem];
cuda_assert(cuTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL));
texture_info[slot].data = (uint64_t)cmem->texobject;
@ -1318,6 +1335,7 @@ void CUDADevice::tex_free(device_texture &mem)
{
if (mem.device_pointer) {
CUDAContextScope scope(this);
thread_scoped_lock lock(cuda_mem_map_mutex);
const CUDAMem &cmem = cuda_mem_map[&mem];
if (cmem.texobject) {

View File

@ -17,6 +17,8 @@
#include <stdlib.h>
#include <string.h>
#include "bvh/bvh2.h"
#include "device/device.h"
#include "device/device_intern.h"
@ -364,6 +366,19 @@ void Device::draw_pixels(device_memory &rgba,
}
}
void Device::build_bvh(BVH *bvh, Progress &progress, bool refit)
{
assert(bvh->params.bvh_layout == BVH_LAYOUT_BVH2);
BVH2 *const bvh2 = static_cast<BVH2 *>(bvh);
if (refit) {
bvh2->refit(progress);
}
else {
bvh2->build(progress, &stats);
}
}
Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background)
{
#ifdef WITH_MULTI

View File

@ -373,12 +373,6 @@ class Device {
return NULL;
}
/* Device specific pointer for BVH creation. Currently only used by Embree. */
virtual void *bvh_device() const
{
return NULL;
}
/* load/compile kernels, must be called before adding tasks */
virtual bool load_kernels(const DeviceRequestedFeatures & /*requested_features*/)
{
@ -427,10 +421,7 @@ class Device {
const DeviceDrawParams &draw_params);
/* acceleration structure building */
virtual bool build_optix_bvh(BVH *)
{
return false;
}
virtual void build_bvh(BVH *bvh, Progress &progress, bool refit);
#ifdef WITH_NETWORK
/* networking */

View File

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

View File

@ -17,11 +17,14 @@
#include <sstream>
#include <stdlib.h>
#include "bvh/bvh_multi.h"
#include "device/device.h"
#include "device/device_intern.h"
#include "device/device_network.h"
#include "render/buffers.h"
#include "render/geometry.h"
#include "util/util_foreach.h"
#include "util/util_list.h"
@ -164,9 +167,24 @@ class MultiDevice : public Device {
virtual BVHLayoutMask get_bvh_layout_mask() const
{
BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL;
BVHLayoutMask bvh_layout_mask_all = BVH_LAYOUT_NONE;
foreach (const SubDevice &sub_device, devices) {
bvh_layout_mask &= sub_device.device->get_bvh_layout_mask();
BVHLayoutMask device_bvh_layout_mask = sub_device.device->get_bvh_layout_mask();
bvh_layout_mask &= device_bvh_layout_mask;
bvh_layout_mask_all |= device_bvh_layout_mask;
}
/* With multiple OptiX devices, every device needs its own acceleration structure */
if (bvh_layout_mask == BVH_LAYOUT_OPTIX) {
return BVH_LAYOUT_MULTI_OPTIX;
}
/* When devices do not share a common BVH layout, fall back to creating one for each */
const BVHLayoutMask BVH_LAYOUT_OPTIX_EMBREE = (BVH_LAYOUT_OPTIX | BVH_LAYOUT_EMBREE);
if ((bvh_layout_mask_all & BVH_LAYOUT_OPTIX_EMBREE) == BVH_LAYOUT_OPTIX_EMBREE) {
return BVH_LAYOUT_MULTI_OPTIX_EMBREE;
}
return bvh_layout_mask;
}
@ -227,21 +245,58 @@ class MultiDevice : public Device {
return result;
}
bool build_optix_bvh(BVH *bvh)
void build_bvh(BVH *bvh, Progress &progress, bool refit) override
{
/* Broadcast acceleration structure build to all render devices */
foreach (SubDevice &sub, devices) {
if (!sub.device->build_optix_bvh(bvh))
return false;
/* Try to build and share a single acceleration structure, if possible */
if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2) {
devices.back().device->build_bvh(bvh, progress, refit);
return;
}
return true;
}
virtual void *bvh_device() const
{
/* CPU devices will always be at the back, so simply choose the last one.
There should only ever be one CPU device anyway and we need the Embree device for it. */
return devices.back().device->bvh_device();
BVHMulti *const bvh_multi = static_cast<BVHMulti *>(bvh);
bvh_multi->sub_bvhs.resize(devices.size());
vector<BVHMulti *> geom_bvhs;
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()

View File

@ -31,6 +31,7 @@
# include "util/util_logging.h"
# include "util/util_md5.h"
# include "util/util_path.h"
# include "util/util_progress.h"
# include "util/util_time.h"
# ifdef WITH_CUDA_DYNLOAD
@ -186,7 +187,6 @@ class OptiXDevice : public CUDADevice {
bool motion_blur = false;
device_vector<SbtRecord> sbt_data;
device_only_memory<KernelParams> launch_params;
vector<CUdeviceptr> as_mem;
OptixTraversableHandle tlas_handle = 0;
OptixDenoiser denoiser = NULL;
@ -258,11 +258,6 @@ class OptiXDevice : public CUDADevice {
// Make CUDA context current
const CUDAContextScope scope(cuContext);
// Free all acceleration structures
for (CUdeviceptr mem : as_mem) {
cuMemFree(mem);
}
sbt_data.free();
texture_info.free();
launch_params.free();
@ -1136,11 +1131,10 @@ class OptiXDevice : public CUDADevice {
}
}
bool build_optix_bvh(const OptixBuildInput &build_input,
uint16_t num_motion_steps,
OptixTraversableHandle &out_handle,
CUdeviceptr &out_data,
OptixBuildOperation operation)
bool build_optix_bvh(BVHOptiX *bvh,
OptixBuildOperation operation,
const OptixBuildInput &build_input,
uint16_t num_motion_steps)
{
const CUDAContextScope scope(cuContext);
@ -1166,24 +1160,21 @@ class OptiXDevice : public CUDADevice {
optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes));
// Allocate required output buffers
device_only_memory<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);
if (!temp_mem.device_pointer)
return false; // Make sure temporary memory allocation succeeded
// Move textures to host memory if there is not enough room
size_t size = 0, free = 0;
cuMemGetInfo(&free, &size);
size = sizes.outputSizeInBytes + device_working_headroom;
if (size >= free && can_map_host) {
move_textures_to_host(size - free, false);
}
device_only_memory<char> &out_data = bvh->as_data;
if (operation == OPTIX_BUILD_OPERATION_BUILD) {
check_result_cuda_ret(cuMemAlloc(&out_data, sizes.outputSizeInBytes));
assert(out_data.device == this);
out_data.alloc_to_device(sizes.outputSizeInBytes);
if (!out_data.device_pointer)
return false;
}
else {
assert(out_data.device_pointer && out_data.device_size >= sizes.outputSizeInBytes);
}
as_mem.push_back(out_data);
// Finally build the acceleration structure
OptixAccelEmitDesc compacted_size_prop;
@ -1192,6 +1183,7 @@ class OptiXDevice : public CUDADevice {
// Make sure this pointer is 8-byte aligned
compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8);
OptixTraversableHandle out_handle = 0;
check_result_optix_ret(optixAccelBuild(context,
NULL,
&options,
@ -1199,11 +1191,12 @@ class OptiXDevice : public CUDADevice {
1,
temp_mem.device_pointer,
sizes.tempSizeInBytes,
out_data,
out_data.device_pointer,
sizes.outputSizeInBytes,
&out_handle,
background ? &compacted_size_prop : NULL,
background ? 1 : 0));
bvh->traversable_handle = static_cast<uint64_t>(out_handle);
// Wait for all operations to finish
check_result_cuda_ret(cuStreamSynchronize(NULL));
@ -1219,81 +1212,60 @@ class OptiXDevice : public CUDADevice {
// There is no point compacting if the size does not change
if (compacted_size < sizes.outputSizeInBytes) {
CUdeviceptr compacted_data = 0;
if (cuMemAlloc(&compacted_data, compacted_size) != CUDA_SUCCESS)
device_only_memory<char> compacted_data(this, "optix compacted as");
compacted_data.alloc_to_device(compacted_size);
if (!compacted_data.device_pointer)
// Do not compact if memory allocation for compacted acceleration structure fails
// Can just use the uncompacted one then, so succeed here regardless
return true;
as_mem.push_back(compacted_data);
check_result_optix_ret(optixAccelCompact(
context, NULL, out_handle, compacted_data, compacted_size, &out_handle));
check_result_optix_ret(optixAccelCompact(context,
NULL,
out_handle,
compacted_data.device_pointer,
compacted_size,
&out_handle));
bvh->traversable_handle = static_cast<uint64_t>(out_handle);
// Wait for compaction to finish
check_result_cuda_ret(cuStreamSynchronize(NULL));
// Free uncompacted acceleration structure
cuMemFree(out_data);
as_mem.erase(as_mem.end() - 2); // Remove 'out_data' from 'as_mem' array
std::swap(out_data.device_size, compacted_data.device_size);
std::swap(out_data.device_pointer, compacted_data.device_pointer);
}
}
return true;
}
bool build_optix_bvh(BVH *bvh) override
void build_bvh(BVH *bvh, Progress &progress, bool refit) override
{
assert(bvh->params.top_level);
BVHOptiX *const bvh_optix = static_cast<BVHOptiX *>(bvh);
unsigned int num_instances = 0;
unordered_map<Geometry *, OptixTraversableHandle> geometry;
geometry.reserve(bvh->geometry.size());
progress.set_substatus("Building OptiX acceleration structure");
// Free all previous acceleration structures which can not be refit
std::set<CUdeviceptr> refit_mem;
if (!bvh->params.top_level) {
assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1);
for (Geometry *geom : bvh->geometry) {
if (static_cast<BVHOptiX *>(geom->bvh)->do_refit) {
refit_mem.insert(static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle);
}
}
for (CUdeviceptr mem : as_mem) {
if (refit_mem.find(mem) == refit_mem.end()) {
cuMemFree(mem);
}
}
as_mem.clear();
// Build bottom level acceleration structures (BLAS)
// Note: Always keep this logic in sync with bvh_optix.cpp!
for (Object *ob : bvh->objects) {
// Skip geometry for which acceleration structure already exists
Geometry *geom = ob->get_geometry();
if (geometry.find(geom) != geometry.end())
continue;
OptixTraversableHandle handle;
OptixBuildOperation operation;
CUdeviceptr out_data;
// Refit is only possible in viewport for now.
if (static_cast<BVHOptiX *>(geom->bvh)->do_refit && !background) {
out_data = static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle;
handle = static_cast<BVHOptiX *>(geom->bvh)->optix_handle;
// Refit is only possible in viewport for now (because AS is built with
// OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above)
OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD;
if (refit && !background) {
assert(bvh_optix->traversable_handle != 0);
operation = OPTIX_BUILD_OPERATION_UPDATE;
}
else {
out_data = 0;
handle = 0;
operation = OPTIX_BUILD_OPERATION_BUILD;
bvh_optix->as_data.free();
bvh_optix->traversable_handle = 0;
}
// Build bottom level acceleration structures (BLAS)
Geometry *const geom = bvh->geometry[0];
if (geom->geometry_type == Geometry::HAIR) {
// Build BLAS for curve primitives
Hair *const hair = static_cast<Hair *const>(ob->get_geometry());
Hair *const hair = static_cast<Hair *const>(geom);
if (hair->num_curves() == 0) {
continue;
return;
}
const size_t num_segments = hair->num_segments();
@ -1304,10 +1276,10 @@ class OptiXDevice : public CUDADevice {
num_motion_steps = hair->get_motion_steps();
}
device_vector<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
device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY);
device_vector<float4> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY);
device_vector<int> index_data(this, "optix temp index data", MEM_READ_ONLY);
device_vector<float4> vertex_data(this, "optix temp vertex data", MEM_READ_ONLY);
// Four control points for each curve segment
const size_t num_vertices = num_segments * 4;
if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) {
@ -1325,7 +1297,7 @@ class OptiXDevice : public CUDADevice {
size_t center_step = (num_motion_steps - 1) / 2;
if (step != center_step) {
size_t attr_offset = (step > center_step) ? step - 1 : step;
// Technically this is a float4 array, but sizeof(float3) is the same as sizeof(float4)
// Technically this is a float4 array, but sizeof(float3) == sizeof(float4)
keys = motion_keys->data_float3() + attr_offset * hair->get_curve_keys().size();
}
@ -1452,22 +1424,15 @@ class OptiXDevice : public CUDADevice {
# endif
}
// Allocate memory for new BLAS and build it
if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) {
geometry.insert({ob->get_geometry(), handle});
static_cast<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;
if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
progress.set_error("Failed to build OptiX acceleration structure");
}
}
else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
// Build BLAS for triangle primitives
Mesh *const mesh = static_cast<Mesh *const>(ob->get_geometry());
Mesh *const mesh = static_cast<Mesh *const>(geom);
if (mesh->num_triangles() == 0) {
continue;
return;
}
const size_t num_verts = mesh->get_verts().size();
@ -1478,12 +1443,12 @@ class OptiXDevice : public CUDADevice {
num_motion_steps = mesh->get_motion_steps();
}
device_vector<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());
memcpy(index_data.data(),
mesh->get_triangles().data(),
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);
for (size_t step = 0; step < num_motion_steps; ++step) {
@ -1528,190 +1493,208 @@ class OptiXDevice : public CUDADevice {
build_input.triangleArray.numSbtRecords = 1;
build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset;
// Allocate memory for new BLAS and build it
if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) {
geometry.insert({ob->get_geometry(), handle});
static_cast<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;
if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
progress.set_error("Failed to build OptiX acceleration structure");
}
}
}
else {
unsigned int num_instances = 0;
// Fill instance descriptions
bvh_optix->as_data.free();
bvh_optix->traversable_handle = 0;
bvh_optix->motion_transform_data.free();
// Fill instance descriptions
# if OPTIX_ABI_VERSION < 41
device_vector<OptixAabb> aabbs(this, "tlas_aabbs", MEM_READ_ONLY);
aabbs.alloc(bvh->objects.size());
device_vector<OptixAabb> aabbs(this, "optix tlas aabbs", MEM_READ_ONLY);
aabbs.alloc(bvh->objects.size());
# endif
device_vector<OptixInstance> instances(this, "tlas_instances", MEM_READ_ONLY);
instances.alloc(bvh->objects.size());
device_vector<OptixInstance> instances(this, "optix tlas instances", MEM_READ_ONLY);
instances.alloc(bvh->objects.size());
for (Object *ob : bvh->objects) {
// Skip non-traceable objects
if (!ob->is_traceable())
continue;
// Calculate total motion transform size and allocate memory for them
size_t motion_transform_offset = 0;
if (motion_blur) {
size_t total_motion_transform_size = 0;
for (Object *const ob : bvh->objects) {
if (ob->is_traceable() && ob->use_motion()) {
total_motion_transform_size = align_up(total_motion_transform_size,
OPTIX_TRANSFORM_BYTE_ALIGNMENT);
const size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
total_motion_transform_size = total_motion_transform_size +
sizeof(OptixSRTMotionTransform) +
motion_keys * sizeof(OptixSRTData);
}
}
// Create separate instance for triangle/curve meshes of an object
const auto handle_it = geometry.find(ob->get_geometry());
if (handle_it == geometry.end()) {
continue;
}
OptixTraversableHandle handle = handle_it->second;
# if OPTIX_ABI_VERSION < 41
OptixAabb &aabb = aabbs[num_instances];
aabb.minX = ob->bounds.min.x;
aabb.minY = ob->bounds.min.y;
aabb.minZ = ob->bounds.min.z;
aabb.maxX = ob->bounds.max.x;
aabb.maxY = ob->bounds.max.y;
aabb.maxZ = ob->bounds.max.z;
# endif
OptixInstance &instance = instances[num_instances++];
memset(&instance, 0, sizeof(instance));
// Clear transform to identity matrix
instance.transform[0] = 1.0f;
instance.transform[5] = 1.0f;
instance.transform[10] = 1.0f;
// Set user instance ID to object index
instance.instanceId = ob->get_device_index();
// Have to have at least one bit in the mask, or else instance would always be culled
instance.visibilityMask = 1;
if (ob->get_geometry()->has_volume) {
// Volumes have a special bit set in the visibility mask so a trace can mask only volumes
instance.visibilityMask |= 2;
assert(bvh_optix->motion_transform_data.device == this);
bvh_optix->motion_transform_data.alloc_to_device(total_motion_transform_size);
}
if (ob->get_geometry()->geometry_type == Geometry::HAIR) {
// Same applies to curves (so they can be skipped in local trace calls)
instance.visibilityMask |= 4;
for (Object *ob : bvh->objects) {
// Skip non-traceable objects
if (!ob->is_traceable())
continue;
BVHOptiX *const blas = static_cast<BVHOptiX *>(ob->get_geometry()->bvh);
OptixTraversableHandle handle = blas->traversable_handle;
# if OPTIX_ABI_VERSION < 41
OptixAabb &aabb = aabbs[num_instances];
aabb.minX = ob->bounds.min.x;
aabb.minY = ob->bounds.min.y;
aabb.minZ = ob->bounds.min.z;
aabb.maxX = ob->bounds.max.x;
aabb.maxY = ob->bounds.max.y;
aabb.maxZ = ob->bounds.max.z;
# endif
OptixInstance &instance = instances[num_instances++];
memset(&instance, 0, sizeof(instance));
// Clear transform to identity matrix
instance.transform[0] = 1.0f;
instance.transform[5] = 1.0f;
instance.transform[10] = 1.0f;
// Set user instance ID to object index
instance.instanceId = ob->get_device_index();
// Have to have at least one bit in the mask, or else instance would always be culled
instance.visibilityMask = 1;
if (ob->get_geometry()->has_volume) {
// Volumes have a special bit set in the visibility mask so a trace can mask only volumes
instance.visibilityMask |= 2;
}
if (ob->get_geometry()->geometry_type == Geometry::HAIR) {
// Same applies to curves (so they can be skipped in local trace calls)
instance.visibilityMask |= 4;
# if OPTIX_ABI_VERSION >= 36
if (motion_blur && ob->get_geometry()->has_motion_blur() &&
DebugFlags().optix.curves_api &&
static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) {
// Select between motion blur and non-motion blur built-in intersection module
instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
}
if (motion_blur && ob->get_geometry()->has_motion_blur() &&
DebugFlags().optix.curves_api &&
static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) {
// Select between motion blur and non-motion blur built-in intersection module
instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
}
# endif
}
// Insert motion traversable if object has motion
if (motion_blur && ob->use_motion()) {
size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
size_t motion_transform_size = sizeof(OptixSRTMotionTransform) +
motion_keys * sizeof(OptixSRTData);
const CUDAContextScope scope(cuContext);
CUdeviceptr motion_transform_gpu = 0;
check_result_cuda_ret(cuMemAlloc(&motion_transform_gpu, motion_transform_size));
as_mem.push_back(motion_transform_gpu);
// Allocate host side memory for motion transform and fill it with transform data
OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>(
new uint8_t[motion_transform_size]);
motion_transform.child = handle;
motion_transform.motionOptions.numKeys = ob->get_motion().size();
motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE;
motion_transform.motionOptions.timeBegin = 0.0f;
motion_transform.motionOptions.timeEnd = 1.0f;
OptixSRTData *const srt_data = motion_transform.srtData;
array<DecomposedTransform> decomp(ob->get_motion().size());
transform_motion_decompose(
decomp.data(), ob->get_motion().data(), ob->get_motion().size());
for (size_t i = 0; i < ob->get_motion().size(); ++i) {
// Scale
srt_data[i].sx = decomp[i].y.w; // scale.x.x
srt_data[i].sy = decomp[i].z.w; // scale.y.y
srt_data[i].sz = decomp[i].w.w; // scale.z.z
// Shear
srt_data[i].a = decomp[i].z.x; // scale.x.y
srt_data[i].b = decomp[i].z.y; // scale.x.z
srt_data[i].c = decomp[i].w.x; // scale.y.z
assert(decomp[i].z.z == 0.0f); // scale.y.x
assert(decomp[i].w.y == 0.0f); // scale.z.x
assert(decomp[i].w.z == 0.0f); // scale.z.y
// Pivot point
srt_data[i].pvx = 0.0f;
srt_data[i].pvy = 0.0f;
srt_data[i].pvz = 0.0f;
// Rotation
srt_data[i].qx = decomp[i].x.x;
srt_data[i].qy = decomp[i].x.y;
srt_data[i].qz = decomp[i].x.z;
srt_data[i].qw = decomp[i].x.w;
// Translation
srt_data[i].tx = decomp[i].y.x;
srt_data[i].ty = decomp[i].y.y;
srt_data[i].tz = decomp[i].y.z;
}
// Upload motion transform to GPU
cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size);
delete[] reinterpret_cast<uint8_t *>(&motion_transform);
// Insert motion traversable if object has motion
if (motion_blur && ob->use_motion()) {
size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
size_t motion_transform_size = sizeof(OptixSRTMotionTransform) +
motion_keys * sizeof(OptixSRTData);
// Disable instance transform if object uses motion transform already
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
const CUDAContextScope scope(cuContext);
// Get traversable handle to motion transform
optixConvertPointerToTraversableHandle(context,
motion_transform_gpu,
OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM,
&instance.traversableHandle);
}
else {
instance.traversableHandle = handle;
motion_transform_offset = align_up(motion_transform_offset,
OPTIX_TRANSFORM_BYTE_ALIGNMENT);
CUdeviceptr motion_transform_gpu = bvh_optix->motion_transform_data.device_pointer +
motion_transform_offset;
motion_transform_offset += motion_transform_size;
if (ob->get_geometry()->is_instanced()) {
// Set transform matrix
memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform));
// Allocate host side memory for motion transform and fill it with transform data
OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>(
new uint8_t[motion_transform_size]);
motion_transform.child = handle;
motion_transform.motionOptions.numKeys = ob->get_motion().size();
motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE;
motion_transform.motionOptions.timeBegin = 0.0f;
motion_transform.motionOptions.timeEnd = 1.0f;
OptixSRTData *const srt_data = motion_transform.srtData;
array<DecomposedTransform> decomp(ob->get_motion().size());
transform_motion_decompose(
decomp.data(), ob->get_motion().data(), ob->get_motion().size());
for (size_t i = 0; i < ob->get_motion().size(); ++i) {
// Scale
srt_data[i].sx = decomp[i].y.w; // scale.x.x
srt_data[i].sy = decomp[i].z.w; // scale.y.y
srt_data[i].sz = decomp[i].w.w; // scale.z.z
// Shear
srt_data[i].a = decomp[i].z.x; // scale.x.y
srt_data[i].b = decomp[i].z.y; // scale.x.z
srt_data[i].c = decomp[i].w.x; // scale.y.z
assert(decomp[i].z.z == 0.0f); // scale.y.x
assert(decomp[i].w.y == 0.0f); // scale.z.x
assert(decomp[i].w.z == 0.0f); // scale.z.y
// Pivot point
srt_data[i].pvx = 0.0f;
srt_data[i].pvy = 0.0f;
srt_data[i].pvz = 0.0f;
// Rotation
srt_data[i].qx = decomp[i].x.x;
srt_data[i].qy = decomp[i].x.y;
srt_data[i].qz = decomp[i].x.z;
srt_data[i].qw = decomp[i].x.w;
// Translation
srt_data[i].tx = decomp[i].y.x;
srt_data[i].ty = decomp[i].y.y;
srt_data[i].tz = decomp[i].y.z;
}
// Upload motion transform to GPU
cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size);
delete[] reinterpret_cast<uint8_t *>(&motion_transform);
// Disable instance transform if object uses motion transform already
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
// Get traversable handle to motion transform
optixConvertPointerToTraversableHandle(context,
motion_transform_gpu,
OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM,
&instance.traversableHandle);
}
else {
// Disable instance transform if geometry already has it applied to vertex data
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
// Non-instanced objects read ID from prim_object, so
// distinguish them from instanced objects with high bit set
instance.instanceId |= 0x800000;
instance.traversableHandle = handle;
if (ob->get_geometry()->is_instanced()) {
// Set transform matrix
memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform));
}
else {
// Disable instance transform if geometry already has it applied to vertex data
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
// Non-instanced objects read ID from prim_object, so
// distinguish them from instanced objects with high bit set
instance.instanceId |= 0x800000;
}
}
}
}
// Upload instance descriptions
// Upload instance descriptions
# if OPTIX_ABI_VERSION < 41
aabbs.resize(num_instances);
aabbs.copy_to_device();
aabbs.resize(num_instances);
aabbs.copy_to_device();
# endif
instances.resize(num_instances);
instances.copy_to_device();
instances.resize(num_instances);
instances.copy_to_device();
// Build top-level acceleration structure (TLAS)
OptixBuildInput build_input = {};
build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
// Build top-level acceleration structure (TLAS)
OptixBuildInput build_input = {};
build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
# if OPTIX_ABI_VERSION < 41 // Instance AABBs no longer need to be set since OptiX 7.2
build_input.instanceArray.aabbs = aabbs.device_pointer;
build_input.instanceArray.numAabbs = num_instances;
build_input.instanceArray.aabbs = aabbs.device_pointer;
build_input.instanceArray.numAabbs = num_instances;
# endif
build_input.instanceArray.instances = instances.device_pointer;
build_input.instanceArray.numInstances = num_instances;
build_input.instanceArray.instances = instances.device_pointer;
build_input.instanceArray.numInstances = num_instances;
CUdeviceptr out_data = 0;
tlas_handle = 0;
return build_optix_bvh(build_input, 0, tlas_handle, out_data, OPTIX_BUILD_OPERATION_BUILD);
if (!build_optix_bvh(bvh_optix, OPTIX_BUILD_OPERATION_BUILD, build_input, 0)) {
progress.set_error("Failed to build OptiX acceleration structure");
}
tlas_handle = bvh_optix->traversable_handle;
}
}
void const_copy_to(const char *name, void *host, size_t size) override
@ -1724,7 +1707,7 @@ class OptiXDevice : public CUDADevice {
if (strcmp(name, "__data") == 0) {
assert(size <= sizeof(KernelData));
// Fix traversable handle on multi devices
// Update traversable handle (since it is different for each device on multi devices)
KernelData *const data = (KernelData *)host;
*(OptixTraversableHandle *)&data->bvh.scene = tlas_handle;

View File

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

View File

@ -1397,10 +1397,12 @@ typedef enum KernelBVHLayout {
BVH_LAYOUT_BVH2 = (1 << 0),
BVH_LAYOUT_EMBREE = (1 << 1),
BVH_LAYOUT_OPTIX = (1 << 2),
BVH_LAYOUT_MULTI_OPTIX = (1 << 3),
BVH_LAYOUT_MULTI_OPTIX_EMBREE = (1 << 4),
/* Default BVH layout to use for CPU. */
BVH_LAYOUT_AUTO = BVH_LAYOUT_EMBREE,
BVH_LAYOUT_ALL = (unsigned int)(~0u),
BVH_LAYOUT_ALL = BVH_LAYOUT_BVH2 | BVH_LAYOUT_EMBREE | BVH_LAYOUT_OPTIX,
} KernelBVHLayout;
typedef struct KernelBVH {

View File

@ -15,8 +15,7 @@
*/
#include "bvh/bvh.h"
#include "bvh/bvh_build.h"
#include "bvh/bvh_embree.h"
#include "bvh/bvh2.h"
#include "device/device.h"
@ -41,6 +40,7 @@
#include "util/util_foreach.h"
#include "util/util_logging.h"
#include "util/util_progress.h"
#include "util/util_task.h"
CCL_NAMESPACE_BEGIN
@ -162,7 +162,8 @@ int Geometry::motion_step(float time) const
bool Geometry::need_build_bvh(BVHLayout layout) const
{
return !transform_applied || has_surface_bssrdf || layout == BVH_LAYOUT_OPTIX;
return is_instanced() || layout == BVH_LAYOUT_OPTIX || layout == BVH_LAYOUT_MULTI_OPTIX ||
layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE;
}
bool Geometry::is_instanced() const
@ -218,7 +219,7 @@ void Geometry::compute_bvh(
bvh->geometry = geometry;
bvh->objects = objects;
bvh->refit(*progress);
device->build_bvh(bvh, *progress, true);
}
else {
progress->set_status(msg, "Building BVH");
@ -235,7 +236,7 @@ void Geometry::compute_bvh(
delete bvh;
bvh = BVH::create(bparams, geometry, objects, device);
MEM_GUARDED_CALL(progress, bvh->build, *progress);
MEM_GUARDED_CALL(progress, device->build_bvh, bvh, *progress, false);
}
}
@ -1162,25 +1163,66 @@ void GeometryManager::device_update_bvh(Device *device,
VLOG(1) << "Using " << bvh_layout_name(bparams.bvh_layout) << " layout.";
BVH *bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
bvh->build(progress, &device->stats);
delete scene->bvh;
BVH *bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
device->build_bvh(bvh, progress, false);
if (progress.get_cancel()) {
#ifdef WITH_EMBREE
if (dscene->data.bvh.scene) {
BVHEmbree::destroy(dscene->data.bvh.scene);
dscene->data.bvh.scene = NULL;
}
#endif
delete bvh;
return;
}
PackedBVH pack;
if (bparams.bvh_layout == BVH_LAYOUT_BVH2) {
pack = std::move(static_cast<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 */
progress.set_status("Updating Scene BVH", "Copying BVH to device");
PackedBVH &pack = bvh->pack;
if (pack.nodes.size()) {
dscene->bvh_nodes.steal_data(pack.nodes);
dscene->bvh_nodes.copy_to_device();
@ -1226,10 +1268,8 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->data.bvh.bvh_layout = bparams.bvh_layout;
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions();
bvh->copy_to_device(progress, dscene);
delete bvh;
/* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */
dscene->data.bvh.scene = NULL;
}
void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress)
@ -1653,14 +1693,6 @@ void GeometryManager::device_update(Device *device,
void GeometryManager::device_free(Device *device, DeviceScene *dscene)
{
#ifdef WITH_EMBREE
if (dscene->data.bvh.scene) {
if (dscene->data.bvh.bvh_layout == BVH_LAYOUT_EMBREE)
BVHEmbree::destroy(dscene->data.bvh.scene);
dscene->data.bvh.scene = NULL;
}
#endif
dscene->bvh_nodes.free();
dscene->bvh_leaf_nodes.free();
dscene->object_node.free();

View File

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

View File

@ -14,6 +14,8 @@
* limitations under the License.
*/
#include "bvh/bvh.h"
#include "render/hair.h"
#include "render/curves.h"
#include "render/scene.h"
@ -492,4 +494,35 @@ void Hair::pack_curves(Scene *scene,
}
}
void Hair::pack_primitives(PackedBVH &pack, int object, uint visibility)
{
if (curve_first_key.empty())
return;
const size_t num_prims = num_segments();
pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims);
pack.prim_type.reserve(pack.prim_type.size() + num_prims);
pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims);
pack.prim_index.reserve(pack.prim_index.size() + num_prims);
pack.prim_object.reserve(pack.prim_object.size() + num_prims);
// 'pack.prim_time' is unused by Embree and OptiX
uint type = has_motion_blur() ?
((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
PRIMITIVE_MOTION_CURVE_THICK) :
((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
for (size_t j = 0; j < num_curves(); ++j) {
Curve curve = get_curve(j);
for (size_t k = 0; k < curve.num_segments(); ++k) {
pack.prim_tri_index.push_back_reserved(-1);
pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k));
pack.prim_visibility.push_back_reserved(visibility);
// Each curve segment points back to its curve index
pack.prim_index.push_back_reserved(j + prim_offset);
pack.prim_object.push_back_reserved(object);
}
}
}
CCL_NAMESPACE_END

View File

@ -145,6 +145,8 @@ class Hair : public Geometry {
/* BVH */
void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset);
void pack_primitives(PackedBVH &pack, int object, uint visibility);
};
CCL_NAMESPACE_END

View File

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

View File

@ -184,9 +184,8 @@ class Mesh : public Geometry {
unordered_multimap<int, int>
vert_stitching_map; /* stitching index -> multiple real vert indices */
friend class BVH;
friend class BVH2;
friend class BVHBuild;
friend class BVHEmbree;
friend class BVHSpatialSplit;
friend class DiagSplit;
friend class EdgeDice;
@ -233,6 +232,8 @@ class Mesh : public Geometry {
size_t tri_offset);
void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset);
void pack_primitives(PackedBVH &pack, int object, uint visibility) override;
void tessellate(DiagSplit *split);
SubdFace get_subd_face(size_t index) const;

View File

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

View File

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