Blender V5.0
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
8
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
42 const IndexMask &visible,
44{
45 /* Vertices for loose edges are not shared in the GPU vertex buffers, so the indices are simply
46 * an increasing contiguous range. Ideally this would be generated on the GPU itself, or just
47 * unnecessary, but a large number of loose edges isn't expected to be a common performance
48 * bottleneck either. */
49 threading::memory_bandwidth_bound_task(data.size_in_bytes(), [&]() {
50 const uint loose_start = mr.corners_num;
51 if (visible.size() == mr.loose_edges.size()) {
52 array_utils::fill_index_range(data.cast<uint>(), loose_start);
53 }
54 else {
55 visible.foreach_index_optimized<int>(GrainSize(4096), [&](const int i, const int pos) {
56 data[pos] = loose_start + uint2(i * 2 + 0, i * 2 + 1);
57 });
58 }
59 });
60}
61
63{
64 const Span<int> loose_edges = mr.loose_edges;
65 IndexMask visible(loose_edges.size());
66 if (!mr.hide_edge.is_empty()) {
67 const Span<bool> hide_edge = mr.hide_edge;
69 visible, GrainSize(4096), memory, [&](const int i) { return !hide_edge[loose_edges[i]]; });
70 }
71 if (mr.hide_unmapped_edges && mr.orig_index_edge != nullptr) {
72 const int *orig_index = mr.orig_index_edge;
73 visible = IndexMask::from_predicate(visible, GrainSize(4096), memory, [&](const int64_t i) {
74 return orig_index[loose_edges[i]] != ORIGINDEX_NONE;
75 });
76 }
77 return visible;
78}
79
80static void extract_lines_mesh(const MeshRenderData &mr,
81 gpu::IndexBufPtr *lines,
82 gpu::IndexBufPtr *lines_loose,
83 bool &no_loose_wire)
84{
85 IndexMaskMemory memory;
86 const IndexMask visible_loose_edges = calc_visible_loose_edge_indices(mr, memory);
87 const int max_index = mr.corners_num + mr.loose_edges.size() * 2;
88
89 no_loose_wire = visible_loose_edges.is_empty();
90
91 if (lines_loose && !lines) {
92 GPUIndexBufBuilder builder;
93 GPU_indexbuf_init(&builder, GPU_PRIM_LINES, visible_loose_edges.size(), max_index);
95 fill_loose_lines_ibo(mr, visible_loose_edges, data);
96 *lines_loose = gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, false));
97 return;
98 }
99
100 const IndexMask all_loose_edges = IndexMask::from_indices(mr.loose_edges, memory);
101 const IndexMask non_loose_edges = all_loose_edges.complement(IndexRange(mr.edges_num), memory);
102 const IndexMask visible_non_loose_edges = calc_mesh_edge_visibility(mr, non_loose_edges, memory);
103
104 GPUIndexBufBuilder builder;
105 GPU_indexbuf_init(&builder,
107 visible_non_loose_edges.size() + visible_loose_edges.size(),
108 max_index);
110
111 /* This code fills the index buffer in a non-deterministic way, with non-atomic access to `used`.
112 * This is okay because any of the possible face corner indices are correct, since they all
113 * correspond to the same #Mesh vertex. `used` exists here as a performance optimization to
114 * avoid writing to the VBO. */
115 const OffsetIndices faces = mr.faces;
116 const Span<int> corner_edges = mr.corner_edges;
117 if (visible_non_loose_edges.size() == mr.edges_num) {
118 /* All edges in the mesh are visible. The edges in the GPU buffer will have the same order as
119 * the mesh's edges, so any remapping is unnecessary. Use a boolean array to avoid writing to
120 * the same indices again multiple times from different threads. This is slightly beneficial
121 * because booleans are 8 times smaller than the `uint2` for each edge. */
122 Array<bool> used(mr.edges_num, false);
124 used.as_span().size_in_bytes() + data.size_in_bytes() + corner_edges.size_in_bytes(),
125 [&]() {
126 threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
127 for (const int face_index : range) {
128 const IndexRange face = faces[face_index];
129 for (const int corner : face) {
130 const int edge = corner_edges[corner];
131 if (!used[edge]) {
132 used[edge] = true;
133 data[edge] = edge_from_corners(face, corner);
134 }
135 }
136 }
137 });
138 });
139 }
140 else {
141 Array<int> map(mr.edges_num, -1);
143 map.as_span().size_in_bytes() + data.size_in_bytes() + corner_edges.size_in_bytes(),
144 [&]() {
145 index_mask::build_reverse_map(visible_non_loose_edges, map.as_mutable_span());
146 threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
147 for (const int face_index : range) {
148 const IndexRange face = faces[face_index];
149 for (const int corner : face) {
150 const int edge = corner_edges[corner];
151 const int vbo_index = map[edge];
152 if (vbo_index != -1) {
153 map[edge] = -1;
154 data[vbo_index] = edge_from_corners(face, corner);
155 }
156 }
157 }
158 });
159 });
160 }
161
162 fill_loose_lines_ibo(mr, visible_loose_edges, data.take_back(visible_loose_edges.size()));
163
164 *lines = gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, false));
165 if (lines_loose) {
166 *lines_loose = gpu::IndexBufPtr(GPU_indexbuf_create_subrange(
167 lines->get(), visible_non_loose_edges.size() * 2, visible_loose_edges.size() * 2));
168 }
169}
170
171static void extract_lines_bm(const MeshRenderData &mr,
172 gpu::IndexBufPtr *lines,
173 gpu::IndexBufPtr *lines_loose,
174 bool &no_loose_wire)
175{
176 const BMesh &bm = *mr.bm;
177 const Span<int> loose_edges = mr.loose_edges;
178
179 IndexMaskMemory memory;
180 const IndexMask visible_loose_edges = IndexMask::from_predicate(
181 loose_edges.index_range(), GrainSize(2048), memory, [&](const int i) {
182 const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), loose_edges[i]);
184 });
185 const int max_index = mr.corners_num + loose_edges.size() * 2;
186
187 no_loose_wire = visible_loose_edges.is_empty();
188
189 if (lines_loose && !lines) {
190 GPUIndexBufBuilder builder;
191 GPU_indexbuf_init(&builder, GPU_PRIM_LINES, visible_loose_edges.size(), max_index);
193 fill_loose_lines_ibo(mr, visible_loose_edges, data);
194 *lines_loose = gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, false));
195 return;
196 }
197
198 const IndexMask all_loose_edges = IndexMask::from_indices(mr.loose_edges, memory);
199 const IndexMask non_loose_edges = all_loose_edges.complement(IndexRange(bm.totedge), memory);
200 const IndexMask visible_non_loose_edges = IndexMask::from_predicate(
201 non_loose_edges, GrainSize(2048), memory, [&](const int i) {
202 const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), i);
204 });
205
206 GPUIndexBufBuilder builder;
207 GPU_indexbuf_init(&builder,
209 visible_non_loose_edges.size() + visible_loose_edges.size(),
210 max_index);
212
213 /* Make use of BMesh's edge to loop topology knowledge to iterate over edges instead of
214 * iterating over faces and defining edges implicitly as done in the #Mesh extraction. */
215 visible_non_loose_edges.foreach_index(GrainSize(4096), [&](const int i, const int pos) {
216 const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), i);
218 });
219
220 fill_loose_lines_ibo(mr, visible_loose_edges, data.take_back(visible_loose_edges.size()));
221
222 *lines = gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, false));
223 if (lines_loose) {
225 lines->get(), visible_non_loose_edges.size() * 2, visible_loose_edges.size() * 2));
226 }
227}
228
230 gpu::IndexBufPtr *lines,
231 gpu::IndexBufPtr *lines_loose,
232 bool &no_loose_wire)
233{
235 extract_lines_mesh(mr, lines, lines_loose, no_loose_wire);
236 }
237 else {
238 extract_lines_bm(mr, lines, lines_loose, no_loose_wire);
239 }
240}
241
242static void extract_lines_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
243 const MeshRenderData &mr,
244 const int edge_loose_offset,
245 gpu::IndexBuf *ibo)
246{
247 const Span<int> loose_edges = mr.loose_edges;
248 if (loose_edges.is_empty()) {
249 return;
250 }
251 const int edges_per_edge = subdiv_edges_per_coarse_edge(subdiv_cache);
252 const int loose_edges_num = subdiv_loose_edges_num(mr, subdiv_cache);
253
254 /* Update flags for loose edges, points are already handled. */
256 gpu::VertAttrType::UINT_32);
257
260
261 GPU_vertbuf_data_alloc(*flags, loose_edges_num);
262
263 MutableSpan<uint> flags_data = flags->data<uint>();
264
265 switch (mr.extract_type) {
267 const int *orig_index_edge = (mr.hide_unmapped_edges) ? mr.orig_index_edge : nullptr;
268 if (orig_index_edge == nullptr) {
269 const Span<bool> hide_edge = mr.hide_edge;
270 if (!hide_edge.is_empty()) {
271 for (const int i : loose_edges.index_range()) {
272 const bool value = hide_edge[loose_edges[i]];
273 flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
274 }
275 }
276 else {
277 flags_data.fill(0);
278 }
279 }
280 else {
281 if (mr.bm) {
282 for (const int i : loose_edges.index_range()) {
283 const BMEdge *bm_edge = bm_original_edge_get(mr, loose_edges[i]);
284 const int value = (bm_edge) ? BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN) : true;
285 flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
286 }
287 }
288 else {
289 const Span<bool> hide_edge = mr.hide_edge;
290 if (!hide_edge.is_empty()) {
291 for (const int i : loose_edges.index_range()) {
292 const bool value = (orig_index_edge[loose_edges[i]] == ORIGINDEX_NONE) ?
293 false :
294 hide_edge[loose_edges[i]];
295 flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
296 }
297 }
298 else {
299 flags_data.fill(0);
300 }
301 }
302 }
303 break;
304 }
306 BMesh *bm = mr.bm;
307 for (const int i : loose_edges.index_range()) {
308 const BMEdge *bm_edge = BM_edge_at_index(bm, loose_edges[i]);
309 const bool value = BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN);
310 flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
311 }
312 break;
313 }
314 }
315
317 subdiv_cache, ibo, flags, uint(edge_loose_offset), loose_edges_num);
318
319 GPU_vertbuf_discard(flags);
320}
321
322void extract_lines_subdiv(const DRWSubdivCache &subdiv_cache,
323 const MeshRenderData &mr,
324 gpu::IndexBufPtr *lines,
325 gpu::IndexBufPtr *lines_loose,
326 bool &no_loose_wire)
327{
328 const int loose_ibo_size = subdiv_loose_edges_num(mr, subdiv_cache) * 2;
329 no_loose_wire = loose_ibo_size == 0;
330
331 if (lines_loose && !lines) {
332 *lines_loose = gpu::IndexBufPtr(GPU_indexbuf_build_on_device(loose_ibo_size));
333 extract_lines_loose_geom_subdiv(subdiv_cache, mr, 0, lines_loose->get());
334 return;
335 }
336
337 const int non_loose_ibo_size = subdiv_cache.num_subdiv_loops * 2;
338
339 *lines = gpu::IndexBufPtr(GPU_indexbuf_build_on_device(non_loose_ibo_size + loose_ibo_size));
340 if (non_loose_ibo_size > 0) {
341 draw_subdiv_build_lines_buffer(subdiv_cache, lines->get());
342 }
343 extract_lines_loose_geom_subdiv(subdiv_cache, mr, non_loose_ibo_size, lines->get());
344
345 if (lines_loose) {
346 /* Multiply by 2 because these are edges indices. */
347 *lines_loose = gpu::IndexBufPtr(
348 GPU_indexbuf_create_subrange(lines->get(), non_loose_ibo_size, loose_ibo_size));
349 }
350}
351
352} // namespace blender::draw
#define ORIGINDEX_NONE
unsigned int uint
blender::MutableSpan< uint32_t > GPU_indexbuf_get_data(GPUIndexBufBuilder *)
blender::gpu::IndexBuf * GPU_indexbuf_build_on_device(uint index_len)
void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len)
blender::gpu::IndexBuf * GPU_indexbuf_build_ex(GPUIndexBufBuilder *builder, uint index_min, uint index_max, bool uses_restart_indices)
blender::gpu::IndexBuf * GPU_indexbuf_create_subrange(blender::gpu::IndexBuf *elem_src, uint start, uint length)
@ 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 *)
GPUVertFormat GPU_vertformat_from_attribute(blender::StringRef name, blender::gpu::VertAttrType type)
@ BM_ELEM_HIDDEN
#define BM_elem_index_get(ele)
#define BM_elem_flag_test_bool(ele, hflag)
BMesh const char void * data
BMesh * bm
BLI_INLINE BMEdge * BM_edge_at_index(BMesh *bm, const int index)
long long int int64_t
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
Span< T > as_span() const
Definition BLI_array.hh:243
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_bits(BitSpan bits, IndexMaskMemory &memory)
static IndexMask from_bools_inverse(const VArray< bool > &bools, IndexMaskMemory &memory)
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr int64_t size_in_bytes() const
Definition BLI_span.hh:268
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
MutableSpan< T > data()
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
Extraction of Mesh data into VBO to feed to GPU.
uint pos
VecBase< uint, 2 > uint2
format
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static char faces[256]
static void fill_loose_lines_ibo(const MeshRenderData &mr, const IndexMask &visible, MutableSpan< uint2 > data)
static void extract_lines_bm(const MeshRenderData &mr, gpu::IndexBufPtr *lines, gpu::IndexBufPtr *lines_loose, bool &no_loose_wire)
int subdiv_loose_edges_num(const MeshRenderData &mr, const DRWSubdivCache &cache)
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 IndexMask calc_visible_loose_edge_indices(const MeshRenderData &mr, IndexMaskMemory &memory)
void draw_subdiv_build_lines_buffer(const DRWSubdivCache &cache, gpu::IndexBuf *lines_indices)
void extract_lines(const MeshRenderData &mr, gpu::IndexBufPtr *lines, gpu::IndexBufPtr *lines_loose, bool &no_loose_wire)
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)
static void extract_lines_mesh(const MeshRenderData &mr, gpu::IndexBufPtr *lines, gpu::IndexBufPtr *lines_loose, bool &no_loose_wire)
void extract_lines_subdiv(const DRWSubdivCache &subdiv_cache, const MeshRenderData &mr, gpu::IndexBufPtr *lines, gpu::IndexBufPtr *lines_loose, bool &no_loose_wire)
std::unique_ptr< IndexBuf, IndexBufDeleter > IndexBufPtr
void memory_bandwidth_bound_task(const int64_t approximate_bytes_touched, const Function &function)
Definition BLI_task.hh:265
VecBase< uint32_t, 2 > uint2
struct BMLoop * l
struct BMLoop * next
MeshRuntimeHandle * runtime
OffsetIndices< int > faces
i
Definition text_draw.cc:230