DrawManager: Graph Task Scheduling

This patch uses a graph flow scheduler for creating all mesh batches.
On a Ryzen 1700 the framerate of Spring/020_02A.anim.blend went from 10 fps to 11.5 fps.

For each mesh where batches needs to be updated a sub-graph will be added to the task_graph.
This sub-graph starts with an extract_render_data_node. This fills/converts the required data
from Mesh.

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 4096 items. these nodes are
linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the userdata
needed for the extraction based on the data extracted from the mesh.

Note: If the `lines` and `lines_loose` are requested, the `lines_loose` sub-buffer is created
as part of the lines extraction. When the lines_loose is only requested the sub-buffer is
created from the existing `lines` buffer. It is assumed that the lines buffer is always
requested before or together with the lines_loose what is always the case (see
`DRW_batch_requested(cache->batch.loose_edges, GPU_PRIM_LINES)` in `draw_cache_impl_mesh.c`).

Reviewed By: Clément Foucault

Differential Revision: https://developer.blender.org/D7618
This commit is contained in:
Jeroen Bakker
2020-06-02 15:07:17 +02:00
committed by Jeroen Bakker
parent 21443056b7
commit bf34b0c8f4
9 changed files with 423 additions and 136 deletions

View File

@@ -134,15 +134,12 @@ typedef struct MeshRenderData {
float (*poly_normals)[3];
int *lverts, *ledges;
} MeshRenderData;
static MeshRenderData *mesh_render_data_create(Mesh *me,
const bool is_editmode,
const bool is_paint_mode,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
const eMRIterType iter_type,
const eMRDataType data_flag,
const DRW_MeshCDMask *UNUSED(cd_used),
const ToolSettings *ts)
{
@@ -152,9 +149,6 @@ static MeshRenderData *mesh_render_data_create(Mesh *me,
copy_m4_m4(mr->obmat, obmat);
const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0;
const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI;
if (is_editmode) {
BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final);
mr->bm = me->edit_mesh->bm;
@@ -244,7 +238,32 @@ static MeshRenderData *mesh_render_data_create(Mesh *me,
mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
}
else {
/* BMesh */
BMesh *bm = mr->bm;
mr->vert_len = bm->totvert;
mr->edge_len = bm->totedge;
mr->loop_len = bm->totloop;
mr->poly_len = bm->totface;
mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
}
return mr;
}
/* Part of the creation of the MeshRenderData that happens in a thread. */
static void mesh_render_data_update(MeshRenderData *mr,
const eMRIterType iter_type,
const eMRDataType data_flag)
{
Mesh *me = mr->me;
const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0;
const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI;
if (mr->extract_type != MR_EXTRACT_BMESH) {
/* Mesh */
if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) {
mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__);
BKE_mesh_calc_normals_poly((MVert *)mr->mvert,
@@ -323,13 +342,6 @@ static MeshRenderData *mesh_render_data_create(Mesh *me,
else {
/* BMesh */
BMesh *bm = mr->bm;
mr->vert_len = bm->totvert;
mr->edge_len = bm->totedge;
mr->loop_len = bm->totloop;
mr->poly_len = bm->totface;
mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
if (data_flag & MR_DATA_POLY_NOR) {
/* Use bmface->no instead. */
}
@@ -394,7 +406,6 @@ static MeshRenderData *mesh_render_data_create(Mesh *me,
mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2;
}
}
return mr;
}
static void mesh_render_data_free(MeshRenderData *mr)
@@ -742,46 +753,15 @@ static const MeshExtract extract_lines = {
0,
false,
};
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Extract Loose Edges Indices
/** \name Extract Loose Edges Sub Buffer
* \{ */
static void *extract_lines_loose_init(const MeshRenderData *UNUSED(mr), void *UNUSED(buf))
{
return NULL;
}
static void extract_lines_loose_ledge_mesh(const MeshRenderData *UNUSED(mr),
int UNUSED(e),
const MEdge *UNUSED(medge),
void *UNUSED(elb))
{
/* This function is intentionally empty. The existence of this functions ensures that
* `iter_type` `MR_ITER_LVERT` is set when initializing the `MeshRenderData` (See
* `mesh_extract_iter_type`). This flag ensures that `mr->edge_loose_len` field is filled. This
* field we use in the `extract_lines_loose_finish` function to create a subrange from the
* `ibo.lines`. */
}
static void extract_lines_loose_ledge_bmesh(const MeshRenderData *UNUSED(mr),
int UNUSED(e),
BMEdge *UNUSED(eed),
void *UNUSED(elb))
{
/* This function is intentionally empty. The existence of this functions ensures that
* `iter_type` `MR_ITER_LVERT` is set when initializing the `MeshRenderData` (See
* `mesh_extract_iter_type`). This flag ensures that `mr->edge_loose_len` field is filled. This
* field we use in the `extract_lines_loose_finish` function to create a subrange from the
* `ibo.lines`. */
}
static void extract_lines_loose_finish(const MeshRenderData *mr,
void *UNUSED(ibo),
void *UNUSED(elb))
static void extract_lines_loose_subbuffer(const MeshRenderData *mr)
{
BLI_assert(mr->cache->final.ibo.lines);
/* Multiply by 2 because these are edges indices. */
const int start = mr->edge_len * 2;
const int len = mr->edge_loose_len * 2;
@@ -790,17 +770,24 @@ static void extract_lines_loose_finish(const MeshRenderData *mr,
mr->cache->no_loose_wire = (len == 0);
}
static const MeshExtract extract_lines_loose = {
extract_lines_loose_init,
static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, void *ibo, void *elb)
{
GPU_indexbuf_build_in_place(elb, ibo);
extract_lines_loose_subbuffer(mr);
MEM_freeN(elb);
}
static const MeshExtract extract_lines_with_lines_loose = {
extract_lines_init,
NULL,
NULL,
extract_lines_loop_bmesh,
extract_lines_loop_mesh,
extract_lines_ledge_bmesh,
extract_lines_ledge_mesh,
NULL,
NULL,
extract_lines_loose_ledge_bmesh,
extract_lines_loose_ledge_mesh,
NULL,
NULL,
extract_lines_loose_finish,
extract_lines_with_lines_loose_finish,
0,
false,
};
@@ -1716,8 +1703,8 @@ static void extract_lnor_hq_loop_mesh(
}
/* Flag for paint mode overlay.
* Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. In paint
* mode it will use the unmapped data to draw the wireframe. */
* Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. In
* paint mode it will use the unmapped data to draw the wireframe. */
if (mpoly->flag & ME_HIDE ||
(mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
mr->v_origindex[mloop->v] == ORIGINDEX_NONE)) {
@@ -1795,8 +1782,8 @@ static void extract_lnor_loop_mesh(
}
/* Flag for paint mode overlay.
* Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. In paint
* mode it will use the unmapped data to draw the wireframe. */
* Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. In
* paint mode it will use the unmapped data to draw the wireframe. */
if (mpoly->flag & ME_HIDE ||
(mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
mr->v_origindex[mloop->v] == ORIGINDEX_NONE)) {
@@ -4522,10 +4509,14 @@ static const MeshExtract extract_fdot_idx = {
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Extract Loop
/** \name ExtractTaskData
* \{ */
typedef struct ExtractUserData {
void *user_data;
} ExtractUserData;
typedef struct ExtractTaskData {
void *next, *prev;
const MeshRenderData *mr;
const MeshExtract *extract;
eMRIterType iter_type;
@@ -4533,9 +4524,16 @@ typedef struct ExtractTaskData {
/** Decremented each time a task is finished. */
int32_t *task_counter;
void *buf;
void *user_data;
ExtractUserData *user_data;
} ExtractTaskData;
static void extract_task_data_free(void *data)
{
ExtractTaskData *task_data = data;
MEM_SAFE_FREE(task_data->user_data);
MEM_freeN(task_data);
}
BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
const eMRIterType iter_type,
int start,
@@ -4612,31 +4610,184 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
}
}
static void extract_run(TaskPool *__restrict UNUSED(pool), void *taskdata)
static void extract_init(ExtractTaskData *data)
{
ExtractTaskData *data = taskdata;
mesh_extract_iter(
data->mr, data->iter_type, data->start, data->end, data->extract, data->user_data);
data->user_data->user_data = data->extract->init(data->mr, data->buf);
}
static void extract_run(void *__restrict taskdata)
{
ExtractTaskData *data = (ExtractTaskData *)taskdata;
mesh_extract_iter(data->mr,
data->iter_type,
data->start,
data->end,
data->extract,
data->user_data->user_data);
/* If this is the last task, we do the finish function. */
int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1);
if (remainin_tasks == 0 && data->extract->finish != NULL) {
data->extract->finish(data->mr, data->buf, data->user_data);
data->extract->finish(data->mr, data->buf, data->user_data->user_data);
}
}
static void extract_range_task_create(
TaskPool *task_pool, ExtractTaskData *taskdata, const eMRIterType type, int start, int length)
static void extract_init_and_run(void *__restrict taskdata)
{
extract_init((ExtractTaskData *)taskdata);
extract_run(taskdata);
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Task Node - Update Mesh Render Data
* \{ */
typedef struct MeshRenderDataUpdateTaskData {
MeshRenderData *mr;
eMRIterType iter_type;
eMRDataType data_flag;
} MeshRenderDataUpdateTaskData;
static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata)
{
BLI_assert(taskdata);
mesh_render_data_free(taskdata->mr);
MEM_freeN(taskdata);
}
static void mesh_extract_render_data_node_exec(void *__restrict task_data)
{
MeshRenderDataUpdateTaskData *update_task_data = task_data;
mesh_render_data_update(
update_task_data->mr, update_task_data->iter_type, update_task_data->data_flag);
}
static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph,
MeshRenderData *mr,
const eMRIterType iter_type,
const eMRDataType data_flag)
{
MeshRenderDataUpdateTaskData *task_data = MEM_mallocN(sizeof(MeshRenderDataUpdateTaskData),
__func__);
task_data->mr = mr;
task_data->iter_type = iter_type;
task_data->data_flag = data_flag;
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 Task Node - Extract Single Threaded
* \{ */
typedef struct ExtractSingleThreadedTaskData {
ListBase task_datas;
} ExtractSingleThreadedTaskData;
static void extract_single_threaded_task_data_free(ExtractSingleThreadedTaskData *taskdata)
{
BLI_assert(taskdata);
LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) {
extract_task_data_free(td);
}
BLI_listbase_clear(&taskdata->task_datas);
MEM_freeN(taskdata);
}
static void extract_single_threaded_task_node_exec(void *__restrict task_data)
{
ExtractSingleThreadedTaskData *extract_task_data = task_data;
LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) {
extract_init_and_run(td);
}
}
static struct TaskNode *extract_single_threaded_task_node_create(
struct TaskGraph *task_graph, ExtractSingleThreadedTaskData *task_data)
{
struct TaskNode *task_node = BLI_task_graph_node_create(
task_graph,
extract_single_threaded_task_node_exec,
task_data,
(TaskGraphNodeFreeFunction)extract_single_threaded_task_data_free);
return task_node;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Task Node - UserData Initializer
* \{ */
typedef struct UserDataInitTaskData {
ListBase task_datas;
int32_t *task_counters;
} UserDataInitTaskData;
static void user_data_init_task_data_free(UserDataInitTaskData *taskdata)
{
BLI_assert(taskdata);
LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) {
extract_task_data_free(td);
}
BLI_listbase_clear(&taskdata->task_datas);
MEM_SAFE_FREE(taskdata->task_counters);
MEM_freeN(taskdata);
}
static void user_data_init_task_data_exec(void *__restrict task_data)
{
UserDataInitTaskData *extract_task_data = task_data;
LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) {
extract_init(td);
}
}
static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph,
UserDataInitTaskData *task_data)
{
struct TaskNode *task_node = BLI_task_graph_node_create(
task_graph,
user_data_init_task_data_exec,
task_data,
(TaskGraphNodeFreeFunction)user_data_init_task_data_free);
return task_node;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Extract Loop
* \{ */
static void extract_range_task_create(struct TaskGraph *task_graph,
struct TaskNode *task_node_user_data_init,
ExtractTaskData *taskdata,
const eMRIterType type,
int start,
int length)
{
taskdata = MEM_dupallocN(taskdata);
atomic_add_and_fetch_int32(taskdata->task_counter, 1);
taskdata->iter_type = type;
taskdata->start = start;
taskdata->end = start + length;
BLI_task_pool_push(task_pool, extract_run, taskdata, true, NULL);
struct TaskNode *task_node = BLI_task_graph_node_create(
task_graph, extract_run, taskdata, MEM_freeN);
BLI_task_graph_edge_create(task_node_user_data_init, task_node);
}
static void extract_task_create(TaskPool *task_pool,
static void extract_task_create(struct TaskGraph *task_graph,
struct TaskNode *task_node_mesh_render_data,
struct TaskNode *task_node_user_data_init,
ListBase *single_threaded_task_datas,
ListBase *user_data_init_task_datas,
const Scene *scene,
const MeshRenderData *mr,
const MeshExtract *extract,
@@ -4654,58 +4805,75 @@ static void extract_task_create(TaskPool *task_pool,
/* Divide extraction of the VBO/IBO into sensible chunks of works. */
ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), "ExtractTaskData");
taskdata->next = NULL;
taskdata->prev = NULL;
taskdata->mr = mr;
taskdata->extract = extract;
taskdata->buf = buf;
taskdata->user_data = extract->init(mr, buf);
/* ExtractUserData is shared between the iterations as it holds counters to detect if the
* extraction is finished. To make sure the duplication of the userdata does not create a new
* instance of the counters we allocate the userdata in its own container.
*
* This structure makes sure that when extract_init is called, that the user data of all
* iterations are updated. */
taskdata->user_data = MEM_callocN(sizeof(ExtractUserData), __func__);
taskdata->iter_type = mesh_extract_iter_type(extract);
taskdata->task_counter = task_counter;
taskdata->start = 0;
taskdata->end = INT_MAX;
/* Simple heuristic. */
const bool use_thread = (mr->loop_len + mr->loop_loose_len) > 8192;
const int chunk_size = 8192;
const bool use_thread = (mr->loop_len + mr->loop_loose_len) > chunk_size;
if (use_thread && extract->use_threading) {
/* Divide task into sensible chunks. */
const int chunk_size = 8192;
if (taskdata->iter_type & MR_ITER_LOOPTRI) {
for (int i = 0; i < mr->tri_len; i += chunk_size) {
extract_range_task_create(task_pool, taskdata, MR_ITER_LOOPTRI, i, chunk_size);
extract_range_task_create(
task_graph, task_node_user_data_init, taskdata, MR_ITER_LOOPTRI, i, chunk_size);
}
}
if (taskdata->iter_type & MR_ITER_LOOP) {
for (int i = 0; i < mr->poly_len; i += chunk_size) {
extract_range_task_create(task_pool, taskdata, MR_ITER_LOOP, i, chunk_size);
extract_range_task_create(
task_graph, task_node_user_data_init, taskdata, MR_ITER_LOOP, i, chunk_size);
}
}
if (taskdata->iter_type & MR_ITER_LEDGE) {
for (int i = 0; i < mr->edge_loose_len; i += chunk_size) {
extract_range_task_create(task_pool, taskdata, MR_ITER_LEDGE, i, chunk_size);
extract_range_task_create(
task_graph, task_node_user_data_init, taskdata, MR_ITER_LEDGE, i, chunk_size);
}
}
if (taskdata->iter_type & MR_ITER_LVERT) {
for (int i = 0; i < mr->vert_loose_len; i += chunk_size) {
extract_range_task_create(task_pool, taskdata, MR_ITER_LVERT, i, chunk_size);
extract_range_task_create(
task_graph, task_node_user_data_init, taskdata, MR_ITER_LVERT, i, chunk_size);
}
}
MEM_freeN(taskdata);
BLI_addtail(user_data_init_task_datas, taskdata);
}
else if (use_thread) {
/* One task for the whole VBO. */
(*task_counter)++;
BLI_task_pool_push(task_pool, extract_run, taskdata, true, NULL);
struct TaskNode *one_task = BLI_task_graph_node_create(
task_graph, extract_init_and_run, taskdata, extract_task_data_free);
BLI_task_graph_edge_create(task_node_mesh_render_data, one_task);
}
else {
/* Single threaded extraction. */
(*task_counter)++;
extract_run(NULL, taskdata);
MEM_freeN(taskdata);
BLI_addtail(single_threaded_task_datas, taskdata);
}
}
void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
MeshBatchCache *cache,
MeshBufferCache mbc,
Mesh *me,
const bool is_editmode,
const bool is_paint_mode,
const float obmat[4][4],
@@ -4717,9 +4885,41 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
const ToolSettings *ts,
const bool use_hide)
{
/* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph.
* This sub-graph starts with an extract_render_data_node. This fills/converts the required data
* from Mesh.
*
* 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
* linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the userdata
* 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.
*
* 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 |
* +----------------------+
*/
eMRIterType iter_flag = 0;
eMRDataType data_flag = 0;
const bool do_lines_loose_subbuffer = mbc.ibo.lines_loose != NULL;
#define TEST_ASSIGN(type, type_lowercase, name) \
do { \
if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \
@@ -4757,7 +4957,6 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
TEST_ASSIGN(IBO, ibo, fdots);
TEST_ASSIGN(IBO, ibo, lines_paint_mask);
TEST_ASSIGN(IBO, ibo, lines_adjacency);
TEST_ASSIGN(IBO, ibo, lines_loose);
TEST_ASSIGN(IBO, ibo, edituv_tris);
TEST_ASSIGN(IBO, ibo, edituv_lines);
TEST_ASSIGN(IBO, ibo, edituv_points);
@@ -4769,16 +4968,8 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
double rdata_start = PIL_check_seconds_timer();
#endif
MeshRenderData *mr = mesh_render_data_create(me,
is_editmode,
is_paint_mode,
obmat,
do_final,
do_uvedit,
iter_flag,
data_flag,
cd_layer_used,
ts);
MeshRenderData *mr = mesh_render_data_create(
me, is_editmode, is_paint_mode, obmat, do_final, do_uvedit, cd_layer_used, ts);
mr->cache = cache; /* HACK */
mr->use_hide = use_hide;
mr->use_subsurf_fdots = use_subsurf_fdots;
@@ -4788,20 +4979,32 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
double rdata_end = PIL_check_seconds_timer();
#endif
TaskPool *task_pool;
/* Create a suspended pool as the finalize method could be called too early.
* See `extract_run`. */
task_pool = BLI_task_pool_create_suspended(NULL, TASK_PRIORITY_HIGH);
size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t);
int32_t *task_counters = MEM_callocN(counters_size, __func__);
int counter_used = 0;
struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create(
task_graph, mr, iter_flag, data_flag);
ExtractSingleThreadedTaskData *single_threaded_task_data = MEM_callocN(
sizeof(ExtractSingleThreadedTaskData), __func__);
UserDataInitTaskData *user_data_init_task_data = MEM_callocN(sizeof(UserDataInitTaskData),
__func__);
user_data_init_task_data->task_counters = task_counters;
struct TaskNode *task_node_user_data_init = user_data_init_task_node_create(
task_graph, user_data_init_task_data);
#define EXTRACT(buf, name) \
if (mbc.buf.name) { \
extract_task_create( \
task_pool, scene, mr, &extract_##name, mbc.buf.name, &task_counters[counter_used++]); \
extract_task_create(task_graph, \
task_node_mesh_render_data, \
task_node_user_data_init, \
&single_threaded_task_data->task_datas, \
&user_data_init_task_data->task_datas, \
scene, \
mr, \
&extract_##name, \
mbc.buf.name, \
&task_counters[counter_used++]); \
} \
((void)0)
@@ -4829,7 +5032,33 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
EXTRACT(vbo, skin_roots);
EXTRACT(ibo, tris);
EXTRACT(ibo, lines);
if (mbc.ibo.lines) {
/* When `lines` and `lines_loose` are requested, schedule lines extraction that also creates
* the `lines_loose` sub-buffer. */
const MeshExtract *lines_extractor = do_lines_loose_subbuffer ?
&extract_lines_with_lines_loose :
&extract_lines;
extract_task_create(task_graph,
task_node_mesh_render_data,
task_node_user_data_init,
&single_threaded_task_data->task_datas,
&user_data_init_task_data->task_datas,
scene,
mr,
lines_extractor,
mbc.ibo.lines,
&task_counters[counter_used++]);
}
else {
if (do_lines_loose_subbuffer) {
/* When `lines_loose` is requested without `lines` we can create the sub-buffer on the fly as
* the `lines` buffer should then already be up-to-date.
* (see `DRW_batch_requested(cache->batch.loose_edges, GPU_PRIM_LINES)` in
* `DRW_mesh_batch_cache_create_requested`).
*/
extract_lines_loose_subbuffer(mr);
}
}
EXTRACT(ibo, points);
EXTRACT(ibo, fdots);
EXTRACT(ibo, lines_paint_mask);
@@ -4839,27 +5068,29 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
EXTRACT(ibo, edituv_points);
EXTRACT(ibo, edituv_fdots);
/* TODO(fclem) Ideally, we should have one global pool for all
* objects and wait for finish only before drawing when buffers
* need to be ready. */
BLI_task_pool_work_and_wait(task_pool);
/* Only create the edge when there are user datas that needs to be inited.
* The task is still part of the graph so the task_data will be freed when the graph is freed.
*/
if (!BLI_listbase_is_empty(&user_data_init_task_data->task_datas)) {
BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init);
}
/* The next task(s) rely on the result of the tasks above. */
if (!BLI_listbase_is_empty(&single_threaded_task_data->task_datas)) {
struct TaskNode *task_node = extract_single_threaded_task_node_create(
task_graph, single_threaded_task_data);
BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
}
else {
extract_single_threaded_task_data_free(single_threaded_task_data);
}
/* The `lines_loose` is a sub buffer from `ibo.lines`.
* We schedule it here due to potential synchronization issues.*/
EXTRACT(ibo, lines_loose);
BLI_task_pool_work_and_wait(task_pool);
/* Trigger the sub-graph for this mesh. */
BLI_task_graph_node_push_work(task_node_mesh_render_data);
#undef EXTRACT
BLI_task_pool_free(task_pool);
MEM_freeN(task_counters);
mesh_render_data_free(mr);
#ifdef DEBUG_TIME
BLI_task_graph_work_and_wait(task_graph);
double end = PIL_check_seconds_timer();
static double avg = 0;