Mesh: Lines index buffer creation improvement #120720
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue