diff --git a/source/blender/draw/intern/draw_cache_extract.hh b/source/blender/draw/intern/draw_cache_extract.hh index 5f6a8f1e065..e30df083003 100644 --- a/source/blender/draw/intern/draw_cache_extract.hh +++ b/source/blender/draw/intern/draw_cache_extract.hh @@ -113,7 +113,7 @@ struct MeshBufferList { gpu::IndexBuf *tris; /* Loose edges last. */ gpu::IndexBuf *lines; - /* Sub buffer of `lines` only containing the loose edges. */ + /* Potentially a sub buffer of `lines` only containing the loose edges. */ gpu::IndexBuf *lines_loose; gpu::IndexBuf *points; gpu::IndexBuf *fdots; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index e280a04fbbc..b5333b95525 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -654,27 +654,6 @@ void mesh_buffer_cache_create_requested(TaskGraph *task_graph, EXTRACT_ADD_REQUESTED(vbo, vnor); EXTRACT_ADD_REQUESTED(ibo, tris); - if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) { - /* `ibo.lines_loose` require the `ibo.lines` buffer. */ - if (mbuflist->ibo.lines == nullptr) { - DRW_ibo_request(nullptr, &mbuflist->ibo.lines); - } - const MeshExtract *extractor = DRW_ibo_requested(mbuflist->ibo.lines) ? - &extract_lines_with_lines_loose : - &extract_lines_loose_only; - extractors.append(extractor); - } - else if (DRW_ibo_requested(mbuflist->ibo.lines)) { - const MeshExtract *extractor; - if (mbuflist->ibo.lines_loose != nullptr) { - /* Update `ibo.lines_loose` as it depends on `ibo.lines`. */ - extractor = &extract_lines_with_lines_loose; - } - else { - extractor = &extract_lines; - } - extractors.append(extractor); - } EXTRACT_ADD_REQUESTED(ibo, points); EXTRACT_ADD_REQUESTED(ibo, fdots); EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask); @@ -686,7 +665,9 @@ void mesh_buffer_cache_create_requested(TaskGraph *task_graph, #undef EXTRACT_ADD_REQUESTED - if (extractors.is_empty()) { + if (extractors.is_empty() && !DRW_ibo_requested(mbuflist->ibo.lines) && + !DRW_ibo_requested(mbuflist->ibo.lines_loose)) + { return; } @@ -721,6 +702,26 @@ void mesh_buffer_cache_create_requested(TaskGraph *task_graph, /* Simple heuristic. */ const bool use_thread = (mr->corners_num + mr->loose_indices_num) > MIN_RANGE_LEN; + if (DRW_ibo_requested(mbuflist->ibo.lines) || DRW_ibo_requested(mbuflist->ibo.lines_loose)) { + struct LooseEdgedata { + MeshRenderData &mr; + MeshBufferList &buffers; + MeshBatchCache &cache; + }; + TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + [](void *__restrict task_data) { + const LooseEdgedata &data = *static_cast(task_data); + extract_lines(data.mr, + data.buffers.ibo.lines, + data.buffers.ibo.lines_loose, + data.cache.no_loose_wire); + }, + new LooseEdgedata{*mr, *mbuflist, cache}, + [](void *task_data) { delete static_cast(task_data); }); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + } + if (use_thread) { /* First run the requested extractors that do not support asynchronous ranges. */ for (const ExtractorRunData &run_data : extractors) { @@ -830,27 +831,6 @@ void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache &cache, extractors.append(&extract_fdots_pos); } - if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) { - /* `ibo.lines_loose` require the `ibo.lines` buffer. */ - if (mbuflist->ibo.lines == nullptr) { - DRW_ibo_request(nullptr, &mbuflist->ibo.lines); - } - const MeshExtract *extractor = DRW_ibo_requested(mbuflist->ibo.lines) ? - &extract_lines_with_lines_loose : - &extract_lines_loose_only; - extractors.append(extractor); - } - else if (DRW_ibo_requested(mbuflist->ibo.lines)) { - const MeshExtract *extractor; - if (mbuflist->ibo.lines_loose != nullptr) { - /* Update `ibo.lines_loose` as it depends on `ibo.lines`. */ - extractor = &extract_lines_with_lines_loose; - } - else { - extractor = &extract_lines; - } - extractors.append(extractor); - } EXTRACT_ADD_REQUESTED(ibo, edituv_points); EXTRACT_ADD_REQUESTED(ibo, edituv_tris); EXTRACT_ADD_REQUESTED(ibo, edituv_lines); @@ -873,7 +853,9 @@ void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache &cache, #undef EXTRACT_ADD_REQUESTED - if (extractors.is_empty()) { + if (extractors.is_empty() && !DRW_ibo_requested(mbuflist->ibo.lines) && + !DRW_ibo_requested(mbuflist->ibo.lines_loose)) + { return; } @@ -883,6 +865,9 @@ void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache &cache, mr, mbc, MR_ITER_LOOSE_EDGE | MR_ITER_LOOSE_VERT, MR_DATA_LOOSE_GEOM); DRW_subdivide_loose_geom(&subdiv_cache, &mbc); + extract_lines_subdiv( + subdiv_cache, mr, mbuflist->ibo.lines, mbuflist->ibo.lines_loose, cache.no_loose_wire); + void *data_stack = MEM_mallocN(extractors.data_size_total(), __func__); uint32_t data_offset = 0; for (const ExtractorRunData &run_data : extractors) { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index 3d772f788b1..b1aa48d251b 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -1576,7 +1576,6 @@ void DRW_mesh_batch_cache_create_requested(TaskGraph *task_graph, } assert_deps_valid(MBC_LOOSE_EDGES, {BUFFER_INDEX(ibo.lines_loose), BUFFER_INDEX(vbo.pos)}); if (DRW_batch_requested(cache.batch.loose_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(nullptr, &mbuflist->ibo.lines); DRW_ibo_request(cache.batch.loose_edges, &mbuflist->ibo.lines_loose); DRW_vbo_request(cache.batch.loose_edges, &mbuflist->vbo.pos); } diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 8fcaebf2a9a..4cb7c8321e6 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -1332,7 +1332,8 @@ static void draw_subdiv_init_ubo_storage(const DRWSubdivCache &cache, const int src_offset, const int dst_offset, const uint total_dispatch_size, - const bool has_sculpt_mask) + const bool has_sculpt_mask, + const uint edge_loose_offset) { ubo->src_offset = src_offset; ubo->dst_offset = dst_offset; @@ -1342,7 +1343,7 @@ static void draw_subdiv_init_ubo_storage(const DRWSubdivCache &cache, ubo->patches_are_triangular = cache.gpu_patch_map.patches_are_triangular; ubo->coarse_face_count = cache.num_coarse_faces; ubo->num_subdiv_loops = cache.num_subdiv_loops; - ubo->edge_loose_offset = cache.num_subdiv_loops * 2; + ubo->edge_loose_offset = edge_loose_offset; ubo->has_sculpt_mask = has_sculpt_mask; ubo->coarse_face_smooth_mask = SUBDIV_COARSE_FACE_FLAG_SMOOTH_MASK; ubo->coarse_face_select_mask = SUBDIV_COARSE_FACE_FLAG_SELECT_MASK; @@ -1359,11 +1360,17 @@ static void draw_subdiv_ubo_update_and_bind(const DRWSubdivCache &cache, const int src_offset, const int dst_offset, const uint total_dispatch_size, - const bool has_sculpt_mask = false) + const bool has_sculpt_mask = false, + const uint edge_loose_offset = 0) { DRWSubdivUboStorage storage; - draw_subdiv_init_ubo_storage( - cache, &storage, src_offset, dst_offset, total_dispatch_size, has_sculpt_mask); + draw_subdiv_init_ubo_storage(cache, + &storage, + src_offset, + dst_offset, + total_dispatch_size, + has_sculpt_mask, + edge_loose_offset); if (!cache.ubo) { const_cast(&cache)->ubo = GPU_uniformbuf_create_ex( @@ -1396,7 +1403,8 @@ static void drw_subdiv_compute_dispatch(const DRWSubdivCache &cache, const int src_offset, const int dst_offset, uint total_dispatch_size, - const bool has_sculpt_mask = false) + const bool has_sculpt_mask = false, + const uint edge_loose_offset = 0) { const uint max_res_x = uint(GPU_max_work_group_count(0)); @@ -1423,8 +1431,13 @@ static void drw_subdiv_compute_dispatch(const DRWSubdivCache &cache, * we presume it all fits. */ BLI_assert(dispatch_ry < uint(GPU_max_work_group_count(1))); - draw_subdiv_ubo_update_and_bind( - cache, shader, src_offset, dst_offset, total_dispatch_size, has_sculpt_mask); + draw_subdiv_ubo_update_and_bind(cache, + shader, + src_offset, + dst_offset, + total_dispatch_size, + has_sculpt_mask, + edge_loose_offset); GPU_compute_dispatch(shader, dispatch_rx, dispatch_ry, 1); } @@ -1858,6 +1871,7 @@ void draw_subdiv_build_lines_buffer(const DRWSubdivCache &cache, gpu::IndexBuf * 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) { GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_LINES_LOOSE); @@ -1866,7 +1880,7 @@ void draw_subdiv_build_lines_loose_buffer(const DRWSubdivCache &cache, GPU_indexbuf_bind_as_ssbo(lines_indices, 3); GPU_vertbuf_bind_as_ssbo(lines_flags, 4); - drw_subdiv_compute_dispatch(cache, shader, 0, 0, num_loose_edges); + drw_subdiv_compute_dispatch(cache, shader, 0, 0, num_loose_edges, false, edge_loose_offset); /* This generates an index buffer, so we need to put a barrier on the element array. */ GPU_memory_barrier(GPU_BARRIER_ELEMENT_ARRAY); diff --git a/source/blender/draw/intern/draw_subdivision.hh b/source/blender/draw/intern/draw_subdivision.hh index 335ab10fbaa..d98f026c903 100644 --- a/source/blender/draw/intern/draw_subdivision.hh +++ b/source/blender/draw/intern/draw_subdivision.hh @@ -277,6 +277,7 @@ void draw_subdiv_build_lines_buffer(const DRWSubdivCache &cache, gpu::IndexBuf * 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 draw_subdiv_build_fdots_buffers(const DRWSubdivCache &cache, diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh index 5567b96f62b..d7effcb6665 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh @@ -337,10 +337,17 @@ void mesh_render_data_loop_edge_flag(const MeshRenderData &mr, template void extract_vert_normals(const MeshRenderData &mr, MutableSpan normals); +void extract_lines(const MeshRenderData &mr, + gpu::IndexBuf *lines, + gpu::IndexBuf *lines_loose, + bool &no_loose_wire); +void extract_lines_subdiv(const DRWSubdivCache &subdiv_cache, + const MeshRenderData &mr, + gpu::IndexBuf *lines, + gpu::IndexBuf *lines_loose, + bool &no_loose_wire); + extern const MeshExtract extract_tris; -extern const MeshExtract extract_lines; -extern const MeshExtract extract_lines_with_lines_loose; -extern const MeshExtract extract_lines_loose_only; extern const MeshExtract extract_points; extern const MeshExtract extract_fdots; extern const MeshExtract extract_lines_paint_mask; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc index 2eadc0c0bbb..091c8302a51 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc @@ -6,195 +6,216 @@ * \ingroup draw */ +#include "BLI_array_utils.hh" + #include "GPU_index_buffer.hh" #include "extract_mesh.hh" +#include "draw_cache_inline.hh" #include "draw_subdivision.hh" namespace blender::draw { -/* ---------------------------------------------------------------------- */ -/** \name Extract Edges Indices - * \{ */ - -struct MeshExtract_LinesData { - GPUIndexBufBuilder elb; - BitSpan optimal_display_edges; - const int *e_origindex; - Span hide_edge; - bool test_visibility; -}; - -BLI_INLINE bool is_edge_visible(const MeshExtract_LinesData *data, const int edge) +static IndexMask calc_mesh_edge_visibility(const MeshRenderData &mr, + const IndexMask &mask, + IndexMaskMemory &memory) { - if (!data->hide_edge.is_empty() && data->hide_edge[edge]) { - return false; + IndexMask visible = mask; + if (!mr.mesh->runtime->subsurf_optimal_display_edges.is_empty()) { + const BoundedBitSpan visible_bits = mr.mesh->runtime->subsurf_optimal_display_edges; + visible = IndexMask::from_bits(visible, visible_bits, memory); } - if (data->e_origindex && data->e_origindex[edge] == ORIGINDEX_NONE) { - return false; + if (!mr.hide_edge.is_empty()) { + /* Like #IndexMask::from_bools but reversed. */ + const Span hide_edge = mr.hide_edge; + visible = IndexMask::from_predicate( + visible, GrainSize(4096), memory, [&](const int64_t i) { return !hide_edge[i]; }); } - if (!data->optimal_display_edges.is_empty() && !data->optimal_display_edges[edge]) { - return false; + if (mr.hide_unmapped_edges && mr.e_origindex != nullptr) { + const int *orig_index = mr.e_origindex; + visible = IndexMask::from_predicate(visible, GrainSize(4096), memory, [&](const int64_t i) { + return orig_index[i] != ORIGINDEX_NONE; + }); } - return true; + return visible; } -static void extract_lines_init(const MeshRenderData &mr, - MeshBatchCache & /*cache*/, - void * /*buf*/, - void *tls_data) +/* In the GPU vertex buffers, the value for each vertex is duplicated to each of its vertex + * corners. So the edges on the GPU connect face corners rather than vertices. */ +static uint2 edge_from_corners(const IndexRange face, const int corner) { - MeshExtract_LinesData *data = static_cast(tls_data); - /* Put loose edges at the end. */ - GPU_indexbuf_init(&data->elb, - GPU_PRIM_LINES, - mr.edges_num + mr.loose_edges_num, - mr.corners_num + mr.loose_indices_num); - - if (mr.extract_type == MR_EXTRACT_MESH) { - data->optimal_display_edges = mr.mesh->runtime->subsurf_optimal_display_edges; - data->e_origindex = mr.hide_unmapped_edges ? mr.e_origindex : nullptr; - data->hide_edge = mr.use_hide ? Span(mr.hide_edge) : Span(); - - data->test_visibility = !data->optimal_display_edges.is_empty() || data->e_origindex || - !data->hide_edge.is_empty(); - } + const int corner_next = bke::mesh::face_corner_next(face, corner); + return uint2(corner, corner_next); } -static void extract_lines_iter_face_bm(const MeshRenderData & /*mr*/, - const BMFace *f, - const int /*f_index*/, - void *tls_data) +static void fill_loose_lines_ibo(const uint corners_num, MutableSpan data) { - MeshExtract_LinesData *data = static_cast(tls_data); - GPUIndexBufBuilder *elb = &data->elb; - BMLoop *l_iter, *l_first; - /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ - l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; - do { - if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_line_verts(elb, - BM_elem_index_get(l_iter->e), - BM_elem_index_get(l_iter), - BM_elem_index_get(l_iter->next)); - } - else { - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); - } - } while ((l_iter = l_iter->next) != l_first); + /* Vertices for loose edges are not shared in the GPU vertex buffers, so the indices are simply + * an increasing contiguous range. Ideally this would be generated on the GPU itself, or just + * unnecessary, but a large number of loose edges isn't expected to be a common performance + * bottleneck either. */ + threading::memory_bandwidth_bound_task(data.size_in_bytes(), [&]() { + array_utils::fill_index_range(data.cast(), corners_num); + }); } -static void extract_lines_iter_face_mesh(const MeshRenderData &mr, - const int face_index, - void *tls_data) +static void extract_lines_mesh(const MeshRenderData &mr, + gpu::IndexBuf *lines, + gpu::IndexBuf *lines_loose, + bool &no_loose_wire) { - MeshExtract_LinesData *data = static_cast(tls_data); - GPUIndexBufBuilder *elb = &data->elb; + IndexMaskMemory memory; + const IndexMask all_loose_edges = IndexMask::from_indices(mr.loose_edges, memory); + const IndexMask visible_loose_edges = calc_mesh_edge_visibility(mr, all_loose_edges, memory); + const int max_index = mr.corners_num + visible_loose_edges.size() * 2; - const IndexRange face = mr.faces[face_index]; + no_loose_wire = visible_loose_edges.is_empty(); - /* Using face & loop iterator would complicate accessing the adjacent loop. */ - if (data->test_visibility) { - for (const int corner : face) { - const int edge = mr.corner_edges[corner]; - const int corner_next = bke::mesh::face_corner_next(face, corner); - if (is_edge_visible(data, edge)) { - GPU_indexbuf_set_line_verts(elb, edge, corner, corner_next); - } - else { - GPU_indexbuf_set_line_restart(elb, edge); - } - } - } - else { - for (const int corner : face) { - const int edge = mr.corner_edges[corner]; - const int corner_next = bke::mesh::face_corner_next(face, corner); - GPU_indexbuf_set_line_verts(elb, edge, corner, corner_next); - } - } -} - -static void extract_lines_iter_loose_edge_bm(const MeshRenderData &mr, - const BMEdge *eed, - const int loose_edge_i, - void *tls_data) -{ - MeshExtract_LinesData *data = static_cast(tls_data); - GPUIndexBufBuilder *elb = &data->elb; - const int l_index_offset = mr.edges_num + loose_edge_i; - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - const int l_index = mr.corners_num + loose_edge_i * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); -} - -static void extract_lines_iter_loose_edge_mesh(const MeshRenderData &mr, - const int2 /*edge*/, - const int loose_edge_i, - void *tls_data) -{ - MeshExtract_LinesData *data = static_cast(tls_data); - GPUIndexBufBuilder *elb = &data->elb; - const int l_index_offset = mr.edges_num + loose_edge_i; - const int e_index = mr.loose_edges[loose_edge_i]; - if (is_edge_visible(data, e_index)) { - const int l_index = mr.corners_num + loose_edge_i * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, e_index); -} - -static void extract_lines_task_reduce(void *_userdata_to, void *_userdata_from) -{ - GPUIndexBufBuilder *elb_to = static_cast(_userdata_to); - GPUIndexBufBuilder *elb_from = static_cast(_userdata_from); - GPU_indexbuf_join(elb_to, elb_from); -} - -static void extract_lines_finish(const MeshRenderData & /*mr*/, - MeshBatchCache & /*cache*/, - void *buf, - void *tls_data) -{ - MeshExtract_LinesData *data = static_cast(tls_data); - GPUIndexBufBuilder *elb = &data->elb; - gpu::IndexBuf *ibo = static_cast(buf); - GPU_indexbuf_build_in_place(elb, ibo); -} - -static void extract_lines_init_subdiv(const DRWSubdivCache &subdiv_cache, - const MeshRenderData & /*mr*/, - MeshBatchCache & /*cache*/, - void *buffer, - void * /*data*/) -{ - const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; - gpu::IndexBuf *ibo = static_cast(buffer); - GPU_indexbuf_init_build_on_device(ibo, - subdiv_cache.num_subdiv_loops * 2 + loose_geom.edge_len * 2); - - if (subdiv_cache.num_subdiv_loops == 0) { + if (DRW_ibo_requested(lines_loose) && !DRW_ibo_requested(lines)) { + GPUIndexBufBuilder builder; + GPU_indexbuf_init(&builder, GPU_PRIM_LINES, visible_loose_edges.size(), max_index); + MutableSpan data = GPU_indexbuf_get_data(&builder).cast(); + fill_loose_lines_ibo(mr.corners_num, data); + GPU_indexbuf_build_in_place_ex(&builder, 0, max_index, false, lines_loose); return; } - draw_subdiv_build_lines_buffer(subdiv_cache, ibo); + const IndexMask non_loose_edges = all_loose_edges.complement(mr.edges.index_range(), memory); + const IndexMask visible_non_loose_edges = calc_mesh_edge_visibility(mr, non_loose_edges, memory); + + GPUIndexBufBuilder builder; + GPU_indexbuf_init(&builder, + GPU_PRIM_LINES, + visible_non_loose_edges.size() + visible_loose_edges.size(), + max_index); + MutableSpan data = GPU_indexbuf_get_data(&builder).cast(); + + const OffsetIndices faces = mr.faces; + const Span corner_edges = mr.corner_edges; + if (visible_non_loose_edges.size() == mr.edges_num) { + /* All edges in the mesh are visible. The edges in the GPU buffer will have the same order as + * the mesh's edges, so any remapping is unnecessary. Use a boolean array to avoid writing to + * the same indices again multiple times from different threads. This is slightly beneficial + * because booleans are 8 times smaller than the `uint2` for each edge. */ + Array used(mr.edges_num, false); + threading::memory_bandwidth_bound_task( + used.as_span().size_in_bytes() + data.size_in_bytes() + corner_edges.size_in_bytes(), + [&]() { + threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) { + for (const int face_index : range) { + const IndexRange face = faces[face_index]; + for (const int corner : face) { + const int edge = corner_edges[corner]; + /* Access to `used` don't need to be atomic because any of the possible face corner + * indices from `edge_from_corners` are correct, since they all correspond to the + * same #Mesh vertex. `used` only exists here as a performance optimization to + * avoid writing to the VBO unnecessarily. */ + if (used[edge]) { + continue; + } + data[edge] = edge_from_corners(face, corner); + used[edge] = true; + } + } + }); + }); + } + else { + Array map(mr.corners_num, -1); + threading::memory_bandwidth_bound_task( + map.as_span().size_in_bytes() + data.size_in_bytes() + corner_edges.size_in_bytes(), + [&]() { + index_mask::build_reverse_map(visible_non_loose_edges, map.as_mutable_span()); + threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) { + for (const int face_index : range) { + const IndexRange face = faces[face_index]; + for (const int corner : face) { + const int edge = corner_edges[corner]; + if (map[edge] == -1) { + continue; + } + data[map[edge]] = edge_from_corners(face, corner); + map[edge] = -1; + } + } + }); + }); + } + + fill_loose_lines_ibo(mr.corners_num, data.take_back(visible_loose_edges.size())); + + GPU_indexbuf_build_in_place_ex(&builder, 0, max_index, false, lines); + if (DRW_ibo_requested(lines_loose)) { + GPU_indexbuf_create_subrange_in_place( + lines_loose, lines, visible_non_loose_edges.size() * 2, visible_loose_edges.size() * 2); + } +} + +static IndexMask calc_bm_edge_visibility(BMesh &bm, const IndexMask &mask, IndexMaskMemory &memory) +{ + return IndexMask::from_predicate(mask, GrainSize(2048), memory, [&](const int i) { + return !BM_elem_flag_test_bool(BM_edge_at_index(&bm, i), BM_ELEM_HIDDEN); + }); +} + +static void extract_lines_bm(const MeshRenderData &mr, + gpu::IndexBuf *lines, + gpu::IndexBuf *lines_loose, + bool &no_loose_wire) +{ + BMesh &bm = *mr.bm; + + IndexMaskMemory memory; + const IndexMask all_loose_edges = IndexMask::from_indices(mr.loose_edges, memory); + const IndexMask visible_loose_edges = calc_bm_edge_visibility(bm, all_loose_edges, memory); + const int max_index = mr.corners_num + visible_loose_edges.size() * 2; + + no_loose_wire = visible_loose_edges.is_empty(); + + const IndexMask non_loose_edges = all_loose_edges.complement(IndexRange(bm.totedge), memory); + const IndexMask visible_non_loose_edges = calc_bm_edge_visibility(bm, non_loose_edges, memory); + + GPUIndexBufBuilder builder; + GPU_indexbuf_init(&builder, + GPU_PRIM_LINES, + visible_non_loose_edges.size() + visible_loose_edges.size(), + max_index); + MutableSpan data = GPU_indexbuf_get_data(&builder).cast(); + + /* Make use of BMesh's edge to loop topology knowledge to iterate over edges instead of + * iterating over faces and defining edges implicitly as done in the #Mesh extraction. */ + visible_non_loose_edges.foreach_index(GrainSize(4096), [&](const int i, const int pos) { + const BMEdge &edge = *BM_edge_at_index(&bm, i); + data[pos] = uint2(BM_elem_index_get(edge.l), BM_elem_index_get(edge.l->next)); + }); + + fill_loose_lines_ibo(mr.corners_num, data.take_back(visible_loose_edges.size())); + + GPU_indexbuf_build_in_place_ex(&builder, 0, max_index, false, lines); + if (DRW_ibo_requested(lines_loose)) { + GPU_indexbuf_create_subrange_in_place( + lines_loose, lines, visible_non_loose_edges.size() * 2, visible_loose_edges.size() * 2); + } +} + +void extract_lines(const MeshRenderData &mr, + gpu::IndexBuf *lines, + gpu::IndexBuf *lines_loose, + bool &no_loose_wire) +{ + if (mr.extract_type == MR_EXTRACT_MESH) { + extract_lines_mesh(mr, lines, lines_loose, no_loose_wire); + } + else { + extract_lines_bm(mr, lines, lines_loose, no_loose_wire); + } } static void extract_lines_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache, const MeshRenderData &mr, - void *buffer, - void * /*data*/) + const int edge_loose_offset, + gpu::IndexBuf *ibo) { const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; if (loose_geom.edge_len == 0) { @@ -267,137 +288,41 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache, } } - gpu::IndexBuf *ibo = static_cast(buffer); - draw_subdiv_build_lines_loose_buffer(subdiv_cache, ibo, flags, uint(loose_geom.edge_len)); + draw_subdiv_build_lines_loose_buffer( + subdiv_cache, ibo, flags, uint(edge_loose_offset), uint(loose_geom.edge_len)); GPU_vertbuf_discard(flags); } -constexpr MeshExtract create_extractor_lines() +void extract_lines_subdiv(const DRWSubdivCache &subdiv_cache, + const MeshRenderData &mr, + gpu::IndexBuf *lines, + gpu::IndexBuf *lines_loose, + bool &no_loose_wire) { - MeshExtract extractor = {nullptr}; - extractor.init = extract_lines_init; - extractor.iter_face_bm = extract_lines_iter_face_bm; - extractor.iter_face_mesh = extract_lines_iter_face_mesh; - extractor.iter_loose_edge_bm = extract_lines_iter_loose_edge_bm; - extractor.iter_loose_edge_mesh = extract_lines_iter_loose_edge_mesh; - extractor.init_subdiv = extract_lines_init_subdiv; - extractor.iter_loose_geom_subdiv = extract_lines_loose_geom_subdiv; - extractor.task_reduce = extract_lines_task_reduce; - extractor.finish = extract_lines_finish; - extractor.data_type = MR_DATA_NONE; - extractor.data_size = sizeof(MeshExtract_LinesData); - extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines); - return extractor; + const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; + const int loose_num = loose_geom.edge_len * 2; + no_loose_wire = loose_num == 0; + if (subdiv_cache.num_subdiv_loops == 0) { + return; + } + + if (DRW_ibo_requested(lines_loose) && !DRW_ibo_requested(lines)) { + GPU_indexbuf_init_build_on_device(lines_loose, loose_num); + extract_lines_loose_geom_subdiv(subdiv_cache, mr, 0, lines_loose); + return; + } + + const int non_loose_num = subdiv_cache.num_subdiv_loops * 2; + + GPU_indexbuf_init_build_on_device(lines, non_loose_num + loose_num); + draw_subdiv_build_lines_buffer(subdiv_cache, lines); + extract_lines_loose_geom_subdiv(subdiv_cache, mr, non_loose_num, lines); + + if (DRW_ibo_requested(lines_loose)) { + /* Multiply by 2 because these are edges indices. */ + GPU_indexbuf_create_subrange_in_place(lines_loose, lines, non_loose_num, loose_num); + } } -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Lines and Loose Edges Sub Buffer - * \{ */ - -static void extract_lines_loose_subbuffer(const MeshRenderData &mr, MeshBatchCache &cache) -{ - BLI_assert(cache.final.buff.ibo.lines); - /* Multiply by 2 because these are edges indices. */ - const int start = mr.edges_num * 2; - const int len = mr.loose_edges_num * 2; - GPU_indexbuf_create_subrange_in_place( - cache.final.buff.ibo.lines_loose, cache.final.buff.ibo.lines, start, len); - cache.no_loose_wire = (len == 0); -} - -static void extract_lines_with_lines_loose_finish(const MeshRenderData &mr, - MeshBatchCache &cache, - void *buf, - void *tls_data) -{ - MeshExtract_LinesData *data = static_cast(tls_data); - GPUIndexBufBuilder *elb = &data->elb; - gpu::IndexBuf *ibo = static_cast(buf); - GPU_indexbuf_build_in_place(elb, ibo); - extract_lines_loose_subbuffer(mr, cache); -} - -static void extract_lines_with_lines_loose_finish_subdiv(const DRWSubdivCache &subdiv_cache, - const MeshRenderData & /*mr*/, - MeshBatchCache &cache, - void * /*buf*/, - void * /*_data*/) -{ - /* Multiply by 2 because these are edges indices. */ - const int start = subdiv_cache.num_subdiv_loops * 2; - const int len = subdiv_cache.loose_geom.edge_len * 2; - GPU_indexbuf_create_subrange_in_place( - cache.final.buff.ibo.lines_loose, cache.final.buff.ibo.lines, start, len); - cache.no_loose_wire = (len == 0); -} - -constexpr MeshExtract create_extractor_lines_with_lines_loose() -{ - MeshExtract extractor = {nullptr}; - extractor.init = extract_lines_init; - extractor.iter_face_bm = extract_lines_iter_face_bm; - extractor.iter_face_mesh = extract_lines_iter_face_mesh; - extractor.iter_loose_edge_bm = extract_lines_iter_loose_edge_bm; - extractor.iter_loose_edge_mesh = extract_lines_iter_loose_edge_mesh; - extractor.task_reduce = extract_lines_task_reduce; - extractor.finish = extract_lines_with_lines_loose_finish; - extractor.init_subdiv = extract_lines_init_subdiv; - extractor.iter_loose_geom_subdiv = extract_lines_loose_geom_subdiv; - extractor.finish_subdiv = extract_lines_with_lines_loose_finish_subdiv; - extractor.data_type = MR_DATA_NONE; - extractor.data_size = sizeof(MeshExtract_LinesData); - extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines); - return extractor; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Loose Edges Sub Buffer - * \{ */ - -static void extract_lines_loose_only_init(const MeshRenderData &mr, - MeshBatchCache &cache, - void *buf, - void * /*tls_data*/) -{ - BLI_assert(buf == cache.final.buff.ibo.lines_loose); - UNUSED_VARS_NDEBUG(buf); - extract_lines_loose_subbuffer(mr, cache); -} - -static void extract_lines_loose_only_init_subdiv(const DRWSubdivCache & /*subdiv_cache*/, - const MeshRenderData &mr, - MeshBatchCache &cache, - void *buffer, - void * /*data*/) -{ - BLI_assert(buffer == cache.final.buff.ibo.lines_loose); - UNUSED_VARS_NDEBUG(buffer); - extract_lines_loose_subbuffer(mr, cache); -} - -constexpr MeshExtract create_extractor_lines_loose_only() -{ - MeshExtract extractor = {nullptr}; - extractor.init = extract_lines_loose_only_init; - extractor.init_subdiv = extract_lines_loose_only_init_subdiv; - extractor.data_type = MR_DATA_LOOSE_GEOM; - extractor.data_size = 0; - extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines_loose); - return extractor; -} - -/** \} */ - -const MeshExtract extract_lines = create_extractor_lines(); -const MeshExtract extract_lines_with_lines_loose = create_extractor_lines_with_lines_loose(); -const MeshExtract extract_lines_loose_only = create_extractor_lines_loose_only(); - } // namespace blender::draw