Geometry: Avoid unnecessary initialization when resizing data arrays

When resizing mesh and curves attribute storage, avoid initializing the
new memory for basic types. Also, avoid skipping "no free" layers; all
layers should be reallocated to the new size since they may be accessed.

The semantics introduced in 25237d2625 are essential for this
change, because otherwise we don't have a way to construct non-trivial
types in the new memory.

In a basic test of the extrude node, I observed a performance
improvement of about 30%, from 55ms to 42ms.

Differential Revision: https://developer.blender.org/D15818
This commit is contained in:
Hans Goudey 2022-09-12 11:35:33 -05:00
parent 225b5a3491
commit 9088a1f476
Notes: blender-bot 2023-02-14 06:00:46 +01:00
Referenced by commit 1fbd300adb, Fix: Curves sculpt adding resets attribute values
9 changed files with 99 additions and 36 deletions

View File

@ -178,13 +178,11 @@ bool CustomData_merge_mesh_to_bmesh(const struct CustomData *source,
int totelem);
/**
* Reallocate custom data to a new element count.
* Only affects on data layers which are owned by the CustomData itself,
* referenced data is kept unchanged,
*
* \note Take care of referenced layers by yourself!
* Reallocate custom data to a new element count. If the new size is larger, the new values use
* the #CD_CONSTRUCT behavior, so trivial types must be initialized by the caller. After being
* resized, the #CustomData does not contain any referenced layers.
*/
void CustomData_realloc(struct CustomData *data, int totelem);
void CustomData_realloc(struct CustomData *data, int old_size, int new_size);
/**
* BMesh version of CustomData_merge; merges the layouts of source and `dest`,

View File

@ -726,8 +726,22 @@ bool CustomDataAttributes::remove(const AttributeIDRef &attribute_id)
void CustomDataAttributes::reallocate(const int size)
{
const int old_size = size_;
size_ = size;
CustomData_realloc(&data, size);
CustomData_realloc(&data, old_size, size_);
if (size_ > old_size) {
/* Fill default new values. */
const int new_elements_num = size_ - old_size;
this->foreach_attribute(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
GMutableSpan new_data = this->get_for_write(id)->take_back(new_elements_num);
const CPPType &type = new_data.type();
type.fill_assign_n(type.default_value(), new_data.data(), new_data.size());
return true;
},
/* Dummy. */
ATTR_DOMAIN_POINT);
}
}
void CustomDataAttributes::clear()

View File

@ -963,11 +963,11 @@ void CurvesGeometry::ensure_can_interpolate_to_evaluated() const
void CurvesGeometry::resize(const int points_num, const int curves_num)
{
if (points_num != this->point_num) {
CustomData_realloc(&this->point_data, points_num);
CustomData_realloc(&this->point_data, this->points_num(), points_num);
this->point_num = points_num;
}
if (curves_num != this->curve_num) {
CustomData_realloc(&this->curve_data, curves_num);
CustomData_realloc(&this->curve_data, this->curves_num(), curves_num);
this->curve_num = curves_num;
this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1));
}

View File

@ -2408,19 +2408,37 @@ bool CustomData_merge_mesh_to_bmesh(const CustomData *source,
return result;
}
void CustomData_realloc(CustomData *data, const int totelem)
void CustomData_realloc(CustomData *data, const int old_size, const int new_size)
{
BLI_assert(totelem >= 0);
BLI_assert(new_size >= 0);
for (int i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
const LayerTypeInfo *typeInfo;
const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type);
const int64_t old_size_in_bytes = int64_t(old_size) * typeInfo->size;
const int64_t new_size_in_bytes = int64_t(new_size) * typeInfo->size;
if (layer->flag & CD_FLAG_NOFREE) {
continue;
const void *old_data = layer->data;
layer->data = MEM_malloc_arrayN(new_size, typeInfo->size, __func__);
if (typeInfo->copy) {
typeInfo->copy(old_data, layer->data, std::min(old_size, new_size));
}
else {
std::memcpy(layer->data, old_data, std::min(old_size_in_bytes, new_size_in_bytes));
}
layer->flag &= ~CD_FLAG_NOFREE;
}
else {
layer->data = MEM_reallocN(layer->data, new_size_in_bytes);
}
if (new_size > old_size) {
/* Initialize new values for non-trivial types. */
if (typeInfo->construct) {
const int new_elements_num = new_size - old_size;
typeInfo->construct(POINTER_OFFSET(layer->data, old_size_in_bytes), new_elements_num);
}
}
typeInfo = layerType_getInfo(layer->type);
/* Use calloc to avoid the need to manually initialize new data in layers.
* Useful for types like #MDeformVert which contain a pointer. */
layer->data = MEM_recallocN(layer->data, (size_t)totelem * typeInfo->size);
}
}

