Subdiv: Simplify GPU subdivision loose geometry handling

The main change is avoid storage of redundant data in the subdivision
draw cache, mainly by replacing reverse lookup from subdivided edge to
coarse edge. This way loops are structured as iteration over coarse
edges instead of iteration over subdivided edges with optional behavior
for vertices with matching base mesh faces. With that inversion the
information in the draw cache is trivial (or duplicated from an array
in `MeshRenderData`), so it's all removed, except for the subdivided
loose edge positions. That array is also shrunk though, by not
duplicating positions in between each subdivided edge. Its calculation
is more efficient for the same reason too.

Overall, besides code simplification, the effect should be lower
overhead with loose edges with GPU subdivision. Admittedly this isn't
a very important use case, but it's part of a general refactor trying
to use better data oriented design in this area (#116901).

Pull Request: https://projects.blender.org/blender/blender/pulls/122071
This commit is contained in:
Hans Goudey 2024-05-21 21:46:11 +02:00 committed by Hans Goudey
parent 9cb298f706
commit d93b27a12e
9 changed files with 247 additions and 345 deletions

@ -685,11 +685,7 @@ void draw_subdiv_cache_free(DRWSubdivCache &cache)
GPU_uniformbuf_free(cache.ubo); GPU_uniformbuf_free(cache.ubo);
cache.ubo = nullptr; cache.ubo = nullptr;
} }
MEM_SAFE_FREE(cache.loose_geom.edges); cache.loose_edge_positions = {};
MEM_SAFE_FREE(cache.loose_geom.verts);
cache.loose_geom.edge_len = 0;
cache.loose_geom.vert_len = 0;
cache.loose_geom.loop_len = 0;
} }
/* Flags used in #DRWSubdivCache.extra_coarse_face_data. The flags are packed in the upper bits of /* Flags used in #DRWSubdivCache.extra_coarse_face_data. The flags are packed in the upper bits of
@ -2209,15 +2205,12 @@ static bool draw_subdiv_create_requested_buffers(Object &ob,
void DRW_subdivide_loose_geom(DRWSubdivCache &subdiv_cache, const MeshBufferCache &cache) void DRW_subdivide_loose_geom(DRWSubdivCache &subdiv_cache, const MeshBufferCache &cache)
{ {
const int coarse_loose_vert_len = cache.loose_geom.verts.size(); const Span<int> loose_edges = cache.loose_geom.edges;
const int coarse_loose_edge_len = cache.loose_geom.edges.size(); if (loose_edges.is_empty()) {
if (coarse_loose_vert_len == 0 && coarse_loose_edge_len == 0) {
/* Nothing to do. */
return; return;
} }
if (subdiv_cache.loose_geom.edges || subdiv_cache.loose_geom.verts) { if (!subdiv_cache.loose_edge_positions.is_empty()) {
/* Already processed. */ /* Already processed. */
return; return;
} }
@ -2227,27 +2220,7 @@ void DRW_subdivide_loose_geom(DRWSubdivCache &subdiv_cache, const MeshBufferCach
const int resolution = subdiv_cache.resolution; const int resolution = subdiv_cache.resolution;
const int resolution_1 = resolution - 1; const int resolution_1 = resolution - 1;
const float inv_resolution_1 = 1.0f / float(resolution_1); const float inv_resolution_1 = 1.0f / float(resolution_1);
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const int num_subdivided_edge = coarse_loose_edge_len *
(num_subdiv_vertices_per_coarse_edge + 1);
/* Each edge will store data for its 2 verts, that way we can keep the overall logic simple, here
* and in the buffer extractors. Although it duplicates memory (and work), the buffers also store
* duplicate values. */
const int num_subdivided_verts = num_subdivided_edge * 2;
DRWSubdivLooseEdge *loose_subd_edges = static_cast<DRWSubdivLooseEdge *>(
MEM_callocN(sizeof(DRWSubdivLooseEdge) * num_subdivided_edge, "DRWSubdivLooseEdge"));
DRWSubdivLooseVertex *loose_subd_verts = static_cast<DRWSubdivLooseVertex *>(
MEM_callocN(sizeof(DRWSubdivLooseVertex) * (num_subdivided_verts + coarse_loose_vert_len),
"DRWSubdivLooseEdge"));
int subd_edge_offset = 0;
int subd_vert_offset = 0;
/* Subdivide each loose coarse edge. */
const Span<float3> coarse_positions = coarse_mesh->vert_positions(); const Span<float3> coarse_positions = coarse_mesh->vert_positions();
const Span<int2> coarse_edges = coarse_mesh->edges(); const Span<int2> coarse_edges = coarse_mesh->edges();
@ -2256,60 +2229,24 @@ void DRW_subdivide_loose_geom(DRWSubdivCache &subdiv_cache, const MeshBufferCach
const GroupedSpan<int> vert_to_edge_map = bke::mesh::build_vert_to_edge_map( const GroupedSpan<int> vert_to_edge_map = bke::mesh::build_vert_to_edge_map(
coarse_edges, coarse_mesh->verts_num, vert_to_edge_offsets, vert_to_edge_indices); coarse_edges, coarse_mesh->verts_num, vert_to_edge_offsets, vert_to_edge_indices);
for (int i = 0; i < coarse_loose_edge_len; i++) { /* Also store the last vertex to simplify copying the positions to the VBO. */
const int coarse_edge_index = cache.loose_geom.edges[i]; subdiv_cache.loose_edge_positions.reinitialize(loose_edges.size() * resolution);
const int2 &coarse_edge = coarse_edges[cache.loose_geom.edges[i]]; MutableSpan<float3> edge_positions = subdiv_cache.loose_edge_positions;
/* Perform interpolation of each vertex. */ threading::parallel_for(loose_edges.index_range(), 1024, [&](const IndexRange range) {
for (int i = 0; i < resolution - 1; i++, subd_edge_offset++) { for (const int i : range) {
DRWSubdivLooseEdge &subd_edge = loose_subd_edges[subd_edge_offset]; const int coarse_edge = loose_edges[i];
subd_edge.coarse_edge_index = coarse_edge_index; MutableSpan positions = edge_positions.slice(i * resolution, resolution);
for (const int j : positions.index_range()) {
/* First vert. */ positions[j] = bke::subdiv::mesh_interpolate_position_on_edge(coarse_positions,
DRWSubdivLooseVertex &subd_v1 = loose_subd_verts[subd_vert_offset]; coarse_edges,
subd_v1.coarse_vertex_index = (i == 0) ? coarse_edge[0] : -1u; vert_to_edge_map,
const float u1 = i * inv_resolution_1; coarse_edge,
subd_v1.co = bke::subdiv::mesh_interpolate_position_on_edge( is_simple,
coarse_positions, coarse_edges, vert_to_edge_map, coarse_edge_index, is_simple, u1); j * inv_resolution_1);
}
subd_edge.loose_subdiv_v1_index = subd_vert_offset++;
/* Second vert. */
DRWSubdivLooseVertex &subd_v2 = loose_subd_verts[subd_vert_offset];
subd_v2.coarse_vertex_index = ((i + 1) == resolution - 1) ? coarse_edge[1] : -1u;
const float u2 = (i + 1) * inv_resolution_1;
subd_v2.co = bke::subdiv::mesh_interpolate_position_on_edge(
coarse_positions, coarse_edges, vert_to_edge_map, coarse_edge_index, is_simple, u2);
subd_edge.loose_subdiv_v2_index = subd_vert_offset++;
} }
} });
/* Copy the remaining loose_verts. */
for (int i = 0; i < coarse_loose_vert_len; i++) {
const int coarse_vertex_index = cache.loose_geom.verts[i];
DRWSubdivLooseVertex &subd_v = loose_subd_verts[subd_vert_offset++];
subd_v.coarse_vertex_index = cache.loose_geom.verts[i];
copy_v3_v3(subd_v.co, coarse_positions[coarse_vertex_index]);
}
subdiv_cache.loose_geom.edges = loose_subd_edges;
subdiv_cache.loose_geom.verts = loose_subd_verts;
subdiv_cache.loose_geom.edge_len = num_subdivided_edge;
subdiv_cache.loose_geom.vert_len = coarse_loose_vert_len;
subdiv_cache.loose_geom.loop_len = num_subdivided_edge * 2 + coarse_loose_vert_len;
}
Span<DRWSubdivLooseEdge> draw_subdiv_cache_get_loose_edges(const DRWSubdivCache &cache)
{
return {cache.loose_geom.edges, int64_t(cache.loose_geom.edge_len)};
}
Span<DRWSubdivLooseVertex> draw_subdiv_cache_get_loose_verts(const DRWSubdivCache &cache)
{
return {cache.loose_geom.verts + cache.loose_geom.edge_len * 2,
int64_t(cache.loose_geom.vert_len)};
} }
static OpenSubdiv_EvaluatorCache *g_evaluator_cache = nullptr; static OpenSubdiv_EvaluatorCache *g_evaluator_cache = nullptr;

@ -4,10 +4,14 @@
#pragma once #pragma once
#include "BLI_array.hh"
#include "BLI_math_matrix_types.hh" #include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_span.hh" #include "BLI_span.hh"
#include "BLI_sys_types.h" #include "BLI_sys_types.h"
#include "mesh_extractors/extract_mesh.hh"
struct BMesh; struct BMesh;
struct GPUUniformBuf; struct GPUUniformBuf;
namespace blender::gpu { namespace blender::gpu {
@ -46,56 +50,6 @@ struct DRWPatchMap {
/** \} */ /** \} */
/* -------------------------------------------------------------------- */
/** \name DRWSubdivLooseEdge
*
* This stores information about a subdivided loose edge.
* \{ */
struct DRWSubdivLooseEdge {
/* The corresponding coarse edge, this is always valid. */
int coarse_edge_index;
/* Pointers into #DRWSubdivLooseGeom.verts. */
int loose_subdiv_v1_index;
int loose_subdiv_v2_index;
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name DRWSubdivLooseVertex
*
* This stores information about a subdivided loose vertex, that may or may not come from a loose
* edge.
* \{ */
struct DRWSubdivLooseVertex {
/* The corresponding coarse vertex, or -1 if this vertex is the result
* of subdivision. */
unsigned int coarse_vertex_index;
/* Position and normal of the vertex. */
float3 co;
float3 nor;
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name DRWSubdivLooseGeom
*
* This stores the subdivided vertices and edges of loose geometry from #MeshExtractLooseGeom.
* \{ */
struct DRWSubdivLooseGeom {
DRWSubdivLooseEdge *edges;
DRWSubdivLooseVertex *verts;
int edge_len;
int vert_len;
int loop_len;
};
/** \} */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name DRWSubdivCache /** \name DRWSubdivCache
* *
@ -177,7 +131,11 @@ struct DRWSubdivCache {
DRWPatchMap gpu_patch_map; DRWPatchMap gpu_patch_map;
DRWSubdivLooseGeom loose_geom; /**
* Subdivided vertices of loose edges. The size of this array is the number of loose edges
* multiplied with the resolution. For storage in the VBO the data is duplicated for each edge.
*/
Array<float3> loose_edge_positions;
/* UBO to store settings for the various compute shaders. */ /* UBO to store settings for the various compute shaders. */
GPUUniformBuf *ubo; GPUUniformBuf *ubo;
@ -298,11 +256,28 @@ void draw_subdiv_build_edituv_stretch_angle_buffer(const DRWSubdivCache &cache,
/** Return the format used for the positions and normals VBO. */ /** Return the format used for the positions and normals VBO. */
GPUVertFormat *draw_subdiv_get_pos_nor_format(); GPUVertFormat *draw_subdiv_get_pos_nor_format();
/* Helper to access the loose edges. */ /** For every coarse edge, there are `resolution - 1` subdivided edges. */
Span<DRWSubdivLooseEdge> draw_subdiv_cache_get_loose_edges(const DRWSubdivCache &cache); inline int subdiv_edges_per_coarse_edge(const DRWSubdivCache &cache)
{
return cache.resolution - 1;
}
/* Helper to access only the loose vertices, i.e. not the ones attached to loose edges. To access /** For every subdivided edge, there are two coarse vertices stored in vertex buffers. */
* loose vertices of loose edges #draw_subdiv_cache_get_loose_edges should be used. */ inline int subdiv_verts_per_coarse_edge(const DRWSubdivCache &cache)
Span<DRWSubdivLooseVertex> draw_subdiv_cache_get_loose_verts(const DRWSubdivCache &cache); {
return subdiv_edges_per_coarse_edge(cache) * 2;
}
/** The number of subdivided edges from base mesh loose edges. */
inline int subdiv_loose_edges_num(const MeshRenderData &mr, const DRWSubdivCache &cache)
{
return mr.loose_edges.size() * subdiv_edges_per_coarse_edge(cache);
}
/** Size of vertex buffers including all face corners, loose edges, and loose vertices. */
inline int subdiv_full_vbo_size(const MeshRenderData &mr, const DRWSubdivCache &cache)
{
return cache.num_subdiv_loops + subdiv_loose_edges_num(mr, cache) * 2 + mr.loose_verts.size();
}
} // namespace blender::draw } // namespace blender::draw

@ -214,10 +214,12 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
const int edge_loose_offset, const int edge_loose_offset,
gpu::IndexBuf *ibo) gpu::IndexBuf *ibo)
{ {
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; const Span<int> loose_edges = mr.loose_edges;
if (loose_geom.edge_len == 0) { if (loose_edges.is_empty()) {
return; return;
} }
const int edges_per_edge = subdiv_edges_per_coarse_edge(subdiv_cache);
const int loose_edges_num = subdiv_loose_edges_num(mr, subdiv_cache);
/* Update flags for loose edges, points are already handled. */ /* Update flags for loose edges, points are already handled. */
static GPUVertFormat format; static GPUVertFormat format;
@ -228,10 +230,9 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
gpu::VertBuf *flags = GPU_vertbuf_calloc(); gpu::VertBuf *flags = GPU_vertbuf_calloc();
GPU_vertbuf_init_with_format(flags, &format); GPU_vertbuf_init_with_format(flags, &format);
Span<DRWSubdivLooseEdge> loose_edges = draw_subdiv_cache_get_loose_edges(subdiv_cache); GPU_vertbuf_data_alloc(flags, loose_edges_num);
GPU_vertbuf_data_alloc(flags, loose_edges.size());
uint *flags_data = static_cast<uint *>(GPU_vertbuf_get_data(flags)); MutableSpan<uint> flags_data(static_cast<uint *>(GPU_vertbuf_get_data(flags)), loose_edges_num);
switch (mr.extract_type) { switch (mr.extract_type) {
case MR_EXTRACT_MESH: { case MR_EXTRACT_MESH: {
@ -239,37 +240,35 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
if (e_origindex == nullptr) { if (e_origindex == nullptr) {
const Span<bool> hide_edge = mr.hide_edge; const Span<bool> hide_edge = mr.hide_edge;
if (!hide_edge.is_empty()) { if (!hide_edge.is_empty()) {
for (DRWSubdivLooseEdge edge : loose_edges) { for (const int i : loose_edges.index_range()) {
*flags_data++ = hide_edge[edge.coarse_edge_index]; const bool value = hide_edge[loose_edges[i]];
flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
} }
} }
else { else {
MutableSpan<uint>(flags_data, loose_edges.size()).fill(0); flags_data.fill(0);
} }
} }
else { else {
if (mr.bm) { if (mr.bm) {
for (DRWSubdivLooseEdge edge : loose_edges) { for (const int i : loose_edges.index_range()) {
const BMEdge *bm_edge = bm_original_edge_get(mr, edge.coarse_edge_index); const BMEdge *bm_edge = bm_original_edge_get(mr, loose_edges[i]);
*flags_data++ = (bm_edge) ? BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN) != 0 : 1; const int value = (bm_edge) ? BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN) : true;
flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
} }
} }
else { else {
const Span<bool> hide_edge = mr.hide_edge; const Span<bool> hide_edge = mr.hide_edge;
if (!hide_edge.is_empty()) { if (!hide_edge.is_empty()) {
for (DRWSubdivLooseEdge edge : loose_edges) { for (const int i : loose_edges.index_range()) {
int e = edge.coarse_edge_index; const bool value = (e_origindex[loose_edges[i]] == ORIGINDEX_NONE) ?
false :
if (e_origindex && e_origindex[e] != ORIGINDEX_NONE) { hide_edge[loose_edges[i]];
*flags_data++ = hide_edge[edge.coarse_edge_index]; flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
}
else {
*flags_data++ = false;
}
} }
} }
else { else {
MutableSpan<uint>(flags_data, loose_edges.size()).fill(0); flags_data.fill(0);
} }
} }
} }
@ -277,16 +276,17 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
} }
case MR_EXTRACT_BMESH: { case MR_EXTRACT_BMESH: {
BMesh *bm = mr.bm; BMesh *bm = mr.bm;
for (DRWSubdivLooseEdge edge : loose_edges) { for (const int i : loose_edges.index_range()) {
const BMEdge *bm_edge = BM_edge_at_index(bm, edge.coarse_edge_index); const BMEdge *bm_edge = BM_edge_at_index(bm, loose_edges[i]);
*flags_data++ = BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN) != 0; const bool value = BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN);
flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
} }
break; break;
} }
} }
draw_subdiv_build_lines_loose_buffer( draw_subdiv_build_lines_loose_buffer(
subdiv_cache, ibo, flags, uint(edge_loose_offset), uint(loose_geom.edge_len)); subdiv_cache, ibo, flags, uint(edge_loose_offset), loose_edges_num);
GPU_vertbuf_discard(flags); GPU_vertbuf_discard(flags);
} }
@ -297,27 +297,26 @@ void extract_lines_subdiv(const DRWSubdivCache &subdiv_cache,
gpu::IndexBuf *lines_loose, gpu::IndexBuf *lines_loose,
bool &no_loose_wire) bool &no_loose_wire)
{ {
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; const int loose_ibo_size = subdiv_loose_edges_num(mr, subdiv_cache) * 2;
const int loose_num = loose_geom.edge_len * 2; no_loose_wire = loose_ibo_size == 0;
no_loose_wire = loose_num == 0;
if (DRW_ibo_requested(lines_loose) && !DRW_ibo_requested(lines)) { if (DRW_ibo_requested(lines_loose) && !DRW_ibo_requested(lines)) {
GPU_indexbuf_init_build_on_device(lines_loose, loose_num); GPU_indexbuf_init_build_on_device(lines_loose, loose_ibo_size);
extract_lines_loose_geom_subdiv(subdiv_cache, mr, 0, lines_loose); extract_lines_loose_geom_subdiv(subdiv_cache, mr, 0, lines_loose);
return; return;
} }
const int non_loose_num = subdiv_cache.num_subdiv_loops * 2; const int non_loose_ibo_size = subdiv_cache.num_subdiv_loops * 2;
GPU_indexbuf_init_build_on_device(lines, non_loose_num + loose_num); GPU_indexbuf_init_build_on_device(lines, non_loose_ibo_size + loose_ibo_size);
if (non_loose_num > 0) { if (non_loose_ibo_size > 0) {
draw_subdiv_build_lines_buffer(subdiv_cache, lines); draw_subdiv_build_lines_buffer(subdiv_cache, lines);
} }
extract_lines_loose_geom_subdiv(subdiv_cache, mr, non_loose_num, lines); extract_lines_loose_geom_subdiv(subdiv_cache, mr, non_loose_ibo_size, lines);
if (DRW_ibo_requested(lines_loose)) { if (DRW_ibo_requested(lines_loose)) {
/* Multiply by 2 because these are edges indices. */ /* Multiply by 2 because these are edges indices. */
GPU_indexbuf_create_subrange_in_place(lines_loose, lines, non_loose_num, loose_num); GPU_indexbuf_create_subrange_in_place(lines_loose, lines, non_loose_ibo_size, loose_ibo_size);
} }
} }

