Blender  V2.93
mesh_validate.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
21 #include "DNA_mesh_types.h"
22 #include "DNA_meshdata_types.h"
23 #include "DNA_object_types.h"
24 
25 #include "BLI_edgehash.h"
26 #include "BLI_map.hh"
27 #include "BLI_math_base.h"
28 #include "BLI_task.hh"
29 #include "BLI_threads.h"
30 #include "BLI_timeit.hh"
31 
32 #include "BKE_customdata.h"
33 #include "BKE_mesh.h"
34 
36 
38 struct OrderedEdge {
39  int v_low, v_high;
40 
41  OrderedEdge(const int v1, const int v2)
42  {
43  if (v1 < v2) {
44  v_low = v1;
45  v_high = v2;
46  }
47  else {
48  v_low = v2;
49  v_high = v1;
50  }
51  }
52 
53  OrderedEdge(const uint v1, const uint v2)
54  : OrderedEdge(static_cast<int>(v1), static_cast<int>(v2))
55  {
56  }
57 
58  uint64_t hash() const
59  {
60  return (this->v_low << 8) ^ this->v_high;
61  }
62 
65  uint64_t hash2() const
66  {
67  return this->v_low;
68  }
69 
70  friend bool operator==(const OrderedEdge &e1, const OrderedEdge &e2)
71  {
72  BLI_assert(e1.v_low < e1.v_high);
73  BLI_assert(e2.v_low < e2.v_high);
74  return e1.v_low == e2.v_low && e1.v_high == e2.v_high;
75  }
76 };
77 
78 /* The map first contains an edge pointer and later an index. */
81  int index;
82 };
84 
85 static void reserve_hash_maps(const Mesh *mesh,
86  const bool keep_existing_edges,
87  MutableSpan<EdgeMap> edge_maps)
88 {
89  const int totedge_guess = std::max(keep_existing_edges ? mesh->totedge : 0, mesh->totpoly * 2);
91  edge_maps, [&](EdgeMap &edge_map) { edge_map.reserve(totedge_guess / edge_maps.size()); });
92 }
93 
95  MutableSpan<EdgeMap> edge_maps,
96  uint32_t parallel_mask)
97 {
98  /* Assume existing edges are valid. */
99  parallel_for_each(edge_maps, [&](EdgeMap &edge_map) {
100  const int task_index = &edge_map - &edge_maps[0];
101  for (const MEdge &edge : Span(mesh->medge, mesh->totedge)) {
102  OrderedEdge ordered_edge{edge.v1, edge.v2};
103  /* Only add the edge when it belongs into this map. */
104  if (task_index == (parallel_mask & ordered_edge.hash2())) {
105  edge_map.add_new(ordered_edge, {&edge});
106  }
107  }
108  });
109 }
110 
112  MutableSpan<EdgeMap> edge_maps,
113  uint32_t parallel_mask)
114 {
115  const Span<MLoop> loops{mesh->mloop, mesh->totloop};
116  parallel_for_each(edge_maps, [&](EdgeMap &edge_map) {
117  const int task_index = &edge_map - &edge_maps[0];
118  for (const MPoly &poly : Span(mesh->mpoly, mesh->totpoly)) {
119  Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
120  const MLoop *prev_loop = &poly_loops.last();
121  for (const MLoop &next_loop : poly_loops) {
122  /* Can only be the same when the mesh data is invalid. */
123  if (prev_loop->v != next_loop.v) {
124  OrderedEdge ordered_edge{prev_loop->v, next_loop.v};
125  /* Only add the edge when it belongs into this map. */
126  if (task_index == (parallel_mask & ordered_edge.hash2())) {
127  edge_map.lookup_or_add(ordered_edge, {nullptr});
128  }
129  }
130  prev_loop = &next_loop;
131  }
132  }
133  });
134 }
135 
137  MutableSpan<MEdge> new_edges,
138  short new_edge_flag)
139 {
140  /* All edges are distributed in the hash tables now. They have to be serialized into a single
141  * array below. To be able to parallelize this, we have to compute edge index offsets for each
142  * map. */
143  Array<int> edge_index_offsets(edge_maps.size());
144  edge_index_offsets[0] = 0;
145  for (const int i : IndexRange(edge_maps.size() - 1)) {
146  edge_index_offsets[i + 1] = edge_index_offsets[i] + edge_maps[i].size();
147  }
148 
149  parallel_for_each(edge_maps, [&](EdgeMap &edge_map) {
150  const int task_index = &edge_map - &edge_maps[0];
151 
152  int new_edge_index = edge_index_offsets[task_index];
153  for (EdgeMap::MutableItem item : edge_map.items()) {
154  MEdge &new_edge = new_edges[new_edge_index];
155  const MEdge *orig_edge = item.value.original_edge;
156  if (orig_edge != nullptr) {
157  /* Copy values from original edge. */
158  new_edge = *orig_edge;
159  }
160  else {
161  /* Initialize new edge. */
162  new_edge.v1 = item.key.v_low;
163  new_edge.v2 = item.key.v_high;
164  new_edge.flag = new_edge_flag;
165  }
166  item.value.index = new_edge_index;
167  new_edge_index++;
168  }
169  });
170 }
171 
173  Span<EdgeMap> edge_maps,
174  uint32_t parallel_mask)
175 {
176  const MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
177  parallel_for(IndexRange(mesh->totpoly), 100, [&](IndexRange range) {
178  for (const int poly_index : range) {
179  MPoly &poly = mesh->mpoly[poly_index];
180  MutableSpan<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
181 
182  MLoop *prev_loop = &poly_loops.last();
183  for (MLoop &next_loop : poly_loops) {
184  int edge_index;
185  if (prev_loop->v != next_loop.v) {
186  OrderedEdge ordered_edge{prev_loop->v, next_loop.v};
187  /* Double lookup: First find the map that contains the edge, then lookup the edge. */
188  const EdgeMap &edge_map = edge_maps[parallel_mask & ordered_edge.hash2()];
189  edge_index = edge_map.lookup(ordered_edge).index;
190  }
191  else {
192  /* This is an invalid edge; normally this does not happen in Blender,
193  * but it can be part of an imported mesh with invalid geometry. See
194  * T76514. */
195  edge_index = 0;
196  }
197  prev_loop->e = edge_index;
198  prev_loop = &next_loop;
199  }
200  }
201  });
202 }
203 
205 {
206  /* Don't use parallelization when the mesh is small. */
207  if (mesh->totpoly < 1000) {
208  return 1;
209  }
210  /* Use at most 8 separate hash tables. Using more threads has diminishing returns. These threads
211  * can better do something more useful instead. */
212  const int system_thread_count = BLI_system_thread_count();
213  return power_of_2_min_i(std::min(8, system_thread_count));
214 }
215 
217 {
218  parallel_for_each(edge_maps, [](EdgeMap &edge_map) { edge_map.clear(); });
219 }
220 
221 } // namespace blender::bke::calc_edges
222 
226 void BKE_mesh_calc_edges(Mesh *mesh, bool keep_existing_edges, const bool select_new_edges)
227 {
228  using namespace blender;
229  using namespace blender::bke;
230  using namespace blender::bke::calc_edges;
231 
232  /* Parallelization is achieved by having multiple hash tables for different subsets of edges.
233  * Each edge is assigned to one of the hash maps based on the lower bits of a hash value. */
234  const int parallel_maps = get_parallel_maps_count(mesh);
235  BLI_assert(is_power_of_2_i(parallel_maps));
236  const uint32_t parallel_mask = static_cast<uint32_t>(parallel_maps) - 1;
237  Array<EdgeMap> edge_maps(parallel_maps);
238  reserve_hash_maps(mesh, keep_existing_edges, edge_maps);
239 
240  /* Add all edges. */
241  if (keep_existing_edges) {
242  calc_edges::add_existing_edges_to_hash_maps(mesh, edge_maps, parallel_mask);
243  }
244  calc_edges::add_polygon_edges_to_hash_maps(mesh, edge_maps, parallel_mask);
245 
246  /* Compute total number of edges. */
247  int new_totedge = 0;
248  for (EdgeMap &edge_map : edge_maps) {
249  new_totedge += edge_map.size();
250  }
251 
252  /* Create new edges. */
253  MutableSpan<MEdge> new_edges{
254  static_cast<MEdge *>(MEM_calloc_arrayN(new_totedge, sizeof(MEdge), __func__)), new_totedge};
255  const short new_edge_flag = (ME_EDGEDRAW | ME_EDGERENDER) | (select_new_edges ? SELECT : 0);
256  calc_edges::serialize_and_initialize_deduplicated_edges(edge_maps, new_edges, new_edge_flag);
257  calc_edges::update_edge_indices_in_poly_loops(mesh, edge_maps, parallel_mask);
258 
259  /* Free old CustomData and assign new one. */
260  CustomData_free(&mesh->edata, mesh->totedge);
261  CustomData_reset(&mesh->edata);
262  CustomData_add_layer(&mesh->edata, CD_MEDGE, CD_ASSIGN, new_edges.data(), new_totedge);
263  mesh->totedge = new_totedge;
264  mesh->medge = new_edges.data();
265 
266  /* Explicitly clear edge maps, because that way it can be parallelized. */
267  clear_hash_tables(edge_maps);
268 }
CustomData interface, see also DNA_customdata_types.h.
void CustomData_free(struct CustomData *data, int totelem)
Definition: customdata.c:2239
@ CD_ASSIGN
void * CustomData_add_layer(struct CustomData *data, int type, eCDAllocType alloctype, void *layer, int totelem)
Definition: customdata.c:2620
void CustomData_reset(struct CustomData *data)
Definition: customdata.c:2233
#define BLI_assert(a)
Definition: BLI_assert.h:58
MINLINE int power_of_2_min_i(int n)
MINLINE int is_power_of_2_i(int n)
unsigned int uint
Definition: BLI_sys_types.h:83
int BLI_system_thread_count(void)
Definition: threads.cc:309
@ ME_EDGEDRAW
@ ME_EDGERENDER
Object is a sort of wrapper for general info.
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
ATTR_WARN_UNUSED_RESULT const BMVert * v2
void clear()
Definition: BLI_map.hh:916
ItemIterator items() const
Definition: BLI_map.hh:825
void reserve(int64_t n)
Definition: BLI_map.hh:906
constexpr int64_t size() const
Definition: BLI_span.hh:524
#define SELECT
void *(* MEM_calloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:46
void BKE_mesh_calc_edges(Mesh *mesh, bool keep_existing_edges, const bool select_new_edges)
static int get_parallel_maps_count(const Mesh *mesh)
static void add_polygon_edges_to_hash_maps(Mesh *mesh, MutableSpan< EdgeMap > edge_maps, uint32_t parallel_mask)
static void update_edge_indices_in_poly_loops(Mesh *mesh, Span< EdgeMap > edge_maps, uint32_t parallel_mask)
static void serialize_and_initialize_deduplicated_edges(MutableSpan< EdgeMap > edge_maps, MutableSpan< MEdge > new_edges, short new_edge_flag)
static void reserve_hash_maps(const Mesh *mesh, const bool keep_existing_edges, MutableSpan< EdgeMap > edge_maps)
static void clear_hash_tables(MutableSpan< EdgeMap > edge_maps)
static void add_existing_edges_to_hash_maps(Mesh *mesh, MutableSpan< EdgeMap > edge_maps, uint32_t parallel_mask)
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:62
void parallel_for_each(Range &range, const Function &function)
Definition: BLI_task.hh:50
#define min(a, b)
Definition: sort.c:51
unsigned int uint32_t
Definition: stdint.h:83
unsigned __int64 uint64_t
Definition: stdint.h:93
struct MEdge * medge
int totedge
struct MLoop * mloop
int totpoly
int totloop
struct MPoly * mpoly
OrderedEdge(const uint v1, const uint v2)
OrderedEdge(const int v1, const int v2)
friend bool operator==(const OrderedEdge &e1, const OrderedEdge &e2)
float max