View File

@ -2095,11 +2095,11 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals)
const bool do_edges = (num_new_edges > 0);
/* Reallocate all vert and edge related data. */
CustomData_realloc(&mesh->vdata, mesh->totvert, mesh->totvert + num_new_verts);
mesh->totvert += num_new_verts;
CustomData_realloc(&mesh->vdata, mesh->totvert);
if (do_edges) {
CustomData_realloc(&mesh->edata, mesh->totedge, mesh->totedge + num_new_edges);
mesh->totedge += num_new_edges;
CustomData_realloc(&mesh->edata, mesh->totedge);
}
/* Update normals manually to avoid recalculation after this operation. */

View File

@ -105,8 +105,9 @@ static void make_edges_mdata_extend(Mesh &mesh)
#endif
if (totedge_new) {
CustomData_realloc(&mesh.edata, totedge + totedge_new);
/* The only layer should be edges, so no other layers need to be initialized. */
BLI_assert(mesh.edata.totlayer == 1);
CustomData_realloc(&mesh.edata, totedge, totedge + totedge_new);
mesh.totedge += totedge_new;
MutableSpan<MEdge> edges = mesh.edges_for_write();
MEdge *medge = &edges[totedge];
@ -634,9 +635,11 @@ void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud)
using namespace blender;
BLI_assert(me != nullptr);
/* The pointcloud should only contain the position attribute, otherwise more attributes would
* need to be initialized below. */
BLI_assert(pointcloud->attributes().all_ids().size() == 1);
CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint, me->totvert);
pointcloud->totpoint = me->totvert;
CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
/* Copy over all attributes. */
CustomData_merge(&me->vdata, &pointcloud->pdata, CD_MASK_PROP_ALL, CD_DUPLICATE, me->totvert);

View File

@ -189,8 +189,9 @@ IDTypeInfo IDType_ID_PT = {
static void pointcloud_random(PointCloud *pointcloud)
{
BLI_assert(pointcloud->totpoint == 0);
pointcloud->totpoint = 400;
CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
CustomData_realloc(&pointcloud->pdata, 0, pointcloud->totpoint);
RNG *rng = BLI_rng_new(0);
@ -238,9 +239,6 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint)
nullptr, ID_PT, BKE_idtype_idcode_to_name(ID_PT), LIB_ID_CREATE_LOCALIZE));
pointcloud_init_data(&pointcloud->id);
pointcloud->totpoint = totpoint;
CustomData_add_layer_named(&pointcloud->pdata,
CD_PROP_FLOAT,
CD_SET_DEFAULT,
@ -248,8 +246,8 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint)
pointcloud->totpoint,
POINTCLOUD_ATTR_RADIUS);
CustomData_realloc(&pointcloud->pdata, 0, totpoint);
pointcloud->totpoint = totpoint;
CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
return pointcloud;
}

View File