@ -140,10 +140,7 @@ static void extract_points_init_subdiv(const DRWSubdivCache &subdiv_cache,
void *data) void *data)
{ {
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data); GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
GPU_indexbuf_init(elb, GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr.verts_num, subdiv_full_vbo_size(mr, subdiv_cache));
GPU_PRIM_POINTS,
mr.verts_num,
subdiv_cache.num_subdiv_loops + subdiv_cache.loose_geom.loop_len);
} }
static void extract_points_iter_subdiv_common(GPUIndexBufBuilder *elb, static void extract_points_iter_subdiv_common(GPUIndexBufBuilder *elb,
@ -209,65 +206,55 @@ static void extract_points_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
void * /*buffer*/, void * /*buffer*/,
void *data) void *data)
{ {
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; const Span<int> loose_verts = mr.loose_verts;
const int loose_indices_num = loose_geom.loop_len; const Span<int> loose_edges = mr.loose_edges;
if (loose_indices_num == 0) { if (loose_verts.is_empty() && loose_edges.is_empty()) {
return; return;
} }
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data); GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
uint offset = subdiv_cache.num_subdiv_loops; const Span<int2> edges = mr.edges;
const int loose_start = subdiv_cache.num_subdiv_loops;
const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
const int loose_verts_start = loose_start + verts_per_edge * loose_edges.size();
if (mr.extract_type != MR_EXTRACT_BMESH) { if (mr.extract_type != MR_EXTRACT_BMESH) {
Span<DRWSubdivLooseEdge> loose_edges = draw_subdiv_cache_get_loose_edges(subdiv_cache); threading::parallel_for(loose_edges.index_range(), 2048, [&](const IndexRange range) {
for (const int i : range) {
for (const DRWSubdivLooseEdge &loose_edge : loose_edges) { const IndexRange edge_vbo_range(loose_start + i * verts_per_edge, verts_per_edge);
const DRWSubdivLooseVertex &v1 = loose_geom.verts[loose_edge.loose_subdiv_v1_index]; vert_set_mesh(elb, mr, edges[loose_edges[i]][0], edge_vbo_range.first());
const DRWSubdivLooseVertex &v2 = loose_geom.verts[loose_edge.loose_subdiv_v2_index]; vert_set_mesh(elb, mr, edges[loose_edges[i]][1], edge_vbo_range.last());
if (v1.coarse_vertex_index != -1u) {
vert_set_mesh(elb, mr, v1.coarse_vertex_index, offset);
} }
if (v2.coarse_vertex_index != -1u) { });
vert_set_mesh(elb, mr, v2.coarse_vertex_index, offset + 1);
const Span<int> loose_verts = mr.loose_verts;
threading::parallel_for(loose_verts.index_range(), 2048, [&](const IndexRange range) {
for (const int i : range) {
vert_set_mesh(elb, mr, loose_verts[i], loose_verts_start + i);
} }
});
offset += 2;
}
Span<DRWSubdivLooseVertex> loose_verts = draw_subdiv_cache_get_loose_verts(subdiv_cache);
for (const DRWSubdivLooseVertex &loose_vert : loose_verts) {
vert_set_mesh(elb, mr, loose_vert.coarse_vertex_index, offset);
offset += 1;
}
} }
else { else {
Span<DRWSubdivLooseEdge> loose_edges = draw_subdiv_cache_get_loose_edges(subdiv_cache); threading::parallel_for(loose_edges.index_range(), 2048, [&](const IndexRange range) {
for (const int i : range) {
for (const DRWSubdivLooseEdge &loose_edge : loose_edges) { const IndexRange edge_vbo_range(loose_start + i * verts_per_edge, verts_per_edge);
const DRWSubdivLooseVertex &v1 = loose_geom.verts[loose_edge.loose_subdiv_v1_index]; BMVert *vert_0 = mr.v_origindex ? bm_original_vert_get(mr, edges[loose_edges[i]][0]) :
const DRWSubdivLooseVertex &v2 = loose_geom.verts[loose_edge.loose_subdiv_v2_index]; BM_vert_at_index(mr.bm, edges[loose_edges[i]][0]);
if (v1.coarse_vertex_index != -1u) { BMVert *vert_1 = mr.v_origindex ? bm_original_vert_get(mr, edges[loose_edges[i]][1]) :
BMVert *eve = mr.v_origindex ? bm_original_vert_get(mr, v1.coarse_vertex_index) : BM_vert_at_index(mr.bm, edges[loose_edges[i]][1]);
BM_vert_at_index(mr.bm, v1.coarse_vertex_index); vert_set_bm(elb, vert_0, edge_vbo_range.first());
vert_set_bm(elb, eve, offset); vert_set_bm(elb, vert_1, edge_vbo_range.last());
} }
if (v2.coarse_vertex_index != -1u) { });
BMVert *eve = mr.v_origindex ? bm_original_vert_get(mr, v2.coarse_vertex_index) :
BM_vert_at_index(mr.bm, v2.coarse_vertex_index); threading::parallel_for(loose_verts.index_range(), 2048, [&](const IndexRange range) {
vert_set_bm(elb, eve, offset + 1); for (const int i : range) {
BMVert *vert = mr.v_origindex ? bm_original_vert_get(mr, loose_verts[i]) :
BM_vert_at_index(mr.bm, loose_verts[i]);
vert_set_bm(elb, vert, loose_verts_start + i);
} }
});
offset += 2;
}
Span<DRWSubdivLooseVertex> loose_verts = draw_subdiv_cache_get_loose_verts(subdiv_cache);
for (const DRWSubdivLooseVertex &loose_vert : loose_verts) {
BMVert *eve = mr.v_origindex ? bm_original_vert_get(mr, loose_vert.coarse_vertex_index) :
BM_vert_at_index(mr.bm, loose_vert.coarse_vertex_index);
vert_set_bm(elb, eve, offset);
offset += 1;
}
} }
} }

