2023-08-15 16:20:26 +02:00
|
|
|
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2019-07-14 16:49:44 +02:00
|
|
|
|
|
|
|
/** \file
|
|
|
|
* \ingroup draw
|
|
|
|
*
|
|
|
|
* \brief Extraction of Mesh data into VBO to feed to GPU.
|
|
|
|
*/
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
2021-06-08 16:35:33 +02:00
|
|
|
#include <optional>
|
|
|
|
|
2021-06-01 09:23:37 +02:00
|
|
|
#include "atomic_ops.h"
|
2020-07-10 04:38:28 +02:00
|
|
|
|
2021-06-01 09:23:37 +02:00
|
|
|
#include "DNA_mesh_types.h"
|
|
|
|
#include "DNA_scene_types.h"
|
2020-07-10 04:38:28 +02:00
|
|
|
|
2021-06-08 16:35:33 +02:00
|
|
|
#include "BLI_array.hh"
|
|
|
|
#include "BLI_math_bits.h"
|
2021-06-01 09:23:37 +02:00
|
|
|
#include "BLI_task.h"
|
2021-06-01 12:12:13 +02:00
|
|
|
#include "BLI_vector.hh"
|
2019-07-14 16:49:44 +02:00
|
|
|
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_editmesh.hh"
|
2021-05-31 17:11:01 +02:00
|
|
|
|
2021-06-01 09:23:37 +02:00
|
|
|
#include "GPU_capabilities.h"
|
2020-07-10 04:38:28 +02:00
|
|
|
|
2022-06-05 12:04:42 +02:00
|
|
|
#include "draw_cache_extract.hh"
|
2021-06-01 09:23:37 +02:00
|
|
|
#include "draw_cache_inline.h"
|
2023-11-21 04:04:08 +01:00
|
|
|
#include "draw_subdivision.hh"
|
2021-05-31 17:11:01 +02:00
|
|
|
|
2022-06-05 12:04:42 +02:00
|
|
|
#include "mesh_extractors/extract_mesh.hh"
|
2021-07-26 14:54:59 +02:00
|
|
|
|
2021-06-01 09:23:37 +02:00
|
|
|
// #define DEBUG_TIME
|
2019-07-14 16:49:44 +02:00
|
|
|
|
2021-06-01 09:23:37 +02:00
|
|
|
#ifdef DEBUG_TIME
|
|
|
|
# include "PIL_time_utildefines.h"
|
|
|
|
#endif
|
2019-07-14 16:49:44 +02:00
|
|
|
|
2021-06-01 12:12:13 +02:00
|
|
|
namespace blender::draw {
|
|
|
|
|
2019-07-14 16:49:44 +02:00
|
|
|
/* ---------------------------------------------------------------------- */
|
2021-06-01 09:23:37 +02:00
|
|
|
/** \name Mesh Elements Extract Struct
|
2019-07-14 16:49:44 +02:00
|
|
|
* \{ */
|
2021-12-14 05:49:31 +01:00
|
|
|
|
2021-06-08 16:35:33 +02:00
|
|
|
using TaskId = int;
|
|
|
|
using TaskLen = int;
|
2019-07-14 16:49:44 +02:00
|
|
|
|
2021-06-01 12:59:15 +02:00
|
|
|
struct ExtractorRunData {
|
|
|
|
/* Extractor where this run data belongs to. */
|
2021-06-01 09:23:37 +02:00
|
|
|
const MeshExtract *extractor;
|
2021-06-01 12:59:15 +02:00
|
|
|
/* During iteration the VBO/IBO that is being build. */
|
|
|
|
void *buffer = nullptr;
|
2021-06-10 16:01:36 +02:00
|
|
|
uint32_t data_offset = 0;
|
2019-07-14 16:49:44 +02:00
|
|
|
|
2023-03-29 16:50:54 +02:00
|
|
|
ExtractorRunData(const MeshExtract *extractor) : extractor(extractor) {}
|
2021-06-07 13:34:30 +02:00
|
|
|
|
|
|
|
#ifdef WITH_CXX_GUARDEDALLOC
|
|
|
|
MEM_CXX_CLASS_ALLOC_FUNCS("DRAW:ExtractorRunData")
|
|
|
|
#endif
|
2021-06-01 12:59:15 +02:00
|
|
|
};
|
2021-01-11 09:48:42 +01:00
|
|
|
|
2021-06-01 12:59:15 +02:00
|
|
|
class ExtractorRunDatas : public Vector<ExtractorRunData> {
|
|
|
|
public:
|
2021-07-16 21:18:54 +02:00
|
|
|
void filter_into(ExtractorRunDatas &result, eMRIterType iter_type, const bool is_mesh) const
|
2021-06-01 12:59:15 +02:00
|
|
|
{
|
|
|
|
for (const ExtractorRunData &data : *this) {
|
|
|
|
const MeshExtract *extractor = data.extractor;
|
2021-07-16 21:18:54 +02:00
|
|
|
if ((iter_type & MR_ITER_LOOPTRI) && *(&extractor->iter_looptri_bm + is_mesh)) {
|
2021-06-01 12:59:15 +02:00
|
|
|
result.append(data);
|
|
|
|
continue;
|
|
|
|
}
|
2023-07-24 22:06:55 +02:00
|
|
|
if ((iter_type & MR_ITER_POLY) && *(&extractor->iter_face_bm + is_mesh)) {
|
2021-06-01 12:59:15 +02:00
|
|
|
result.append(data);
|
|
|
|
continue;
|
|
|
|
}
|
2023-03-19 23:43:32 +01:00
|
|
|
if ((iter_type & MR_ITER_LOOSE_EDGE) && *(&extractor->iter_loose_edge_bm + is_mesh)) {
|
2021-06-01 12:59:15 +02:00
|
|
|
result.append(data);
|
|
|
|
continue;
|
|
|
|
}
|
2023-03-19 23:43:32 +01:00
|
|
|
if ((iter_type & MR_ITER_LOOSE_VERT) && *(&extractor->iter_loose_vert_bm + is_mesh)) {
|
2021-06-01 12:59:15 +02:00
|
|
|
result.append(data);
|
|
|
|
continue;
|
|
|
|
}
|
2021-01-11 09:48:42 +01:00
|
|
|
}
|
2021-06-01 12:59:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void filter_threaded_extractors_into(ExtractorRunDatas &result)
|
|
|
|
{
|
|
|
|
for (const ExtractorRunData &data : *this) {
|
|
|
|
const MeshExtract *extractor = data.extractor;
|
|
|
|
if (extractor->use_threading) {
|
|
|
|
result.append(extractor);
|
|
|
|
}
|
2019-07-14 16:49:44 +02:00
|
|
|
}
|
2021-06-01 12:59:15 +02:00
|
|
|
}
|
|
|
|
|
2021-06-08 16:35:33 +02:00
|
|
|
eMRIterType iter_types() const
|
2021-06-01 12:59:15 +02:00
|
|
|
{
|
|
|
|
eMRIterType iter_type = static_cast<eMRIterType>(0);
|
|
|
|
|
|
|
|
for (const ExtractorRunData &data : *this) {
|
|
|
|
const MeshExtract *extractor = data.extractor;
|
|
|
|
iter_type |= mesh_extract_iter_type(extractor);
|
2020-06-30 16:13:39 +02:00
|
|
|
}
|
2021-06-01 12:59:15 +02:00
|
|
|
return iter_type;
|
2019-07-14 16:49:44 +02:00
|
|
|
}
|
|
|
|
|
2021-06-18 06:27:43 +02:00
|
|
|
uint iter_types_len() const
|
2021-06-08 16:35:33 +02:00
|
|
|
{
|
|
|
|
const eMRIterType iter_type = iter_types();
|
2022-09-25 10:30:50 +02:00
|
|
|
uint bits = uint(iter_type);
|
2021-06-08 16:35:33 +02:00
|
|
|
return count_bits_i(bits);
|
|
|
|
}
|
|
|
|
|
2021-06-01 12:59:15 +02:00
|
|
|
eMRDataType data_types()
|
|
|
|
{
|
|
|
|
eMRDataType data_type = static_cast<eMRDataType>(0);
|
|
|
|
for (const ExtractorRunData &data : *this) {
|
|
|
|
const MeshExtract *extractor = data.extractor;
|
|
|
|
data_type |= extractor->data_type;
|
2019-10-15 01:49:53 +02:00
|
|
|
}
|
2021-06-01 12:59:15 +02:00
|
|
|
return data_type;
|
2019-10-15 01:49:53 +02:00
|
|
|
}
|
2021-06-07 13:34:30 +02:00
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
size_t data_size_total()
|
|
|
|
{
|
|
|
|
size_t data_size = 0;
|
|
|
|
for (const ExtractorRunData &data : *this) {
|
|
|
|
const MeshExtract *extractor = data.extractor;
|
|
|
|
data_size += extractor->data_size;
|
|
|
|
}
|
|
|
|
return data_size;
|
|
|
|
}
|
|
|
|
|
2021-06-07 13:34:30 +02:00
|
|
|
#ifdef WITH_CXX_GUARDEDALLOC
|
|
|
|
MEM_CXX_CLASS_ALLOC_FUNCS("DRAW:ExtractorRunDatas")
|
|
|
|
#endif
|
2021-06-01 12:59:15 +02:00
|
|
|
};
|
2019-10-15 01:49:53 +02:00
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
2021-05-31 17:11:01 +02:00
|
|
|
/* ---------------------------------------------------------------------- */
|
2021-06-10 16:01:36 +02:00
|
|
|
/** \name ExtractTaskData
|
|
|
|
* \{ */
|
2021-12-14 05:49:31 +01:00
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
struct ExtractTaskData {
|
|
|
|
const MeshRenderData *mr = nullptr;
|
|
|
|
MeshBatchCache *cache = nullptr;
|
|
|
|
ExtractorRunDatas *extractors = nullptr;
|
2021-08-23 17:33:12 +02:00
|
|
|
MeshBufferList *mbuflist = nullptr;
|
2021-06-10 16:01:36 +02:00
|
|
|
|
|
|
|
eMRIterType iter_type;
|
|
|
|
bool use_threading = false;
|
|
|
|
|
2023-08-03 05:59:31 +02:00
|
|
|
ExtractTaskData(const MeshRenderData &mr,
|
|
|
|
MeshBatchCache &cache,
|
2021-06-10 16:01:36 +02:00
|
|
|
ExtractorRunDatas *extractors,
|
2021-08-23 17:33:12 +02:00
|
|
|
MeshBufferList *mbuflist,
|
2021-06-10 16:01:36 +02:00
|
|
|
const bool use_threading)
|
2023-08-03 05:59:31 +02:00
|
|
|
: mr(&mr),
|
|
|
|
cache(&cache),
|
2021-08-23 17:33:12 +02:00
|
|
|
extractors(extractors),
|
|
|
|
mbuflist(mbuflist),
|
|
|
|
use_threading(use_threading)
|
2021-06-10 16:01:36 +02:00
|
|
|
{
|
|
|
|
iter_type = extractors->iter_types();
|
|
|
|
};
|
|
|
|
|
|
|
|
ExtractTaskData(const ExtractTaskData &src) = default;
|
|
|
|
|
|
|
|
~ExtractTaskData()
|
|
|
|
{
|
|
|
|
delete extractors;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef WITH_CXX_GUARDEDALLOC
|
|
|
|
MEM_CXX_CLASS_ALLOC_FUNCS("DRW:ExtractTaskData")
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static void extract_task_data_free(void *data)
|
|
|
|
{
|
|
|
|
ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data);
|
|
|
|
delete task_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/** \name Extract Init and Finish
|
2021-05-31 17:11:01 +02:00
|
|
|
* \{ */
|
|
|
|
|
2023-08-03 05:59:31 +02:00
|
|
|
BLI_INLINE void extract_init(const MeshRenderData &mr,
|
|
|
|
MeshBatchCache &cache,
|
2021-06-01 12:59:15 +02:00
|
|
|
ExtractorRunDatas &extractors,
|
2021-08-23 17:33:12 +02:00
|
|
|
MeshBufferList *mbuflist,
|
2021-06-10 16:01:36 +02:00
|
|
|
void *data_stack)
|
2021-05-31 17:11:01 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
uint32_t data_offset = 0;
|
2021-06-01 12:59:15 +02:00
|
|
|
for (ExtractorRunData &run_data : extractors) {
|
2021-06-01 12:12:13 +02:00
|
|
|
const MeshExtract *extractor = run_data.extractor;
|
2021-08-23 17:33:12 +02:00
|
|
|
run_data.buffer = mesh_extract_buffer_get(extractor, mbuflist);
|
2021-06-10 16:01:36 +02:00
|
|
|
run_data.data_offset = data_offset;
|
|
|
|
extractor->init(mr, cache, run_data.buffer, POINTER_OFFSET(data_stack, data_offset));
|
2022-09-25 10:33:28 +02:00
|
|
|
data_offset += uint32_t(extractor->data_size);
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-03 05:59:31 +02:00
|
|
|
BLI_INLINE void extract_finish(const MeshRenderData &mr,
|
|
|
|
MeshBatchCache &cache,
|
2021-06-10 16:01:36 +02:00
|
|
|
const ExtractorRunDatas &extractors,
|
|
|
|
void *data_stack)
|
2021-05-31 17:11:01 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
for (const ExtractorRunData &run_data : extractors) {
|
|
|
|
const MeshExtract *extractor = run_data.extractor;
|
|
|
|
if (extractor->finish) {
|
|
|
|
extractor->finish(
|
|
|
|
mr, cache, run_data.buffer, POINTER_OFFSET(data_stack, run_data.data_offset));
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/** \name Extract In Parallel Ranges
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
struct ExtractorIterData {
|
2021-06-01 12:59:15 +02:00
|
|
|
ExtractorRunDatas extractors;
|
2021-06-10 16:01:36 +02:00
|
|
|
const MeshRenderData *mr = nullptr;
|
|
|
|
const void *elems = nullptr;
|
|
|
|
const int *loose_elems = nullptr;
|
2021-05-31 17:11:01 +02:00
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
#ifdef WITH_CXX_GUARDEDALLOC
|
|
|
|
MEM_CXX_CLASS_ALLOC_FUNCS("DRW:MeshRenderDataUpdateTaskData")
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static void extract_task_reduce(const void *__restrict userdata,
|
|
|
|
void *__restrict chunk_to,
|
|
|
|
void *__restrict chunk_from)
|
|
|
|
{
|
|
|
|
const ExtractorIterData *data = static_cast<const ExtractorIterData *>(userdata);
|
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
|
|
|
const MeshExtract *extractor = run_data.extractor;
|
|
|
|
if (extractor->task_reduce) {
|
|
|
|
extractor->task_reduce(POINTER_OFFSET(chunk_to, run_data.data_offset),
|
|
|
|
POINTER_OFFSET(chunk_from, run_data.data_offset));
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
static void extract_range_iter_looptri_bm(void *__restrict userdata,
|
|
|
|
const int iter,
|
|
|
|
const TaskParallelTLS *__restrict tls)
|
2021-05-31 17:11:01 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
|
|
|
|
void *extract_data = tls->userdata_chunk;
|
2023-08-03 05:59:31 +02:00
|
|
|
const MeshRenderData &mr = *data->mr;
|
2021-06-10 16:01:36 +02:00
|
|
|
BMLoop **elt = ((BMLoop * (*)[3]) data->elems)[iter];
|
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
|
|
|
run_data.extractor->iter_looptri_bm(
|
|
|
|
mr, elt, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
static void extract_range_iter_looptri_mesh(void *__restrict userdata,
|
|
|
|
const int iter,
|
|
|
|
const TaskParallelTLS *__restrict tls)
|
2021-05-31 17:11:01 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
void *extract_data = tls->userdata_chunk;
|
2021-05-31 17:11:01 +02:00
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
|
2023-08-03 05:59:31 +02:00
|
|
|
const MeshRenderData &mr = *data->mr;
|
2021-06-10 16:01:36 +02:00
|
|
|
const MLoopTri *mlt = &((const MLoopTri *)data->elems)[iter];
|
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
|
|
|
run_data.extractor->iter_looptri_mesh(
|
|
|
|
mr, mlt, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-24 22:06:55 +02:00
|
|
|
static void extract_range_iter_face_bm(void *__restrict userdata,
|
2021-06-10 16:01:36 +02:00
|
|
|
const int iter,
|
|
|
|
const TaskParallelTLS *__restrict tls)
|
2021-05-31 17:11:01 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
void *extract_data = tls->userdata_chunk;
|
2021-05-31 17:11:01 +02:00
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
|
2023-08-03 05:59:31 +02:00
|
|
|
const MeshRenderData &mr = *data->mr;
|
2021-06-10 16:01:36 +02:00
|
|
|
const BMFace *f = ((const BMFace **)data->elems)[iter];
|
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
2023-07-24 22:06:55 +02:00
|
|
|
run_data.extractor->iter_face_bm(
|
2021-06-10 16:01:36 +02:00
|
|
|
mr, f, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-24 22:06:55 +02:00
|
|
|
static void extract_range_iter_face_mesh(void *__restrict userdata,
|
2021-06-10 16:01:36 +02:00
|
|
|
const int iter,
|
|
|
|
const TaskParallelTLS *__restrict tls)
|
2021-05-31 17:11:01 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
void *extract_data = tls->userdata_chunk;
|
2021-05-31 17:11:01 +02:00
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
|
2023-08-03 05:59:31 +02:00
|
|
|
const MeshRenderData &mr = *data->mr;
|
2021-06-10 16:01:36 +02:00
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
2023-07-24 22:06:55 +02:00
|
|
|
run_data.extractor->iter_face_mesh(
|
Mesh: Replace MPoly struct with offset indices
Implements #95967.
Currently the `MPoly` struct is 12 bytes, and stores the index of a
face's first corner and the number of corners/verts/edges. Polygons
and corners are always created in order by Blender, meaning each
face's corners will be after the previous face's corners. We can take
advantage of this fact and eliminate the redundancy in mesh face
storage by only storing a single integer corner offset for each face.
The size of the face is then encoded by the offset of the next face.
The size of a single integer is 4 bytes, so this reduces memory
usage by 3 times.
The same method is used for `CurvesGeometry`, so Blender already has
an abstraction to simplify using these offsets called `OffsetIndices`.
This class is used to easily retrieve a range of corner indices for
each face. This also gives the opportunity for sharing some logic with
curves.
Another benefit of the change is that the offsets and sizes stored in
`MPoly` can no longer disagree with each other. Storing faces in the
order of their corners can simplify some code too.
Face/polygon variables now use the `IndexRange` type, which comes with
quite a few utilities that can simplify code.
Some:
- The offset integer array has to be one longer than the face count to
avoid a branch for every face, which means the data is no longer part
of the mesh's `CustomData`.
- We lose the ability to "reference" an original mesh's offset array
until more reusable CoW from #104478 is committed. That will be added
in a separate commit.
- Since they aren't part of `CustomData`, poly offsets often have to be
copied manually.
- To simplify using `OffsetIndices` in many places, some functions and
structs in headers were moved to only compile in C++.
- All meshes created by Blender use the same order for faces and face
corners, but just in case, meshes with mismatched order are fixed by
versioning code.
- `MeshPolygon.totloop` is no longer editable in RNA. This API break is
necessary here unfortunately. It should be worth it in 3.6, since
that's the best way to allow loading meshes from 4.0, which is
important for an LTS version.
Pull Request: https://projects.blender.org/blender/blender/pulls/105938
2023-04-04 20:39:28 +02:00
|
|
|
mr, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-19 23:43:32 +01:00
|
|
|
static void extract_range_iter_loose_edge_bm(void *__restrict userdata,
|
|
|
|
const int iter,
|
|
|
|
const TaskParallelTLS *__restrict tls)
|
2021-05-31 17:11:01 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
void *extract_data = tls->userdata_chunk;
|
2021-05-31 17:11:01 +02:00
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
|
2023-08-03 05:59:31 +02:00
|
|
|
const MeshRenderData &mr = *data->mr;
|
2023-05-25 02:28:18 +02:00
|
|
|
const int loose_edge_i = data->loose_elems[iter];
|
|
|
|
const BMEdge *eed = ((const BMEdge **)data->elems)[loose_edge_i];
|
2021-06-10 16:01:36 +02:00
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
2023-03-19 23:43:32 +01:00
|
|
|
run_data.extractor->iter_loose_edge_bm(
|
2021-06-10 16:01:36 +02:00
|
|
|
mr, eed, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-19 23:43:32 +01:00
|
|
|
static void extract_range_iter_loose_edge_mesh(void *__restrict userdata,
|
|
|
|
const int iter,
|
|
|
|
const TaskParallelTLS *__restrict tls)
|
2021-05-31 17:11:01 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
void *extract_data = tls->userdata_chunk;
|
2021-05-31 17:11:01 +02:00
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
|
2023-08-03 05:59:31 +02:00
|
|
|
const MeshRenderData &mr = *data->mr;
|
2023-05-25 02:28:18 +02:00
|
|
|
const int loose_edge_i = data->loose_elems[iter];
|
|
|
|
const int2 edge = ((const int2 *)data->elems)[loose_edge_i];
|
2021-06-10 16:01:36 +02:00
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
2023-03-19 23:43:32 +01:00
|
|
|
run_data.extractor->iter_loose_edge_mesh(
|
2023-03-01 21:57:50 +01:00
|
|
|
mr, edge, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-19 23:43:32 +01:00
|
|
|
static void extract_range_iter_loose_vert_bm(void *__restrict userdata,
|
|
|
|
const int iter,
|
|
|
|
const TaskParallelTLS *__restrict tls)
|
2021-05-31 17:11:01 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
void *extract_data = tls->userdata_chunk;
|
|
|
|
|
|
|
|
const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
|
2023-08-03 05:59:31 +02:00
|
|
|
const MeshRenderData &mr = *data->mr;
|
2023-05-25 02:28:18 +02:00
|
|
|
const int loose_vert_i = data->loose_elems[iter];
|
|
|
|
const BMVert *eve = ((const BMVert **)data->elems)[loose_vert_i];
|
2021-06-10 16:01:36 +02:00
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
2023-03-19 23:43:32 +01:00
|
|
|
run_data.extractor->iter_loose_vert_bm(
|
2021-06-10 16:01:36 +02:00
|
|
|
mr, eve, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-19 23:43:32 +01:00
|
|
|
static void extract_range_iter_loose_vert_mesh(void *__restrict userdata,
|
|
|
|
const int iter,
|
|
|
|
const TaskParallelTLS *__restrict tls)
|
2021-06-08 16:35:33 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
void *extract_data = tls->userdata_chunk;
|
|
|
|
|
|
|
|
const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
|
2023-08-03 05:59:31 +02:00
|
|
|
const MeshRenderData &mr = *data->mr;
|
2021-06-10 16:01:36 +02:00
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
2023-03-19 23:43:32 +01:00
|
|
|
run_data.extractor->iter_loose_vert_mesh(
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 06:10:43 +01:00
|
|
|
mr, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
|
2021-06-08 16:35:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-03 05:59:31 +02:00
|
|
|
BLI_INLINE void extract_task_range_run_iter(const MeshRenderData &mr,
|
2021-06-10 16:01:36 +02:00
|
|
|
ExtractorRunDatas *extractors,
|
|
|
|
const eMRIterType iter_type,
|
|
|
|
bool is_mesh,
|
|
|
|
TaskParallelSettings *settings)
|
2021-06-08 16:35:33 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
ExtractorIterData range_data;
|
2023-08-03 05:59:31 +02:00
|
|
|
range_data.mr = &mr;
|
2021-06-10 16:01:36 +02:00
|
|
|
|
|
|
|
TaskParallelRangeFunc func;
|
|
|
|
int stop;
|
|
|
|
switch (iter_type) {
|
|
|
|
case MR_ITER_LOOPTRI:
|
2023-08-03 05:59:31 +02:00
|
|
|
range_data.elems = is_mesh ? mr.looptris.data() : (void *)mr.edit_bmesh->looptris;
|
2021-06-10 16:01:36 +02:00
|
|
|
func = is_mesh ? extract_range_iter_looptri_mesh : extract_range_iter_looptri_bm;
|
2023-08-03 05:59:31 +02:00
|
|
|
stop = mr.tri_len;
|
2021-06-10 16:01:36 +02:00
|
|
|
break;
|
|
|
|
case MR_ITER_POLY:
|
2023-09-07 03:57:52 +02:00
|
|
|
range_data.elems = is_mesh ? mr.faces.data().data() : (void *)mr.bm->ftable;
|
2023-07-24 22:06:55 +02:00
|
|
|
func = is_mesh ? extract_range_iter_face_mesh : extract_range_iter_face_bm;
|
2023-08-03 05:59:31 +02:00
|
|
|
stop = mr.face_len;
|
2021-06-10 16:01:36 +02:00
|
|
|
break;
|
2023-03-19 23:43:32 +01:00
|
|
|
case MR_ITER_LOOSE_EDGE:
|
2023-08-03 05:59:31 +02:00
|
|
|
range_data.loose_elems = mr.loose_edges.data();
|
|
|
|
range_data.elems = is_mesh ? mr.edges.data() : (void *)mr.bm->etable;
|
2023-03-19 23:43:32 +01:00
|
|
|
func = is_mesh ? extract_range_iter_loose_edge_mesh : extract_range_iter_loose_edge_bm;
|
2023-08-03 05:59:31 +02:00
|
|
|
stop = mr.edge_loose_len;
|
2021-06-10 16:01:36 +02:00
|
|
|
break;
|
2023-03-19 23:43:32 +01:00
|
|
|
case MR_ITER_LOOSE_VERT:
|
2023-08-03 05:59:31 +02:00
|
|
|
range_data.loose_elems = mr.loose_verts.data();
|
|
|
|
range_data.elems = is_mesh ? mr.vert_positions.data() : (void *)mr.bm->vtable;
|
2023-03-19 23:43:32 +01:00
|
|
|
func = is_mesh ? extract_range_iter_loose_vert_mesh : extract_range_iter_loose_vert_bm;
|
2023-08-03 05:59:31 +02:00
|
|
|
stop = mr.vert_loose_len;
|
2021-06-10 16:01:36 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BLI_assert(false);
|
|
|
|
return;
|
2021-06-08 16:35:33 +02:00
|
|
|
}
|
2021-06-10 16:01:36 +02:00
|
|
|
|
2021-07-16 21:18:54 +02:00
|
|
|
extractors->filter_into(range_data.extractors, iter_type, is_mesh);
|
2021-06-10 16:01:36 +02:00
|
|
|
BLI_task_parallel_range(0, stop, &range_data, func, settings);
|
2021-06-08 16:35:33 +02:00
|
|
|
}
|
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
static void extract_task_range_run(void *__restrict taskdata)
|
2021-05-31 17:11:01 +02:00
|
|
|
{
|
2021-06-10 16:01:36 +02:00
|
|
|
ExtractTaskData *data = (ExtractTaskData *)taskdata;
|
|
|
|
const eMRIterType iter_type = data->iter_type;
|
|
|
|
const bool is_mesh = data->mr->extract_type != MR_EXTRACT_BMESH;
|
2021-06-08 16:35:33 +02:00
|
|
|
|
2021-06-10 16:13:01 +02:00
|
|
|
size_t userdata_chunk_size = data->extractors->data_size_total();
|
2021-06-18 13:25:36 +02:00
|
|
|
void *userdata_chunk = MEM_callocN(userdata_chunk_size, __func__);
|
2021-06-10 16:13:01 +02:00
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
TaskParallelSettings settings;
|
|
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
|
|
settings.use_threading = data->use_threading;
|
2021-06-10 16:13:01 +02:00
|
|
|
settings.userdata_chunk = userdata_chunk;
|
|
|
|
settings.userdata_chunk_size = userdata_chunk_size;
|
|
|
|
settings.func_reduce = extract_task_reduce;
|
2021-06-18 16:09:35 +02:00
|
|
|
settings.min_iter_per_thread = MIN_RANGE_LEN;
|
2021-06-10 16:01:36 +02:00
|
|
|
|
2023-08-03 05:59:31 +02:00
|
|
|
extract_init(*data->mr, *data->cache, *data->extractors, data->mbuflist, userdata_chunk);
|
2021-05-31 17:11:01 +02:00
|
|
|
|
|
|
|
if (iter_type & MR_ITER_LOOPTRI) {
|
2023-08-03 05:59:31 +02:00
|
|
|
extract_task_range_run_iter(*data->mr, data->extractors, MR_ITER_LOOPTRI, is_mesh, &settings);
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
if (iter_type & MR_ITER_POLY) {
|
2023-08-03 05:59:31 +02:00
|
|
|
extract_task_range_run_iter(*data->mr, data->extractors, MR_ITER_POLY, is_mesh, &settings);
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
2023-03-19 23:43:32 +01:00
|
|
|
if (iter_type & MR_ITER_LOOSE_EDGE) {
|
|
|
|
extract_task_range_run_iter(
|
2023-08-03 05:59:31 +02:00
|
|
|
*data->mr, data->extractors, MR_ITER_LOOSE_EDGE, is_mesh, &settings);
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
2023-03-19 23:43:32 +01:00
|
|
|
if (iter_type & MR_ITER_LOOSE_VERT) {
|
|
|
|
extract_task_range_run_iter(
|
2023-08-03 05:59:31 +02:00
|
|
|
*data->mr, data->extractors, MR_ITER_LOOSE_VERT, is_mesh, &settings);
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
2021-06-10 16:01:36 +02:00
|
|
|
|
2023-08-03 05:59:31 +02:00
|
|
|
extract_finish(*data->mr, *data->cache, *data->extractors, userdata_chunk);
|
2021-06-18 13:25:36 +02:00
|
|
|
MEM_freeN(userdata_chunk);
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
2019-07-14 16:49:44 +02:00
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
2021-06-10 16:13:01 +02:00
|
|
|
/** \name Extract In Parallel Ranges
|
2019-07-14 16:49:44 +02:00
|
|
|
* \{ */
|
2021-06-01 12:12:13 +02:00
|
|
|
|
2023-06-03 00:36:28 +02:00
|
|
|
static TaskNode *extract_task_node_create(TaskGraph *task_graph,
|
2023-08-03 05:59:31 +02:00
|
|
|
const MeshRenderData &mr,
|
|
|
|
MeshBatchCache &cache,
|
2023-06-03 00:36:28 +02:00
|
|
|
ExtractorRunDatas *extractors,
|
|
|
|
MeshBufferList *mbuflist,
|
|
|
|
const bool use_threading)
|
2020-06-02 15:07:17 +02:00
|
|
|
{
|
2021-08-23 17:33:12 +02:00
|
|
|
ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbuflist, use_threading);
|
2023-06-03 00:36:28 +02:00
|
|
|
TaskNode *task_node = BLI_task_graph_node_create(
|
2021-06-10 16:01:36 +02:00
|
|
|
task_graph,
|
|
|
|
extract_task_range_run,
|
|
|
|
taskdata,
|
|
|
|
(TaskGraphNodeFreeFunction)extract_task_data_free);
|
|
|
|
return task_node;
|
2020-06-02 15:07:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/** \name Task Node - Update Mesh Render Data
|
|
|
|
* \{ */
|
2021-12-14 05:49:31 +01:00
|
|
|
|
2021-06-01 12:12:13 +02:00
|
|
|
struct MeshRenderDataUpdateTaskData {
|
|
|
|
MeshRenderData *mr = nullptr;
|
2021-08-23 17:35:43 +02:00
|
|
|
MeshBufferCache *cache = nullptr;
|
2020-06-02 15:07:17 +02:00
|
|
|
eMRIterType iter_type;
|
|
|
|
eMRDataType data_flag;
|
2021-06-01 12:12:13 +02:00
|
|
|
|
2023-08-03 05:59:31 +02:00
|
|
|
MeshRenderDataUpdateTaskData(MeshRenderData &mr,
|
|
|
|
MeshBufferCache &cache,
|
2021-06-15 15:31:17 +02:00
|
|
|
eMRIterType iter_type,
|
|
|
|
eMRDataType data_flag)
|
2023-08-03 05:59:31 +02:00
|
|
|
: mr(&mr), cache(&cache), iter_type(iter_type), data_flag(data_flag)
|
2021-06-07 16:06:30 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-06-01 12:12:13 +02:00
|
|
|
~MeshRenderDataUpdateTaskData()
|
|
|
|
{
|
|
|
|
mesh_render_data_free(mr);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef WITH_CXX_GUARDEDALLOC
|
|
|
|
MEM_CXX_CLASS_ALLOC_FUNCS("DRW:MeshRenderDataUpdateTaskData")
|
|
|
|
#endif
|
|
|
|
};
|
2020-06-02 15:07:17 +02:00
|
|
|
|
2021-06-07 16:06:30 +02:00
|
|
|
static void mesh_render_data_update_task_data_free(void *data)
|
2020-06-02 15:07:17 +02:00
|
|
|
{
|
2021-06-07 16:06:30 +02:00
|
|
|
MeshRenderDataUpdateTaskData *taskdata = static_cast<MeshRenderDataUpdateTaskData *>(data);
|
2020-06-02 15:07:17 +02:00
|
|
|
BLI_assert(taskdata);
|
2021-06-01 12:12:13 +02:00
|
|
|
delete taskdata;
|
2020-06-02 15:07:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void mesh_extract_render_data_node_exec(void *__restrict task_data)
|
|
|
|
{
|
2021-06-01 12:12:13 +02:00
|
|
|
MeshRenderDataUpdateTaskData *update_task_data = static_cast<MeshRenderDataUpdateTaskData *>(
|
|
|
|
task_data);
|
2023-08-03 05:59:31 +02:00
|
|
|
MeshRenderData &mr = *update_task_data->mr;
|
2020-06-15 15:25:25 +02:00
|
|
|
const eMRIterType iter_type = update_task_data->iter_type;
|
|
|
|
const eMRDataType data_flag = update_task_data->data_flag;
|
|
|
|
|
2021-06-01 13:18:04 +02:00
|
|
|
mesh_render_data_update_normals(mr, data_flag);
|
2020-06-15 15:25:25 +02:00
|
|
|
mesh_render_data_update_looptris(mr, iter_type, data_flag);
|
2023-08-03 05:59:31 +02:00
|
|
|
mesh_render_data_update_loose_geom(mr, *update_task_data->cache, iter_type, data_flag);
|
|
|
|
mesh_render_data_update_faces_sorted(mr, *update_task_data->cache, data_flag);
|
2020-06-02 15:07:17 +02:00
|
|
|
}
|
|
|
|
|
2023-06-03 00:36:28 +02:00
|
|
|
static TaskNode *mesh_extract_render_data_node_create(TaskGraph *task_graph,
|
2023-08-03 05:59:31 +02:00
|
|
|
MeshRenderData &mr,
|
|
|
|
MeshBufferCache &cache,
|
2023-06-03 00:36:28 +02:00
|
|
|
const eMRIterType iter_type,
|
|
|
|
const eMRDataType data_flag)
|
2020-06-02 15:07:17 +02:00
|
|
|
{
|
2021-06-07 16:06:30 +02:00
|
|
|
MeshRenderDataUpdateTaskData *task_data = new MeshRenderDataUpdateTaskData(
|
2021-06-15 15:31:17 +02:00
|
|
|
mr, cache, iter_type, data_flag);
|
2020-06-02 15:07:17 +02:00
|
|
|
|
2023-06-03 00:36:28 +02:00
|
|
|
TaskNode *task_node = BLI_task_graph_node_create(
|
2020-06-02 15:07:17 +02:00
|
|
|
task_graph,
|
|
|
|
mesh_extract_render_data_node_exec,
|
|
|
|
task_data,
|
|
|
|
(TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free);
|
|
|
|
return task_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/** \name Extract Loop
|
|
|
|
* \{ */
|
|
|
|
|
2023-06-03 00:36:28 +02:00
|
|
|
void mesh_buffer_cache_create_requested(TaskGraph *task_graph,
|
2023-08-03 05:59:31 +02:00
|
|
|
MeshBatchCache &cache,
|
|
|
|
MeshBufferCache &mbc,
|
2022-06-05 12:04:42 +02:00
|
|
|
Object *object,
|
|
|
|
Mesh *me,
|
|
|
|
|
|
|
|
const bool is_editmode,
|
|
|
|
const bool is_paint_mode,
|
|
|
|
const bool is_mode_active,
|
|
|
|
const float obmat[4][4],
|
|
|
|
const bool do_final,
|
|
|
|
const bool do_uvedit,
|
|
|
|
const Scene *scene,
|
|
|
|
const ToolSettings *ts,
|
|
|
|
const bool use_hide)
|
2019-07-14 16:49:44 +02:00
|
|
|
{
|
2020-06-02 15:07:17 +02:00
|
|
|
/* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph.
|
2021-05-31 17:11:01 +02:00
|
|
|
* This sub-graph starts with an extract_render_data_node. This fills/converts the required
|
|
|
|
* data from Mesh.
|
2020-06-02 15:07:17 +02:00
|
|
|
*
|
|
|
|
* Small extractions and extractions that can't be multi-threaded are grouped in a single
|
|
|
|
* `extract_single_threaded_task_node`.
|
|
|
|
*
|
|
|
|
* Other extractions will create a node for each loop exceeding 8192 items. these nodes are
|
2020-06-25 08:56:49 +02:00
|
|
|
* linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the
|
|
|
|
* user_data needed for the extraction based on the data extracted from the mesh.
|
|
|
|
* counters are used to check if the finalize of a task has to be called.
|
2020-06-02 15:07:17 +02:00
|
|
|
*
|
|
|
|
* Mesh extraction sub graph
|
|
|
|
*
|
|
|
|
* +----------------------+
|
|
|
|
* +-----> | extract_task1_loop_1 |
|
|
|
|
* | +----------------------+
|
|
|
|
* +------------------+ +----------------------+ +----------------------+
|
|
|
|
* | mesh_render_data | --> | | --> | extract_task1_loop_2 |
|
|
|
|
* +------------------+ | | +----------------------+
|
|
|
|
* | | | +----------------------+
|
|
|
|
* | | user_data_init | --> | extract_task2_loop_1 |
|
|
|
|
* v | | +----------------------+
|
|
|
|
* +------------------+ | | +----------------------+
|
|
|
|
* | single_threaded | | | --> | extract_task2_loop_2 |
|
|
|
|
* +------------------+ +----------------------+ +----------------------+
|
|
|
|
* | +----------------------+
|
|
|
|
* +-----> | extract_task2_loop_3 |
|
|
|
|
* +----------------------+
|
|
|
|
*/
|
2021-05-31 17:11:01 +02:00
|
|
|
const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
|
|
|
|
GPU_use_hq_normals_workaround();
|
2022-01-11 15:42:07 +01:00
|
|
|
const bool override_single_mat = mesh_render_mat_len_get(object, me) <= 1;
|
2019-07-14 16:49:44 +02:00
|
|
|
|
2021-05-31 17:11:01 +02:00
|
|
|
/* Create an array containing all the extractors that needs to be executed. */
|
2021-06-01 12:59:15 +02:00
|
|
|
ExtractorRunDatas extractors;
|
2020-06-02 15:07:17 +02:00
|
|
|
|
2023-08-03 05:59:31 +02:00
|
|
|
MeshBufferList *mbuflist = &mbc.buff;
|
2021-08-23 18:28:55 +02:00
|
|
|
|
2021-06-28 15:13:08 +02:00
|
|
|
#define EXTRACT_ADD_REQUESTED(type, name) \
|
2019-07-14 16:49:44 +02:00
|
|
|
do { \
|
2021-08-23 17:33:12 +02:00
|
|
|
if (DRW_##type##_requested(mbuflist->type.name)) { \
|
2021-06-09 16:20:38 +02:00
|
|
|
const MeshExtract *extractor = mesh_extract_override_get( \
|
|
|
|
&extract_##name, do_hq_normals, override_single_mat); \
|
2021-06-01 12:59:15 +02:00
|
|
|
extractors.append(extractor); \
|
2019-07-14 16:49:44 +02:00
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2021-06-28 15:13:08 +02:00
|
|
|
EXTRACT_ADD_REQUESTED(vbo, pos_nor);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, lnor);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, uv);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, tan);
|
|
|
|
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, edit_data);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edituv_data);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_area);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, mesh_analysis);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, fdots_pos);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, fdots_nor);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, fdots_uv);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, fdots_edituv_data);
|
2023-07-24 22:06:55 +02:00
|
|
|
EXTRACT_ADD_REQUESTED(vbo, face_idx);
|
2021-06-28 15:13:08 +02:00
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edge_idx);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, vert_idx);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, fdot_idx);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, skin_roots);
|
2021-10-26 23:16:33 +02:00
|
|
|
for (int i = 0; i < GPU_MAX_ATTR; i++) {
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, attr[i]);
|
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
EXTRACT_ADD_REQUESTED(vbo, attr_viewer);
|
2021-06-28 15:13:08 +02:00
|
|
|
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, tris);
|
2021-08-23 17:33:12 +02:00
|
|
|
if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) {
|
2021-06-28 15:59:40 +02:00
|
|
|
/* `ibo.lines_loose` require the `ibo.lines` buffer. */
|
2021-08-23 17:33:12 +02:00
|
|
|
if (mbuflist->ibo.lines == nullptr) {
|
|
|
|
DRW_ibo_request(nullptr, &mbuflist->ibo.lines);
|
2021-06-28 15:59:40 +02:00
|
|
|
}
|
2021-08-23 17:33:12 +02:00
|
|
|
const MeshExtract *extractor = DRW_ibo_requested(mbuflist->ibo.lines) ?
|
2021-06-28 15:59:40 +02:00
|
|
|
&extract_lines_with_lines_loose :
|
|
|
|
&extract_lines_loose_only;
|
|
|
|
extractors.append(extractor);
|
|
|
|
}
|
2021-08-23 17:33:12 +02:00
|
|
|
else if (DRW_ibo_requested(mbuflist->ibo.lines)) {
|
2021-06-05 14:46:00 +02:00
|
|
|
const MeshExtract *extractor;
|
2021-08-23 17:33:12 +02:00
|
|
|
if (mbuflist->ibo.lines_loose != nullptr) {
|
2021-06-28 15:59:40 +02:00
|
|
|
/* Update `ibo.lines_loose` as it depends on `ibo.lines`. */
|
2021-06-05 14:46:00 +02:00
|
|
|
extractor = &extract_lines_with_lines_loose;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
extractor = &extract_lines;
|
|
|
|
}
|
|
|
|
extractors.append(extractor);
|
|
|
|
}
|
2021-06-28 15:13:08 +02:00
|
|
|
EXTRACT_ADD_REQUESTED(ibo, points);
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, fdots);
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask);
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, lines_adjacency);
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, edituv_tris);
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, edituv_lines);
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, edituv_points);
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, edituv_fdots);
|
2021-05-31 17:11:01 +02:00
|
|
|
|
|
|
|
#undef EXTRACT_ADD_REQUESTED
|
|
|
|
|
2021-06-01 12:12:13 +02:00
|
|
|
if (extractors.is_empty()) {
|
2021-05-28 16:51:05 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-31 21:28:00 +02:00
|
|
|
#ifdef DEBUG_TIME
|
|
|
|
double rdata_start = PIL_check_seconds_timer();
|
|
|
|
#endif
|
|
|
|
|
2021-07-22 13:59:43 +02:00
|
|
|
MeshRenderData *mr = mesh_render_data_create(
|
2022-01-11 15:42:07 +01:00
|
|
|
object, me, is_editmode, is_paint_mode, is_mode_active, obmat, do_final, do_uvedit, ts);
|
2019-07-14 16:49:44 +02:00
|
|
|
mr->use_hide = use_hide;
|
2023-02-14 17:27:34 +01:00
|
|
|
mr->use_subsurf_fdots = mr->me && !mr->me->runtime->subsurf_face_dot_tags.is_empty();
|
2019-07-14 16:49:44 +02:00
|
|
|
mr->use_final_mesh = do_final;
|
|
|
|
|
|
|
|
#ifdef DEBUG_TIME
|
2021-05-31 21:28:00 +02:00
|
|
|
double rdata_end = PIL_check_seconds_timer();
|
2019-07-14 16:49:44 +02:00
|
|
|
#endif
|
|
|
|
|
2021-07-21 19:45:02 +02:00
|
|
|
eMRIterType iter_type = extractors.iter_types();
|
|
|
|
eMRDataType data_flag = extractors.data_types();
|
|
|
|
|
2023-06-03 00:36:28 +02:00
|
|
|
TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create(
|
2023-08-03 05:59:31 +02:00
|
|
|
task_graph, *mr, mbc, iter_type, data_flag);
|
2019-07-14 16:49:44 +02:00
|
|
|
|
2021-05-31 17:11:01 +02:00
|
|
|
/* Simple heuristic. */
|
2021-06-18 16:09:35 +02:00
|
|
|
const bool use_thread = (mr->loop_len + mr->loop_loose_len) > MIN_RANGE_LEN;
|
2021-05-31 17:11:01 +02:00
|
|
|
|
|
|
|
if (use_thread) {
|
|
|
|
/* First run the requested extractors that do not support asynchronous ranges. */
|
2021-06-01 12:59:15 +02:00
|
|
|
for (const ExtractorRunData &run_data : extractors) {
|
2021-06-01 12:12:13 +02:00
|
|
|
const MeshExtract *extractor = run_data.extractor;
|
2021-05-31 17:11:01 +02:00
|
|
|
if (!extractor->use_threading) {
|
2021-06-01 12:59:15 +02:00
|
|
|
ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas();
|
|
|
|
single_threaded_extractors->append(extractor);
|
2023-06-03 00:36:28 +02:00
|
|
|
TaskNode *task_node = extract_task_node_create(
|
2023-08-03 05:59:31 +02:00
|
|
|
task_graph, *mr, cache, single_threaded_extractors, mbuflist, false);
|
2021-06-10 16:01:36 +02:00
|
|
|
|
2021-05-31 17:11:01 +02:00
|
|
|
BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Distribute the remaining extractors into ranges per core. */
|
2021-06-01 12:59:15 +02:00
|
|
|
ExtractorRunDatas *multi_threaded_extractors = new ExtractorRunDatas();
|
|
|
|
extractors.filter_threaded_extractors_into(*multi_threaded_extractors);
|
2021-06-01 12:12:13 +02:00
|
|
|
if (!multi_threaded_extractors->is_empty()) {
|
2023-06-03 00:36:28 +02:00
|
|
|
TaskNode *task_node = extract_task_node_create(
|
2023-08-03 05:59:31 +02:00
|
|
|
task_graph, *mr, cache, multi_threaded_extractors, mbuflist, true);
|
2021-06-10 16:01:36 +02:00
|
|
|
|
|
|
|
BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* No tasks created freeing extractors list. */
|
2021-06-01 12:12:13 +02:00
|
|
|
delete multi_threaded_extractors;
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
2020-06-02 15:07:17 +02:00
|
|
|
}
|
|
|
|
else {
|
2021-05-31 17:11:01 +02:00
|
|
|
/* Run all requests on the same thread. */
|
2021-06-01 12:59:15 +02:00
|
|
|
ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors);
|
2023-06-03 00:36:28 +02:00
|
|
|
TaskNode *task_node = extract_task_node_create(
|
2023-08-03 05:59:31 +02:00
|
|
|
task_graph, *mr, cache, extractors_copy, mbuflist, false);
|
2021-05-31 17:11:01 +02:00
|
|
|
|
|
|
|
BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
|
2020-06-02 15:07:17 +02:00
|
|
|
}
|
2019-12-03 08:27:55 +01:00
|
|
|
|
2020-06-02 15:07:17 +02:00
|
|
|
/* Trigger the sub-graph for this mesh. */
|
|
|
|
BLI_task_graph_node_push_work(task_node_mesh_render_data);
|
2019-12-05 08:14:32 +01:00
|
|
|
|
2019-07-14 16:49:44 +02:00
|
|
|
#ifdef DEBUG_TIME
|
2020-06-02 15:07:17 +02:00
|
|
|
BLI_task_graph_work_and_wait(task_graph);
|
2019-07-14 16:49:44 +02:00
|
|
|
double end = PIL_check_seconds_timer();
|
|
|
|
|
|
|
|
static double avg = 0;
|
|
|
|
static double avg_fps = 0;
|
|
|
|
static double avg_rdata = 0;
|
|
|
|
static double end_prev = 0;
|
|
|
|
|
|
|
|
if (end_prev == 0) {
|
|
|
|
end_prev = end;
|
|
|
|
}
|
|
|
|
|
|
|
|
avg = avg * 0.95 + (end - rdata_end) * 0.05;
|
|
|
|
avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05;
|
|
|
|
avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05;
|
|
|
|
|
|
|
|
printf(
|
|
|
|
"rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000);
|
|
|
|
|
|
|
|
end_prev = end;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-12-14 05:49:31 +01:00
|
|
|
/** \} */
|
|
|
|
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/** \name Subdivision Extract Loop
|
|
|
|
* \{ */
|
|
|
|
|
2023-08-03 05:59:31 +02:00
|
|
|
void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache &cache,
|
|
|
|
MeshBufferCache &mbc,
|
|
|
|
DRWSubdivCache &subdiv_cache,
|
|
|
|
MeshRenderData &mr)
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
{
|
|
|
|
/* Create an array containing all the extractors that needs to be executed. */
|
|
|
|
ExtractorRunDatas extractors;
|
|
|
|
|
2023-08-03 05:59:31 +02:00
|
|
|
MeshBufferList *mbuflist = &mbc.buff;
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
|
|
|
|
#define EXTRACT_ADD_REQUESTED(type, name) \
|
|
|
|
do { \
|
|
|
|
if (DRW_##type##_requested(mbuflist->type.name)) { \
|
|
|
|
const MeshExtract *extractor = &extract_##name; \
|
|
|
|
extractors.append(extractor); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
/* The order in which extractors are added to the list matters somewhat, as some buffers are
|
|
|
|
* reused when building others. */
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, tris);
|
2022-05-22 09:19:55 +02:00
|
|
|
|
|
|
|
/* Orcos are extracted at the same time as positions. */
|
|
|
|
if (DRW_vbo_requested(mbuflist->vbo.pos_nor) || DRW_vbo_requested(mbuflist->vbo.orco)) {
|
|
|
|
extractors.append(&extract_pos_nor);
|
|
|
|
}
|
|
|
|
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
EXTRACT_ADD_REQUESTED(vbo, lnor);
|
|
|
|
for (int i = 0; i < GPU_MAX_ATTR; i++) {
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, attr[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We use only one extractor for face dots, as the work is done in a single compute shader. */
|
|
|
|
if (DRW_vbo_requested(mbuflist->vbo.fdots_nor) || DRW_vbo_requested(mbuflist->vbo.fdots_pos) ||
|
|
|
|
DRW_ibo_requested(mbuflist->ibo.fdots))
|
|
|
|
{
|
|
|
|
extractors.append(&extract_fdots_pos);
|
|
|
|
}
|
|
|
|
|
2022-03-02 15:10:26 +01:00
|
|
|
if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) {
|
|
|
|
/* `ibo.lines_loose` require the `ibo.lines` buffer. */
|
|
|
|
if (mbuflist->ibo.lines == nullptr) {
|
|
|
|
DRW_ibo_request(nullptr, &mbuflist->ibo.lines);
|
|
|
|
}
|
|
|
|
const MeshExtract *extractor = DRW_ibo_requested(mbuflist->ibo.lines) ?
|
|
|
|
&extract_lines_with_lines_loose :
|
|
|
|
&extract_lines_loose_only;
|
|
|
|
extractors.append(extractor);
|
|
|
|
}
|
|
|
|
else if (DRW_ibo_requested(mbuflist->ibo.lines)) {
|
|
|
|
const MeshExtract *extractor;
|
|
|
|
if (mbuflist->ibo.lines_loose != nullptr) {
|
|
|
|
/* Update `ibo.lines_loose` as it depends on `ibo.lines`. */
|
|
|
|
extractor = &extract_lines_with_lines_loose;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
extractor = &extract_lines;
|
|
|
|
}
|
|
|
|
extractors.append(extractor);
|
|
|
|
}
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
EXTRACT_ADD_REQUESTED(ibo, edituv_points);
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, edituv_tris);
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, edituv_lines);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, vert_idx);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edge_idx);
|
2023-07-24 22:06:55 +02:00
|
|
|
EXTRACT_ADD_REQUESTED(vbo, face_idx);
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edge_fac);
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, points);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edit_data);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edituv_data);
|
|
|
|
/* Make sure UVs are computed before edituv stuffs. */
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, uv);
|
2022-03-02 00:58:40 +01:00
|
|
|
EXTRACT_ADD_REQUESTED(vbo, tan);
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_area);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle);
|
2022-03-29 12:00:33 +02:00
|
|
|
EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask);
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
EXTRACT_ADD_REQUESTED(ibo, lines_adjacency);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, weights);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, sculpt_data);
|
|
|
|
|
|
|
|
#undef EXTRACT_ADD_REQUESTED
|
|
|
|
|
|
|
|
if (extractors.is_empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-02 00:58:40 +01:00
|
|
|
mesh_render_data_update_looptris(mr, MR_ITER_LOOPTRI, MR_DATA_LOOPTRI);
|
2022-03-08 13:27:54 +01:00
|
|
|
mesh_render_data_update_normals(mr, MR_DATA_TAN_LOOP_NOR);
|
2023-03-19 23:43:32 +01:00
|
|
|
mesh_render_data_update_loose_geom(
|
|
|
|
mr, mbc, MR_ITER_LOOSE_EDGE | MR_ITER_LOOSE_VERT, MR_DATA_LOOSE_GEOM);
|
2023-08-03 05:59:31 +02:00
|
|
|
DRW_subdivide_loose_geom(&subdiv_cache, &mbc);
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
|
|
|
|
void *data_stack = MEM_mallocN(extractors.data_size_total(), __func__);
|
|
|
|
uint32_t data_offset = 0;
|
|
|
|
for (const ExtractorRunData &run_data : extractors) {
|
|
|
|
const MeshExtract *extractor = run_data.extractor;
|
|
|
|
void *buffer = mesh_extract_buffer_get(extractor, mbuflist);
|
|
|
|
void *data = POINTER_OFFSET(data_stack, data_offset);
|
|
|
|
|
2022-02-14 10:09:06 +01:00
|
|
|
extractor->init_subdiv(subdiv_cache, mr, cache, buffer, data);
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
|
2022-01-16 08:40:38 +01:00
|
|
|
if (extractor->iter_subdiv_mesh || extractor->iter_subdiv_bm) {
|
2023-08-03 05:59:31 +02:00
|
|
|
int *subdiv_loop_face_index = subdiv_cache.subdiv_loop_face_index;
|
|
|
|
if (mr.extract_type == MR_EXTRACT_BMESH) {
|
|
|
|
for (uint i = 0; i < subdiv_cache.num_subdiv_quads; i++) {
|
2023-07-24 22:06:55 +02:00
|
|
|
/* Multiply by 4 to have the start index of the quad's loop, as subdiv_loop_face_index is
|
2022-01-16 08:40:38 +01:00
|
|
|
* based on the subdivision loops. */
|
2023-07-24 22:06:55 +02:00
|
|
|
const int poly_origindex = subdiv_loop_face_index[i * 4];
|
2023-08-03 05:59:31 +02:00
|
|
|
const BMFace *efa = BM_face_at_index(mr.bm, poly_origindex);
|
2022-02-14 10:09:06 +01:00
|
|
|
extractor->iter_subdiv_bm(subdiv_cache, mr, data, i, efa);
|
2022-01-16 08:40:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2023-08-03 05:59:31 +02:00
|
|
|
for (uint i = 0; i < subdiv_cache.num_subdiv_quads; i++) {
|
2023-07-24 22:06:55 +02:00
|
|
|
/* Multiply by 4 to have the start index of the quad's loop, as subdiv_loop_face_index is
|
2022-01-16 08:40:38 +01:00
|
|
|
* based on the subdivision loops. */
|
2023-07-24 22:06:55 +02:00
|
|
|
const int poly_origindex = subdiv_loop_face_index[i * 4];
|
Mesh: Replace MPoly struct with offset indices
Implements #95967.
Currently the `MPoly` struct is 12 bytes, and stores the index of a
face's first corner and the number of corners/verts/edges. Polygons
and corners are always created in order by Blender, meaning each
face's corners will be after the previous face's corners. We can take
advantage of this fact and eliminate the redundancy in mesh face
storage by only storing a single integer corner offset for each face.
The size of the face is then encoded by the offset of the next face.
The size of a single integer is 4 bytes, so this reduces memory
usage by 3 times.
The same method is used for `CurvesGeometry`, so Blender already has
an abstraction to simplify using these offsets called `OffsetIndices`.
This class is used to easily retrieve a range of corner indices for
each face. This also gives the opportunity for sharing some logic with
curves.
Another benefit of the change is that the offsets and sizes stored in
`MPoly` can no longer disagree with each other. Storing faces in the
order of their corners can simplify some code too.
Face/polygon variables now use the `IndexRange` type, which comes with
quite a few utilities that can simplify code.
Some:
- The offset integer array has to be one longer than the face count to
avoid a branch for every face, which means the data is no longer part
of the mesh's `CustomData`.
- We lose the ability to "reference" an original mesh's offset array
until more reusable CoW from #104478 is committed. That will be added
in a separate commit.
- Since they aren't part of `CustomData`, poly offsets often have to be
copied manually.
- To simplify using `OffsetIndices` in many places, some functions and
structs in headers were moved to only compile in C++.
- All meshes created by Blender use the same order for faces and face
corners, but just in case, meshes with mismatched order are fixed by
versioning code.
- `MeshPolygon.totloop` is no longer editable in RNA. This API break is
necessary here unfortunately. It should be worth it in 3.6, since
that's the best way to allow loading meshes from 4.0, which is
important for an LTS version.
Pull Request: https://projects.blender.org/blender/blender/pulls/105938
2023-04-04 20:39:28 +02:00
|
|
|
extractor->iter_subdiv_mesh(subdiv_cache, mr, data, i, poly_origindex);
|
2022-01-16 08:40:38 +01:00
|
|
|
}
|
2022-01-16 07:31:12 +01:00
|
|
|
}
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (extractor->iter_loose_geom_subdiv) {
|
2022-03-02 15:10:26 +01:00
|
|
|
extractor->iter_loose_geom_subdiv(subdiv_cache, mr, buffer, data);
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (extractor->finish_subdiv) {
|
2022-02-14 10:09:06 +01:00
|
|
|
extractor->finish_subdiv(subdiv_cache, mr, cache, buffer, data);
|
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for
faster renders of meshes with a subdivision surface modifier placed at the last
position in the modifier list.
When evaluating the subsurf modifier, we detect whether we can delegate evaluation
to the draw code. If so, the subdivision is first evaluated on the GPU using our own
custom evaluator (only the coarse data needs to be initially sent to the GPU), then,
buffers for the final `MeshBufferCache` are filled on the GPU using a set of
compute shaders. However, some buffers are still filled on the CPU side, if doing so
on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose
logic is hardly GPU compatible).
This is done at the mesh buffer extraction level so that the result can be readily used
in the various OpenGL engines, without having to write custom geometry or tesselation
shaders.
We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in
order to control the data layout, and interpolation. For example, we store vertex colors
as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float
types.
In order to still access the modified geometry on the CPU side, for use in modifiers
or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`.
Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will
create such a wrapper if possible. If the final subdivision surface is not needed on
the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used.
Enabling or disabling GPU subdivision can be done through the user preferences (under
Viewport -> Subdivision).
See patch description for benchmarks.
Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport
Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
MEM_freeN(data_stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
2021-06-01 12:12:13 +02:00
|
|
|
} // namespace blender::draw
|