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
4 changed files with 25 additions and 25 deletions
Showing only changes of commit e4c4520eac - Show all commits

View File

@ -90,7 +90,7 @@ void normals_calc_poly_vert(Span<float3> vert_positions,
* coordinate space used to convert normals between the "custom normal" #short2 representation and
* a regular #float3 format.
*/
struct NormalFanSpace {
struct CornerNormalSpace {
/** Reference vector, orthogonal to corner normal. */
float3 vec_ref;
/** Third vector, orthogonal to corner normal and #vec_ref. */
@ -102,22 +102,22 @@ struct NormalFanSpace {
};

Worth mentioning some things that might seem obvious, that this stores normal data for an entire mesh.

Worth mentioning some things that might seem obvious, that this stores normal data for an entire mesh.
/**
* Storage for coordinate spaces and connection information during normal calculation.
* Storage for corner fan coordinate spaces for an entire mesh.

The names NormalFanSpaces & NormalFanSpace are too similar, the old suffix "Array" wasn't exactly accurate either (as it's not just an array) but at least it was easily distinguishable.

The names `NormalFanSpaces` & `NormalFanSpace` are too similar, the old suffix "Array" wasn't exactly accurate either (as it's not just an array) but at least it was easily distinguishable.
*/
struct NormalFanSpaces {
struct CornerNormalSpaceArray {
/**
* The normal coordinate spaces, potentially shared between multiple face corners in a smooth fan
* connected to a vertex. Depending on the mesh (the amount of sharing / number of sharp edges /
* size of each fan), there may be many fewer spaces than face corners, so they are stored in a
* separate array.
* connected to a vertex (and not per face corner). Depending on the mesh (the amount of sharing
* / number of sharp edges / size of each fan), there may be many fewer spaces than face corners,
* so they are stored in a separate array.

Worth mentioning this array isn't aligned to geometry and must be accessed via corner_space_indices.

Worth mentioning this array isn't aligned to geometry and must be accessed via `corner_space_indices`.
*/
Array<NormalFanSpace> spaces;
Array<CornerNormalSpace> spaces;
Review

Prefer to keep the term loop here as a corner isn't obviously aligned with the a mesh loops. e.g. loop_space_indices

Doc-string should mention -1 are used to references loops without a reference to a space-array.

Prefer to keep the term `loop` here as a corner isn't obviously aligned with the a mesh loops. e.g. `loop_space_indices` Doc-string should mention `-1` are used to references loops without a reference to a space-array.
Review

Moving away from the term "loop" in general applies here too IMO, the values here are per face corner.

Moving away from the term "loop" in general applies here too IMO, the values here are per face corner.
Review

In this case the doc-string should note that this array is aligned with a meshes loops (even if logically it's a face corner the index is a loop index. When reading new code with fresh eyes - lookup tables should not clearly what indexes them and what the they index into (CornerNormalSpaceArray::spaces in this case). The term loop index is currently a meaningful term which communicates alignment).

In this case the doc-string should note that this array is aligned with a meshes loops (even if logically it's a face corner the index is a loop index. When reading new code with fresh eyes - lookup tables should not clearly what indexes them and what the they index into (`CornerNormalSpaceArray::spaces` in this case). The term `loop index` is currently a meaningful term which communicates alignment).
Review

Noting that these values are for each face corner isn't explicitly stating this is aligned with mesh loops which I think its worth noting.

That -1 is used to define values as not yet having an entry in spaces (worth noting too), are there actually cases these values will be left at -1 once CornerNormalSpaceArray is initialized? If so mention under what condition that happens.

Noting that these values are for each face corner isn't explicitly stating this is aligned with mesh loops which I think its worth noting. That -1 is used to define values as not yet having an entry in `spaces` (worth noting too), are there actually cases these values will be left at -1 once `CornerNormalSpaceArray` is initialized? If so mention under what condition that happens.
Review

To be honest, your use of "aligned with" is a bit confusing to me. It makes me think of memory address alignment, which is a different thing. But I'll make it more clear.

That -1 is used to define values as uninitialized (worth noting too), are there actually cases these values will be left at -1

I don't know to be honest. Some existing comments mention that, but it seems a bit paranoid and maybe worth fixing if it happens? Either way, I'm still working on understanding the whole algorithm, so I'm not able to add more details yet.

To be honest, your use of "aligned with" is a bit confusing to me. It makes me think of memory address alignment, which is a different thing. But I'll make it more clear. >That -1 is used to define values as uninitialized (worth noting too), are there actually cases these values will be left at -1 I don't know to be honest. Some existing comments mention that, but it seems a bit paranoid and maybe worth fixing if it happens? Either way, I'm still working on understanding the whole algorithm, so I'm not able to add more details yet.
Review

It seems likely they're never -1 and this is just used to check initialization, since this is updating the existing comment, seems fine to keep as-is.

It seems likely they're never -1 and this is just used to check initialization, since this is updating the existing comment, seems fine to keep as-is.
Review

To be honest, your use of "aligned with" is a bit confusing to me. It makes me think of memory address alignment, which is a different thing. But I'll make it more clear.

The exact working I'm not so fussed about although I think it's reasonable to say the MDeformVert array is aligned with the vert_positions (for example), in this case I think it's not talking about memory alignment.
Whatever the case, geometry indices might apply to vertices, edges, polygons, loops, looptris... I think it's reasonable that this is always clear if not from the name then the doc-string.

That -1 is used to define values as uninitialized (worth noting too), are there actually cases these values will be left at -1

I don't know to be honest. Some existing comments mention that, but it seems a bit paranoid and maybe worth fixing if it happens? Either way, I'm still working on understanding the whole algorithm, so I'm not able to add more details yet.

Ah, in that case removing the comment or expanding on it is outside the scope of the patch. It seems possible they're never -1 and this is just used to check initialization.

> To be honest, your use of "aligned with" is a bit confusing to me. It makes me think of memory address alignment, which is a different thing. But I'll make it more clear. The exact working I'm not so fussed about although I think it's reasonable to say the `MDeformVert` array is aligned with the `vert_positions` (for example), in this case I think it's not talking about memory alignment. Whatever the case, geometry indices might apply to vertices, edges, polygons, loops, looptris... I think it's reasonable that this is always clear if not from the name then the doc-string. > >That -1 is used to define values as uninitialized (worth noting too), are there actually cases these values will be left at -1 > > I don't know to be honest. Some existing comments mention that, but it seems a bit paranoid and maybe worth fixing if it happens? Either way, I'm still working on understanding the whole algorithm, so I'm not able to add more details yet. Ah, in that case removing the comment or expanding on it is outside the scope of the patch. It seems possible they're never -1 and this is just used to check initialization.
/** The index of the data in the #spaces array for each face corner. */
Array<int> corner_space_indices;
};

Prefer naming is based on the public function BKE_lnor_space_custom_normal_to_data (e.g. lnor_space_custom_normal_to_data_impl).

Prefer naming is based on the public function `BKE_lnor_space_custom_normal_to_data` (e.g. `lnor_space_custom_normal_to_data_impl`).

Longer term the public functions would be replaced, there was just no need to expose these in BKE_mesh.hh yet. But that's fine with me for now.

Longer term the public functions would be replaced, there was just no need to expose these in `BKE_mesh.hh` yet. But that's fine with me for now.
void fan_space_custom_normal_to_data(const NormalFanSpace *lnor_space,
void fan_space_custom_normal_to_data(const CornerNormalSpace *lnor_space,
const float3 lnor_no_custom,
const float custom_lnor[3],
short r_clnor_data[2]);

The naming here introduces "fan_space_custom_normal" which reads as if it might be different from CornerNormalSpace (as if there might be a "fan space" and a "corner normal space").

This could be more clearly names that it's a direct conversion from CornerNormalSpace to CD_CUSTOMLOOPNORMAL.

e.g. corner_normal_space_to_clnor.

The naming here introduces "fan_space_custom_normal" which reads as if it might be different from `CornerNormalSpace` (as if there might be a _"fan space"_ and a _"corner normal space"_). This could be more clearly names that it's a direct conversion from `CornerNormalSpace` to `CD_CUSTOMLOOPNORMAL`. e.g. `corner_normal_space_to_clnor`.

Ah, sorry, I meant to rename that and keep the same name as before like you suggested.

Ah, sorry, I meant to rename that and keep the same name as before like you suggested.
@ -146,7 +146,7 @@ void normals_calc_loop(Span<float3> vert_positions,
bool use_split_normals,
float split_angle,
short2 *clnors_data,
NormalFanSpaces *r_lnors_spacearr,
CornerNormalSpaceArray *r_lnors_spacearr,
MutableSpan<float3> r_loop_normals);
void normals_loop_custom_set(Span<float3> vert_positions,

View File

@ -390,7 +390,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
CustomData *ldata = &result->ldata;
blender::short2 *clnors = static_cast<blender::short2 *>(
CustomData_get_layer_for_write(ldata, CD_CUSTOMLOOPNORMAL, result->totloop));
blender::bke::mesh::NormalFanSpaces lnors_spacearr;
blender::bke::mesh::CornerNormalSpaceArray lnors_spacearr;
/* The transform matrix of a normal must be
* the transpose of inverse of transform matrix of the geometry... */

View File

@ -468,7 +468,7 @@ MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr)
#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f)
namespace blender::bke::mesh {
static void normal_fan_space_define(NormalFanSpace *lnor_space,
static void normal_fan_space_define(CornerNormalSpace *lnor_space,
const float lnor[3],
float vec_ref[3],
float vec_other[3],
@ -540,7 +540,7 @@ void BKE_lnor_space_define(MLoopNorSpace *lnor_space,
const blender::Span<blender::float3> edge_vectors)
{
using namespace blender::bke::mesh;
NormalFanSpace space{};
CornerNormalSpace space{};
normal_fan_space_define(&space, lnor, vec_ref, vec_other, edge_vectors);
copy_v3_v3(lnor_space->vec_lnor, lnor);
copy_v3_v3(lnor_space->vec_ref, space.vec_ref);
@ -585,7 +585,7 @@ MINLINE short unit_float_to_short(const float val)
}
namespace blender::bke::mesh {
static void fan_space_custom_data_to_normal(const NormalFanSpace *lnor_space,
static void fan_space_custom_data_to_normal(const CornerNormalSpace *lnor_space,

Prefer old name (can remove the BKE prefix).

Prefer old name (can remove the BKE prefix).
const float3 lnor_no_custom,
const short clnor_data[2],
float r_custom_lnor[3])
@ -626,7 +626,7 @@ void BKE_lnor_space_custom_data_to_normal(const MLoopNorSpace *lnor_space,
float r_custom_lnor[3])
{
using namespace blender::bke::mesh;
NormalFanSpace space;
CornerNormalSpace space;
space.vec_ref = lnor_space->vec_ref;
space.vec_ortho = lnor_space->vec_ortho;
space.ref_alpha = lnor_space->ref_alpha;
@ -635,7 +635,7 @@ void BKE_lnor_space_custom_data_to_normal(const MLoopNorSpace *lnor_space,
}
namespace blender::bke::mesh {
void fan_space_custom_normal_to_data(const NormalFanSpace *lnor_space,
void fan_space_custom_normal_to_data(const CornerNormalSpace *lnor_space,
const float3 lnor_no_custom,
const float custom_lnor[3],
short r_clnor_data[2])
@ -695,7 +695,7 @@ void BKE_lnor_space_custom_normal_to_data(const MLoopNorSpace *lnor_space,
short r_clnor_data[2])
{
using namespace blender::bke::mesh;
NormalFanSpace space;
CornerNormalSpace space;
space.vec_ref = lnor_space->vec_ref;
space.vec_ortho = lnor_space->vec_ortho;
space.ref_alpha = lnor_space->ref_alpha;
@ -709,7 +709,7 @@ struct LoopSplitTaskDataCommon {
/* Read/write.
* Note we do not need to protect it, though, since two different tasks will *always* affect
* different elements in the arrays. */
NormalFanSpaces *lnors_spacearr;
CornerNormalSpaceArray *lnors_spacearr;
MutableSpan<float3> loop_normals;
MutableSpan<short2> clnors_data;
@ -889,7 +889,7 @@ static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data,
loop_normals[ml_curr_index] = poly_normals[loop_to_poly[ml_curr_index]];
if (NormalFanSpaces *lnors_spacearr = common_data->lnors_spacearr) {
if (CornerNormalSpaceArray *lnors_spacearr = common_data->lnors_spacearr) {
const Span<float3> positions = common_data->positions;
const Span<int2> edges = common_data->edges;
const OffsetIndices polys = common_data->polys;
@ -912,7 +912,7 @@ static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data,
sub_v3_v3v3(vec_prev, positions[vert_3], positions[vert_pivot]);
normalize_v3(vec_prev);
NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index];
CornerNormalSpace *lnor_space = &lnors_spacearr->spaces[space_index];
normal_fan_space_define(lnor_space, loop_normals[ml_curr_index], vec_curr, vec_prev, {});
lnors_spacearr->corner_space_indices[ml_curr_index] = space_index;
@ -930,7 +930,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
const int space_index,
Vector<float3> *edge_vectors)
{
NormalFanSpaces *lnors_spacearr = common_data->lnors_spacearr;
CornerNormalSpaceArray *lnors_spacearr = common_data->lnors_spacearr;
MutableSpan<float3> loop_normals = common_data->loop_normals;
MutableSpan<short2> clnors_data = common_data->clnors_data;
@ -1072,7 +1072,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
length = 1.0f;
}
NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index];
CornerNormalSpace *lnor_space = &lnors_spacearr->spaces[space_index];
normal_fan_space_define(lnor_space, lnor, vec_org, vec_curr, *edge_vectors);
lnors_spacearr->corner_space_indices.as_mutable_span().fill_indices(
processed_corners.as_span(), space_index);
@ -1270,7 +1270,7 @@ void normals_calc_loop(const Span<float3> vert_positions,
bool use_split_normals,
float split_angle,
short2 *clnors_data,
NormalFanSpaces *r_lnors_spacearr,
CornerNormalSpaceArray *r_lnors_spacearr,
MutableSpan<float3> r_loop_normals)
{
/* For now this is not supported.
@ -1328,7 +1328,7 @@ void normals_calc_loop(const Span<float3> vert_positions,
/* When using custom loop normals, disable the angle feature! */
const bool check_angle = (split_angle < float(M_PI)) && (clnors_data == nullptr);
NormalFanSpaces _lnors_spacearr;
CornerNormalSpaceArray _lnors_spacearr;
#ifdef DEBUG_TIME
SCOPED_TIMER_AVERAGED(__func__);
@ -1457,7 +1457,7 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
* function *is not* performance-critical, since it is mostly expected to be called by io add-ons
* when importing custom normals, and modifier (and perhaps from some editing tools later?). So
* better to keep some simplicity here, and just call #bke::mesh::normals_calc_loop() twice! */
NormalFanSpaces lnors_spacearr;
CornerNormalSpaceArray lnors_spacearr;
BitVector<> done_loops(corner_verts.size(), false);
Array<float3> loop_normals(corner_verts.size());
const Array<int> loop_to_poly = mesh_topology::build_loop_to_poly_map(polys);

View File

@ -205,7 +205,7 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd,
const bool has_clnors = wn_data->has_clnors;
const float split_angle = wn_data->split_angle;
bke::mesh::NormalFanSpaces lnors_spacearr;
bke::mesh::CornerNormalSpaceArray lnors_spacearr;
const bool keep_sharp = (wnmd->flag & MOD_WEIGHTEDNORMAL_KEEP_SHARP) != 0;
const bool use_face_influence = (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) != 0 &&