@ -280,16 +280,17 @@ static gpu::VertBuf *build_poly_other_map_vbo(const DRWSubdivCache &subdiv_cache
} }
static void extract_edge_fac_init_subdiv(const DRWSubdivCache &subdiv_cache, static void extract_edge_fac_init_subdiv(const DRWSubdivCache &subdiv_cache,
const MeshRenderData & /*mr*/, const MeshRenderData &mr,
MeshBatchCache &cache, MeshBatchCache &cache,
void *buffer, void *buffer,
void * /*data*/) void * /*data*/)
{ {
gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer); gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer);
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; GPU_vertbuf_init_build_on_device(vbo,
GPU_vertbuf_init_build_on_device( get_subdiv_edge_fac_format(),
vbo, get_subdiv_edge_fac_format(), subdiv_cache.num_subdiv_loops + loose_geom.loop_len); subdiv_cache.num_subdiv_loops +
subdiv_loose_edges_num(mr, subdiv_cache) * 2);
gpu::VertBuf *pos_nor = cache.final.buff.vbo.pos; gpu::VertBuf *pos_nor = cache.final.buff.vbo.pos;
gpu::VertBuf *poly_other_map = build_poly_other_map_vbo(subdiv_cache); gpu::VertBuf *poly_other_map = build_poly_other_map_vbo(subdiv_cache);
@ -301,12 +302,12 @@ static void extract_edge_fac_init_subdiv(const DRWSubdivCache &subdiv_cache,
} }
static void extract_edge_fac_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache, static void extract_edge_fac_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
const MeshRenderData & /*mr*/, const MeshRenderData &mr,
void *buffer, void *buffer,
void * /*data*/) void * /*data*/)
{ {
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; const int loose_edges_num = subdiv_loose_edges_num(mr, subdiv_cache);
if (loose_geom.edge_len == 0) { if (loose_edges_num == 0) {
return; return;
} }
@ -315,18 +316,18 @@ static void extract_edge_fac_loose_geom_subdiv(const DRWSubdivCache &subdiv_cach
/* Make sure buffer is active for sending loose data. */ /* Make sure buffer is active for sending loose data. */
GPU_vertbuf_use(vbo); GPU_vertbuf_use(vbo);
uint offset = subdiv_cache.num_subdiv_loops; const int offset = subdiv_cache.num_subdiv_loops;
for (int i = 0; i < loose_geom.edge_len; i++) { if (GPU_crappy_amd_driver() || GPU_minimum_per_vertex_stride() > 1) {
if (GPU_crappy_amd_driver() || GPU_minimum_per_vertex_stride() > 1) { const float values[2] = {1.0f, 1.0f};
float loose_edge_fac[2] = {1.0f, 1.0f}; for (const int i : IndexRange(loose_edges_num)) {
GPU_vertbuf_update_sub(vbo, offset * sizeof(float), sizeof(loose_edge_fac), loose_edge_fac); GPU_vertbuf_update_sub(vbo, (offset + i * 2) * sizeof(float), sizeof(values), values);
} }
else { }
char loose_edge_fac[2] = {255, 255}; else {
GPU_vertbuf_update_sub(vbo, offset * sizeof(char), sizeof(loose_edge_fac), loose_edge_fac); const char values[2] = {255, 255};
for (const int i : IndexRange(loose_edges_num)) {
GPU_vertbuf_update_sub(vbo, (offset + i * 2) * sizeof(char), sizeof(values), values);
} }
offset += 2;
} }
} }

@ -239,15 +239,14 @@ static void extract_edit_data_iter_loose_vert_mesh(const MeshRenderData &mr,
} }
static void extract_edit_data_init_subdiv(const DRWSubdivCache &subdiv_cache, static void extract_edit_data_init_subdiv(const DRWSubdivCache &subdiv_cache,
const MeshRenderData & /*mr*/, const MeshRenderData &mr,
MeshBatchCache & /*cache*/, MeshBatchCache & /*cache*/,
void *buf, void *buf,
void *data) void *data)
{ {
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom;
gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buf); gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buf);
GPU_vertbuf_init_with_format(vbo, get_edit_data_format()); GPU_vertbuf_init_with_format(vbo, get_edit_data_format());
GPU_vertbuf_data_alloc(vbo, subdiv_cache.num_subdiv_loops + loose_geom.loop_len); GPU_vertbuf_data_alloc(vbo, subdiv_full_vbo_size(mr, subdiv_cache));
EditLoopData *vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo); EditLoopData *vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo);
*(EditLoopData **)data = vbo_data; *(EditLoopData **)data = vbo_data;
} }
@ -305,44 +304,53 @@ static void extract_edit_data_iter_subdiv_mesh(const DRWSubdivCache &subdiv_cach
static void extract_edit_data_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache, static void extract_edit_data_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
const MeshRenderData &mr, const MeshRenderData &mr,
void * /*buffer*/, void *buffer,
void *_data) void * /*data*/)
{ {
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; const Span<int> loose_verts = mr.loose_verts;
if (loose_geom.edge_len == 0) { const Span<int> loose_edges = mr.loose_edges;
if (loose_verts.is_empty() && loose_edges.is_empty()) {
return; return;
} }
Span<DRWSubdivLooseEdge> loose_edges = draw_subdiv_cache_get_loose_edges(subdiv_cache); gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer);
MutableSpan<EditLoopData> vbo_data(static_cast<EditLoopData *>(GPU_vertbuf_get_data(vbo)),
subdiv_full_vbo_size(mr, subdiv_cache));
const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
EditLoopData *vbo_data = *(EditLoopData **)_data; MutableSpan<EditLoopData> edge_data = vbo_data.slice(subdiv_cache.num_subdiv_loops,
int loose_edge_i = 0; loose_edges.size() * verts_per_edge);
threading::parallel_for(loose_edges.index_range(), 2048, [&](const IndexRange range) {
for (const int i : range) {
MutableSpan<EditLoopData> data = edge_data.slice(i * verts_per_edge, verts_per_edge);
if (BMEdge *edge = mr.e_origindex ? bm_original_edge_get(mr, loose_edges[i]) :
BM_edge_at_index(mr.bm, loose_edges[i]))
{
EditLoopData value{};
mesh_render_data_edge_flag(mr, edge, &value);
data.fill(value);
for (const DRWSubdivLooseEdge &loose_edge : loose_edges) { mesh_render_data_vert_flag(mr, edge->v1, &data.first());
const int offset = subdiv_cache.num_subdiv_loops + loose_edge_i++ * 2; mesh_render_data_vert_flag(mr, edge->v2, &data.last());
EditLoopData *data = &vbo_data[offset];
memset(data, 0, sizeof(EditLoopData));
const int edge_index = loose_edge.coarse_edge_index;
BMEdge *eed = mr.e_origindex ? bm_original_edge_get(mr, edge_index) :
BM_edge_at_index(mr.bm, edge_index);
if (eed) {
mesh_render_data_edge_flag(mr, eed, &data[0]);
data[1] = data[0];
const DRWSubdivLooseVertex &v1 = loose_geom.verts[loose_edge.loose_subdiv_v1_index];
const DRWSubdivLooseVertex &v2 = loose_geom.verts[loose_edge.loose_subdiv_v2_index];
if (v1.coarse_vertex_index != -1u) {
mesh_render_data_vert_flag(mr, eed->v1, &data[0]);
} }
if (v2.coarse_vertex_index != -1u) { else {
mesh_render_data_vert_flag(mr, eed->v2, &data[1]); data.fill({});
} }
} }
else { });
memset(&data[1], 0, sizeof(EditLoopData));
MutableSpan<EditLoopData> vert_data = vbo_data.take_back(loose_verts.size());
threading::parallel_for(loose_verts.index_range(), 2048, [&](const IndexRange range) {
for (const int i : range) {
EditLoopData value{};
if (BMVert *vert = mr.v_origindex ? bm_original_vert_get(mr, loose_verts[i]) :
BM_vert_at_index(mr.bm, loose_verts[i]))
{
mesh_render_data_vert_flag(mr, vert, &value);
}
vert_data[i] = value;
} }
} });
} }
constexpr MeshExtract create_extractor_edit_data() constexpr MeshExtract create_extractor_edit_data()

