Mesh: Parallelize remesh reprojections

In high poly meshes the reprojections after remesh can become slow.
This parallelizes reprojection of face sets, paint mask and vertex paint.
It also adds flags to disable dependency graph updates on
setting remesh options to remove UI lag.

Profiling of remeshing a 3.4M poly mesh (in sculpt mode):
Before: 19.6s
After: 8.7s

Differential Revision: https://developer.blender.org/D15638
This commit is contained in:
Erik Abrahamsson 2022-11-02 23:23:20 +01:00
parent a38b98478a
commit bad55d56bc
2 changed files with 91 additions and 69 deletions

View File

@ -19,6 +19,7 @@
#include "BLI_math_vec_types.hh"
#include "BLI_math_vector.h"
#include "BLI_span.hh"
#include "BLI_task.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@ -302,17 +303,20 @@ void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, const Mesh *source)
&target->vdata, CD_PAINT_MASK, CD_CONSTRUCT, nullptr, target->totvert);
}
for (int i = 0; i < target->totvert; i++) {
float from_co[3];
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
copy_v3_v3(from_co, target_verts[i].co);
BLI_bvhtree_find_nearest(bvhtree.tree, from_co, &nearest, bvhtree.nearest_callback, &bvhtree);
if (nearest.index != -1) {
target_mask[i] = source_mask[nearest.index];
blender::threading::parallel_for(IndexRange(target->totvert), 4096, [&](const IndexRange range) {
for (const int i : range) {
float from_co[3];
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
copy_v3_v3(from_co, target_verts[i].co);
BLI_bvhtree_find_nearest(
bvhtree.tree, from_co, &nearest, bvhtree.nearest_callback, &bvhtree);
if (nearest.index != -1) {
target_mask[i] = source_mask[nearest.index];
}
}
}
});
free_bvhtree_from_mesh(&bvhtree);
}
@ -344,21 +348,24 @@ void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, const Mesh *source)
BVHTreeFromMesh bvhtree = {nullptr};
BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_LOOPTRI, 2);
for (int i = 0; i < target->totpoly; i++) {
float from_co[3];
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
const MPoly *mpoly = &target_polys[i];
BKE_mesh_calc_poly_center(mpoly, &target_loops[mpoly->loopstart], target_verts, from_co);
BLI_bvhtree_find_nearest(bvhtree.tree, from_co, &nearest, bvhtree.nearest_callback, &bvhtree);
if (nearest.index != -1) {
dst[i] = src[looptri[nearest.index].poly];
blender::threading::parallel_for(IndexRange(target->totpoly), 2048, [&](const IndexRange range) {
for (const int i : range) {
float from_co[3];
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
const MPoly *mpoly = &target_polys[i];
BKE_mesh_calc_poly_center(mpoly, &target_loops[mpoly->loopstart], target_verts, from_co);
BLI_bvhtree_find_nearest(
bvhtree.tree, from_co, &nearest, bvhtree.nearest_callback, &bvhtree);
if (nearest.index != -1) {
dst[i] = src[looptri[nearest.index].poly];
}
else {
dst[i] = 1;
}
}
else {
dst[i] = 1;
}
}
});
free_bvhtree_from_mesh(&bvhtree);
dst_face_sets.finish();
}
@ -399,19 +406,22 @@ void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source)
MVert *target_verts = (MVert *)CustomData_get_layer(&target->vdata, CD_MVERT);
if (domain == ATTR_DOMAIN_POINT) {
for (int i = 0; i < target->totvert; i++) {
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
BLI_bvhtree_find_nearest(
bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree);
blender::threading::parallel_for(
IndexRange(target->totvert), 4096, [&](const IndexRange range) {
for (const int i : range) {
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
BLI_bvhtree_find_nearest(
bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree);
if (nearest.index != -1) {
memcpy(POINTER_OFFSET(target_data, size_t(i) * data_size),
POINTER_OFFSET(source_data, size_t(nearest.index) * data_size),
data_size);
}
}
if (nearest.index != -1) {
memcpy(POINTER_OFFSET(target_data, size_t(i) * data_size),
POINTER_OFFSET(source_data, size_t(nearest.index) * data_size),
data_size);
}
}
});
}
else {
/* Lazily init vertex -> loop maps. */
@ -438,46 +448,50 @@ void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source)
target->totloop);
}
for (int i = 0; i < target->totvert; i++) {
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
BLI_bvhtree_find_nearest(
bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree);
blender::threading::parallel_for(
IndexRange(target->totvert), 2048, [&](const IndexRange range) {
for (const int i : range) {
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
BLI_bvhtree_find_nearest(
bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree);
if (nearest.index == -1) {
continue;
}
if (nearest.index == -1) {
continue;
}
MeshElemMap *source_loops = source_lmap + nearest.index;
MeshElemMap *target_loops = target_lmap + i;
MeshElemMap *source_loops = source_lmap + nearest.index;
MeshElemMap *target_loops = target_lmap + i;
if (target_loops->count == 0 || source_loops->count == 0) {
continue;
}
if (target_loops->count == 0 || source_loops->count == 0) {
continue;
}
/*
* Average color data for loops around the source vertex into
* the first target loop around the target vertex
*/
/*
* Average color data for loops around the source vertex into
* the first target loop around the target vertex
*/
CustomData_interp(source_cdata,
target_cdata,
source_loops->indices,
nullptr,
nullptr,
source_loops->count,
target_loops->indices[0]);
CustomData_interp(source_cdata,
target_cdata,
source_loops->indices,
nullptr,
nullptr,
source_loops->count,
target_loops->indices[0]);
void *elem = POINTER_OFFSET(target_data, size_t(target_loops->indices[0]) * data_size);
void *elem = POINTER_OFFSET(target_data,
size_t(target_loops->indices[0]) * data_size);
/* Copy to rest of target loops. */
for (int j = 1; j < target_loops->count; j++) {
memcpy(POINTER_OFFSET(target_data, size_t(target_loops->indices[j]) * data_size),
elem,
data_size);
}
}
/* Copy to rest of target loops. */
for (int j = 1; j < target_loops->count; j++) {
memcpy(POINTER_OFFSET(target_data, size_t(target_loops->indices[j]) * data_size),
elem,
data_size);
}
}
});
}
}

