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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user