@ -242,11 +242,9 @@ static void extract_edituv_stretch_angle_init_subdiv(const DRWSubdivCache &subdi
* data should already be evaluated if we are here. This can happen if the subsurf modifier is * data should already be evaluated if we are here. This can happen if the subsurf modifier is
* only enabled in edit-mode. See #96338. */ * only enabled in edit-mode. See #96338. */
if (!pos_nor) { if (!pos_nor) {
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom;
pos_nor = GPU_vertbuf_calloc(); pos_nor = GPU_vertbuf_calloc();
GPU_vertbuf_init_build_on_device(pos_nor, GPU_vertbuf_init_build_on_device(
draw_subdiv_get_pos_nor_format(), pos_nor, draw_subdiv_get_pos_nor_format(), subdiv_full_vbo_size(mr, subdiv_cache));
subdiv_cache.num_subdiv_loops + loose_geom.loop_len);
draw_subdiv_extract_pos_nor(subdiv_cache, nullptr, pos_nor, nullptr); draw_subdiv_extract_pos_nor(subdiv_cache, nullptr, pos_nor, nullptr);
} }

@ -134,11 +134,10 @@ static void extract_pos_init_subdiv(const DRWSubdivCache &subdiv_cache,
void * /*data*/) void * /*data*/)
{ {
gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer); gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer);
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom;
/* Initialize the vertex buffer, it was already allocated. */ /* Initialize the vertex buffer, it was already allocated. */
GPU_vertbuf_init_build_on_device( GPU_vertbuf_init_build_on_device(
vbo, draw_subdiv_get_pos_nor_format(), subdiv_cache.num_subdiv_loops + loose_geom.loop_len); vbo, draw_subdiv_get_pos_nor_format(), subdiv_full_vbo_size(mr, subdiv_cache));
if (subdiv_cache.num_subdiv_loops == 0) { if (subdiv_cache.num_subdiv_loops == 0) {
return; return;
@ -221,17 +220,17 @@ static void extract_pos_init_subdiv(const DRWSubdivCache &subdiv_cache,
} }
static void extract_pos_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache, static void extract_pos_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
const MeshRenderData & /*mr*/, const MeshRenderData &mr,
void *buffer, void *buffer,
void * /*data*/) void * /*data*/)
{ {
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; const Span<int> loose_verts = mr.loose_verts;
if (loose_geom.loop_len == 0) { const int loose_edges_num = mr.loose_edges.size();
if (loose_verts.is_empty() && loose_edges_num == 0) {
return; return;
} }
gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer); gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer);
uint offset = subdiv_cache.num_subdiv_loops;
/* TODO(@kevindietrich): replace this when compressed normals are supported. */ /* TODO(@kevindietrich): replace this when compressed normals are supported. */
struct SubdivPosNorLoop { struct SubdivPosNorLoop {
@ -243,34 +242,39 @@ static void extract_pos_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
/* Make sure buffer is active for sending loose data. */ /* Make sure buffer is active for sending loose data. */
GPU_vertbuf_use(vbo); GPU_vertbuf_use(vbo);
Span<DRWSubdivLooseEdge> loose_edges = draw_subdiv_cache_get_loose_edges(subdiv_cache); const int resolution = subdiv_cache.resolution;
const Span<float3> cached_positions = subdiv_cache.loose_edge_positions;
const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
const int edges_per_edge = subdiv_edges_per_coarse_edge(subdiv_cache);
const int loose_geom_start = subdiv_cache.num_subdiv_loops;
SubdivPosNorLoop edge_data[2]; SubdivPosNorLoop edge_data[2];
memset(edge_data, 0, sizeof(SubdivPosNorLoop) * 2); memset(edge_data, 0, sizeof(SubdivPosNorLoop) * 2);
for (const DRWSubdivLooseEdge &loose_edge : loose_edges) { for (const int i : IndexRange(loose_edges_num)) {
const DRWSubdivLooseVertex &v1 = loose_geom.verts[loose_edge.loose_subdiv_v1_index]; const int edge_offset = loose_geom_start + i * verts_per_edge;
const DRWSubdivLooseVertex &v2 = loose_geom.verts[loose_edge.loose_subdiv_v2_index]; const Span<float3> positions = cached_positions.slice(i * resolution, resolution);
for (const int edge : IndexRange(edges_per_edge)) {
copy_v3_v3(edge_data[0].pos, v1.co); copy_v3_v3(edge_data[0].pos, positions[edge + 0]);
copy_v3_v3(edge_data[1].pos, v2.co); copy_v3_v3(edge_data[1].pos, positions[edge + 1]);
GPU_vertbuf_update_sub(vbo,
GPU_vertbuf_update_sub( (edge_offset + edge * 2) * sizeof(SubdivPosNorLoop),
vbo, offset * sizeof(SubdivPosNorLoop), sizeof(SubdivPosNorLoop) * 2, &edge_data); sizeof(SubdivPosNorLoop) * 2,
&edge_data);
offset += 2; }
} }
const int loose_verts_start = loose_geom_start + loose_edges_num * verts_per_edge;
const Span<float3> positions = mr.vert_positions;
SubdivPosNorLoop vert_data; SubdivPosNorLoop vert_data;
memset(&vert_data, 0, sizeof(SubdivPosNorLoop)); memset(&vert_data, 0, sizeof(SubdivPosNorLoop));
Span<DRWSubdivLooseVertex> loose_verts = draw_subdiv_cache_get_loose_verts(subdiv_cache); for (const int i : loose_verts.index_range()) {
copy_v3_v3(vert_data.pos, positions[loose_verts[i]]);
for (const DRWSubdivLooseVertex &loose_vert : loose_verts) { GPU_vertbuf_update_sub(vbo,
copy_v3_v3(vert_data.pos, loose_vert.co); (loose_verts_start + i) * sizeof(SubdivPosNorLoop),
sizeof(SubdivPosNorLoop),
GPU_vertbuf_update_sub( &vert_data);
vbo, offset * sizeof(SubdivPosNorLoop), sizeof(SubdivPosNorLoop), &vert_data);
offset += 1;
} }
} }

