Mesh: Cache loose vertices #105567

Merged
Hans Goudey merged 29 commits from HooglyBoogly/blender:mesh-loose-vert-cache into main 2023-04-22 13:46:23 +02:00
11 changed files with 83 additions and 106 deletions
Showing only changes of commit 41de436fcb - Show all commits

View File

@ -176,8 +176,8 @@ struct MeshRuntime {
* unchanged topology. Accessed with #Mesh::loose_edges()/loose_verts().
*/
SharedCache<LooseEdgeCache> loose_edges_cache;
SharedCache<LooseVertCache> loose_verts_no_edge_cache;
SharedCache<LooseVertCache> loose_verts_no_face_cache;
SharedCache<LooseVertCache> loose_verts_cache;
SharedCache<LooseVertCache> verts_no_face_cache;
/**
* A bit vector the size of the number of vertices, set to true for the center vertices of

View File

@ -1213,7 +1213,7 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
switch (bvh_cache_type) {
case BVHTREE_FROM_LOOSEVERTS: {
const blender::bke::LooseVertCache &loose_verts = mesh->loose_verts_no_edge();
const blender::bke::LooseVertCache &loose_verts = mesh->loose_verts();
data->tree = bvhtree_from_mesh_verts_create_tree(0.0f,
tree_type,
6,

View File

@ -237,7 +237,8 @@ struct ResultOffsets {
Array<int> profile_indices;
/** Whether any curve in the profile or curve input has only a single evaluated point. */
bool any_single_point_curve;
bool any_single_point_main;
bool any_single_point_profile;
};
static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool fill_caps)
{
@ -315,10 +316,8 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool
}
}
},
[&]() {
result.any_single_point_curve = offsets_contain_single_point(main_offsets) ||
offsets_contain_single_point(profile_offsets);
});
[&]() { result.any_single_point_main = offsets_contain_single_point(main_offsets); },
[&]() { result.any_single_point_profile = offsets_contain_single_point(profile_offsets); });
return result;
}
@ -765,10 +764,13 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
positions.slice(info.vert_range));
});
if (!offsets.any_single_point_curve) {
/* If there are no single point curves, every curve combination will always have faces. */
if (!offsets.any_single_point_main) {
/* If there are no single point curves, every combination will have at least loose edges. */
mesh->tag_loose_verts_none();
mesh->loose_edges_tag_none();
if (!offsets.any_single_point_profile) {
/* If there are no single point profiles, every combination will have faces. */
mesh->loose_edges_tag_none();
}
}
SpanAttributeWriter<bool> sharp_edges;

View File

@ -200,7 +200,7 @@ void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
}
/* Deselect loose vertices without corners that are still selected from the 'true' default. */
const bke::LooseVertCache &loose_verts = mesh.loose_verts_no_face();
const bke::LooseVertCache &loose_verts = mesh.verts_no_face();
if (loose_verts.count > 0) {
const BitSpan loose = loose_verts.is_loose_bits;
threading::parallel_for(loose.index_range(), 2048, [loose, r_values](const IndexRange range) {
@ -753,12 +753,12 @@ static bool can_simple_adapt_for_single(const Mesh &mesh,
return true;
case ATTR_DOMAIN_EDGE:
if (to_domain == ATTR_DOMAIN_POINT) {
return mesh.loose_verts_no_edge().count == 0;
return mesh.loose_verts().count == 0;
}
return true;
case ATTR_DOMAIN_FACE:
if (to_domain == ATTR_DOMAIN_POINT) {
return mesh.loose_verts_no_face().count == 0;
return mesh.verts_no_face().count == 0;
}
if (to_domain == ATTR_DOMAIN_EDGE) {
return mesh.loose_edges().count == 0;
@ -766,7 +766,7 @@ static bool can_simple_adapt_for_single(const Mesh &mesh,
return true;
case ATTR_DOMAIN_CORNER:
if (to_domain == ATTR_DOMAIN_POINT) {
return mesh.loose_verts_no_face().count == 0;
return mesh.verts_no_face().count == 0;
}
if (to_domain == ATTR_DOMAIN_EDGE) {
return mesh.loose_edges().count == 0;

View File

@ -130,8 +130,8 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
* when the source is persistent and edits to the destination mesh don't affect the caches.
* Caches will be "un-shared" as necessary later on. */
mesh_dst->runtime->bounds_cache = mesh_src->runtime->bounds_cache;
mesh_dst->runtime->loose_verts_no_edge_cache = mesh_src->runtime->loose_verts_no_edge_cache;
mesh_dst->runtime->loose_verts_no_face_cache = mesh_src->runtime->loose_verts_no_face_cache;
mesh_dst->runtime->loose_verts_cache = mesh_src->runtime->loose_verts_cache;
mesh_dst->runtime->verts_no_face_cache = mesh_src->runtime->verts_no_face_cache;
mesh_dst->runtime->loose_edges_cache = mesh_src->runtime->loose_edges_cache;
mesh_dst->runtime->looptris_cache = mesh_src->runtime->looptris_cache;

View File

@ -106,54 +106,50 @@ MeshRuntime::~MeshRuntime()
}
}
} // namespace blender::bke
const blender::bke::LooseVertCache &Mesh::loose_verts_no_edge() const
static int reset_bits_and_count(const Span<int> indices_to_reset, MutableBitSpan bits)
{
using namespace blender::bke;
this->runtime->loose_verts_no_edge_cache.ensure([&](LooseVertCache &r_data) {
blender::BitVector<> &loose_verts = r_data.is_loose_bits;
loose_verts.resize(0);
loose_verts.resize(this->totvert, true);
int count = this->totvert;
for (const int vert : this->edges().cast<int>()) {
if (loose_verts[vert]) {
loose_verts[vert].reset();
count--;
}
int count = bits.size();
for (const int vert : indices_to_reset) {
if (bits[vert]) {
bits[vert].reset();
count--;
}
if (count == 0) {
loose_verts.clear_and_shrink();
}
r_data.count = count;
});
return this->runtime->loose_verts_no_edge_cache.data();
}
return count;
}
const blender::bke::LooseVertCache &Mesh::loose_verts_no_face() const
} // namespace blender::bke
const blender::bke::LooseVertCache &Mesh::loose_verts() const
{
using namespace blender::bke;
this->runtime->loose_verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
this->runtime->loose_verts_cache.ensure([&](LooseVertCache &r_data) {
blender::BitVector<> &loose_verts = r_data.is_loose_bits;
loose_verts.resize(0);
loose_verts.resize(this->totvert, true);
int count = this->totvert;
for (const int vert : this->corner_verts()) {
if (loose_verts[vert]) {
loose_verts[vert].reset();
count--;
}
}
if (count == 0) {
r_data.count = reset_bits_and_count(this->edges().cast<int>(), loose_verts);
if (r_data.count == 0) {
loose_verts.clear_and_shrink();
}
r_data.count = count;
});
return this->runtime->loose_verts_no_face_cache.data();
return this->runtime->loose_verts_cache.data();
}
const blender::bke::LooseVertCache &Mesh::verts_no_face() const
{
using namespace blender::bke;
this->runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
blender::BitVector<> &loose_verts = r_data.is_loose_bits;
loose_verts.resize(0);
loose_verts.resize(this->totvert, true);
r_data.count = reset_bits_and_count(this->corner_verts(), loose_verts);
if (r_data.count == 0) {
loose_verts.clear_and_shrink();
}
});
return this->runtime->verts_no_face_cache.data();
}
const blender::bke::LooseEdgeCache &Mesh::loose_edges() const
@ -163,43 +159,31 @@ const blender::bke::LooseEdgeCache &Mesh::loose_edges() const
blender::BitVector<> &loose_edges = r_data.is_loose_bits;
loose_edges.resize(0);
loose_edges.resize(this->totedge, true);
int count = this->totedge;
for (const int edge : this->corner_edges()) {
if (loose_edges[edge]) {
loose_edges[edge].reset();
count--;
}
}
if (count == 0) {
r_data.count = reset_bits_and_count(this->corner_edges(), loose_edges);
if (r_data.count == 0) {
loose_edges.clear_and_shrink();
}
r_data.count = count;
});
return this->runtime->loose_edges_cache.data();
}
void Mesh::tag_loose_verts_no_edge_none() const
{
using namespace blender::bke;
this->runtime->loose_verts_no_edge_cache.ensure([&](LooseVertCache &r_data) {
r_data.is_loose_bits.clear_and_shrink();
r_data.count = 0;
});
}
void Mesh::tag_loose_verts_no_face_none() const
{
using namespace blender::bke;
this->runtime->loose_verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
r_data.is_loose_bits.clear_and_shrink();
r_data.count = 0;
});
}
void Mesh::tag_loose_verts_none() const
{
this->tag_loose_verts_no_edge_none();
this->tag_loose_verts_no_face_none();
using namespace blender::bke;
this->runtime->loose_verts_cache.ensure([&](LooseVertCache &r_data) {
r_data.is_loose_bits.clear_and_shrink();
r_data.count = 0;
});
}
void Mesh::tag_verts_no_face_none() const
{
using namespace blender::bke;
this->runtime->verts_no_face_cache.ensure([&](LooseEdgeCache &r_data) {
r_data.is_loose_bits.clear_and_shrink();
r_data.count = 0;
});
}
void Mesh::loose_edges_tag_none() const
@ -289,8 +273,8 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
free_subdiv_ccg(*mesh->runtime);
mesh->runtime->bounds_cache.tag_dirty();
mesh->runtime->loose_edges_cache.tag_dirty();
mesh->runtime->loose_verts_no_edge_cache.tag_dirty();
mesh->runtime->loose_verts_no_face_cache.tag_dirty();
mesh->runtime->loose_verts_cache.tag_dirty();
mesh->runtime->verts_no_face_cache.tag_dirty();
mesh->runtime->looptris_cache.tag_dirty();
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
@ -309,8 +293,8 @@ void BKE_mesh_tag_edges_split(struct Mesh *mesh)
reset_normals(*mesh->runtime);
free_subdiv_ccg(*mesh->runtime);
mesh->runtime->loose_edges_cache.tag_dirty();
mesh->runtime->loose_verts_no_edge_cache.tag_dirty();
mesh->runtime->loose_verts_no_face_cache.tag_dirty();
mesh->runtime->loose_verts_cache.tag_dirty();
mesh->runtime->verts_no_face_cache.tag_dirty();
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
if (mesh->runtime->shrinkwrap_data) {

View File

@ -354,7 +354,7 @@ static void initialize_manifold_indices(ConverterStorage *storage)
{
using namespace blender;
const Mesh *mesh = storage->mesh;
const bke::LooseVertCache &loose_verts = mesh->loose_verts_no_face();
const bke::LooseVertCache &loose_verts = mesh->verts_no_face();
const bke::LooseEdgeCache &loose_edges = mesh->loose_edges();
initialize_manifold_index_array(loose_verts.is_loose_bits,
mesh->totvert,

View File

@ -46,7 +46,7 @@ static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, MeshBuffe
}
}
const bke::LooseVertCache &loose_verts = mr->me->loose_verts_no_edge();
const bke::LooseVertCache &loose_verts = mr->me->loose_verts();
if (loose_verts.count > 0) {
cache->loose_geom.verts.reinitialize(loose_verts.count);

View File

@ -204,7 +204,6 @@ struct AllMeshesInfo {
/** True if we know that there are no loose edges in any of the input meshes. */
bool no_loose_edges_hint = false;
bool no_loose_verts_edge_hint = false;
bool no_loose_verts_face_hint = false;
};
struct AllCurvesInfo {
@ -951,13 +950,7 @@ static AllMeshesInfo preprocess_meshes(const GeometrySet &geometry_set,
});
info.no_loose_verts_edge_hint = std::all_of(
info.order.begin(), info.order.end(), [](const Mesh *mesh) {
return mesh->runtime->loose_verts_no_edge_cache.is_cached() &&
mesh->loose_verts_no_edge().count == 0;
});
info.no_loose_verts_face_hint = std::all_of(
info.order.begin(), info.order.end(), [](const Mesh *mesh) {
return mesh->runtime->loose_verts_no_face_cache.is_cached() &&
mesh->loose_verts_no_face().count == 0;
return mesh->runtime->loose_verts_cache.is_cached() && mesh->loose_verts().count == 0;
});
return info;
@ -1168,10 +1161,7 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
dst_mesh->loose_edges_tag_none();
}
if (all_meshes_info.no_loose_verts_edge_hint) {
dst_mesh->tag_loose_verts_no_edge_none();
}
if (all_meshes_info.no_loose_verts_face_hint) {
dst_mesh->tag_loose_verts_no_face_none();
dst_mesh->tag_loose_verts_none();
}
}

