Files
test/source/blender/draw/intern/draw_cache_extract_mesh.cc
Hans Goudey 760cf70d63 Draw: Use VectorSet for generic attribute requests
Replace `DRW_Attributes` with a VectorSet of std::string. The max number of
attributes is still the same. The inline buffer size is 4, and std::string's inline
buffer is smaller than the previous char array size of 64, but it seems
reasonable to save those optimizations for shorter attribute names and
fewer attributes. In return we significantly decrease the size of the batch
caches, simplify the code, and remove the attribute name length limit.

I observed roughly an 8% increase in the 30k cube objects file, a change from
12 to 13 FPS. I'm guessing this is mostly because `VectorSet<std::string>` is
smaller than `DRW_Attributes`.

Pull Request: https://projects.blender.org/blender/blender/pulls/138946
2025-05-15 20:43:31 +02:00

462 lines
17 KiB
C++

/* SPDX-FileCopyrightText: 2017 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*
* \brief Extraction of Mesh data into VBO to feed to GPU.
*/
#include "DNA_mesh_types.h"
#include "DNA_scene_types.h"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_task.hh"
#include "GPU_capabilities.hh"
#include "draw_cache_extract.hh"
#include "draw_subdivision.hh"
#include "mesh_extractors/extract_mesh.hh"
// #define DEBUG_TIME
#ifdef DEBUG_TIME
# include "BLI_time_utildefines.h"
#endif
namespace blender::draw {
static void ensure_dependency_data(MeshRenderData &mr,
Span<IBOType> ibo_requests,
Span<VBOType> vbo_requests,
MeshBufferCache &cache)
{
const bool request_face_normals = vbo_requests.contains(VBOType::CornerNormal) ||
vbo_requests.contains(VBOType::FaceDotNormal) ||
vbo_requests.contains(VBOType::EdgeFactor) ||
vbo_requests.contains(VBOType::MeshAnalysis);
const bool request_corner_normals = vbo_requests.contains(VBOType::CornerNormal);
const bool force_corner_normals = vbo_requests.contains(VBOType::Tangents);
if (request_face_normals) {
mesh_render_data_update_face_normals(mr);
}
if ((request_corner_normals && mr.normals_domain == bke::MeshNormalDomain::Corner &&
!mr.use_simplify_normals) ||
force_corner_normals)
{
mesh_render_data_update_corner_normals(mr);
}
const bool calc_loose_geom = ibo_requests.contains(IBOType::Lines) ||
ibo_requests.contains(IBOType::LinesLoose) ||
ibo_requests.contains(IBOType::Points) ||
vbo_requests.contains(VBOType::Position) ||
vbo_requests.contains(VBOType::EditData) ||
vbo_requests.contains(VBOType::VertexNormal) ||
vbo_requests.contains(VBOType::IndexVert) ||
vbo_requests.contains(VBOType::IndexEdge) ||
vbo_requests.contains(VBOType::EdgeFactor);
if (calc_loose_geom) {
mesh_render_data_update_loose_geom(mr, cache);
}
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Extract Loop
* \{ */
void mesh_buffer_cache_create_requested(TaskGraph & /*task_graph*/,
const Scene &scene,
MeshBatchCache &cache,
MeshBufferCache &mbc,
const Span<IBOType> ibo_requests,
const Span<VBOType> vbo_requests,
Object &object,
Mesh &mesh,
const bool is_editmode,
const bool is_paint_mode,
const bool do_final,
const bool do_uvedit,
const bool use_hide)
{
if (ibo_requests.is_empty() && vbo_requests.is_empty()) {
return;
}
MeshBufferList &buffers = mbc.buff;
Vector<IBOType, 16> ibos_to_create;
for (const IBOType request : ibo_requests) {
if (!buffers.ibos.contains(request)) {
ibos_to_create.append(request);
}
}
Vector<VBOType, 16> vbos_to_create;
for (const VBOType request : vbo_requests) {
if (!buffers.vbos.contains(request)) {
vbos_to_create.append(request);
}
}
if (ibos_to_create.is_empty() && vbos_to_create.is_empty()) {
return;
}
#ifdef DEBUG_TIME
SCOPED_TIMER(__func__);
#endif
MeshRenderData mr = mesh_render_data_create(
object, mesh, is_editmode, is_paint_mode, do_final, do_uvedit, use_hide, scene.toolsettings);
ensure_dependency_data(mr, ibo_requests, vbo_requests, mbc);
mr.use_subsurf_fdots = mr.mesh && !mr.mesh->runtime->subsurf_face_dot_tags.is_empty();
mr.use_simplify_normals = (scene.r.mode & R_SIMPLIFY) && (scene.r.mode & R_SIMPLIFY_NORMALS);
Array<gpu::IndexBufPtr, 16> created_ibos(ibos_to_create.size());
{
/* Because lines and loose lines are stored in the same buffer, they're handled separately
* rather than from potentially multiple threads in the parallel_for_each loop below. */
const int lines_index = ibos_to_create.as_span().first_index_try(IBOType::Lines);
const int loose_lines_index = ibos_to_create.as_span().first_index_try(IBOType::LinesLoose);
if (lines_index != -1 || loose_lines_index != -1) {
extract_lines(mr,
lines_index == -1 ? nullptr : &created_ibos[lines_index],
loose_lines_index == -1 ? nullptr : &created_ibos[loose_lines_index],
cache.no_loose_wire);
}
}
threading::parallel_for_each(ibos_to_create.index_range(), [&](const int i) {
switch (ibos_to_create[i]) {
case IBOType::Tris:
created_ibos[i] = extract_tris(mr, mesh_render_data_faces_sorted_ensure(mr, mbc));
break;
case IBOType::Lines:
case IBOType::LinesLoose:
/* Handled as a special case above. */
break;
case IBOType::Points:
created_ibos[i] = extract_points(mr);
break;
case IBOType::FaceDots:
created_ibos[i] = extract_face_dots(mr);
break;
case IBOType::LinesPaintMask:
created_ibos[i] = extract_lines_paint_mask(mr);
break;
case IBOType::LinesAdjacency:
created_ibos[i] = extract_lines_adjacency(mr, cache.is_manifold);
break;
case IBOType::UVLines:
created_ibos[i] = extract_edituv_lines(mr, false);
break;
case IBOType::EditUVTris:
created_ibos[i] = extract_edituv_tris(mr);
break;
case IBOType::EditUVLines:
created_ibos[i] = extract_edituv_lines(mr, true);
break;
case IBOType::EditUVPoints:
created_ibos[i] = extract_edituv_points(mr);
break;
case IBOType::EditUVFaceDots:
created_ibos[i] = extract_edituv_face_dots(mr);
break;
}
});
Array<gpu::VertBufPtr, 16> created_vbos(vbos_to_create.size());
const bool do_hq_normals = (scene.r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
GPU_use_hq_normals_workaround();
threading::parallel_for_each(vbos_to_create.index_range(), [&](const int i) {
switch (vbos_to_create[i]) {
case VBOType::Position:
created_vbos[i] = extract_positions(mr);
break;
case VBOType::CornerNormal:
created_vbos[i] = extract_normals(mr, do_hq_normals);
break;
case VBOType::EdgeFactor:
created_vbos[i] = extract_edge_factor(mr);
break;
case VBOType::VertexGroupWeight:
created_vbos[i] = extract_weights(mr, cache);
break;
case VBOType::UVs:
created_vbos[i] = extract_uv_maps(mr, cache);
break;
case VBOType::Tangents:
created_vbos[i] = extract_tangents(mr, cache, do_hq_normals);
break;
case VBOType::SculptData:
created_vbos[i] = extract_sculpt_data(mr);
break;
case VBOType::Orco:
created_vbos[i] = extract_orco(mr);
break;
case VBOType::EditData:
created_vbos[i] = extract_edit_data(mr);
break;
case VBOType::EditUVData:
created_vbos[i] = extract_edituv_data(mr);
break;
case VBOType::EditUVStretchArea:
created_vbos[i] = extract_edituv_stretch_area(mr, cache.tot_area, cache.tot_uv_area);
break;
case VBOType::EditUVStretchAngle:
created_vbos[i] = extract_edituv_stretch_angle(mr);
break;
case VBOType::MeshAnalysis:
created_vbos[i] = extract_mesh_analysis(mr, object.object_to_world());
break;
case VBOType::FaceDotPosition:
created_vbos[i] = extract_face_dots_position(mr);
break;
case VBOType::FaceDotNormal:
created_vbos[i] = extract_face_dot_normals(mr, do_hq_normals);
break;
case VBOType::FaceDotUV:
created_vbos[i] = extract_face_dots_uv(mr);
break;
case VBOType::FaceDotEditUVData:
created_vbos[i] = extract_face_dots_edituv_data(mr);
break;
case VBOType::SkinRoots:
created_vbos[i] = extract_skin_roots(mr);
break;
case VBOType::IndexVert:
created_vbos[i] = extract_vert_index(mr);
break;
case VBOType::IndexEdge:
created_vbos[i] = extract_edge_index(mr);
break;
case VBOType::IndexFace:
created_vbos[i] = extract_face_index(mr);
break;
case VBOType::IndexFaceDot:
created_vbos[i] = extract_face_dot_index(mr);
break;
case VBOType::Attr0:
case VBOType::Attr1:
case VBOType::Attr2:
case VBOType::Attr3:
case VBOType::Attr5:
case VBOType::Attr6:
case VBOType::Attr7:
case VBOType::Attr8:
case VBOType::Attr9:
case VBOType::Attr10:
case VBOType::Attr11:
case VBOType::Attr12:
case VBOType::Attr13:
case VBOType::Attr14:
case VBOType::Attr15: {
const int8_t attr_index = int8_t(vbos_to_create[i]) - int8_t(VBOType::Attr0);
created_vbos[i] = extract_attribute(mr, cache.attr_used[attr_index]);
break;
}
case VBOType::AttrViewer:
created_vbos[i] = extract_attr_viewer(mr);
break;
case VBOType::VertexNormal:
created_vbos[i] = extract_vert_normals(mr);
break;
}
});
for (const int i : ibos_to_create.index_range()) {
buffers.ibos.add_new(ibos_to_create[i], std::move(created_ibos[i]));
}
for (const int i : vbos_to_create.index_range()) {
buffers.vbos.add_new(vbos_to_create[i], std::move(created_vbos[i]));
}
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Subdivision Extract Loop
* \{ */
void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache &cache,
MeshBufferCache &mbc,
const Span<IBOType> ibo_requests,
const Span<VBOType> vbo_requests,
DRWSubdivCache &subdiv_cache,
MeshRenderData &mr)
{
if (ibo_requests.is_empty() && vbo_requests.is_empty()) {
return;
}
MeshBufferList &buffers = mbc.buff;
mesh_render_data_update_corner_normals(mr);
mesh_render_data_update_loose_geom(mr, mbc);
DRW_subdivide_loose_geom(subdiv_cache, mbc);
Set<IBOType, 16> ibos_to_create;
for (const IBOType request : ibo_requests) {
if (!buffers.ibos.contains(request)) {
ibos_to_create.add_new(request);
}
}
Set<VBOType, 16> vbos_to_create;
for (const VBOType request : vbo_requests) {
if (!buffers.vbos.contains(request)) {
vbos_to_create.add_new(request);
}
}
if (ibos_to_create.is_empty() && vbos_to_create.is_empty()) {
return;
}
if (vbos_to_create.contains(VBOType::Position) || vbos_to_create.contains(VBOType::Orco)) {
gpu::VertBufPtr orco_vbo;
buffers.vbos.add_new(
VBOType::Position,
extract_positions_subdiv(
subdiv_cache, mr, vbos_to_create.contains(VBOType::Orco) ? &orco_vbo : nullptr));
if (orco_vbo) {
buffers.vbos.add_new(VBOType::Orco, std::move(orco_vbo));
}
}
if (vbos_to_create.contains(VBOType::CornerNormal)) {
/* The corner normals calculation uses positions and normals stored in the `pos` VBO. */
buffers.vbos.add_new(
VBOType::CornerNormal,
extract_normals_subdiv(mr, subdiv_cache, *buffers.vbos.lookup(VBOType::Position)));
}
if (vbos_to_create.contains(VBOType::EdgeFactor)) {
buffers.vbos.add_new(
VBOType::EdgeFactor,
extract_edge_factor_subdiv(subdiv_cache, mr, *buffers.vbos.lookup(VBOType::Position)));
}
if (ibos_to_create.contains(IBOType::Lines) || ibos_to_create.contains(IBOType::LinesLoose)) {
gpu::IndexBufPtr lines_ibo;
gpu::IndexBufPtr lines_loose_ibo;
extract_lines_subdiv(subdiv_cache,
mr,
ibos_to_create.contains(IBOType::Lines) ? &lines_ibo : nullptr,
ibos_to_create.contains(IBOType::LinesLoose) ? &lines_loose_ibo : nullptr,
cache.no_loose_wire);
if (lines_ibo) {
buffers.ibos.add_new(IBOType::Lines, std::move(lines_ibo));
}
if (lines_loose_ibo) {
buffers.ibos.add_new(IBOType::LinesLoose, std::move(lines_loose_ibo));
}
}
if (ibos_to_create.contains(IBOType::Tris)) {
buffers.ibos.add_new(IBOType::Tris, extract_tris_subdiv(subdiv_cache, cache));
}
if (ibos_to_create.contains(IBOType::Points)) {
buffers.ibos.add_new(IBOType::Points, extract_points_subdiv(mr, subdiv_cache));
}
if (vbos_to_create.contains(VBOType::EditData)) {
buffers.vbos.add_new(VBOType::EditData, extract_edit_data_subdiv(mr, subdiv_cache));
}
if (vbos_to_create.contains(VBOType::Tangents)) {
buffers.vbos.add_new(VBOType::Tangents, extract_tangents_subdiv(mr, subdiv_cache, cache));
}
if (vbos_to_create.contains(VBOType::IndexVert)) {
buffers.vbos.add_new(VBOType::IndexVert, extract_vert_index_subdiv(subdiv_cache, mr));
}
if (vbos_to_create.contains(VBOType::IndexEdge)) {
buffers.vbos.add_new(VBOType::IndexEdge, extract_edge_index_subdiv(subdiv_cache, mr));
}
if (vbos_to_create.contains(VBOType::IndexFace)) {
buffers.vbos.add_new(VBOType::IndexFace, extract_face_index_subdiv(subdiv_cache, mr));
}
if (vbos_to_create.contains(VBOType::VertexGroupWeight)) {
buffers.vbos.add_new(VBOType::VertexGroupWeight,
extract_weights_subdiv(mr, subdiv_cache, cache));
}
if (vbos_to_create.contains(VBOType::FaceDotNormal) ||
vbos_to_create.contains(VBOType::FaceDotPosition) ||
ibos_to_create.contains(IBOType::FaceDots))
{
gpu::VertBufPtr face_dot_position_vbo;
gpu::VertBufPtr face_dot_normal_vbo;
gpu::IndexBufPtr face_dot_ibo;
/* We use only one extractor for face dots, as the work is done in a single compute shader. */
extract_face_dots_subdiv(
subdiv_cache,
face_dot_position_vbo,
vbos_to_create.contains(VBOType::FaceDotNormal) ? &face_dot_normal_vbo : nullptr,
face_dot_ibo);
buffers.vbos.add_new(VBOType::FaceDotPosition, std::move(face_dot_position_vbo));
if (face_dot_normal_vbo) {
buffers.vbos.add_new(VBOType::FaceDotNormal, std::move(face_dot_normal_vbo));
}
buffers.ibos.add_new(IBOType::FaceDots, std::move(face_dot_ibo));
}
if (ibos_to_create.contains(IBOType::LinesPaintMask)) {
buffers.ibos.add_new(IBOType::LinesPaintMask,
extract_lines_paint_mask_subdiv(mr, subdiv_cache));
}
if (ibos_to_create.contains(IBOType::LinesAdjacency)) {
buffers.ibos.add_new(IBOType::LinesAdjacency,
extract_lines_adjacency_subdiv(subdiv_cache, cache.is_manifold));
}
if (vbos_to_create.contains(VBOType::SculptData)) {
buffers.vbos.add_new(VBOType::SculptData, extract_sculpt_data_subdiv(mr, subdiv_cache));
}
if (vbos_to_create.contains(VBOType::UVs)) {
/* Make sure UVs are computed before edituv stuffs. */
buffers.vbos.add_new(VBOType::UVs, extract_uv_maps_subdiv(subdiv_cache, cache));
}
if (ibos_to_create.contains(IBOType::UVLines)) {
buffers.ibos.add_new(IBOType::UVLines, extract_edituv_lines_subdiv(mr, subdiv_cache, false));
}
if (vbos_to_create.contains(VBOType::EditUVStretchArea)) {
buffers.vbos.add_new(
VBOType::EditUVStretchArea,
extract_edituv_stretch_area_subdiv(mr, subdiv_cache, cache.tot_area, cache.tot_uv_area));
}
if (vbos_to_create.contains(VBOType::EditUVStretchAngle)) {
buffers.vbos.add_new(VBOType::EditUVStretchAngle,
extract_edituv_stretch_angle_subdiv(mr, subdiv_cache, cache));
}
if (vbos_to_create.contains(VBOType::EditUVData)) {
buffers.vbos.add_new(VBOType::EditUVData, extract_edituv_data_subdiv(mr, subdiv_cache));
}
if (ibos_to_create.contains(IBOType::EditUVTris)) {
buffers.ibos.add_new(IBOType::EditUVTris, extract_edituv_tris_subdiv(mr, subdiv_cache));
}
if (ibos_to_create.contains(IBOType::EditUVLines)) {
buffers.ibos.add_new(IBOType::EditUVLines,
extract_edituv_lines_subdiv(mr, subdiv_cache, true));
}
if (ibos_to_create.contains(IBOType::EditUVPoints)) {
buffers.ibos.add_new(IBOType::EditUVPoints, extract_edituv_points_subdiv(mr, subdiv_cache));
}
for (const int8_t i : IndexRange(GPU_MAX_ATTR)) {
const VBOType request = VBOType(int8_t(VBOType::Attr0) + i);
if (vbos_to_create.contains(request)) {
buffers.vbos.add_new(request,
extract_attribute_subdiv(mr, subdiv_cache, cache.attr_used[i]));
}
}
}
/** \} */
} // namespace blender::draw