@ -372,6 +372,28 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
curves.fill_curve_types(new_curves_range, CURVE_TYPE_CATMULL_ROM);
/* Explicitly set all other attributes besides those processed above to default values. */
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
Set<std::string> attributes_to_skip{{"position",
"curve_type",
"surface_uv_coordinate",
".selection_point_float",
".selection_curve_float"}};
attributes.for_all(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
if (id.is_named() && attributes_to_skip.contains(id.name())) {
return true;
}
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
const int new_elements_num = attribute.domain == ATTR_DOMAIN_POINT ? new_points_num :
new_curves_num;
const CPPType &type = attribute.span.type();
GMutableSpan new_data = attribute.span.take_back(new_elements_num);
type.fill_assign_n(type.default_value(), new_data.data(), new_data.size());
attribute.finish();
return true;
});
return outputs;
}

View File

@ -94,24 +94,24 @@ static void expand_mesh(Mesh &mesh,
const int loop_expand)
{
if (vert_expand != 0) {
CustomData_duplicate_referenced_layers(&mesh.vdata, mesh.totvert);
const int old_verts_num = mesh.totvert;
mesh.totvert += vert_expand;
CustomData_realloc(&mesh.vdata, mesh.totvert);
CustomData_realloc(&mesh.vdata, old_verts_num, mesh.totvert);
}
if (edge_expand != 0) {
CustomData_duplicate_referenced_layers(&mesh.edata, mesh.totedge);
const int old_edges_num = mesh.totedge;
mesh.totedge += edge_expand;
CustomData_realloc(&mesh.edata, mesh.totedge);
CustomData_realloc(&mesh.edata, old_edges_num, mesh.totedge);
}
if (poly_expand != 0) {
CustomData_duplicate_referenced_layers(&mesh.pdata, mesh.totpoly);
const int old_polys_num = mesh.totpoly;
mesh.totpoly += poly_expand;
CustomData_realloc(&mesh.pdata, mesh.totpoly);
CustomData_realloc(&mesh.pdata, old_polys_num, mesh.totpoly);
}
if (loop_expand != 0) {
CustomData_duplicate_referenced_layers(&mesh.ldata, mesh.totloop);
const int old_loops_num = mesh.totloop;
mesh.totloop += loop_expand;
CustomData_realloc(&mesh.ldata, mesh.totloop);
CustomData_realloc(&mesh.ldata, old_loops_num, mesh.totloop);
}
}
@ -147,6 +147,7 @@ static MEdge new_edge(const int v1, const int v2)
MEdge edge;
edge.v1 = v1;
edge.v2 = v2;
edge.crease = 0;
edge.flag = (ME_EDGEDRAW | ME_EDGERENDER);
return edge;
}
@ -156,6 +157,7 @@ static MEdge new_loose_edge(const int v1, const int v2)
MEdge edge;
edge.v1 = v1;
edge.v2 = v2;
edge.crease = 0;
edge.flag = ME_LOOSEEDGE;
return edge;
}
@ -286,6 +288,7 @@ static void extrude_mesh_vertices(Mesh &mesh,
for (const int i : range) {
const float3 offset = offsets[selection[i]];
add_v3_v3(new_verts[i].co, offset);
new_verts[i].flag = 0;
}
});
});
@ -608,6 +611,7 @@ static void extrude_mesh_edges(Mesh &mesh,
threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
add_v3_v3(new_verts[i].co, offset);
new_verts[i].flag = 0;
}
});
}
@ -615,6 +619,7 @@ static void extrude_mesh_edges(Mesh &mesh,
threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
add_v3_v3(new_verts[i].co, vert_offsets[new_vert_indices[i]]);
new_verts[i].flag = 0;
}
});
}
@ -996,6 +1001,10 @@ static void extrude_mesh_face_regions(Mesh &mesh,
});
}
for (MVert &vert : verts.slice(new_vert_range)) {
vert.flag = 0;
}
MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT);
vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE);
@ -1253,6 +1262,7 @@ static void extrude_individual_mesh_faces(Mesh &mesh,
const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection);
for (MVert &vert : new_verts.slice(poly_corner_range)) {
add_v3_v3(vert.co, poly_offset[poly_selection[i_selection]]);
vert.flag = 0;
}
}
});