@ -6,6 +6,8 @@
* \ingroup draw * \ingroup draw
*/ */
#include "BLI_array_utils.hh"
#include "draw_subdivision.hh" #include "draw_subdivision.hh"
#include "extract_mesh.hh" #include "extract_mesh.hh"
@ -179,12 +181,11 @@ static void extract_vert_idx_init_subdiv(const DRWSubdivCache &subdiv_cache,
void * /*data*/) void * /*data*/)
{ {
gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buf); gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buf);
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom;
/* Each element points to an element in the `ibo.points`. */ /* Each element points to an element in the `ibo.points`. */
draw_subdiv_init_origindex_buffer(vbo, draw_subdiv_init_origindex_buffer(vbo,
(int32_t *)GPU_vertbuf_get_data(subdiv_cache.verts_orig_index), (int32_t *)GPU_vertbuf_get_data(subdiv_cache.verts_orig_index),
subdiv_cache.num_subdiv_loops, subdiv_cache.num_subdiv_loops,
loose_geom.loop_len); subdiv_full_vbo_size(mr, subdiv_cache));
if (!mr.v_origindex) { if (!mr.v_origindex) {
return; return;
} }
@ -206,56 +207,48 @@ static void extract_vert_idx_loose_geom_subdiv(const DRWSubdivCache &subdiv_cach
void *buffer, void *buffer,
void * /*data*/) void * /*data*/)
{ {
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; const Span<int> loose_verts = mr.loose_verts;
if (loose_geom.loop_len == 0) { const Span<int> loose_edges = mr.loose_edges;
if (loose_edges.is_empty() && loose_verts.is_empty()) {
return; return;
} }
gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer); gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer);
int32_t *vert_idx_data = (int32_t *)GPU_vertbuf_get_data(vbo); MutableSpan<int32_t> vbo_data(static_cast<int32_t *>(GPU_vertbuf_get_data(vbo)),
uint offset = subdiv_cache.num_subdiv_loops; subdiv_full_vbo_size(mr, subdiv_cache));
Span<DRWSubdivLooseEdge> loose_edges = draw_subdiv_cache_get_loose_edges(subdiv_cache); const Span<int2> coarse_edges = mr.edges;
const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
for (const DRWSubdivLooseEdge &loose_edge : loose_edges) { MutableSpan<int32_t> edge_data = vbo_data.slice(subdiv_cache.num_subdiv_loops,
const DRWSubdivLooseVertex &v1 = loose_geom.verts[loose_edge.loose_subdiv_v1_index]; loose_edges.size() * verts_per_edge);
const DRWSubdivLooseVertex &v2 = loose_geom.verts[loose_edge.loose_subdiv_v2_index]; for (const int i : loose_edges.index_range()) {
const int2 edge = coarse_edges[loose_edges[i]];
if (v1.coarse_vertex_index != -1u) { MutableSpan data = edge_data.slice(i * verts_per_edge, verts_per_edge);
vert_idx_data[offset] = mr.v_origindex ? mr.v_origindex[v1.coarse_vertex_index] : data.first() = mr.v_origindex ? mr.v_origindex[edge[0]] : edge[0];
v1.coarse_vertex_index; data.last() = mr.v_origindex ? mr.v_origindex[edge[1]] : edge[1];
}
if (v2.coarse_vertex_index != -1u) {
vert_idx_data[offset + 1] = mr.v_origindex ? mr.v_origindex[v2.coarse_vertex_index] :
v2.coarse_vertex_index;
}
offset += 2;
} }
Span<DRWSubdivLooseVertex> loose_verts = draw_subdiv_cache_get_loose_verts(subdiv_cache); MutableSpan<int32_t> loose_vert_data = vbo_data.take_back(loose_verts.size());
if (mr.v_origindex) {
for (const DRWSubdivLooseVertex &loose_vert : loose_verts) { array_utils::gather(Span(mr.v_origindex, mr.verts_num), loose_verts, loose_vert_data);
vert_idx_data[offset] = mr.v_origindex ? mr.v_origindex[loose_vert.coarse_vertex_index] : }
loose_vert.coarse_vertex_index; else {
offset += 1; array_utils::copy(loose_verts, loose_vert_data);
} }
} }
static void extract_edge_idx_init_subdiv(const DRWSubdivCache &subdiv_cache, static void extract_edge_idx_init_subdiv(const DRWSubdivCache &subdiv_cache,
const MeshRenderData & /*mr*/, const MeshRenderData &mr,
MeshBatchCache & /*cache*/, MeshBatchCache & /*cache*/,
void *buf, void *buf,
void * /*data*/) void * /*data*/)
{ {
gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buf); gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buf);
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom;
draw_subdiv_init_origindex_buffer( draw_subdiv_init_origindex_buffer(
vbo, vbo,
static_cast<int32_t *>(GPU_vertbuf_get_data(subdiv_cache.edges_orig_index)), static_cast<int32_t *>(GPU_vertbuf_get_data(subdiv_cache.edges_orig_index)),
subdiv_cache.num_subdiv_loops, subdiv_cache.num_subdiv_loops,
loose_geom.edge_len * 2); subdiv_loose_edges_num(mr, subdiv_cache) * 2);
} }
static void extract_edge_idx_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache, static void extract_edge_idx_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
@ -263,22 +256,22 @@ static void extract_edge_idx_loose_geom_subdiv(const DRWSubdivCache &subdiv_cach
void *buffer, void *buffer,
void * /*data*/) void * /*data*/)
{ {
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom; const Span<int> loose_edges = mr.loose_edges;
if (loose_geom.edge_len == 0) { if (loose_edges.is_empty()) {
return; return;
} }
gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer); gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer);
int32_t *vert_idx_data = (int32_t *)GPU_vertbuf_get_data(vbo); MutableSpan<int32_t> vbo_data(static_cast<int32_t *>(GPU_vertbuf_get_data(vbo)),
uint offset = subdiv_cache.num_subdiv_loops; subdiv_full_vbo_size(mr, subdiv_cache));
Span<DRWSubdivLooseEdge> loose_edges = draw_subdiv_cache_get_loose_edges(subdiv_cache); const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
for (const DRWSubdivLooseEdge &loose_edge : loose_edges) { MutableSpan data = vbo_data.slice(subdiv_cache.num_subdiv_loops,
const int coarse_edge_index = mr.e_origindex ? mr.e_origindex[loose_edge.coarse_edge_index] : loose_edges.size() * verts_per_edge);
loose_edge.coarse_edge_index; for (const int i : loose_edges.index_range()) {
vert_idx_data[offset] = coarse_edge_index; const int edge = loose_edges[i];
vert_idx_data[offset + 1] = coarse_edge_index; const int index = mr.e_origindex ? mr.e_origindex[edge] : edge;
offset += 2; data.slice(i * verts_per_edge, verts_per_edge).fill(index);
} }
} }