View File

@ -4024,6 +4024,7 @@ static void rna_def_mesh(BlenderRNA *brna)
"Size of the voxel in object space used for volume evaluation. Lower "
"values preserve finer details");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
prop = RNA_def_property(srna, "remesh_voxel_adaptivity", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "remesh_voxel_adaptivity");
@ -4035,11 +4036,13 @@ static void rna_def_mesh(BlenderRNA *brna)
"Reduces the final face count by simplifying geometry where detail is not needed, "
"generating triangles. A value greater than 0 disables Fix Poles");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
prop = RNA_def_property(srna, "use_remesh_fix_poles", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_FIX_POLES);
RNA_def_property_ui_text(prop, "Fix Poles", "Produces less poles and a better topology flow");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
prop = RNA_def_property(srna, "use_remesh_preserve_volume", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_VOLUME);
@ -4048,29 +4051,34 @@ static void rna_def_mesh(BlenderRNA *brna)
"Preserve Volume",
"Projects the mesh to preserve the volume and details of the original mesh");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
prop = RNA_def_property(srna, "use_remesh_preserve_paint_mask", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_PAINT_MASK);
RNA_def_property_ui_text(prop, "Preserve Paint Mask", "Keep the current mask on the new mesh");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
prop = RNA_def_property(srna, "use_remesh_preserve_sculpt_face_sets", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_SCULPT_FACE_SETS);
RNA_def_property_ui_text(
prop, "Preserve Face Sets", "Keep the current Face Sets on the new mesh");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
prop = RNA_def_property(srna, "use_remesh_preserve_vertex_colors", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_VERTEX_COLORS);
RNA_def_property_ui_text(
prop, "Preserve Vertex Colors", "Keep the current vertex colors on the new mesh");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
prop = RNA_def_property(srna, "remesh_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "remesh_mode");
RNA_def_property_enum_items(prop, rna_enum_mesh_remesh_mode_items);
RNA_def_property_ui_text(prop, "Remesh Mode", "");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
/* End remesh */