Mesh: Reduce custom normal calculation memory usage #107592

Merged
Hans Goudey merged 32 commits from HooglyBoogly/blender:corner-normals-refactor-storage into main 2023-05-10 14:41:16 +02:00
2 changed files with 57 additions and 47 deletions
Showing only changes of commit 0e1f63140d - Show all commits

View File

@ -68,7 +68,6 @@ struct NormalFanSpace {
float ref_alpha; float ref_alpha;
/** Reference angle, around vec_lnor, in [0, 2pi] range (0.0 marks that space as invalid). */ /** Reference angle, around vec_lnor, in [0, 2pi] range (0.0 marks that space as invalid). */
float ref_beta; float ref_beta;
Vector<int> corners;
}; };
/** /**
@ -77,20 +76,14 @@ struct NormalFanSpace {
struct MeshNormalFanSpaces { struct MeshNormalFanSpaces {
/** /**
* The normal coordinate spaces, potentially shared between multiple face corners in a smooth fan * The normal coordinate spaces, potentially shared between multiple face corners in a smooth fan
* connected to a vertex. Depending on the geometry (the amount of sharing / size of each fan), * connected to a vertex. Depending on the mesh (the amount of sharing / number of sharp edges /
* there may be many fewer spaces than face corners, so they are stored in a separate array. * size of each fan), there may be many fewer spaces than face corners, so they are stored in a
* separate array.
*/ */
Array<NormalFanSpace> spaces; Array<NormalFanSpace> spaces;
/** The index in #spaces for each face corner. */ /** The index of the data in the #spaces array for each face corner. */
Array<int> corner_space_indices; Array<int> corner_space_indices;
/**
* An index-based linked list of the face corners in each smooth fan, starting at each corner.
* Each index stores the index of the next corner in the fan, or -1 if it is the last. "Single"
* fans with only a single corner store -1 at that corner index directly.
*/
Array<int> corner_group_lists;
}; };
void fan_space_custom_normal_to_data(const NormalFanSpace *lnor_space, void fan_space_custom_normal_to_data(const NormalFanSpace *lnor_space,

View File

@ -879,17 +879,6 @@ static void loop_manifold_fan_around_vert_next(const Span<int> corner_verts,
} }
} }
static void fan_space_add_corners(MeshNormalFanSpaces &fan_spaces,
const Span<int> corners,
const int space_index)
{
for (const int i : corners.index_range().drop_back(1)) {
fan_spaces.corner_group_lists[corners[i]] = corners[i + 1];
}
fan_spaces.corner_group_lists[corners.last()] = -1;
fan_spaces.corner_space_indices.as_mutable_span().fill_indices(corners, space_index);
}
static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data, static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data,
const int ml_curr_index, const int ml_curr_index,
const int space_index) const int space_index)
@ -925,8 +914,7 @@ static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data,
NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index]; NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index];
normal_fan_space_define(lnor_space, loop_normals[ml_curr_index], vec_curr, vec_prev, {}); normal_fan_space_define(lnor_space, loop_normals[ml_curr_index], vec_curr, vec_prev, {});
/* We know there is only one loop in this space, no need to create a link-list in this case. */ lnors_spacearr->corner_space_indices[ml_curr_index] = space_index;
fan_space_add_corners(*lnors_spacearr, {ml_curr_index}, space_index);
if (!clnors_data.is_empty()) { if (!clnors_data.is_empty()) {
fan_space_custom_data_to_normal(lnor_space, fan_space_custom_data_to_normal(lnor_space,
@ -1078,16 +1066,16 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
* and optionally compute final lnor from custom data too! * and optionally compute final lnor from custom data too!
*/ */
if (lnors_spacearr) { if (lnors_spacearr) {
fan_space_add_corners(*lnors_spacearr, processed_corners, space_index);
if (UNLIKELY(length == 0.0f)) { if (UNLIKELY(length == 0.0f)) {
/* Use vertex normal as fallback! */ /* Use vertex normal as fallback! */
copy_v3_v3(lnor, loop_normals[mlfan_vert_index]); lnor = loop_normals[mlfan_vert_index];
length = 1.0f; length = 1.0f;
} }
NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index]; NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index];
normal_fan_space_define(lnor_space, lnor, vec_org, vec_curr, *edge_vectors); normal_fan_space_define(lnor_space, lnor, vec_org, vec_curr, *edge_vectors);
lnor_space->corners = std::move(processed_corners); lnors_spacearr->corner_space_indices.as_mutable_span().fill_indices(
processed_corners.as_span(), space_index);
edge_vectors->clear(); edge_vectors->clear();
if (!clnors_data.is_empty()) { if (!clnors_data.is_empty()) {
@ -1098,11 +1086,14 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
if (G.debug & G_DEBUG) { if (G.debug & G_DEBUG) {
printf("Invalid clnors in this fan!\n"); printf("Invalid clnors in this fan!\n");
} }
/* Extra bonus: since small-stack is local to this function, clnors_data.fill_indices(processed_corners.as_span(),
* no more need to empty it at all cost! */ short2(clnors_avg[0], clnors_avg[1]));
// print_v2("new clnors", clnors_avg);
fan_space_custom_data_to_normal(lnor_space, lnor, *clnor_ref, lnor);
} }
/* Extra bonus: since small-stack is local to this function,
* no more need to empty it at all cost! */
fan_space_custom_data_to_normal(lnor_space, lnor, *clnor_ref, lnor);
} }
} }
@ -1387,7 +1378,6 @@ void normals_calc_loop(const Span<float3> vert_positions,
if (r_lnors_spacearr) { if (r_lnors_spacearr) {
r_lnors_spacearr->spaces.reinitialize(single_corners.size() + fan_corners.size()); r_lnors_spacearr->spaces.reinitialize(single_corners.size() + fan_corners.size());
r_lnors_spacearr->corner_group_lists.reinitialize(corner_verts.size());
r_lnors_spacearr->corner_space_indices = Array<int>(corner_verts.size(), -1); r_lnors_spacearr->corner_space_indices = Array<int>(corner_verts.size(), -1);
} }
@ -1418,13 +1408,13 @@ void normals_calc_loop(const Span<float3> vert_positions,
std::cout << " vec_ortho: " << space.vec_ortho << '\n'; std::cout << " vec_ortho: " << space.vec_ortho << '\n';
std::cout << " ref_alpha: " << space.ref_alpha << '\n'; std::cout << " ref_alpha: " << space.ref_alpha << '\n';
std::cout << " ref_beta: " << space.ref_beta << '\n'; std::cout << " ref_beta: " << space.ref_beta << '\n';
std::cout << " fan indices: ("; // std::cout << " fan indices: (";

It's not obvious what r_offsets & r_reverse_indices represent, which should be stated in plan text (in this doc-string).

Or, wrap reverse_index_array in a function which takes lnors_spacearr and include a description of what the values represent there.

Either way noting that fan_corners_data & fan_corner_offset_indices represent inline isn't practical as they're declared twice.

It's not obvious what `r_offsets` & `r_reverse_indices` represent, which should be stated in plan text (in this doc-string). Or, wrap `reverse_index_array` in a function which takes `lnors_spacearr` and include a description of what the values represent there. Either way noting that `fan_corners_data` & `fan_corner_offset_indices` represent inline isn't practical as they're declared twice.
int iter = r_lnors_spacearr->corner_group_lists[corner]; // int iter = r_lnors_spacearr->corner_group_lists[corner];
while (iter != -1) { // while (iter != -1) {
std::cout << iter << ", "; // std::cout << iter << ", ";
iter = r_lnors_spacearr->corner_group_lists[iter]; // iter = r_lnors_spacearr->corner_group_lists[iter];
} // }
std::cout << ")\n"; // std::cout << ")\n";
} }
} }
} }
@ -1433,6 +1423,24 @@ void normals_calc_loop(const Span<float3> vert_positions,
#undef INDEX_INVALID #undef INDEX_INVALID
#undef IS_EDGE_SHARP #undef IS_EDGE_SHARP
static void reverse_space_index_array(const MeshNormalFanSpaces &spaces,
Array<int> &corner_offset_indices,
Array<int> &space_corner_indices)
{
corner_offset_indices = Array<int>(spaces.spaces.size() + 1, 0);
for (const int index : spaces.corner_space_indices) {
corner_offset_indices[index]++;
}
offset_indices::accumulate_counts_to_offsets(corner_offset_indices);
space_corner_indices.reinitialize(corner_offset_indices.last());
Array<int> count(spaces.spaces.size(), 0);
for (const int corner : spaces.corner_space_indices.index_range()) {
const int space = spaces.corner_space_indices[corner];
space_corner_indices[corner_offset_indices[space] + count[space]] = corner;
}
}
/** /**
* Compute internal representation of given custom normals (as an array of float[2]). * Compute internal representation of given custom normals (as an array of float[2]).
* It also makes sure the mesh matches those custom normals, by setting sharp edges flag as needed * It also makes sure the mesh matches those custom normals, by setting sharp edges flag as needed
@ -1513,6 +1521,11 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
done_loops.fill(true); done_loops.fill(true);
} }
else { else {
Array<int> space_corner_offset_indices;
Array<int> space_corner_indices;
reverse_space_index_array(lnors_spacearr, space_corner_offset_indices, space_corner_indices);
const OffsetIndices<int> space_corner_offsets(space_corner_offset_indices);
for (const int i : corner_verts.index_range()) { for (const int i : corner_verts.index_range()) {
if (lnors_spacearr.corner_space_indices[i] == -1) { if (lnors_spacearr.corner_space_indices[i] == -1) {
/* This should not happen in theory, but in some rare case (probably ugly geometry) /* This should not happen in theory, but in some rare case (probably ugly geometry)
@ -1528,6 +1541,10 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
continue; continue;
} }
const int space_index = lnors_spacearr.corner_space_indices[i];
const Span<int> space_corners = space_corner_indices.as_span().slice(
space_corner_offsets[space_index]);
/* Notes: /* Notes:
* - In case of mono-loop smooth fan, we have nothing to do. * - In case of mono-loop smooth fan, we have nothing to do.
* - Loops in this linklist are ordered (in reversed order compared to how they were * - Loops in this linklist are ordered (in reversed order compared to how they were
@ -1537,8 +1554,7 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
* - In smooth fan case, we compare each clnor against a ref one, * - In smooth fan case, we compare each clnor against a ref one,
* to avoid small differences adding up into a real big one in the end! * to avoid small differences adding up into a real big one in the end!
*/ */
int loop_link = lnors_spacearr.corner_group_lists[i]; if (space_corners.is_empty()) {
if (loop_link == -1) {
done_loops[i].set(); done_loops[i].set();
continue; continue;
} }
@ -1546,8 +1562,7 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
int prev_corner = -1; int prev_corner = -1;
const float *org_nor = nullptr; const float *org_nor = nullptr;
while (loop_link != -1) { for (const int lidx : space_corners) {
const int lidx = loop_link;
float *nor = r_custom_loop_normals[lidx]; float *nor = r_custom_loop_normals[lidx];
if (!org_nor) { if (!org_nor) {
@ -1569,7 +1584,6 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
} }
prev_corner = lidx; prev_corner = lidx;
loop_link = lnors_spacearr.corner_group_lists[loop_link];
done_loops[lidx].set(); done_loops[lidx].set();
} }
@ -1612,7 +1626,10 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
loop_normals); loop_normals);
} }
Vector<int> processed_corners; Array<int> space_corner_offset_indices;
Array<int> space_corner_indices;
reverse_space_index_array(lnors_spacearr, space_corner_offset_indices, space_corner_indices);
const OffsetIndices<int> space_corner_offsets(space_corner_offset_indices);
/* And we just have to convert plain object-space custom normals to our /* And we just have to convert plain object-space custom normals to our
* lnor space-encoded ones. */ * lnor space-encoded ones. */
@ -1626,6 +1643,8 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
continue; continue;
} }
const int space_index = lnors_spacearr.corner_space_indices[i];
if (done_loops[i]) { if (done_loops[i]) {
/* Note we accumulate and average all custom normals in current smooth fan, /* Note we accumulate and average all custom normals in current smooth fan,
* to avoid getting different clnors data (tiny differences in plain custom normals can * to avoid getting different clnors data (tiny differences in plain custom normals can
@ -1650,7 +1669,6 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
avg_nor_count++; avg_nor_count++;
add_v3_v3(avg_nor, nor); add_v3_v3(avg_nor, nor);
processed_corners.append(lidx);
loop_link = lnors_spacearr.corner_group_lists[loop_link]; loop_link = lnors_spacearr.corner_group_lists[loop_link];
done_loops[lidx].reset(); done_loops[lidx].reset();
@ -1658,7 +1676,6 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
mul_v3_fl(avg_nor, 1.0f / float(avg_nor_count)); mul_v3_fl(avg_nor, 1.0f / float(avg_nor_count));
short2 clnor_data_tmp; short2 clnor_data_tmp;
const int space_index = lnors_spacearr.corner_space_indices[i];
fan_space_custom_normal_to_data( fan_space_custom_normal_to_data(
&lnors_spacearr.spaces[space_index], loop_normals[i], avg_nor, clnor_data_tmp); &lnors_spacearr.spaces[space_index], loop_normals[i], avg_nor, clnor_data_tmp);