Cycles: Optimize Blender curves attribute extraction
Similar to8d0920ec6dandaef0e72e5a. In a test case with 2 million curves and 15 million points I observed a 10x performance improvement, from 2.2s to 0.2s to copy the data from Blender to Cycles.
This commit is contained in:
@@ -609,59 +609,60 @@ void BlenderSync::sync_particle_hair(
|
||||
}
|
||||
}
|
||||
|
||||
static std::optional<BL::FloatAttribute> find_curves_radius_attribute(BL::Curves b_curves)
|
||||
static const float *find_radius_attribute(BL::Curves b_curves)
|
||||
{
|
||||
for (BL::Attribute &b_attribute : b_curves.attributes) {
|
||||
if (b_attribute.name() != "radius") {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.domain() != BL::Attribute::domain_POINT) {
|
||||
continue;
|
||||
}
|
||||
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_curves_position_attribute(BL::Curves b_curves)
|
||||
static const float (*find_position_attribute(BL::Curves b_curves))[3]
|
||||
{
|
||||
for (BL::Attribute &b_attribute : b_curves.attributes) {
|
||||
if (b_attribute.name() != "position") {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.domain() != BL::Attribute::domain_POINT) {
|
||||
continue;
|
||||
}
|
||||
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_curves.attributes[0]};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename TypeInCycles, typename GetValueAtIndex>
|
||||
static void fill_generic_attribute(BL::Curves &b_curves,
|
||||
static void fill_generic_attribute(const int num_curves,
|
||||
const int num_points,
|
||||
TypeInCycles *data,
|
||||
const AttributeElement element,
|
||||
const GetValueAtIndex &get_value_at_index)
|
||||
{
|
||||
switch (element) {
|
||||
case ATTR_ELEMENT_CURVE_KEY: {
|
||||
const int num_points = b_curves.points.length();
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
data[i] = get_value_at_index(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_ELEMENT_CURVE: {
|
||||
const int num_verts = b_curves.curves.length();
|
||||
for (int i = 0; i < num_verts; i++) {
|
||||
for (int i = 0; i < num_curves; i++) {
|
||||
data[i] = get_value_at_index(i);
|
||||
}
|
||||
break;
|
||||
@@ -681,6 +682,7 @@ static void attr_create_motion(Hair *hair, BL::Attribute &b_attribute, const flo
|
||||
}
|
||||
|
||||
BL::FloatVectorAttribute b_vector_attribute(b_attribute);
|
||||
const float(*src)[3] = static_cast<const float(*)[3]>(b_vector_attribute.data[0].ptr.data);
|
||||
const int num_curve_keys = hair->get_curve_keys().size();
|
||||
|
||||
/* Find or add attribute */
|
||||
@@ -698,23 +700,24 @@ static void attr_create_motion(Hair *hair, BL::Attribute &b_attribute, const flo
|
||||
float3 *mP = attr_mP->data_float3() + step * num_curve_keys;
|
||||
|
||||
for (int i = 0; i < num_curve_keys; i++) {
|
||||
mP[i] = P[i] + get_float3(b_vector_attribute.data[i].vector()) * relative_time;
|
||||
mP[i] = P[i] + make_float3(src[i][0], src[i][1], src[i][2]) * relative_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void attr_create_uv(AttributeSet &attributes,
|
||||
BL::Curves &b_curves,
|
||||
const int num_curves,
|
||||
const int num_points,
|
||||
BL::Attribute &b_attribute,
|
||||
const ustring name)
|
||||
{
|
||||
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(ATTR_STD_UV, name);
|
||||
|
||||
float2 *data = attr->data_float2();
|
||||
fill_generic_attribute(b_curves, data, ATTR_ELEMENT_CURVE, [&](int i) {
|
||||
BL::Array<float, 2> v = b_float2_attribute.data[i].vector();
|
||||
return make_float2(v[0], v[1]);
|
||||
fill_generic_attribute(num_curves, num_points, data, ATTR_ELEMENT_CURVE, [&](int i) {
|
||||
return make_float2(src[i][0], src[i][1]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -724,6 +727,9 @@ static void attr_create_generic(Scene *scene,
|
||||
const bool need_motion,
|
||||
const float motion_scale)
|
||||
{
|
||||
const int num_keys = b_curves.points.length();
|
||||
const int num_curves = b_curves.curves.length();
|
||||
|
||||
AttributeSet &attributes = hair->attributes;
|
||||
static const ustring u_velocity("velocity");
|
||||
const bool need_uv = hair->need_attribute(scene, ATTR_STD_UV);
|
||||
@@ -743,7 +749,7 @@ static void attr_create_generic(Scene *scene,
|
||||
/* Weak, use first float2 attribute as standard UV. */
|
||||
if (need_uv && !have_uv && b_data_type == BL::Attribute::data_type_FLOAT2 &&
|
||||
b_domain == BL::Attribute::domain_CURVE) {
|
||||
attr_create_uv(attributes, b_curves, b_attribute, name);
|
||||
attr_create_uv(attributes, num_curves, num_keys, b_attribute, name);
|
||||
have_uv = true;
|
||||
continue;
|
||||
}
|
||||
@@ -773,57 +779,57 @@ static void attr_create_generic(Scene *scene,
|
||||
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_curves, data, element, [&](int i) { return b_float_attribute.data[i].value(); });
|
||||
fill_generic_attribute(num_curves, num_keys, data, element, [&](int i) { return src[i]; });
|
||||
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_curves, data, element, [&](int i) {
|
||||
return (float)b_bool_attribute.data[i].value();
|
||||
});
|
||||
fill_generic_attribute(
|
||||
num_curves, num_keys, data, element, [&](int i) { return 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_curves, data, element, [&](int i) {
|
||||
return (float)b_int_attribute.data[i].value();
|
||||
});
|
||||
fill_generic_attribute(
|
||||
num_curves, num_keys, data, element, [&](int i) { return 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_curves, data, element, [&](int i) {
|
||||
BL::Array<float, 3> v = b_vector_attribute.data[i].vector();
|
||||
return make_float3(v[0], v[1], v[2]);
|
||||
fill_generic_attribute(num_curves, num_keys, data, element, [&](int i) {
|
||||
return 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_curves, data, element, [&](int i) {
|
||||
BL::Array<float, 4> v = b_color_attribute.data[i].color();
|
||||
return make_float4(v[0], v[1], v[2], v[3]);
|
||||
fill_generic_attribute(num_curves, num_keys, data, element, [&](int i) {
|
||||
return 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_curves, data, element, [&](int i) {
|
||||
BL::Array<float, 2> v = b_float2_attribute.data[i].vector();
|
||||
return make_float2(v[0], v[1]);
|
||||
fill_generic_attribute(num_curves, num_keys, data, element, [&](int i) {
|
||||
return make_float2(src[i][0], src[i][1]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -834,17 +840,18 @@ static void attr_create_generic(Scene *scene,
|
||||
}
|
||||
}
|
||||
|
||||
static float4 hair_point_as_float4(BL::FloatVectorAttribute b_attr_position,
|
||||
std::optional<BL::FloatAttribute> b_attr_radius,
|
||||
static float4 hair_point_as_float4(const float (*b_attr_position)[3],
|
||||
const float *b_attr_radius,
|
||||
const int index)
|
||||
{
|
||||
float4 mP = float3_to_float4(get_float3(b_attr_position.data[index].vector()));
|
||||
mP.w = b_attr_radius ? b_attr_radius->data[index].value() : 0.005f;
|
||||
float4 mP = make_float4(
|
||||
b_attr_position[index][0], b_attr_position[index][1], b_attr_position[index][2], 0.0f);
|
||||
mP.w = b_attr_radius ? b_attr_radius[index] : 0.005f;
|
||||
return mP;
|
||||
}
|
||||
|
||||
static float4 interpolate_hair_points(BL::FloatVectorAttribute b_attr_position,
|
||||
std::optional<BL::FloatAttribute> b_attr_radius,
|
||||
static float4 interpolate_hair_points(const float (*b_attr_position)[3],
|
||||
const float *b_attr_radius,
|
||||
const int first_point_index,
|
||||
const int num_points,
|
||||
const float step)
|
||||
@@ -864,8 +871,6 @@ static void export_hair_curves(Scene *scene,
|
||||
const bool need_motion,
|
||||
const float motion_scale)
|
||||
{
|
||||
/* TODO: optimize so we can straight memcpy arrays from Blender? */
|
||||
|
||||
const int num_keys = b_curves.points.length();
|
||||
const int num_curves = b_curves.curves.length();
|
||||
|
||||
@@ -879,7 +884,6 @@ static void export_hair_curves(Scene *scene,
|
||||
/* Add requested attributes. */
|
||||
float *attr_intercept = NULL;
|
||||
float *attr_length = NULL;
|
||||
float *attr_random = NULL;
|
||||
|
||||
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
|
||||
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT)->data_float();
|
||||
@@ -888,28 +892,40 @@ static void export_hair_curves(Scene *scene,
|
||||
attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH)->data_float();
|
||||
}
|
||||
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
|
||||
attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM)->data_float();
|
||||
float *attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM)->data_float();
|
||||
for (int i = 0; i < num_curves; i++) {
|
||||
attr_random[i] = hash_uint2_to_float(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
BL::FloatVectorAttribute b_attr_position = find_curves_position_attribute(b_curves);
|
||||
std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_curves);
|
||||
const int *point_offsets = static_cast<const int *>(b_curves.curve_offset_data[0].ptr.data);
|
||||
const float(*b_attr_position)[3] = find_position_attribute(b_curves);
|
||||
const float *b_attr_radius = find_radius_attribute(b_curves);
|
||||
|
||||
std::copy(point_offsets, point_offsets + num_curves, curve_first_key);
|
||||
std::fill(curve_shader, curve_shader + num_curves, 0);
|
||||
if (b_attr_radius) {
|
||||
std::copy(b_attr_radius, b_attr_radius + num_keys, curve_radius);
|
||||
}
|
||||
else {
|
||||
std::fill(curve_radius, curve_radius + num_curves, 0.005f);
|
||||
}
|
||||
|
||||
/* Export curves and points. */
|
||||
for (int i = 0; i < num_curves; i++) {
|
||||
const int first_point_index = b_curves.curve_offset_data[i].value();
|
||||
const int num_points = b_curves.curve_offset_data[i + 1].value() - first_point_index;
|
||||
const int first_point_index = point_offsets[i];
|
||||
const int num_points = point_offsets[i + 1] - first_point_index;
|
||||
|
||||
float3 prev_co = zero_float3();
|
||||
float length = 0.0f;
|
||||
|
||||
/* Position and radius. */
|
||||
for (int j = 0; j < num_points; j++) {
|
||||
const int point_offset = first_point_index + j;
|
||||
const float3 co = get_float3(b_attr_position.data[point_offset].vector());
|
||||
const float radius = b_attr_radius ? b_attr_radius->data[point_offset].value() : 0.005f;
|
||||
const int point = first_point_index + j;
|
||||
const float3 co = make_float3(
|
||||
b_attr_position[point][0], b_attr_position[point][1], b_attr_position[point][2]);
|
||||
|
||||
curve_keys[point_offset] = co;
|
||||
curve_radius[point_offset] = radius;
|
||||
curve_keys[point] = co;
|
||||
|
||||
if (attr_length || attr_intercept) {
|
||||
if (j > 0) {
|
||||
@@ -918,7 +934,7 @@ static void export_hair_curves(Scene *scene,
|
||||
prev_co = co;
|
||||
|
||||
if (attr_intercept) {
|
||||
attr_intercept[point_offset] = length;
|
||||
attr_intercept[point] = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -926,8 +942,8 @@ static void export_hair_curves(Scene *scene,
|
||||
/* Normalized 0..1 attribute along curve. */
|
||||
if (attr_intercept && length > 0.0f) {
|
||||
for (int j = 1; j < num_points; j++) {
|
||||
const int point_offset = first_point_index + j;
|
||||
attr_intercept[point_offset] /= length;
|
||||
const int point = first_point_index + j;
|
||||
attr_intercept[point] /= length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,15 +951,6 @@ static void export_hair_curves(Scene *scene,
|
||||
if (attr_length) {
|
||||
attr_length[i] = length;
|
||||
}
|
||||
|
||||
/* Random number per curve. */
|
||||
if (attr_random != NULL) {
|
||||
attr_random[i] = hash_uint2_to_float(i, 0);
|
||||
}
|
||||
|
||||
/* Curve. */
|
||||
curve_shader[i] = 0;
|
||||
curve_first_key[i] = first_point_index;
|
||||
}
|
||||
|
||||
attr_create_generic(scene, hair, b_curves, need_motion, motion_scale);
|
||||
@@ -968,12 +975,13 @@ static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motio
|
||||
int num_motion_keys = 0;
|
||||
int curve_index = 0;
|
||||
|
||||
BL::FloatVectorAttribute b_attr_position = find_curves_position_attribute(b_curves);
|
||||
std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_curves);
|
||||
const int *point_offsets = static_cast<const int *>(b_curves.curve_offset_data[0].ptr.data);
|
||||
const float(*b_attr_position)[3] = find_position_attribute(b_curves);
|
||||
const float *b_attr_radius = find_radius_attribute(b_curves);
|
||||
|
||||
for (int i = 0; i < num_curves; i++) {
|
||||
const int first_point_index = b_curves.curve_offset_data[i].value();
|
||||
const int num_points = b_curves.curve_offset_data[i + 1].value() - first_point_index;
|
||||
const int first_point_index = point_offsets[i];
|
||||
const int num_points = point_offsets[i + 1] - first_point_index;
|
||||
|
||||
Hair::Curve curve = hair->get_curve(curve_index);
|
||||
curve_index++;
|
||||
@@ -981,10 +989,10 @@ static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motio
|
||||
if (num_points == curve.num_keys) {
|
||||
/* Number of keys matches. */
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
int point_index = first_point_index + i;
|
||||
int point = first_point_index + i;
|
||||
|
||||
if (point_index < num_keys) {
|
||||
mP[num_motion_keys] = hair_point_as_float4(b_attr_position, b_attr_radius, point_index);
|
||||
if (point < num_keys) {
|
||||
mP[num_motion_keys] = hair_point_as_float4(b_attr_position, b_attr_radius, point);
|
||||
num_motion_keys++;
|
||||
|
||||
if (!have_motion) {
|
||||
|
||||
Reference in New Issue
Block a user