Blender V4.3
extract_mesh_ibo_lines.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_array_utils.hh"
10
11#include "GPU_index_buffer.hh"
12
13#include "extract_mesh.hh"
14
15#include "draw_cache_inline.hh"
16#include "draw_subdivision.hh"
17
18namespace blender::draw {
19
21 const IndexMask &mask,
22 IndexMaskMemory &memory)
23{
24 IndexMask visible = mask;
25 if (!mr.mesh->runtime->subsurf_optimal_display_edges.is_empty()) {
26 const BoundedBitSpan visible_bits = mr.mesh->runtime->subsurf_optimal_display_edges;
27 visible = IndexMask::from_bits(visible, visible_bits, memory);
28 }
29 if (!mr.hide_edge.is_empty()) {
30 visible = IndexMask::from_bools_inverse(visible, mr.hide_edge, memory);
31 }
32 if (mr.hide_unmapped_edges && mr.orig_index_edge != nullptr) {
33 const int *orig_index = mr.orig_index_edge;
34 visible = IndexMask::from_predicate(visible, GrainSize(4096), memory, [&](const int64_t i) {
35 return orig_index[i] != ORIGINDEX_NONE;
36 });
37 }
38 return visible;
39}
40
41/* In the GPU vertex buffers, the value for each vertex is duplicated to each of its vertex
42 * corners. So the edges on the GPU connect face corners rather than vertices. */
43static uint2 edge_from_corners(const IndexRange face, const int corner)
44{
45 const int corner_next = bke::mesh::face_corner_next(face, corner);
46 return uint2(corner, corner_next);
47}
48
50 const IndexMask &visible,
52{
53 /* Vertices for loose edges are not shared in the GPU vertex buffers, so the indices are simply
54 * an increasing contiguous range. Ideally this would be generated on the GPU itself, or just
55 * unnecessary, but a large number of loose edges isn't expected to be a common performance
56 * bottleneck either. */
57 threading::memory_bandwidth_bound_task(data.size_in_bytes(), [&]() {
58 const uint loose_start = mr.corners_num;
59 if (visible.size() == mr.loose_edges.size()) {
60 array_utils::fill_index_range(data.cast<uint>(), loose_start);
61 }
62 else {
63 visible.foreach_index_optimized<int>(GrainSize(4096), [&](const int i, const int pos) {
64 data[pos] = loose_start + uint2(i * 2 + 0, i * 2 + 1);
65 });
66 }
67 });
68}
69
71{
72 const Span<int> loose_edges = mr.loose_edges;
73 IndexMask visible(loose_edges.size());
74 if (!mr.hide_edge.is_empty()) {
75 const Span<bool> hide_edge = mr.hide_edge;
76 visible = IndexMask::from_predicate(
77 visible, GrainSize(4096), memory, [&](const int i) { return !hide_edge[loose_edges[i]]; });
78 }
79 if (mr.hide_unmapped_edges && mr.orig_index_edge != nullptr) {
80 const int *orig_index = mr.orig_index_edge;
81 visible = IndexMask::from_predicate(visible, GrainSize(4096), memory, [&](const int64_t i) {
82 return orig_index[loose_edges[i]] != ORIGINDEX_NONE;
83 });
84 }
85 return visible;
86}
87
88static void extract_lines_mesh(const MeshRenderData &mr,
89 gpu::IndexBuf *lines,
90 gpu::IndexBuf *lines_loose,
91 bool &no_loose_wire)
92{
93 IndexMaskMemory memory;
94 const IndexMask visible_loose_edges = calc_visible_loose_edge_indices(mr, memory);
95 const int max_index = mr.corners_num + mr.loose_edges.size() * 2;
96
97 no_loose_wire = visible_loose_edges.is_empty();
98
99 if (DRW_ibo_requested(lines_loose) && !DRW_ibo_requested(lines)) {
100 GPUIndexBufBuilder builder;
101 GPU_indexbuf_init(&builder, GPU_PRIM_LINES, visible_loose_edges.size(), max_index);
103 fill_loose_lines_ibo(mr, visible_loose_edges, data);
104 GPU_indexbuf_build_in_place_ex(&builder, 0, max_index, false, lines_loose);
105 return;
106 }
107
108 const IndexMask all_loose_edges = IndexMask::from_indices(mr.loose_edges, memory);
109 const IndexMask non_loose_edges = all_loose_edges.complement(IndexRange(mr.edges_num), memory);
110 const IndexMask visible_non_loose_edges = calc_mesh_edge_visibility(mr, non_loose_edges, memory);
111
112 GPUIndexBufBuilder builder;
113 GPU_indexbuf_init(&builder,
115 visible_non_loose_edges.size() + visible_loose_edges.size(),
116 max_index);
118
119 /* This code fills the index buffer in a non-deterministic way, with non-atomic access to `used`.
120 * This is okay because any of the possible face corner indices are correct, since they all
121 * correspond to the same #Mesh vertex. `used` exists here as a performance optimization to
122 * avoid writing to the VBO. */
123 const OffsetIndices faces = mr.faces;
124 const Span<int> corner_edges = mr.corner_edges;
125 if (visible_non_loose_edges.size() == mr.edges_num) {
126 /* All edges in the mesh are visible. The edges in the GPU buffer will have the same order as
127 * the mesh's edges, so any remapping is unnecessary. Use a boolean array to avoid writing to
128 * the same indices again multiple times from different threads. This is slightly beneficial
129 * because booleans are 8 times smaller than the `uint2` for each edge. */
130 Array<bool> used(mr.edges_num, false);
131 threading::memory_bandwidth_bound_task(
132 used.as_span().size_in_bytes() + data.size_in_bytes() + corner_edges.size_in_bytes(),
133 [&]() {
134 threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
135 for (const int face_index : range) {
136 const IndexRange face = faces[face_index];
137 for (const int corner : face) {
138 const int edge = corner_edges[corner];
139 if (!used[edge]) {
140 used[edge] = true;
141 data[edge] = edge_from_corners(face, corner);
142 }
143 }
144 }
145 });
146 });
147 }
148 else {
149 Array<int> map(mr.edges_num, -1);
150 threading::memory_bandwidth_bound_task(
151 map.as_span().size_in_bytes() + data.size_in_bytes() + corner_edges.size_in_bytes(),
152 [&]() {
153 index_mask::build_reverse_map(visible_non_loose_edges, map.as_mutable_span());
154 threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
155 for (const int face_index : range) {
156 const IndexRange face = faces[face_index];
157 for (const int corner : face) {
158 const int edge = corner_edges[corner];
159 const int vbo_index = map[edge];
160 if (vbo_index != -1) {
161 map[edge] = -1;
162 data[vbo_index] = edge_from_corners(face, corner);
163 }
164 }
165 }
166 });
167 });
168 }
169
170 fill_loose_lines_ibo(mr, visible_loose_edges, data.take_back(visible_loose_edges.size()));
171
172 GPU_indexbuf_build_in_place_ex(&builder, 0, max_index, false, lines);
173 if (DRW_ibo_requested(lines_loose)) {
175 lines_loose, lines, visible_non_loose_edges.size() * 2, visible_loose_edges.size() * 2);
176 }
177}
178
179static void extract_lines_bm(const MeshRenderData &mr,
180 gpu::IndexBuf *lines,
181 gpu::IndexBuf *lines_loose,
182 bool &no_loose_wire)
183{
184 const BMesh &bm = *mr.bm;
185 const Span<int> loose_edges = mr.loose_edges;
186
187 IndexMaskMemory memory;
188 const IndexMask visible_loose_edges = IndexMask::from_predicate(
189 loose_edges.index_range(), GrainSize(2048), memory, [&](const int i) {
190 const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), loose_edges[i]);
192 });
193 const int max_index = mr.corners_num + loose_edges.size() * 2;
194
195 no_loose_wire = visible_loose_edges.is_empty();
196
197 if (DRW_ibo_requested(lines_loose) && !DRW_ibo_requested(lines)) {
198 GPUIndexBufBuilder builder;
199 GPU_indexbuf_init(&builder, GPU_PRIM_LINES, visible_loose_edges.size(), max_index);
201 fill_loose_lines_ibo(mr, visible_loose_edges, data);
202 GPU_indexbuf_build_in_place_ex(&builder, 0, max_index, false, lines_loose);
203 return;
204 }
205
206 const IndexMask all_loose_edges = IndexMask::from_indices(mr.loose_edges, memory);
207 const IndexMask non_loose_edges = all_loose_edges.complement(IndexRange(bm.totedge), memory);
208 const IndexMask visible_non_loose_edges = IndexMask::from_predicate(
209 non_loose_edges, GrainSize(2048), memory, [&](const int i) {
210 const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), i);
212 });
213
214 GPUIndexBufBuilder builder;
215 GPU_indexbuf_init(&builder,
217 visible_non_loose_edges.size() + visible_loose_edges.size(),
218 max_index);
220
221 /* Make use of BMesh's edge to loop topology knowledge to iterate over edges instead of
222 * iterating over faces and defining edges implicitly as done in the #Mesh extraction. */
223 visible_non_loose_edges.foreach_index(GrainSize(4096), [&](const int i, const int pos) {
224 const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), i);
225 data[pos] = uint2(BM_elem_index_get(edge.l), BM_elem_index_get(edge.l->next));
226 });
227
228 fill_loose_lines_ibo(mr, visible_loose_edges, data.take_back(visible_loose_edges.size()));
229
230 GPU_indexbuf_build_in_place_ex(&builder, 0, max_index, false, lines);
231 if (DRW_ibo_requested(lines_loose)) {
233 lines_loose, lines, visible_non_loose_edges.size() * 2, visible_loose_edges.size() * 2);
234 }
235}
236
238 gpu::IndexBuf *lines,
239 gpu::IndexBuf *lines_loose,
240 bool &no_loose_wire)
241{
242 if (mr.extract_type == MR_EXTRACT_MESH) {
243 extract_lines_mesh(mr, lines, lines_loose, no_loose_wire);
244 }
245 else {
246 extract_lines_bm(mr, lines, lines_loose, no_loose_wire);
247 }
248}
249
250static void extract_lines_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
251 const MeshRenderData &mr,
252 const int edge_loose_offset,
253 gpu::IndexBuf *ibo)
254{
255 const Span<int> loose_edges = mr.loose_edges;
256 if (loose_edges.is_empty()) {
257 return;
258 }
259 const int edges_per_edge = subdiv_edges_per_coarse_edge(subdiv_cache);
260 const int loose_edges_num = subdiv_loose_edges_num(mr, subdiv_cache);
261
262 /* Update flags for loose edges, points are already handled. */
263 static GPUVertFormat format;
264 if (format.attr_len == 0) {
266 }
267
270
271 GPU_vertbuf_data_alloc(*flags, loose_edges_num);
272
273 MutableSpan<uint> flags_data = flags->data<uint>();
274
275 switch (mr.extract_type) {
276 case MR_EXTRACT_MESH: {
277 const int *orig_index_edge = (mr.hide_unmapped_edges) ? mr.orig_index_edge : nullptr;
278 if (orig_index_edge == nullptr) {
279 const Span<bool> hide_edge = mr.hide_edge;
280 if (!hide_edge.is_empty()) {
281 for (const int i : loose_edges.index_range()) {
282 const bool value = hide_edge[loose_edges[i]];
283 flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
284 }
285 }
286 else {
287 flags_data.fill(0);
288 }
289 }
290 else {
291 if (mr.bm) {
292 for (const int i : loose_edges.index_range()) {
293 const BMEdge *bm_edge = bm_original_edge_get(mr, loose_edges[i]);
294 const int value = (bm_edge) ? BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN) : true;
295 flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
296 }
297 }
298 else {
299 const Span<bool> hide_edge = mr.hide_edge;
300 if (!hide_edge.is_empty()) {
301 for (const int i : loose_edges.index_range()) {
302 const bool value = (orig_index_edge[loose_edges[i]] == ORIGINDEX_NONE) ?
303 false :
304 hide_edge[loose_edges[i]];
305 flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
306 }
307 }
308 else {
309 flags_data.fill(0);
310 }
311 }
312 }
313 break;
314 }
315 case MR_EXTRACT_BMESH: {
316 BMesh *bm = mr.bm;
317 for (const int i : loose_edges.index_range()) {
318 const BMEdge *bm_edge = BM_edge_at_index(bm, loose_edges[i]);
319 const bool value = BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN);
320 flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
321 }
322 break;
323 }
324 }
325
327 subdiv_cache, ibo, flags, uint(edge_loose_offset), loose_edges_num);
328
329 GPU_vertbuf_discard(flags);
330}
331
332void extract_lines_subdiv(const DRWSubdivCache &subdiv_cache,
333 const MeshRenderData &mr,
334 gpu::IndexBuf *lines,
335 gpu::IndexBuf *lines_loose,
336 bool &no_loose_wire)
337{
338 const int loose_ibo_size = subdiv_loose_edges_num(mr, subdiv_cache) * 2;
339 no_loose_wire = loose_ibo_size == 0;
340
341 if (DRW_ibo_requested(lines_loose) && !DRW_ibo_requested(lines)) {
342 GPU_indexbuf_init_build_on_device(lines_loose, loose_ibo_size);
343 extract_lines_loose_geom_subdiv(subdiv_cache, mr, 0, lines_loose);
344 return;
345 }
346
347 const int non_loose_ibo_size = subdiv_cache.num_subdiv_loops * 2;
348
349 GPU_indexbuf_init_build_on_device(lines, non_loose_ibo_size + loose_ibo_size);
350 if (non_loose_ibo_size > 0) {
351 draw_subdiv_build_lines_buffer(subdiv_cache, lines);
352 }
353 extract_lines_loose_geom_subdiv(subdiv_cache, mr, non_loose_ibo_size, lines);
354
355 if (DRW_ibo_requested(lines_loose)) {
356 /* Multiply by 2 because these are edges indices. */
357 GPU_indexbuf_create_subrange_in_place(lines_loose, lines, non_loose_ibo_size, loose_ibo_size);
358 }
359}
360
361} // namespace blender::draw
#define ORIGINDEX_NONE
unsigned int uint
void GPU_indexbuf_build_in_place_ex(GPUIndexBufBuilder *builder, uint index_min, uint index_max, bool uses_restart_indices, blender::gpu::IndexBuf *elem)
blender::MutableSpan< uint32_t > GPU_indexbuf_get_data(GPUIndexBufBuilder *)
void GPU_indexbuf_create_subrange_in_place(blender::gpu::IndexBuf *elem, blender::gpu::IndexBuf *elem_src, uint start, uint length)
void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len)
void GPU_indexbuf_init_build_on_device(blender::gpu::IndexBuf *elem, uint index_len)
@ GPU_PRIM_LINES
#define GPU_vertbuf_init_with_format(verts, format)
blender::gpu::VertBuf * GPU_vertbuf_calloc()
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
void GPU_vertbuf_discard(blender::gpu::VertBuf *)
@ GPU_FETCH_INT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_U32
@ BM_ELEM_HIDDEN
#define BM_elem_index_get(ele)
#define BM_elem_flag_test_bool(ele, hflag)
ATTR_WARN_UNUSED_RESULT BMesh * bm
BLI_INLINE BMEdge * BM_edge_at_index(BMesh *bm, const int index)
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr MutableSpan< NewT > cast() const
Definition BLI_span.hh:736
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr int64_t size_in_bytes() const
Definition BLI_span.hh:269
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
MutableSpan< T > data()
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_bools_inverse(const IndexMask &universe, Span< bool > bools, IndexMaskMemory &memory)
static IndexMask from_bits(BitSpan bits, IndexMaskMemory &memory)
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
bool DRW_ibo_requested(blender::gpu::IndexBuf *ibo)
Extraction of Mesh data into VBO to feed to GPU.
format
ccl_device_inline float4 mask(const int4 mask, const float4 a)
int face_corner_next(const IndexRange face, const int corner)
Definition BKE_mesh.hh:252
static void extract_lines_mesh(const MeshRenderData &mr, gpu::IndexBuf *lines, gpu::IndexBuf *lines_loose, bool &no_loose_wire)
static void fill_loose_lines_ibo(const MeshRenderData &mr, const IndexMask &visible, MutableSpan< uint2 > data)
int subdiv_loose_edges_num(const MeshRenderData &mr, const DRWSubdivCache &cache)
static uint2 edge_from_corners(const IndexRange face, const int corner)
static IndexMask calc_mesh_edge_visibility(const MeshRenderData &mr, const IndexMask &mask, IndexMaskMemory &memory)
static void extract_lines_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache, const MeshRenderData &mr, const int edge_loose_offset, gpu::IndexBuf *ibo)
BLI_INLINE BMEdge * bm_original_edge_get(const MeshRenderData &mr, int idx)
int subdiv_edges_per_coarse_edge(const DRWSubdivCache &cache)
static void extract_lines_bm(const MeshRenderData &mr, gpu::IndexBuf *lines, gpu::IndexBuf *lines_loose, bool &no_loose_wire)
static IndexMask calc_visible_loose_edge_indices(const MeshRenderData &mr, IndexMaskMemory &memory)
void extract_lines(const MeshRenderData &mr, gpu::IndexBuf *lines, gpu::IndexBuf *lines_loose, bool &no_loose_wire)
void draw_subdiv_build_lines_buffer(const DRWSubdivCache &cache, gpu::IndexBuf *lines_indices)
void draw_subdiv_build_lines_loose_buffer(const DRWSubdivCache &cache, gpu::IndexBuf *lines_indices, gpu::VertBuf *lines_flags, uint edge_loose_offset, uint num_loose_edges)
void extract_lines_subdiv(const DRWSubdivCache &subdiv_cache, const MeshRenderData &mr, gpu::IndexBuf *lines, gpu::IndexBuf *lines_loose, bool &no_loose_wire)
void memory_bandwidth_bound_task(const int64_t approximate_bytes_touched, const Function &function)
Definition BLI_task.hh:243
VecBase< uint32_t, 2 > uint2
__int64 int64_t
Definition stdint.h:89
int totedge
MeshRuntimeHandle * runtime
OffsetIndices< int > faces