Mesh: Draw: Replace extractor abstraction for vertex group buffers

Part of #116901.
For meshes, extract vertex group weights to a temporary vertex-sized
array, then gather them to the final VBO array. This reduces duplicate
work, and eventually the gather step could be done on the GPU too.
This commit is contained in:
Hans Goudey
2024-05-29 23:24:57 -04:00
committed by Hans Goudey
parent 2f3aacb1ac
commit 28c41b1d42
3 changed files with 93 additions and 123 deletions

View File

@@ -646,7 +646,6 @@ void mesh_buffer_cache_create_requested(TaskGraph &task_graph,
EXTRACT_ADD_REQUESTED(vbo, sculpt_data);
EXTRACT_ADD_REQUESTED(vbo, orco);
EXTRACT_ADD_REQUESTED(vbo, edge_fac);
EXTRACT_ADD_REQUESTED(vbo, weights);
EXTRACT_ADD_REQUESTED(vbo, edituv_data);
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_area);
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle);
@@ -677,7 +676,7 @@ void mesh_buffer_cache_create_requested(TaskGraph &task_graph,
!DRW_vbo_requested(buffers.vbo.fdots_nor) && !DRW_vbo_requested(buffers.vbo.tan) &&
!DRW_vbo_requested(buffers.vbo.edit_data) && !DRW_vbo_requested(buffers.vbo.face_idx) &&
!DRW_vbo_requested(buffers.vbo.edge_idx) && !DRW_vbo_requested(buffers.vbo.vert_idx) &&
!DRW_vbo_requested(buffers.vbo.fdot_idx))
!DRW_vbo_requested(buffers.vbo.fdot_idx) && !DRW_vbo_requested(buffers.vbo.weights))
{
return;
}
@@ -887,6 +886,22 @@ void mesh_buffer_cache_create_requested(TaskGraph &task_graph,
[](void *task_data) { delete static_cast<TaskData *>(task_data); });
BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
}
if (DRW_vbo_requested(buffers.vbo.weights)) {
struct TaskData {
MeshRenderData &mr;
MeshBufferList &buffers;
MeshBatchCache &cache;
};
TaskNode *task_node = BLI_task_graph_node_create(
&task_graph,
[](void *__restrict task_data) {
const TaskData &data = *static_cast<TaskData *>(task_data);
extract_weights(data.mr, data.cache, *data.buffers.vbo.weights);
},
new TaskData{*mr, buffers, cache},
[](void *task_data) { delete static_cast<TaskData *>(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. */
@@ -998,7 +1013,6 @@ void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache &cache,
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle);
EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask);
EXTRACT_ADD_REQUESTED(ibo, lines_adjacency);
EXTRACT_ADD_REQUESTED(vbo, weights);
EXTRACT_ADD_REQUESTED(vbo, sculpt_data);
#undef EXTRACT_ADD_REQUESTED
@@ -1009,7 +1023,7 @@ void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache &cache,
!DRW_vbo_requested(buffers.vbo.orco) && !DRW_vbo_requested(buffers.vbo.nor) &&
!DRW_vbo_requested(buffers.vbo.tan) && !DRW_vbo_requested(buffers.vbo.edit_data) &&
!DRW_vbo_requested(buffers.vbo.face_idx) && !DRW_vbo_requested(buffers.vbo.edge_idx) &&
!DRW_vbo_requested(buffers.vbo.vert_idx))
!DRW_vbo_requested(buffers.vbo.vert_idx) && !DRW_vbo_requested(buffers.vbo.weights))
{
return;
}
@@ -1050,6 +1064,9 @@ void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache &cache,
if (DRW_vbo_requested(buffers.vbo.face_idx)) {
extract_face_index_subdiv(subdiv_cache, mr, *buffers.vbo.face_idx);
}
if (DRW_vbo_requested(buffers.vbo.weights)) {
extract_weights_subdiv(mr, subdiv_cache, cache, *buffers.vbo.weights);
}
void *data_stack = MEM_mallocN(extractors.data_size_total(), __func__);
uint32_t data_offset = 0;

View File

@@ -421,6 +421,12 @@ void extract_face_index_subdiv(const DRWSubdivCache &subdiv_cache,
const MeshRenderData &mr,
gpu::VertBuf &vbo);
void extract_weights(const MeshRenderData &mr, const MeshBatchCache &cache, gpu::VertBuf &vbo);
void extract_weights_subdiv(const MeshRenderData &mr,
const DRWSubdivCache &subdiv_cache,
const MeshBatchCache &cache,
gpu::VertBuf &vbo);
extern const MeshExtract extract_fdots;
extern const MeshExtract extract_lines_paint_mask;
extern const MeshExtract extract_lines_adjacency;
@@ -432,7 +438,6 @@ extern const MeshExtract extract_uv;
extern const MeshExtract extract_sculpt_data;
extern const MeshExtract extract_orco;
extern const MeshExtract extract_edge_fac;
extern const MeshExtract extract_weights;
extern const MeshExtract extract_edituv_data;
extern const MeshExtract extract_edituv_stretch_area;
extern const MeshExtract extract_edituv_stretch_angle;

View File

@@ -8,7 +8,7 @@
#include "DNA_meshdata_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_array_utils.hh"
#include "BKE_deform.hh"
#include "BKE_mesh.hh"
@@ -18,26 +18,12 @@
namespace blender::draw {
/* ---------------------------------------------------------------------- */
/** \name Extract Vertex Weight
* \{ */
struct MeshExtract_Weight_Data {
float *vbo_data;
const DRW_MeshWeightState *wstate;
const MDeformVert *dvert; /* For #Mesh. */
int cd_ofs; /* For #BMesh. */
};
static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate)
{
/* Error state. */
if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) {
return -2.0f;
}
if (dvert == nullptr) {
return (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) ? -1.0f : 0.0f;
}
float input = 0.0f;
if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) {
@@ -82,131 +68,93 @@ static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeig
return input;
}
static void extract_weights_init(const MeshRenderData &mr,
MeshBatchCache &cache,
void *buf,
void *tls_data)
static void extract_weights_mesh(const MeshRenderData &mr,
const DRW_MeshWeightState &weight_state,
MutableSpan<float> vbo_data)
{
const Mesh &mesh = *mr.mesh;
const Span<MDeformVert> dverts = mesh.deform_verts();
if (dverts.is_empty()) {
vbo_data.fill(weight_state.alert_mode == OB_DRAW_GROUPUSER_NONE ? 0.0f : -1.0f);
return;
}
Array<float> weights(dverts.size());
threading::parallel_for(weights.index_range(), 1024, [&](const IndexRange range) {
for (const int vert : range) {
weights[vert] = evaluate_vertex_weight(&dverts[vert], &weight_state);
}
});
array_utils::gather(weights.as_span(), mr.corner_verts, vbo_data);
}
static void extract_weights_bm(const MeshRenderData &mr,
const DRW_MeshWeightState &weight_state,
MutableSpan<float> vbo_data)
{
const BMesh &bm = *mr.bm;
const int offset = CustomData_get_offset(&bm.vdata, CD_MDEFORMVERT);
if (offset == -1) {
vbo_data.fill(weight_state.alert_mode == OB_DRAW_GROUPUSER_NONE ? 0.0f : -1.0f);
return;
}
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
for (const int face_index : range) {
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
const int index = BM_elem_index_get(loop);
vbo_data[index] = evaluate_vertex_weight(
static_cast<const MDeformVert *>(BM_ELEM_CD_GET_VOID_P(loop->v, offset)),
&weight_state);
loop = loop->next;
}
}
});
}
void extract_weights(const MeshRenderData &mr, const MeshBatchCache &cache, gpu::VertBuf &vbo)
{
gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buf);
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.corners_num + mr.loose_indices_num);
GPU_vertbuf_init_with_format(&vbo, &format);
GPU_vertbuf_data_alloc(&vbo, mr.corners_num);
MutableSpan<float> vbo_data(static_cast<float *>(GPU_vertbuf_get_data(&vbo)), mr.corners_num);
MeshExtract_Weight_Data *data = static_cast<MeshExtract_Weight_Data *>(tls_data);
data->vbo_data = (float *)GPU_vertbuf_get_data(vbo);
data->wstate = &cache.weight_state;
if (data->wstate->defgroup_active == -1) {
/* Nothing to show. */
data->dvert = nullptr;
data->cd_ofs = -1;
const DRW_MeshWeightState &weight_state = cache.weight_state;
if (weight_state.defgroup_active == -1) {
vbo_data.fill(weight_state.alert_mode == OB_DRAW_GROUPUSER_NONE ? 0.0f : -1.0f);
return;
}
else if (mr.extract_type == MR_EXTRACT_BMESH) {
data->dvert = nullptr;
data->cd_ofs = CustomData_get_offset(&mr.bm->vdata, CD_MDEFORMVERT);
if (mr.extract_type == MR_EXTRACT_MESH) {
extract_weights_mesh(mr, weight_state, vbo_data);
}
else {
data->dvert = mr.mesh->deform_verts().data();
data->cd_ofs = -1;
extract_weights_bm(mr, weight_state, vbo_data);
}
}
static void extract_weights_iter_face_bm(const MeshRenderData & /*mr*/,
const BMFace *f,
const int /*f_index*/,
void *_data)
void extract_weights_subdiv(const MeshRenderData &mr,
const DRWSubdivCache &subdiv_cache,
const MeshBatchCache &cache,
gpu::VertBuf &vbo)
{
MeshExtract_Weight_Data *data = static_cast<MeshExtract_Weight_Data *>(_data);
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
const int l_index = BM_elem_index_get(l_iter);
if (data->cd_ofs != -1) {
const MDeformVert *dvert = (const MDeformVert *)BM_ELEM_CD_GET_VOID_P(l_iter->v,
data->cd_ofs);
data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate);
}
else {
data->vbo_data[l_index] = evaluate_vertex_weight(nullptr, data->wstate);
}
} while ((l_iter = l_iter->next) != l_first);
}
static void extract_weights_iter_face_mesh(const MeshRenderData &mr,
const int face_index,
void *_data)
{
MeshExtract_Weight_Data *data = static_cast<MeshExtract_Weight_Data *>(_data);
for (const int corner : mr.faces[face_index]) {
const int vert = mr.corner_verts[corner];
if (data->dvert != nullptr) {
const MDeformVert *dvert = &data->dvert[vert];
data->vbo_data[corner] = evaluate_vertex_weight(dvert, data->wstate);
}
else {
const MDeformVert *dvert = nullptr;
data->vbo_data[corner] = evaluate_vertex_weight(dvert, data->wstate);
}
}
}
static void extract_weights_init_subdiv(const DRWSubdivCache &subdiv_cache,
const MeshRenderData &mr,
MeshBatchCache &cache,
void *buffer,
void *_data)
{
const Mesh *coarse_mesh = subdiv_cache.mesh;
gpu::VertBuf *vbo = static_cast<gpu::VertBuf *>(buffer);
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
}
GPU_vertbuf_init_build_on_device(vbo, &format, subdiv_cache.num_subdiv_loops);
GPU_vertbuf_init_build_on_device(&vbo, &format, subdiv_cache.num_subdiv_loops);
gpu::VertBuf *coarse_weights = GPU_vertbuf_calloc();
extract_weights_init(mr, cache, coarse_weights, _data);
extract_weights(mr, cache, *coarse_weights);
if (mr.extract_type != MR_EXTRACT_BMESH) {
const OffsetIndices coarse_faces = coarse_mesh->faces();
for (const int i : coarse_faces.index_range()) {
extract_weights_iter_face_mesh(mr, i, _data);
}
}
else {
BMIter f_iter;
BMFace *efa;
int face_index = 0;
BM_ITER_MESH_INDEX (efa, &f_iter, mr.bm, BM_FACES_OF_MESH, face_index) {
extract_weights_iter_face_bm(mr, efa, face_index, _data);
}
}
draw_subdiv_interp_custom_data(subdiv_cache, coarse_weights, vbo, GPU_COMP_F32, 1, 0);
draw_subdiv_interp_custom_data(subdiv_cache, coarse_weights, &vbo, GPU_COMP_F32, 1, 0);
GPU_vertbuf_discard(coarse_weights);
}
constexpr MeshExtract create_extractor_weights()
{
MeshExtract extractor = {nullptr};
extractor.init = extract_weights_init;
extractor.init_subdiv = extract_weights_init_subdiv;
extractor.iter_face_bm = extract_weights_iter_face_bm;
extractor.iter_face_mesh = extract_weights_iter_face_mesh;
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_Weight_Data);
extractor.use_threading = true;
extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.weights);
return extractor;
}
/** \} */
const MeshExtract extract_weights = create_extractor_weights();
} // namespace blender::draw