Mesh: Lines index buffer creation improvement #120720

Merged
Hans Goudey merged 66 commits from HooglyBoogly/blender:mesh-extract-lines-fix into main 2024-04-30 15:55:01 +02:00
7 changed files with 274 additions and 343 deletions

View File

@ -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;

View File

@ -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<LooseEdgedata *>(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<LooseEdgedata *>(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) {

View File

@ -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);
}

View File

@ -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<DRWSubdivCache *>(&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);

View File

@ -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,

View File

@ -337,10 +337,17 @@ void mesh_render_data_loop_edge_flag(const MeshRenderData &mr,
template<typename GPUType>
void extract_vert_normals(const MeshRenderData &mr, MutableSpan<GPUType> 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;

View File

@ -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<bool> 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<bool> 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<MeshExtract_LinesData *>(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<bool>();
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<uint2> data)
{
MeshExtract_LinesData *data = static_cast<MeshExtract_LinesData *>(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<uint>(), 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<MeshExtract_LinesData *>(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<MeshExtract_LinesData *>(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<MeshExtract_LinesData *>(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<GPUIndexBufBuilder *>(_userdata_to);
GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_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<MeshExtract_LinesData *>(tls_data);
GPUIndexBufBuilder *elb = &data->elb;
gpu::IndexBuf *ibo = static_cast<gpu::IndexBuf *>(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<gpu::IndexBuf *>(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<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
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<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
const OffsetIndices faces = mr.faces;
const Span<int> 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<bool> 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<int> 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<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
/* 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<gpu::IndexBuf *>(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<MeshExtract_LinesData *>(tls_data);
GPUIndexBufBuilder *elb = &data->elb;
gpu::IndexBuf *ibo = static_cast<gpu::IndexBuf *>(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