View File

@ -306,11 +306,12 @@ typedef struct Mesh {
/**
* Cached information about vertices that aren't used by any edges.
*/
const blender::bke::LooseVertCache &loose_verts_no_edge() const;
const blender::bke::LooseVertCache &loose_verts() const;
/**
* Cached information about vertices that aren't used by faces (but may be used by loose edges).
* This also contains vertices with no edges from #loose_verts().
*/
const blender::bke::LooseVertCache &loose_verts_no_face() const;
const blender::bke::LooseVertCache &verts_no_face() const;
/**
* Explicitly set the cached number of loose edges to zero. This can improve performance
@ -320,9 +321,12 @@ typedef struct Mesh {
* cache dirty. If the mesh was changed first, the relevant dirty tags should be called first.

It feels a bit like these tag functions are redundant. Shouldn't tag_no_loose_edges and tag_no_loose_verts methods be enough?

It feels a bit like these `tag` functions are redundant. Shouldn't `tag_no_loose_edges` and `tag_no_loose_verts` methods be enough?

Generally they're used in similar situations, but not always. For example, the realize instances node:

  if (all_meshes_info.no_loose_verts_edge_hint) {
    dst_mesh->loose_verts_edge_tag_none();
  }
  if (all_meshes_info.no_loose_verts_face_hint) {
    dst_mesh->loose_verts_face_tag_none();
  }
Generally they're used in similar situations, but not always. For example, the realize instances node: ``` if (all_meshes_info.no_loose_verts_edge_hint) { dst_mesh->loose_verts_edge_tag_none(); } if (all_meshes_info.no_loose_verts_face_hint) { dst_mesh->loose_verts_face_tag_none(); } ```
*/
void loose_edges_tag_none() const;
void tag_loose_verts_no_edge_none() const;
void tag_loose_verts_no_face_none() const;
/**
* Set the number of verices not connected to edges to zero. Similar to #loose_edges_tag_none().
* There may still be vertices only used by loose edges though.
*/
void tag_loose_verts_none() const;
void tag_verts_no_face_none() const;
/**
* Normal direction of polygons, defined by positions and the winding direction of face corners.

View File

@ -153,8 +153,6 @@ static Mesh *create_circle_mesh(const float radius,
std::iota(corner_verts.begin(), corner_verts.end(), 0);
std::iota(corner_edges.begin(), corner_edges.end(), 0);
mesh->tag_loose_verts_none();
}
else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
for (const int i : poly_offsets.index_range()) {
@ -170,10 +168,9 @@ static Mesh *create_circle_mesh(const float radius,
corner_verts[3 * i + 2] = verts_num;
corner_edges[3 * i + 2] = verts_num + i;
}
mesh->tag_loose_verts_no_edge_none();
}
mesh->tag_loose_verts_none();
mesh->bounds_set_eager(calculate_bounds_circle(radius, verts_num));
return mesh;