2019-07-14 16:49:44 +02:00
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2017 by Blender Foundation.
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** \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
|
|
|
|
2021-06-01 09:23:37 +02:00
|
|
|
#include "BKE_editmesh.h"
|
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
|
|
|
|
2021-06-01 09:23:37 +02:00
|
|
|
#include "draw_cache_extract.h"
|
|
|
|
#include "draw_cache_inline.h"
|
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
|
|
|
#include "draw_subdivision.h"
|
2021-05-31 17:11:01 +02:00
|
|
|
|
2021-07-26 14:54:59 +02:00
|
|
|
#include "mesh_extractors/extract_mesh.h"
|
|
|
|
|
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
|
|
|
|
2021-06-01 12:59:15 +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;
|
|
|
|
}
|
2021-07-16 21:18:54 +02:00
|
|
|
if ((iter_type & MR_ITER_POLY) && *(&extractor->iter_poly_bm + is_mesh)) {
|
2021-06-01 12:59:15 +02:00
|
|
|
result.append(data);
|
|
|
|
continue;
|
|
|
|
}
|
2021-07-16 21:18:54 +02:00
|
|
|
if ((iter_type & MR_ITER_LEDGE) && *(&extractor->iter_ledge_bm + is_mesh)) {
|
2021-06-01 12:59:15 +02:00
|
|
|
result.append(data);
|
|
|
|
continue;
|
|
|
|
}
|
2021-07-16 21:18:54 +02:00
|
|
|
if ((iter_type & MR_ITER_LVERT) && *(&extractor->iter_lvert_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();
|
|
|
|
uint bits = static_cast<uint>(iter_type);
|
|
|
|
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;
|
|
|
|
|
|
|
|
ExtractTaskData(const MeshRenderData *mr,
|
|
|
|
struct MeshBatchCache *cache,
|
|
|
|
ExtractorRunDatas *extractors,
|
2021-08-23 17:33:12 +02:00
|
|
|
MeshBufferList *mbuflist,
|
2021-06-10 16:01:36 +02:00
|
|
|
const bool use_threading)
|
2021-08-23 17:33:12 +02:00
|
|
|
: mr(mr),
|
|
|
|
cache(cache),
|
|
|
|
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
|
|
|
* \{ */
|
|
|
|
|
|
|
|
BLI_INLINE void extract_init(const MeshRenderData *mr,
|
|
|
|
struct 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));
|
|
|
|
data_offset += (uint32_t)extractor->data_size;
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
BLI_INLINE void extract_finish(const MeshRenderData *mr,
|
|
|
|
struct MeshBatchCache *cache,
|
|
|
|
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;
|
|
|
|
const MeshRenderData *mr = data->mr;
|
|
|
|
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);
|
|
|
|
const MeshRenderData *mr = data->mr;
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
static void extract_range_iter_poly_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);
|
|
|
|
const MeshRenderData *mr = data->mr;
|
|
|
|
const BMFace *f = ((const BMFace **)data->elems)[iter];
|
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
|
|
|
run_data.extractor->iter_poly_bm(
|
|
|
|
mr, f, 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_poly_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);
|
|
|
|
const MeshRenderData *mr = data->mr;
|
|
|
|
const MPoly *mp = &((const MPoly *)data->elems)[iter];
|
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
|
|
|
run_data.extractor->iter_poly_mesh(
|
|
|
|
mr, mp, 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_ledge_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);
|
|
|
|
const MeshRenderData *mr = data->mr;
|
|
|
|
const int ledge_index = data->loose_elems[iter];
|
|
|
|
const BMEdge *eed = ((const BMEdge **)data->elems)[ledge_index];
|
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
|
|
|
run_data.extractor->iter_ledge_bm(
|
|
|
|
mr, eed, 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_ledge_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);
|
|
|
|
const MeshRenderData *mr = data->mr;
|
|
|
|
const int ledge_index = data->loose_elems[iter];
|
|
|
|
const MEdge *med = &((const MEdge *)data->elems)[ledge_index];
|
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
|
|
|
run_data.extractor->iter_ledge_mesh(
|
|
|
|
mr, med, 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_lvert_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);
|
|
|
|
const MeshRenderData *mr = data->mr;
|
|
|
|
const int lvert_index = data->loose_elems[iter];
|
|
|
|
const BMVert *eve = ((const BMVert **)data->elems)[lvert_index];
|
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
|
|
|
run_data.extractor->iter_lvert_bm(
|
|
|
|
mr, eve, 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_lvert_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);
|
|
|
|
const MeshRenderData *mr = data->mr;
|
|
|
|
const int lvert_index = data->loose_elems[iter];
|
|
|
|
const MVert *mv = &((const MVert *)data->elems)[lvert_index];
|
|
|
|
for (const ExtractorRunData &run_data : data->extractors) {
|
|
|
|
run_data.extractor->iter_lvert_mesh(
|
|
|
|
mr, mv, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
|
2021-06-08 16:35:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
BLI_INLINE void extract_task_range_run_iter(const MeshRenderData *mr,
|
|
|
|
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;
|
|
|
|
range_data.mr = mr;
|
|
|
|
|
|
|
|
TaskParallelRangeFunc func;
|
|
|
|
int stop;
|
|
|
|
switch (iter_type) {
|
|
|
|
case MR_ITER_LOOPTRI:
|
|
|
|
range_data.elems = is_mesh ? mr->mlooptri : (void *)mr->edit_bmesh->looptris;
|
|
|
|
func = is_mesh ? extract_range_iter_looptri_mesh : extract_range_iter_looptri_bm;
|
|
|
|
stop = mr->tri_len;
|
|
|
|
break;
|
|
|
|
case MR_ITER_POLY:
|
|
|
|
range_data.elems = is_mesh ? mr->mpoly : (void *)mr->bm->ftable;
|
|
|
|
func = is_mesh ? extract_range_iter_poly_mesh : extract_range_iter_poly_bm;
|
|
|
|
stop = mr->poly_len;
|
|
|
|
break;
|
|
|
|
case MR_ITER_LEDGE:
|
|
|
|
range_data.loose_elems = mr->ledges;
|
|
|
|
range_data.elems = is_mesh ? mr->medge : (void *)mr->bm->etable;
|
|
|
|
func = is_mesh ? extract_range_iter_ledge_mesh : extract_range_iter_ledge_bm;
|
|
|
|
stop = mr->edge_loose_len;
|
|
|
|
break;
|
|
|
|
case MR_ITER_LVERT:
|
|
|
|
range_data.loose_elems = mr->lverts;
|
|
|
|
range_data.elems = is_mesh ? mr->mvert : (void *)mr->bm->vtable;
|
|
|
|
func = is_mesh ? extract_range_iter_lvert_mesh : extract_range_iter_lvert_bm;
|
|
|
|
stop = mr->vert_loose_len;
|
|
|
|
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
|
|
|
|
2021-08-23 17:33:12 +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) {
|
2021-06-10 16:01:36 +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) {
|
2021-06-10 16:01:36 +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
|
|
|
}
|
|
|
|
if (iter_type & MR_ITER_LEDGE) {
|
2021-06-10 16:01:36 +02:00
|
|
|
extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LEDGE, is_mesh, &settings);
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
|
|
|
if (iter_type & MR_ITER_LVERT) {
|
2021-06-10 16:01:36 +02:00
|
|
|
extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LVERT, is_mesh, &settings);
|
2021-05-31 17:11:01 +02:00
|
|
|
}
|
2021-06-10 16:01:36 +02:00
|
|
|
|
2021-06-18 13:25:36 +02:00
|
|
|
extract_finish(data->mr, data->cache, *data->extractors, userdata_chunk);
|
|
|
|
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
|
|
|
|
2021-06-10 16:01:36 +02:00
|
|
|
static struct TaskNode *extract_task_node_create(struct TaskGraph *task_graph,
|
|
|
|
const MeshRenderData *mr,
|
|
|
|
MeshBatchCache *cache,
|
|
|
|
ExtractorRunDatas *extractors,
|
2021-08-23 17:33:12 +02:00
|
|
|
MeshBufferList *mbuflist,
|
2021-06-10 16:01:36 +02:00
|
|
|
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);
|
2021-06-10 16:01:36 +02:00
|
|
|
struct TaskNode *task_node = BLI_task_graph_node_create(
|
|
|
|
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
|
|
|
|
2021-06-15 15:31:17 +02:00
|
|
|
MeshRenderDataUpdateTaskData(MeshRenderData *mr,
|
2021-08-23 17:35:43 +02:00
|
|
|
MeshBufferCache *cache,
|
2021-06-15 15:31:17 +02:00
|
|
|
eMRIterType iter_type,
|
|
|
|
eMRDataType data_flag)
|
|
|
|
: 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);
|
2020-06-15 15:25:25 +02:00
|
|
|
MeshRenderData *mr = update_task_data->mr;
|
|
|
|
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);
|
2021-07-21 19:45:02 +02:00
|
|
|
mesh_render_data_update_loose_geom(mr, update_task_data->cache, iter_type, data_flag);
|
2021-07-20 16:43:38 +02:00
|
|
|
mesh_render_data_update_polys_sorted(mr, update_task_data->cache, data_flag);
|
2020-06-02 15:07:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph,
|
|
|
|
MeshRenderData *mr,
|
2021-08-23 17:35:43 +02:00
|
|
|
MeshBufferCache *cache,
|
2020-06-02 15:07:17 +02:00
|
|
|
const eMRIterType iter_type,
|
|
|
|
const eMRDataType data_flag)
|
|
|
|
{
|
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
|
|
|
|
|
|
|
struct TaskNode *task_node = BLI_task_graph_node_create(
|
|
|
|
task_graph,
|
|
|
|
mesh_extract_render_data_node_exec,
|
|
|
|
task_data,
|
|
|
|
(TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free);
|
|
|
|
return task_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/** \name Extract Loop
|
|
|
|
* \{ */
|
|
|
|
|
2021-06-01 12:12:13 +02:00
|
|
|
static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
|
|
|
|
MeshBatchCache *cache,
|
2021-08-23 18:28:55 +02:00
|
|
|
MeshBufferCache *mbc,
|
2022-01-11 15:42:07 +01:00
|
|
|
Object *object,
|
2021-06-01 12:12:13 +02:00
|
|
|
Mesh *me,
|
2020-06-02 15:07:17 +02:00
|
|
|
|
2021-06-01 12:12:13 +02:00
|
|
|
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 bool use_subsurf_fdots,
|
|
|
|
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
|
|
|
|
2021-08-23 18:28:55 +02:00
|
|
|
MeshBufferList *mbuflist = &mbc->buff;
|
|
|
|
|
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, vcol);
|
|
|
|
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);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, poly_idx);
|
|
|
|
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]);
|
|
|
|
}
|
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;
|
|
|
|
mr->use_subsurf_fdots = use_subsurf_fdots;
|
|
|
|
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();
|
|
|
|
|
2020-06-02 15:07:17 +02:00
|
|
|
struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create(
|
2021-08-23 18:28:55 +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);
|
2021-06-10 16:01:36 +02:00
|
|
|
struct TaskNode *task_node = extract_task_node_create(
|
2021-08-23 17:33:12 +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()) {
|
2021-06-10 16:01:36 +02:00
|
|
|
struct TaskNode *task_node = extract_task_node_create(
|
2021-08-23 17:33:12 +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);
|
2021-06-10 16:01:36 +02:00
|
|
|
struct TaskNode *task_node = extract_task_node_create(
|
2021-08-23 17:33:12 +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
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache,
|
|
|
|
MeshBufferCache *mbc,
|
|
|
|
DRWSubdivCache *subdiv_cache,
|
2022-02-14 10:09:06 +01:00
|
|
|
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;
|
|
|
|
|
|
|
|
MeshBufferList *mbuflist = &mbc->buff;
|
|
|
|
|
|
|
|
#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);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, pos_nor);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, lines);
|
|
|
|
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);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, poly_idx);
|
|
|
|
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);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_area);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle);
|
|
|
|
EXTRACT_ADD_REQUESTED(ibo, lines_adjacency);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, vcol);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, weights);
|
|
|
|
EXTRACT_ADD_REQUESTED(vbo, sculpt_data);
|
|
|
|
|
|
|
|
#undef EXTRACT_ADD_REQUESTED
|
|
|
|
|
|
|
|
if (extractors.is_empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-14 10:09:06 +01:00
|
|
|
mesh_render_data_update_loose_geom(mr, mbc, MR_ITER_LEDGE | MR_ITER_LVERT, MR_DATA_LOOSE_GEOM);
|
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) {
|
|
|
|
int *subdiv_loop_poly_index = subdiv_cache->subdiv_loop_poly_index;
|
2022-02-14 10:09:06 +01:00
|
|
|
if (mr->extract_type == MR_EXTRACT_BMESH) {
|
2022-01-16 08:40:38 +01:00
|
|
|
for (uint i = 0; i < subdiv_cache->num_subdiv_quads; i++) {
|
|
|
|
/* Multiply by 4 to have the start index of the quad's loop, as subdiv_loop_poly_index is
|
|
|
|
* based on the subdivision loops. */
|
|
|
|
const int poly_origindex = subdiv_loop_poly_index[i * 4];
|
2022-02-14 10:09:06 +01:00
|
|
|
const BMFace *efa = BM_face_at_index(mr->bm, poly_origindex);
|
|
|
|
extractor->iter_subdiv_bm(subdiv_cache, mr, data, i, efa);
|
2022-01-16 08:40:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (uint i = 0; i < subdiv_cache->num_subdiv_quads; i++) {
|
|
|
|
/* Multiply by 4 to have the start index of the quad's loop, as subdiv_loop_poly_index is
|
|
|
|
* based on the subdivision loops. */
|
|
|
|
const int poly_origindex = subdiv_loop_poly_index[i * 4];
|
2022-02-14 10:09:06 +01:00
|
|
|
const MPoly *mp = &mr->mpoly[poly_origindex];
|
|
|
|
extractor->iter_subdiv_mesh(subdiv_cache, mr, data, i, mp);
|
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-02-14 10:09:06 +01:00
|
|
|
extractor->iter_loose_geom_subdiv(subdiv_cache, mr, &mbc->loose_geom, 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
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
|
|
|
|
MeshBatchCache *cache,
|
2021-08-23 18:28:55 +02:00
|
|
|
MeshBufferCache *mbc,
|
2022-01-11 15:42:07 +01:00
|
|
|
Object *object,
|
2021-06-01 12:12:13 +02:00
|
|
|
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 bool use_subsurf_fdots,
|
|
|
|
const Scene *scene,
|
|
|
|
const ToolSettings *ts,
|
|
|
|
const bool use_hide)
|
|
|
|
{
|
|
|
|
blender::draw::mesh_buffer_cache_create_requested(task_graph,
|
|
|
|
cache,
|
2021-08-23 18:28:55 +02:00
|
|
|
mbc,
|
2022-01-11 15:42:07 +01:00
|
|
|
object,
|
2021-06-01 12:12:13 +02:00
|
|
|
me,
|
|
|
|
is_editmode,
|
|
|
|
is_paint_mode,
|
|
|
|
is_mode_active,
|
|
|
|
obmat,
|
|
|
|
do_final,
|
|
|
|
do_uvedit,
|
|
|
|
use_subsurf_fdots,
|
|
|
|
scene,
|
|
|
|
ts,
|
|
|
|
use_hide);
|
|
|
|
}
|
|
|
|
|
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 mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache,
|
|
|
|
MeshBufferCache *mbc,
|
|
|
|
DRWSubdivCache *subdiv_cache,
|
2022-02-14 10:09:06 +01:00
|
|
|
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
|
|
|
{
|
2022-02-14 10:09:06 +01:00
|
|
|
blender::draw::mesh_buffer_cache_create_requested_subdiv(cache, mbc, subdiv_cache, 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
|
|
|
}
|
|
|
|
|
2021-06-01 12:12:13 +02:00
|
|
|
} // extern "C"
|