Cycles: Optimize Blender point cloud attribute extraction

Similar to 8d0920ec6d.

In a test case with 8 million points and 3 attributes, I observed
around a 9x performance improvement, from 1.8s to 0.2s to copy
the data from Blender to Cycles. For some attribute types, using
implicit sharing could remove the need to copy entirely, but removing
the overhead from the RNA API makes sense anyway.
This commit is contained in:
Hans Goudey 2023-04-07 15:25:05 -04:00
parent 44d5a894c1
commit aef0e72e5a
1 changed files with 69 additions and 59 deletions

View File

@ -15,17 +15,6 @@
CCL_NAMESPACE_BEGIN
template<typename TypeInCycles, typename GetValueAtIndex>
static void fill_generic_attribute(BL::PointCloud &b_pointcloud,
TypeInCycles *data,
const GetValueAtIndex &get_value_at_index)
{
const int num_points = b_pointcloud.points.length();
for (int i = 0; i < num_points; i++) {
data[i] = get_value_at_index(i);
}
}
static void attr_create_motion(PointCloud *pointcloud,
BL::Attribute &b_attribute,
const float motion_scale)
@ -63,6 +52,11 @@ static void copy_attributes(PointCloud *pointcloud,
const bool need_motion,
const float motion_scale)
{
const int num_points = b_pointcloud.points.length();
if (num_points == 0) {
return;
}
AttributeSet &attributes = pointcloud->attributes;
static const ustring u_velocity("velocity");
for (BL::Attribute &b_attribute : b_pointcloud.attributes) {
@ -81,56 +75,60 @@ static void copy_attributes(PointCloud *pointcloud,
switch (b_data_type) {
case BL::Attribute::data_type_FLOAT: {
BL::FloatAttribute b_float_attribute{b_attribute};
const float *src = static_cast<const float *>(b_float_attribute.data[0].ptr.data);
Attribute *attr = attributes.add(name, TypeFloat, element);
float *data = attr->data_float();
fill_generic_attribute(
b_pointcloud, data, [&](int i) { return b_float_attribute.data[i].value(); });
std::copy(src, src + num_points, data);
break;
}
case BL::Attribute::data_type_BOOLEAN: {
BL::BoolAttribute b_bool_attribute{b_attribute};
const bool *src = static_cast<const bool *>(b_bool_attribute.data[0].ptr.data);
Attribute *attr = attributes.add(name, TypeFloat, element);
float *data = attr->data_float();
fill_generic_attribute(
b_pointcloud, data, [&](int i) { return (float)b_bool_attribute.data[i].value(); });
for (int i = 0; i < num_points; i++) {
data[i] = float(src[i]);
}
break;
}
case BL::Attribute::data_type_INT: {
BL::IntAttribute b_int_attribute{b_attribute};
const int *src = static_cast<const int *>(b_int_attribute.data[0].ptr.data);
Attribute *attr = attributes.add(name, TypeFloat, element);
float *data = attr->data_float();
fill_generic_attribute(
b_pointcloud, data, [&](int i) { return (float)b_int_attribute.data[i].value(); });
for (int i = 0; i < num_points; i++) {
data[i] = float(src[i]);
}
break;
}
case BL::Attribute::data_type_FLOAT_VECTOR: {
BL::FloatVectorAttribute b_vector_attribute{b_attribute};
const float(*src)[3] = static_cast<const float(*)[3]>(b_vector_attribute.data[0].ptr.data);
Attribute *attr = attributes.add(name, TypeVector, element);
float3 *data = attr->data_float3();
fill_generic_attribute(b_pointcloud, data, [&](int i) {
BL::Array<float, 3> v = b_vector_attribute.data[i].vector();
return make_float3(v[0], v[1], v[2]);
});
for (int i = 0; i < num_points; i++) {
data[i] = make_float3(src[i][0], src[i][1], src[i][2]);
}
break;
}
case BL::Attribute::data_type_FLOAT_COLOR: {
BL::FloatColorAttribute b_color_attribute{b_attribute};
const float(*src)[4] = static_cast<const float(*)[4]>(b_color_attribute.data[0].ptr.data);
Attribute *attr = attributes.add(name, TypeRGBA, element);
float4 *data = attr->data_float4();
fill_generic_attribute(b_pointcloud, data, [&](int i) {
BL::Array<float, 4> v = b_color_attribute.data[i].color();
return make_float4(v[0], v[1], v[2], v[3]);
});
for (int i = 0; i < num_points; i++) {
data[i] = make_float4(src[i][0], src[i][1], src[i][2], src[i][3]);
}
break;
}
case BL::Attribute::data_type_FLOAT2: {
BL::Float2Attribute b_float2_attribute{b_attribute};
const float(*src)[2] = static_cast<const float(*)[2]>(b_float2_attribute.data[0].ptr.data);
Attribute *attr = attributes.add(name, TypeFloat2, element);
float2 *data = attr->data_float2();
fill_generic_attribute(b_pointcloud, data, [&](int i) {
BL::Array<float, 2> v = b_float2_attribute.data[i].vector();
return make_float2(v[0], v[1]);
});
for (int i = 0; i < num_points; i++) {
data[i] = make_float2(src[i][0], src[i][1]);
}
break;
}
default:
@ -140,7 +138,7 @@ static void copy_attributes(PointCloud *pointcloud,
}
}
static std::optional<BL::FloatAttribute> find_radius_attribute(BL::PointCloud b_pointcloud)
static const float *find_radius_attribute(BL::PointCloud b_pointcloud)
{
for (BL::Attribute &b_attribute : b_pointcloud.attributes) {
if (b_attribute.name() != "radius") {
@ -149,12 +147,16 @@ static std::optional<BL::FloatAttribute> find_radius_attribute(BL::PointCloud b_
if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT) {
continue;
}
return BL::FloatAttribute{b_attribute};
BL::FloatAttribute b_float_attribute{b_attribute};
if (b_float_attribute.data.length() == 0) {
return nullptr;
}
return static_cast<const float *>(b_float_attribute.data[0].ptr.data);
}
return std::nullopt;
return nullptr;
}
static BL::FloatVectorAttribute find_position_attribute(BL::PointCloud b_pointcloud)
static const float (*find_position_attribute(BL::PointCloud b_pointcloud))[3]
{
for (BL::Attribute &b_attribute : b_pointcloud.attributes) {
if (b_attribute.name() != "position") {
@ -163,11 +165,15 @@ static BL::FloatVectorAttribute find_position_attribute(BL::PointCloud b_pointcl
if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT_VECTOR) {
continue;
}
return BL::FloatVectorAttribute{b_attribute};
BL::FloatVectorAttribute b_float3_attribute{b_attribute};
if (b_float3_attribute.data.length() == 0) {
return nullptr;
}
return static_cast<const float(*)[3]>(b_float3_attribute.data[0].ptr.data);
}
/* The position attribute must exist. */
assert(false);
return BL::FloatVectorAttribute{b_pointcloud.attributes[0]};
return nullptr;
}
static void export_pointcloud(Scene *scene,
@ -176,34 +182,37 @@ static void export_pointcloud(Scene *scene,
const bool need_motion,
const float motion_scale)
{
/* TODO: optimize so we can straight memcpy arrays from Blender? */
const int num_points = b_pointcloud.points.length();
pointcloud->resize(num_points);
/* Add requested attributes. */
Attribute *attr_random = NULL;
if (pointcloud->need_attribute(scene, ATTR_STD_POINT_RANDOM)) {
attr_random = pointcloud->attributes.add(ATTR_STD_POINT_RANDOM);
const float(*b_attr_position)[3] = find_position_attribute(b_pointcloud);
float3 *points = pointcloud->get_points().data();
std::cout << sizeof(*points) << '\n';
for (int i = 0; i < num_points; i++) {
points[i] = make_float3(b_attr_position[i][0], b_attr_position[i][1], b_attr_position[i][2]);
}
/* Reserve memory. */
const int num_points = b_pointcloud.points.length();
pointcloud->reserve(num_points);
const float *b_attr_radius = find_radius_attribute(b_pointcloud);
float *radius = pointcloud->get_radius().data();
if (b_attr_radius) {
std::copy(b_attr_radius, b_attr_radius + num_points, radius);
}
else {
std::fill(radius, radius + num_points, 0.01f);
}
BL::FloatVectorAttribute b_attr_position = find_position_attribute(b_pointcloud);
std::optional<BL::FloatAttribute> b_attr_radius = find_radius_attribute(b_pointcloud);
int *shader = pointcloud->get_shader().data();
std::fill(shader, shader + num_points, 0);
/* Export points. */
for (int i = 0; i < num_points; i++) {
const float3 co = get_float3(b_attr_position.data[i].vector());
const float radius = b_attr_radius ? b_attr_radius->data[i].value() : 0.01f;
pointcloud->add_point(co, radius);
/* Random number per point. */
if (attr_random != NULL) {
attr_random->add(hash_uint2_to_float(i, 0));
if (pointcloud->need_attribute(scene, ATTR_STD_POINT_RANDOM)) {
Attribute *attr_random = pointcloud->attributes.add(ATTR_STD_POINT_RANDOM);
float *data = attr_random->data_float();
for (int i = 0; i < num_points; i++) {
data[i] = hash_uint2_to_float(i, 0);
}
}
/* Export attributes */
copy_attributes(pointcloud, b_pointcloud, need_motion, motion_scale);
}
@ -230,12 +239,13 @@ static void export_pointcloud_motion(PointCloud *pointcloud,
const array<float3> &pointcloud_points = pointcloud->get_points();
const int b_points_num = b_pointcloud.points.length();
BL::FloatVectorAttribute b_attr_position = find_position_attribute(b_pointcloud);
std::optional<BL::FloatAttribute> b_attr_radius = find_radius_attribute(b_pointcloud);
const float(*b_attr_position)[3] = find_position_attribute(b_pointcloud);
const float *b_attr_radius = find_radius_attribute(b_pointcloud);
for (int i = 0; i < std::min(num_points, b_points_num); i++) {
const float3 P = get_float3(b_attr_position.data[i].vector());
const float radius = b_attr_radius ? b_attr_radius->data[i].value() : 0.01f;
const float3 P = make_float3(
b_attr_position[i][0], b_attr_position[i][1], b_attr_position[i][2]);
const float radius = b_attr_radius ? b_attr_radius[i] : 0.01f;
mP[i] = make_float4(P.x, P.y, P.z, radius);
have_motion = have_motion || (P != pointcloud_points[i]);
}