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
462 lines
17 KiB
C++
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
|