Files
test/source/blender/draw/intern/draw_cache_extract_mesh.cc
Hans Goudey 1f92fd7577 Refactor: Use AttrType instead of CustomData type in attribute API
Change `eCustomDataType` to `bke::AttrType` for uses of the attribute
API (the `AttributeAccessor` one anyway). I didn't touch any values that
might be saved in files; those should be handled on a case by case basis.

Part of #122398

Pull Request: https://projects.blender.org/blender/blender/pulls/141301
2025-07-01 22:14:26 +02:00

505 lines
18 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 "BKE_attribute.hh"
#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
* \{ */
/**
* The mesh normals access functions can end up mixing face corner normals calculated with the
* costly tangent space method. The "Simplify Normals" option is supposed to avoid that, but not
* the "Free" normals which are actually cheaper than calculating true normals.
*/
static bool use_normals_simplify(const Scene &scene, const MeshRenderData &mr)
{
if (!(scene.r.mode & R_SIMPLIFY) || !(scene.r.mode & R_SIMPLIFY_NORMALS)) {
return false;
}
if (!mr.mesh) {
return true;
}
const Mesh &mesh = *mr.mesh;
const std::optional<bke::AttributeMetaData> meta_data = mesh.attributes().lookup_meta_data(
"custom_normal");
if (!meta_data) {
return false;
}
if (meta_data->domain == bke::AttrDomain::Corner &&
meta_data->data_type == bke::AttrType::Int16_2D)
{
return true;
}
return false;
}
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);
mr.use_subsurf_fdots = mr.mesh && !mr.mesh->runtime->subsurf_face_dot_tags.is_empty();
mr.use_simplify_normals = use_normals_simplify(scene, mr);
ensure_dependency_data(mr, ibo_requests, vbo_requests, mbc);
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;
case VBOType::PaintOverlayFlag:
created_vbos[i] = extract_paint_overlay_flags(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;
/* Don't use `add_new` because #VBOType::Orco might be requested after #VBOType::Position
* already exists. It's inefficient to build the position VBO a second time but that's the API
* that GPU subdivision provides. */
buffers.vbos.add(
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);
if (vbos_to_create.contains(VBOType::FaceDotPosition)) {
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));
}
if (ibos_to_create.contains(IBOType::FaceDots)) {
buffers.ibos.add_new(IBOType::FaceDots, std::move(face_dot_ibo));
}
}
if (vbos_to_create.contains(VBOType::PaintOverlayFlag)) {
buffers.vbos.add_new(VBOType::PaintOverlayFlag,
extract_paint_overlay_flags_subdiv(mr, subdiv_cache));
}
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