Subdiv: Skip interpolating topology attributes to improve performance

Currently the corner_vert and corner_edge attributes go through the
generic custom data interpolation, only to be overwritten directly
afterwards. In a profile of the execution of the subdivision surface
modifier, 5% of the time was spent in `layerInterp_propInt`. To fix
this, first build a `CustomData` layout to be used just for face corner
attribute interpolation. Then allocate the topology attributes
separately and add them to the result mesh later. This requires a fair
amount of boilerplate right now, but it should be improved with various
changes to mesh attribute storage in the future.

In a file with many object with a subsurf modifier, this change
improved the overall execution time by between 5 and 10% in my tests.

Pull Request: https://projects.blender.org/blender/blender/pulls/123254
This commit is contained in:
Hans Goudey
2024-06-15 00:53:22 +02:00
committed by Hans Goudey
parent 6cb86eb1b7
commit 31a67805fe

View File

@@ -43,13 +43,25 @@ struct SubdivMeshContext {
OffsetIndices<int> coarse_faces;
Span<int> coarse_corner_verts;
/**
* Contains all face corner custom data from the original coarse mesh except for the
* ".corner_vert" and ".corner_edge" topology layers. This prevents unnecessary interpolation of
* that data which would just be overwritten anyway.
*/
CustomData coarse_corner_data_interp;
Subdiv *subdiv;
Mesh *subdiv_mesh;
MutableSpan<float3> subdiv_positions;
MutableSpan<int2> subdiv_edges;
MutableSpan<int> subdiv_face_offsets;
MutableSpan<int> subdiv_corner_verts;
MutableSpan<int> subdiv_corner_edges;
/**
* Owning pointers to topology arrays, not added to the result mesh until face corner value
* interpolation finishes.
*/
int *subdiv_corner_verts;
int *subdiv_corner_edges;
/* Cached custom data arrays for faster access. */
int *vert_origindex;
@@ -94,8 +106,6 @@ static void subdiv_mesh_ctx_cache_custom_data_layers(SubdivMeshContext *ctx)
ctx->subdiv_positions = subdiv_mesh->vert_positions_for_write();
ctx->subdiv_edges = subdiv_mesh->edges_for_write();
ctx->subdiv_face_offsets = subdiv_mesh->face_offsets_for_write();
ctx->subdiv_corner_verts = subdiv_mesh->corner_verts_for_write();
ctx->subdiv_corner_edges = subdiv_mesh->corner_edges_for_write();
/* Pointers to original indices layers. */
ctx->vert_origindex = static_cast<int *>(CustomData_get_layer_for_write(
&subdiv_mesh->vert_data, CD_ORIGINDEX, subdiv_mesh->verts_num));
@@ -126,6 +136,9 @@ static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vert
static void subdiv_mesh_context_free(SubdivMeshContext *ctx)
{
MEM_SAFE_FREE(ctx->accumulated_counters);
MEM_SAFE_FREE(ctx->subdiv_corner_verts);
MEM_SAFE_FREE(ctx->subdiv_corner_edges);
CustomData_free(&ctx->coarse_corner_data_interp, ctx->coarse_mesh->corners_num);
}
/** \} */
@@ -332,9 +345,8 @@ static void loop_interpolation_init(const SubdivMeshContext *ctx,
LoopsForInterpolation *loop_interpolation,
const IndexRange coarse_face)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
if (coarse_face.size() == 4) {
loop_interpolation->corner_data = &coarse_mesh->corner_data;
loop_interpolation->corner_data = &ctx->coarse_corner_data_interp;
loop_interpolation->loop_indices[0] = coarse_face.start() + 0;
loop_interpolation->loop_indices[1] = coarse_face.start() + 1;
loop_interpolation->loop_indices[2] = coarse_face.start() + 2;
@@ -344,7 +356,7 @@ static void loop_interpolation_init(const SubdivMeshContext *ctx,
else {
loop_interpolation->corner_data = &loop_interpolation->corner_data_storage;
/* Allocate storage for loops corresponding to ptex corners. */
CustomData_copy_layout(&ctx->coarse_mesh->corner_data,
CustomData_copy_layout(&ctx->coarse_corner_data_interp,
&loop_interpolation->corner_data_storage,
CD_MASK_EVERYTHING.lmask,
CD_SET_DEFAULT,
@@ -364,7 +376,7 @@ static void loop_interpolation_init(const SubdivMeshContext *ctx,
weights[i] = weight;
indices[i] = coarse_face.start() + i;
}
CustomData_interp(&coarse_mesh->corner_data,
CustomData_interp(&ctx->coarse_corner_data_interp,
&loop_interpolation->corner_data_storage,
indices.data(),
weights.data(),
@@ -383,7 +395,7 @@ static void loop_interpolation_from_corner(const SubdivMeshContext *ctx,
/* Nothing to do, all indices and data is already assigned. */
}
else {
const CustomData *corner_data = &ctx->coarse_mesh->corner_data;
const CustomData *corner_data = &ctx->coarse_corner_data_interp;
LoopsOfPtex loops_of_ptex;
loops_of_ptex_get(&loops_of_ptex, coarse_face, corner);
/* PTEX face corner corresponds to a face loop with same index. */
@@ -529,12 +541,50 @@ static bool subdiv_mesh_topology_info(const ForeachContext *foreach_context,
mask.lmask &= ~CD_MASK_MULTIRES_GRIDS;
SubdivMeshContext *subdiv_context = static_cast<SubdivMeshContext *>(foreach_context->user_data);
subdiv_context->subdiv_mesh = BKE_mesh_new_nomain_from_template_ex(
subdiv_context->coarse_mesh, num_vertices, num_edges, 0, num_faces, num_loops, mask);
const Mesh &coarse_mesh = *subdiv_context->coarse_mesh;
subdiv_context->subdiv_mesh = bke::mesh_new_no_attributes(
num_vertices, num_edges, num_faces, num_loops);
Mesh &subdiv_mesh = *subdiv_context->subdiv_mesh;
BKE_mesh_copy_parameters_for_eval(subdiv_context->subdiv_mesh, &coarse_mesh);
CustomData_free(&subdiv_mesh.vert_data, 0);
CustomData_copy_layout(
&coarse_mesh.vert_data, &subdiv_mesh.vert_data, mask.vmask, CD_SET_DEFAULT, num_vertices);
CustomData_free(&subdiv_mesh.edge_data, 0);
CustomData_copy_layout(
&coarse_mesh.edge_data, &subdiv_mesh.edge_data, mask.emask, CD_SET_DEFAULT, num_edges);
CustomData_free(&subdiv_mesh.face_data, 0);
CustomData_copy_layout(
&coarse_mesh.face_data, &subdiv_mesh.face_data, mask.pmask, CD_SET_DEFAULT, num_faces);
subdiv_mesh.face_offsets_for_write().last() = num_loops;
/* Create corner data for interpolation without topology attributes. */
CustomData_copy(&coarse_mesh.corner_data,
&subdiv_context->coarse_corner_data_interp,
mask.lmask,
coarse_mesh.corners_num);
CustomData_free_layer_named(
&subdiv_context->coarse_corner_data_interp, ".corner_vert", coarse_mesh.corners_num);
CustomData_free_layer_named(
&subdiv_context->coarse_corner_data_interp, ".corner_edge", coarse_mesh.corners_num);
CustomData_free(&subdiv_mesh.corner_data, 0);
CustomData_copy_layout(&subdiv_context->coarse_corner_data_interp,
&subdiv_mesh.corner_data,
mask.lmask,
CD_SET_DEFAULT,
num_loops);
/* Allocate corner topology arrays which are added to the result at the end. */
subdiv_context->subdiv_corner_verts = static_cast<int *>(
MEM_malloc_arrayN(num_loops, sizeof(int), __func__));
subdiv_context->subdiv_corner_edges = static_cast<int *>(
MEM_malloc_arrayN(num_loops, sizeof(int), __func__));
subdiv_mesh_ctx_cache_custom_data_layers(subdiv_context);
subdiv_mesh_prepare_accumulator(subdiv_context, num_vertices);
subdiv_context->subdiv_mesh->runtime->subsurf_face_dot_tags.clear();
subdiv_context->subdiv_mesh->runtime->subsurf_face_dot_tags.resize(num_vertices);
subdiv_mesh.runtime->subsurf_face_dot_tags.clear();
subdiv_mesh.runtime->subsurf_face_dot_tags.resize(num_vertices);
if (subdiv_context->settings->use_optimal_display) {
subdiv_context->subdiv_display_edges = Array<bool>(num_edges, false);
}
@@ -1152,6 +1202,21 @@ Mesh *subdiv_to_mesh(Subdiv *subdiv, const ToMeshSettings *settings, const Mesh
stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY);
Mesh *result = subdiv_context.subdiv_mesh;
CustomData_add_layer_named_with_data(&result->corner_data,
CD_PROP_INT32,
subdiv_context.subdiv_corner_verts,
result->corners_num,
".corner_vert",
nullptr);
subdiv_context.subdiv_corner_verts = nullptr;
CustomData_add_layer_named_with_data(&result->corner_data,
CD_PROP_INT32,
subdiv_context.subdiv_corner_edges,
result->corners_num,
".corner_edge",
nullptr);
subdiv_context.subdiv_corner_edges = nullptr;
/* NOTE: Using normals from the limit surface gives different results than Blender's vertex
* normal calculation. Since vertex normals are supposed to be a consistent cache, don't bother
* calculating them here. The work may have been pointless anyway if the mesh is deformed or