Fix #122293: Hidden edges affect incorrect loose edge indices

The loose edge section of the index buffer was built incorrectly,
essentially assuming that all the hidden edges came at the end of the
loose edge indices. To correct this, we need to track the visibility of
each loose edge index rather than the visibility of each edge itself.
This is driven by the need to combine the loose and non-loose indices
into one IBO with the loose edges in a contiguous section, and the
desire to avoid restart "invisible" indices in the buffer to upload
less data to the GPU.
This commit is contained in:
Hans Goudey
2024-05-27 16:18:41 -04:00
parent 71b0e9d92a
commit 0dd61a9dc6

View File

@@ -46,26 +46,53 @@ static uint2 edge_from_corners(const IndexRange face, const int corner)
return uint2(corner, corner_next);
}
static void fill_loose_lines_ibo(const uint corners_num, MutableSpan<uint2> data)
static void fill_loose_lines_ibo(const MeshRenderData &mr,
const IndexMask &visible,
MutableSpan<uint2> data)
{
/* 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);
const uint loose_start = mr.corners_num;
if (visible.size() == mr.loose_edges.size()) {
array_utils::fill_index_range(data.cast<uint>(), loose_start);
}
else {
visible.foreach_index_optimized<int>(GrainSize(4096), [&](const int i, const int pos) {
data[pos] = loose_start + uint2(i * 2 + 0, i * 2 + 1);
});
}
});
}
static IndexMask calc_visible_loose_edge_indices(const MeshRenderData &mr, IndexMaskMemory &memory)
{
const Span<int> loose_edges = mr.loose_edges;
IndexMask visible(loose_edges.size());
if (!mr.hide_edge.is_empty()) {
const Span<bool> hide_edge = mr.hide_edge;
visible = IndexMask::from_predicate(
visible, GrainSize(4096), memory, [&](const int i) { return !hide_edge[loose_edges[i]]; });
}
if (mr.hide_unmapped_edges && mr.orig_index_edge != nullptr) {
const int *orig_index = mr.orig_index_edge;
visible = IndexMask::from_predicate(visible, GrainSize(4096), memory, [&](const int64_t i) {
return orig_index[loose_edges[i]] != ORIGINDEX_NONE;
});
}
return visible;
}
static void extract_lines_mesh(const MeshRenderData &mr,
gpu::IndexBuf *lines,
gpu::IndexBuf *lines_loose,
bool &no_loose_wire)
{
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 IndexMask visible_loose_edges = calc_visible_loose_edge_indices(mr, memory);
const int max_index = mr.corners_num + mr.loose_edges.size() * 2;
no_loose_wire = visible_loose_edges.is_empty();
@@ -73,12 +100,13 @@ static void extract_lines_mesh(const MeshRenderData &mr,
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);
fill_loose_lines_ibo(mr, visible_loose_edges, data);
GPU_indexbuf_build_in_place_ex(&builder, 0, max_index, false, lines_loose);
return;
}
const IndexMask non_loose_edges = all_loose_edges.complement(mr.edges.index_range(), memory);
const IndexMask all_loose_edges = IndexMask::from_indices(mr.loose_edges, memory);
const IndexMask non_loose_edges = all_loose_edges.complement(IndexRange(mr.edges_num), memory);
const IndexMask visible_non_loose_edges = calc_mesh_edge_visibility(mr, non_loose_edges, memory);
GPUIndexBufBuilder builder;
@@ -138,7 +166,7 @@ static void extract_lines_mesh(const MeshRenderData &mr,
});
}
fill_loose_lines_ibo(mr.corners_num, data.take_back(visible_loose_edges.size()));
fill_loose_lines_ibo(mr, visible_loose_edges, 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)) {
@@ -147,31 +175,31 @@ static void extract_lines_mesh(const MeshRenderData &mr,
}
}
static IndexMask calc_bm_edge_visibility(const 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(&const_cast<BMesh &>(bm), i), BM_ELEM_HIDDEN);
});
}
static void extract_lines_bm(const MeshRenderData &mr,
gpu::IndexBuf *lines,
gpu::IndexBuf *lines_loose,
bool &no_loose_wire)
{
const BMesh &bm = *mr.bm;
const Span<int> loose_edges = mr.loose_edges;
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;
const IndexMask visible_loose_edges = IndexMask::from_predicate(
loose_edges.index_range(), GrainSize(2048), memory, [&](const int i) {
const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), loose_edges[i]);
return !BM_elem_flag_test_bool(&edge, BM_ELEM_HIDDEN);
});
const int max_index = mr.corners_num + loose_edges.size() * 2;
no_loose_wire = visible_loose_edges.is_empty();
const IndexMask all_loose_edges = IndexMask::from_indices(mr.loose_edges, memory);
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);
const IndexMask visible_non_loose_edges = IndexMask::from_predicate(
non_loose_edges, GrainSize(2048), memory, [&](const int i) {
const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), i);
return !BM_elem_flag_test_bool(&edge, BM_ELEM_HIDDEN);
});
GPUIndexBufBuilder builder;
GPU_indexbuf_init(&builder,
@@ -187,7 +215,7 @@ static void extract_lines_bm(const MeshRenderData &mr,
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()));
fill_loose_lines_ibo(mr, visible_loose_edges, 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)) {