Changeset View
Changeset View
Standalone View
Standalone View
source/blender/draw/intern/draw_cache_extract_mesh.c
| Show First 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | typedef struct MeshRenderData { | ||||
| BMFace *efa_act; | BMFace *efa_act; | ||||
| BMFace *efa_act_uv; | BMFace *efa_act_uv; | ||||
| /* Data created on-demand (usually not for bmesh-based data). */ | /* Data created on-demand (usually not for bmesh-based data). */ | ||||
| MLoopTri *mlooptri; | MLoopTri *mlooptri; | ||||
| float (*loop_normals)[3]; | float (*loop_normals)[3]; | ||||
| float (*poly_normals)[3]; | float (*poly_normals)[3]; | ||||
| int *lverts, *ledges; | int *lverts, *ledges; | ||||
| } MeshRenderData; | } MeshRenderData; | ||||
| static MeshRenderData *mesh_render_data_create(Mesh *me, | static MeshRenderData *mesh_render_data_create(Mesh *me, | ||||
| const bool is_editmode, | const bool is_editmode, | ||||
| const bool is_paint_mode, | const bool is_paint_mode, | ||||
| const float obmat[4][4], | const float obmat[4][4], | ||||
| const bool do_final, | const bool do_final, | ||||
| const bool do_uvedit, | const bool do_uvedit, | ||||
| const eMRIterType iter_type, | const eMRIterType UNUSED(iter_type), | ||||
| const eMRDataType data_flag, | const eMRDataType UNUSED(data_flag), | ||||
| const DRW_MeshCDMask *UNUSED(cd_used), | const DRW_MeshCDMask *UNUSED(cd_used), | ||||
| const ToolSettings *ts) | const ToolSettings *ts) | ||||
| { | { | ||||
| MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); | MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); | ||||
| mr->toolsettings = ts; | mr->toolsettings = ts; | ||||
| mr->mat_len = mesh_render_mat_len_get(me); | mr->mat_len = mesh_render_mat_len_get(me); | ||||
| copy_m4_m4(mr->obmat, obmat); | 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) { | if (is_editmode) { | ||||
| BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); | BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); | ||||
| mr->bm = me->edit_mesh->bm; | mr->bm = me->edit_mesh->bm; | ||||
| mr->edit_bmesh = me->edit_mesh; | mr->edit_bmesh = me->edit_mesh; | ||||
| mr->edit_data = me->runtime.edit_data; | mr->edit_data = me->runtime.edit_data; | ||||
| mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; | mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; | ||||
| bool use_mapped = !do_uvedit && mr->me && !mr->me->runtime.is_original; | bool use_mapped = !do_uvedit && mr->me && !mr->me->runtime.is_original; | ||||
| ▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | if (mr->extract_type != MR_EXTRACT_BMESH) { | ||||
| mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT); | mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT); | ||||
| mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE); | mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE); | ||||
| mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP); | mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP); | ||||
| mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY); | mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY); | ||||
| mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); | mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); | ||||
| mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); | mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); | ||||
| mr->p_origindex = CustomData_get_layer(&mr->me->pdata, 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)) { | 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__); | mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__); | ||||
| BKE_mesh_calc_normals_poly((MVert *)mr->mvert, | BKE_mesh_calc_normals_poly((MVert *)mr->mvert, | ||||
| NULL, | NULL, | ||||
| mr->vert_len, | mr->vert_len, | ||||
| mr->mloop, | mr->mloop, | ||||
| mr->mpoly, | mr->mpoly, | ||||
| mr->loop_len, | mr->loop_len, | ||||
| ▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { | ||||
| MEM_freeN(lvert_map); | MEM_freeN(lvert_map); | ||||
| mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2; | mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* BMesh */ | /* BMesh */ | ||||
| BMesh *bm = mr->bm; | 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) { | if (data_flag & MR_DATA_POLY_NOR) { | ||||
| /* Use bmface->no instead. */ | /* Use bmface->no instead. */ | ||||
| } | } | ||||
| if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { | if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { | ||||
| mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); | mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); | ||||
| int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL); | int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL); | ||||
| BM_loops_calc_normal_vcos(mr->bm, | BM_loops_calc_normal_vcos(mr->bm, | ||||
| NULL, | NULL, | ||||
| Show All 37 Lines | if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { | ||||
| } | } | ||||
| if (mr->edge_loose_len < mr->edge_len) { | if (mr->edge_loose_len < mr->edge_len) { | ||||
| mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); | mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); | ||||
| } | } | ||||
| mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2; | mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2; | ||||
| } | } | ||||
| } | } | ||||
| return mr; | |||||
| } | } | ||||
| static void mesh_render_data_free(MeshRenderData *mr) | static void mesh_render_data_free(MeshRenderData *mr) | ||||
| { | { | ||||
| MEM_SAFE_FREE(mr->mlooptri); | MEM_SAFE_FREE(mr->mlooptri); | ||||
| MEM_SAFE_FREE(mr->poly_normals); | MEM_SAFE_FREE(mr->poly_normals); | ||||
| MEM_SAFE_FREE(mr->loop_normals); | MEM_SAFE_FREE(mr->loop_normals); | ||||
| ▲ Show 20 Lines • Show All 295 Lines • ▼ Show 20 Lines | static const MeshExtract extract_lines = { | ||||
| extract_lines_ledge_bmesh, | extract_lines_ledge_bmesh, | ||||
| extract_lines_ledge_mesh, | extract_lines_ledge_mesh, | ||||
| NULL, | NULL, | ||||
| NULL, | NULL, | ||||
| extract_lines_finish, | extract_lines_finish, | ||||
| 0, | 0, | ||||
| false, | 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)) | static void extract_lines_loose_subbuffer(const MeshRenderData *mr) | ||||
| { | |||||
| 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)) | |||||
| { | { | ||||
| /* Multiply by 2 because these are edges indices. */ | /* Multiply by 2 because these are edges indices. */ | ||||
| const int start = mr->edge_len * 2; | const int start = mr->edge_len * 2; | ||||
| const int len = mr->edge_loose_len * 2; | const int len = mr->edge_loose_len * 2; | ||||
| GPU_indexbuf_create_subrange_in_place( | GPU_indexbuf_create_subrange_in_place( | ||||
| mr->cache->final.ibo.lines_loose, mr->cache->final.ibo.lines, start, len); | mr->cache->final.ibo.lines_loose, mr->cache->final.ibo.lines, start, len); | ||||
| mr->cache->no_loose_wire = (len == 0); | mr->cache->no_loose_wire = (len == 0); | ||||
| } | } | ||||
| static const MeshExtract extract_lines_loose = { | static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, void *ibo, void *elb) | ||||
| extract_lines_loose_init, | { | ||||
| NULL, | GPU_indexbuf_build_in_place(elb, ibo); | ||||
| NULL, | extract_lines_loose_subbuffer(mr); | ||||
| MEM_freeN(elb); | |||||
| } | |||||
| static const MeshExtract extract_lines_with_lines_loose = { | |||||
| extract_lines_init, | |||||
| NULL, | NULL, | ||||
| NULL, | NULL, | ||||
| extract_lines_loose_ledge_bmesh, | extract_lines_loop_bmesh, | ||||
| extract_lines_loose_ledge_mesh, | extract_lines_loop_mesh, | ||||
| extract_lines_ledge_bmesh, | |||||
| extract_lines_ledge_mesh, | |||||
| NULL, | NULL, | ||||
| NULL, | NULL, | ||||
| extract_lines_loose_finish, | extract_lines_with_lines_loose_finish, | ||||
| 0, | 0, | ||||
| false, | false, | ||||
| }; | }; | ||||
| /** \} */ | /** \} */ | ||||
| /* ---------------------------------------------------------------------- */ | /* ---------------------------------------------------------------------- */ | ||||
| /** \name Extract Point Indices | /** \name Extract Point Indices | ||||
| ▲ Show 20 Lines • Show All 902 Lines • ▼ Show 20 Lines | static void extract_lnor_hq_loop_mesh( | ||||
| else if (mpoly->flag & ME_SMOOTH) { | else if (mpoly->flag & ME_SMOOTH) { | ||||
| copy_v3_v3_short(&lnor_data->x, mr->mvert[mloop->v].no); | copy_v3_v3_short(&lnor_data->x, mr->mvert[mloop->v].no); | ||||
| } | } | ||||
| else { | else { | ||||
| normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[p]); | normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[p]); | ||||
| } | } | ||||
| /* Flag for paint mode overlay. | /* Flag for paint mode overlay. | ||||
| * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. In paint | * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. In | ||||
| * mode it will use the unmapped data to draw the wireframe. */ | * paint mode it will use the unmapped data to draw the wireframe. */ | ||||
| if (mpoly->flag & ME_HIDE || | if (mpoly->flag & ME_HIDE || | ||||
| (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && | (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && | ||||
| mr->v_origindex[mloop->v] == ORIGINDEX_NONE)) { | mr->v_origindex[mloop->v] == ORIGINDEX_NONE)) { | ||||
| lnor_data->w = -1; | lnor_data->w = -1; | ||||
| } | } | ||||
| else if (mpoly->flag & ME_FACE_SEL) { | else if (mpoly->flag & ME_FACE_SEL) { | ||||
| lnor_data->w = 1; | lnor_data->w = 1; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | static void extract_lnor_loop_mesh( | ||||
| else if (mpoly->flag & ME_SMOOTH) { | else if (mpoly->flag & ME_SMOOTH) { | ||||
| *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[mloop->v].no); | *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[mloop->v].no); | ||||
| } | } | ||||
| else { | else { | ||||
| *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[p]); | *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[p]); | ||||
| } | } | ||||
| /* Flag for paint mode overlay. | /* Flag for paint mode overlay. | ||||
| * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. In paint | * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. In | ||||
| * mode it will use the unmapped data to draw the wireframe. */ | * paint mode it will use the unmapped data to draw the wireframe. */ | ||||
| if (mpoly->flag & ME_HIDE || | if (mpoly->flag & ME_HIDE || | ||||
| (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && | (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && | ||||
| mr->v_origindex[mloop->v] == ORIGINDEX_NONE)) { | mr->v_origindex[mloop->v] == ORIGINDEX_NONE)) { | ||||
| lnor_data->w = -1; | lnor_data->w = -1; | ||||
| } | } | ||||
| else if (mpoly->flag & ME_FACE_SEL) { | else if (mpoly->flag & ME_FACE_SEL) { | ||||
| lnor_data->w = 1; | lnor_data->w = 1; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 2,673 Lines • ▼ Show 20 Lines | static const MeshExtract extract_fdot_idx = { | ||||
| NULL, | NULL, | ||||
| 0, | 0, | ||||
| true, | true, | ||||
| }; | }; | ||||
| /** \} */ | /** \} */ | ||||
| /* ---------------------------------------------------------------------- */ | /* ---------------------------------------------------------------------- */ | ||||
| /** \name Extract Loop | /** \name ExtractTaskData | ||||
| * \{ */ | * \{ */ | ||||
| typedef struct ExtractUserData { | |||||
| void *user_data; | |||||
| } ExtractUserData; | |||||
| typedef struct ExtractTaskData { | typedef struct ExtractTaskData { | ||||
| void *next, *prev; | |||||
| const MeshRenderData *mr; | const MeshRenderData *mr; | ||||
| const MeshExtract *extract; | const MeshExtract *extract; | ||||
| eMRIterType iter_type; | eMRIterType iter_type; | ||||
| int start, end; | int start, end; | ||||
| /** Decremented each time a task is finished. */ | /** Decremented each time a task is finished. */ | ||||
| int32_t *task_counter; | int32_t *task_counter; | ||||
| void *buf; | void *buf; | ||||
| void *user_data; | ExtractUserData *user_data; | ||||
| } ExtractTaskData; | } ExtractTaskData; | ||||
| static void extract_task_data_free(ExtractTaskData *data) | |||||
| { | |||||
| MEM_SAFE_FREE(data->user_data); | |||||
| MEM_freeN(data); | |||||
| } | |||||
| BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, | BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, | ||||
| const eMRIterType iter_type, | const eMRIterType iter_type, | ||||
| int start, | int start, | ||||
| int end, | int end, | ||||
| const MeshExtract *extract, | const MeshExtract *extract, | ||||
| void *user_data) | void *user_data) | ||||
| { | { | ||||
| switch (mr->extract_type) { | switch (mr->extract_type) { | ||||
| ▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | case MR_EXTRACT_MESH: | ||||
| for (int v = start; v < lv_end; v++) { | for (int v = start; v < lv_end; v++) { | ||||
| extract->iter_lvert(mr, v, &mr->mvert[mr->lverts[v]], user_data); | extract->iter_lvert(mr, v, &mr->mvert[mr->lverts[v]], user_data); | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| static void extract_run(TaskPool *__restrict UNUSED(pool), void *taskdata) | static void extract_init(ExtractTaskData *data) | ||||
| { | { | ||||
| ExtractTaskData *data = taskdata; | data->user_data->user_data = data->extract->init(data->mr, data->buf); | ||||
| mesh_extract_iter( | } | ||||
| data->mr, data->iter_type, data->start, data->end, data->extract, data->user_data); | |||||
| 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. */ | /* If this is the last task, we do the finish function. */ | ||||
| int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); | int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); | ||||
| if (remainin_tasks == 0 && data->extract->finish != NULL) { | 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_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 void extract_range_task_create( | static struct TaskNode *extract_single_threaded_task_node_create( | ||||
| TaskPool *task_pool, ExtractTaskData *taskdata, const eMRIterType type, int start, int length) | 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); | taskdata = MEM_dupallocN(taskdata); | ||||
| atomic_add_and_fetch_int32(taskdata->task_counter, 1); | atomic_add_and_fetch_int32(taskdata->task_counter, 1); | ||||
| taskdata->iter_type = type; | taskdata->iter_type = type; | ||||
| taskdata->start = start; | taskdata->start = start; | ||||
| taskdata->end = start + length; | 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 Scene *scene, | ||||
| const MeshRenderData *mr, | const MeshRenderData *mr, | ||||
| const MeshExtract *extract, | const MeshExtract *extract, | ||||
| void *buf, | void *buf, | ||||
| int32_t *task_counter) | int32_t *task_counter) | ||||
| { | { | ||||
| BLI_assert(scene != NULL); | BLI_assert(scene != NULL); | ||||
| const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0; | const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0; | ||||
| if (do_hq_normals && (extract == &extract_lnor)) { | if (do_hq_normals && (extract == &extract_lnor)) { | ||||
| extract = &extract_lnor_hq; | extract = &extract_lnor_hq; | ||||
| } | } | ||||
| if (do_hq_normals && (extract == &extract_tan)) { | if (do_hq_normals && (extract == &extract_tan)) { | ||||
| extract = &extract_tan_hq; | extract = &extract_tan_hq; | ||||
| } | } | ||||
| /* Divide extraction of the VBO/IBO into sensible chunks of works. */ | /* Divide extraction of the VBO/IBO into sensible chunks of works. */ | ||||
| ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), "ExtractTaskData"); | ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), "ExtractTaskData"); | ||||
| taskdata->next = NULL; | |||||
| taskdata->prev = NULL; | |||||
| taskdata->mr = mr; | taskdata->mr = mr; | ||||
| taskdata->extract = extract; | taskdata->extract = extract; | ||||
| taskdata->buf = buf; | taskdata->buf = buf; | ||||
| taskdata->user_data = extract->init(mr, buf); | taskdata->user_data = MEM_callocN(sizeof(ExtractUserData), __func__); | ||||
fclem: Why is this? why must you separate into 2 allocations? | |||||
Done Inline ActionsI will document. jbakker: I will document.
The taskdata->user_data->user_data contains the counters that must be shared… | |||||
| taskdata->iter_type = mesh_extract_iter_type(extract); | taskdata->iter_type = mesh_extract_iter_type(extract); | ||||
| taskdata->task_counter = task_counter; | taskdata->task_counter = task_counter; | ||||
| taskdata->start = 0; | taskdata->start = 0; | ||||
| taskdata->end = INT_MAX; | taskdata->end = INT_MAX; | ||||
| /* Simple heuristic. */ | /* 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) { | if (use_thread && extract->use_threading) { | ||||
| /* Divide task into sensible chunks. */ | /* Divide task into sensible chunks. */ | ||||
| const int chunk_size = 8192; | |||||
| if (taskdata->iter_type & MR_ITER_LOOPTRI) { | if (taskdata->iter_type & MR_ITER_LOOPTRI) { | ||||
| for (int i = 0; i < mr->tri_len; i += chunk_size) { | 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) { | if (taskdata->iter_type & MR_ITER_LOOP) { | ||||
| for (int i = 0; i < mr->poly_len; i += chunk_size) { | 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) { | if (taskdata->iter_type & MR_ITER_LEDGE) { | ||||
| for (int i = 0; i < mr->edge_loose_len; i += chunk_size) { | 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) { | if (taskdata->iter_type & MR_ITER_LVERT) { | ||||
| for (int i = 0; i < mr->vert_loose_len; i += chunk_size) { | 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) { | else if (use_thread) { | ||||
| /* One task for the whole VBO. */ | /* One task for the whole VBO. */ | ||||
| (*task_counter)++; | (*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 { | else { | ||||
| /* Single threaded extraction. */ | /* Single threaded extraction. */ | ||||
| (*task_counter)++; | (*task_counter)++; | ||||
| extract_run(NULL, taskdata); | BLI_addtail(single_threaded_task_datas, taskdata); | ||||
| MEM_freeN(taskdata); | |||||
| } | } | ||||
| } | } | ||||
| void mesh_buffer_cache_create_requested(MeshBatchCache *cache, | void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,MeshBatchCache *cache, | ||||
| MeshBufferCache mbc, | MeshBufferCache mbc, | ||||
| Mesh *me, | Mesh *me, | ||||
| const bool is_editmode, | const bool is_editmode, | ||||
| const bool is_paint_mode, | const bool is_paint_mode, | ||||
| const float obmat[4][4], | const float obmat[4][4], | ||||
| const bool do_final, | const bool do_final, | ||||
| const bool do_uvedit, | const bool do_uvedit, | ||||
| const bool use_subsurf_fdots, | const bool use_subsurf_fdots, | ||||
| const DRW_MeshCDMask *cd_layer_used, | const DRW_MeshCDMask *cd_layer_used, | ||||
| const Scene *scene, | const Scene *scene, | ||||
| const ToolSettings *ts, | const ToolSettings *ts, | ||||
| const bool use_hide) | const bool use_hide) | ||||
| { | { | ||||
| eMRIterType iter_flag = 0; | eMRIterType iter_flag = 0; | ||||
| eMRDataType data_flag = 0; | eMRDataType data_flag = 0; | ||||
| const bool do_lines_loose_subbuffer = mbc.ibo.lines_loose; | |||||
Done Inline Actionsstyle: check for NULL. I believe the cast can make bool != (1|0) which is not an issue here but just better in some cases. fclem: style: check for NULL. I believe the cast can make bool != (1|0) which is not an issue here but… | |||||
| #define TEST_ASSIGN(type, type_lowercase, name) \ | #define TEST_ASSIGN(type, type_lowercase, name) \ | ||||
| do { \ | do { \ | ||||
| if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \ | if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \ | ||||
| iter_flag |= mesh_extract_iter_type(&extract_##name); \ | iter_flag |= mesh_extract_iter_type(&extract_##name); \ | ||||
| data_flag |= extract_##name.data_flag; \ | data_flag |= extract_##name.data_flag; \ | ||||
| } \ | } \ | ||||
| } while (0) | } while (0) | ||||
| Show All 21 Lines | #define TEST_ASSIGN(type, type_lowercase, name) \ | ||||
| TEST_ASSIGN(VBO, vbo, skin_roots); | TEST_ASSIGN(VBO, vbo, skin_roots); | ||||
| TEST_ASSIGN(IBO, ibo, tris); | TEST_ASSIGN(IBO, ibo, tris); | ||||
| TEST_ASSIGN(IBO, ibo, lines); | TEST_ASSIGN(IBO, ibo, lines); | ||||
| TEST_ASSIGN(IBO, ibo, points); | TEST_ASSIGN(IBO, ibo, points); | ||||
| TEST_ASSIGN(IBO, ibo, fdots); | TEST_ASSIGN(IBO, ibo, fdots); | ||||
| TEST_ASSIGN(IBO, ibo, lines_paint_mask); | TEST_ASSIGN(IBO, ibo, lines_paint_mask); | ||||
| TEST_ASSIGN(IBO, ibo, lines_adjacency); | TEST_ASSIGN(IBO, ibo, lines_adjacency); | ||||
| TEST_ASSIGN(IBO, ibo, lines_loose); | |||||
| TEST_ASSIGN(IBO, ibo, edituv_tris); | TEST_ASSIGN(IBO, ibo, edituv_tris); | ||||
| TEST_ASSIGN(IBO, ibo, edituv_lines); | TEST_ASSIGN(IBO, ibo, edituv_lines); | ||||
| TEST_ASSIGN(IBO, ibo, edituv_points); | TEST_ASSIGN(IBO, ibo, edituv_points); | ||||
| TEST_ASSIGN(IBO, ibo, edituv_fdots); | TEST_ASSIGN(IBO, ibo, edituv_fdots); | ||||
| #undef TEST_ASSIGN | #undef TEST_ASSIGN | ||||
| #ifdef DEBUG_TIME | #ifdef DEBUG_TIME | ||||
| Show All 14 Lines | #endif | ||||
| mr->use_hide = use_hide; | mr->use_hide = use_hide; | ||||
| mr->use_subsurf_fdots = use_subsurf_fdots; | mr->use_subsurf_fdots = use_subsurf_fdots; | ||||
| mr->use_final_mesh = do_final; | mr->use_final_mesh = do_final; | ||||
| #ifdef DEBUG_TIME | #ifdef DEBUG_TIME | ||||
| double rdata_end = PIL_check_seconds_timer(); | double rdata_end = PIL_check_seconds_timer(); | ||||
| #endif | #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); | size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t); | ||||
| int32_t *task_counters = MEM_callocN(counters_size, __func__); | int32_t *task_counters = MEM_callocN(counters_size, __func__); | ||||
| int counter_used = 0; | int counter_used = 0; | ||||
| /* TODO: Move to draw_manager.c */ | |||||
| 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) \ | #define EXTRACT(buf, name) \ | ||||
| if (mbc.buf.name) { \ | if (mbc.buf.name) { \ | ||||
| extract_task_create( \ | extract_task_create(task_graph, \ | ||||
| task_pool, scene, mr, &extract_##name, mbc.buf.name, &task_counters[counter_used++]); \ | 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) | ((void)0) | ||||
| EXTRACT(vbo, pos_nor); | EXTRACT(vbo, pos_nor); | ||||
| EXTRACT(vbo, lnor); | EXTRACT(vbo, lnor); | ||||
| EXTRACT(vbo, uv); | EXTRACT(vbo, uv); | ||||
| EXTRACT(vbo, tan); | EXTRACT(vbo, tan); | ||||
| EXTRACT(vbo, vcol); | EXTRACT(vbo, vcol); | ||||
| Show All 11 Lines | #define EXTRACT(buf, name) \ | ||||
| EXTRACT(vbo, fdots_edituv_data); | EXTRACT(vbo, fdots_edituv_data); | ||||
| EXTRACT(vbo, poly_idx); | EXTRACT(vbo, poly_idx); | ||||
| EXTRACT(vbo, edge_idx); | EXTRACT(vbo, edge_idx); | ||||
| EXTRACT(vbo, vert_idx); | EXTRACT(vbo, vert_idx); | ||||
| EXTRACT(vbo, fdot_idx); | EXTRACT(vbo, fdot_idx); | ||||
| EXTRACT(vbo, skin_roots); | EXTRACT(vbo, skin_roots); | ||||
| EXTRACT(ibo, tris); | EXTRACT(ibo, tris); | ||||
| EXTRACT(ibo, lines); | if (mbc.ibo.lines) { | ||||
| /* Both lines and lines loose are requested. | |||||
| * Schedule lines extraction that also extracts the lines_loose sub buffer. | |||||
| * This saves creating a new task related to the lines task. */ | |||||
| 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 only the lines_loose is requested we create the subbuffer on the go. */ | |||||
| extract_lines_loose_subbuffer(mr); | |||||
| } | |||||
| } | |||||
| EXTRACT(ibo, points); | EXTRACT(ibo, points); | ||||
| EXTRACT(ibo, fdots); | EXTRACT(ibo, fdots); | ||||
| EXTRACT(ibo, lines_paint_mask); | EXTRACT(ibo, lines_paint_mask); | ||||
| EXTRACT(ibo, lines_adjacency); | EXTRACT(ibo, lines_adjacency); | ||||
| EXTRACT(ibo, edituv_tris); | EXTRACT(ibo, edituv_tris); | ||||
| EXTRACT(ibo, edituv_lines); | EXTRACT(ibo, edituv_lines); | ||||
| EXTRACT(ibo, edituv_points); | EXTRACT(ibo, edituv_points); | ||||
| EXTRACT(ibo, edituv_fdots); | EXTRACT(ibo, edituv_fdots); | ||||
| /* TODO(fclem) Ideally, we should have one global pool for all | /* Micro optimalization: Only create the edge when there are user datas that needs to be | ||||
| * objects and wait for finish only before drawing when buffers | * inited. | ||||
| * need to be ready. */ | * The task is still part of the graph so the task_data will be freed when the graph is freed. | ||||
| BLI_task_pool_work_and_wait(task_pool); | */ | ||||
| if (!BLI_listbase_is_empty(&user_data_init_task_data->task_datas)) { | |||||
| /* The next task(s) rely on the result of the tasks above. */ | BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); | ||||
| } | |||||
| /* 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); | |||||
| #undef EXTRACT | 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); | |||||
| } | |||||
| BLI_task_pool_free(task_pool); | /* Trigger the task flow for this mesh. */ | ||||
| MEM_freeN(task_counters); | BLI_task_graph_node_push_work(task_node_mesh_render_data); | ||||
| mesh_render_data_free(mr); | #undef EXTRACT | ||||
| #ifdef DEBUG_TIME | #ifdef DEBUG_TIME | ||||
| double end = PIL_check_seconds_timer(); | double end = PIL_check_seconds_timer(); | ||||
| static double avg = 0; | static double avg = 0; | ||||
| static double avg_fps = 0; | static double avg_fps = 0; | ||||
| static double avg_rdata = 0; | static double avg_rdata = 0; | ||||
| static double end_prev = 0; | static double end_prev = 0; | ||||
| Show All 17 Lines | |||||
Why is this? why must you separate into 2 allocations?