Changeset View
Changeset View
Standalone View
Standalone View
source/blender/draw/intern/draw_cache_extract_mesh_render_data.c
| Show All 19 Lines | |||||
| /** \file | /** \file | ||||
| * \ingroup draw | * \ingroup draw | ||||
| * | * | ||||
| * \brief Extraction of Mesh data into VBO to feed to GPU. | * \brief Extraction of Mesh data into VBO to feed to GPU. | ||||
| */ | */ | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLI_alloca.h" | |||||
| #include "BLI_bitmap.h" | #include "BLI_bitmap.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_task.h" | #include "BLI_task.h" | ||||
| #include "BKE_editmesh.h" | #include "BKE_editmesh.h" | ||||
| #include "BKE_editmesh_cache.h" | #include "BKE_editmesh_cache.h" | ||||
| #include "BKE_mesh.h" | #include "BKE_mesh.h" | ||||
| #include "GPU_batch.h" | #include "GPU_batch.h" | ||||
| #include "ED_mesh.h" | #include "ED_mesh.h" | ||||
| #include "draw_cache_extract_mesh_private.h" | #include "mesh_extractors/extract_mesh.h" | ||||
| /* ---------------------------------------------------------------------- */ | /* ---------------------------------------------------------------------- */ | ||||
| /** \name Update Loose Geometry | /** \name Update Loose Geometry | ||||
| * \{ */ | * \{ */ | ||||
| static void mesh_render_data_lverts_bm(const MeshRenderData *mr, | static void mesh_render_data_lverts_bm(const MeshRenderData *mr, | ||||
| MeshBufferExtractionCache *cache, | MeshBufferExtractionCache *cache, | ||||
| BMesh *bm); | BMesh *bm); | ||||
| ▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { | ||||
| } | } | ||||
| } | } | ||||
| if (cache->loose_geom.edge_len < mr->edge_len) { | if (cache->loose_geom.edge_len < mr->edge_len) { | ||||
| cache->loose_geom.edges = MEM_reallocN( | cache->loose_geom.edges = MEM_reallocN( | ||||
| cache->loose_geom.edges, cache->loose_geom.edge_len * sizeof(*cache->loose_geom.edges)); | cache->loose_geom.edges, cache->loose_geom.edge_len * sizeof(*cache->loose_geom.edges)); | ||||
| } | } | ||||
| } | } | ||||
| void mesh_render_data_update_loose_geom(MeshRenderData *mr, | |||||
| MeshBufferExtractionCache *cache, | |||||
| const eMRIterType iter_type, | |||||
| const eMRDataType data_flag) | |||||
| { | |||||
| if ((iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) || (data_flag & MR_DATA_LOOSE_GEOM)) { | |||||
| mesh_render_data_loose_geom_ensure(mr, cache); | |||||
| mesh_render_data_loose_geom_load(mr, cache); | |||||
| } | |||||
| } | |||||
| /** \} */ | /** \} */ | ||||
| /* ---------------------------------------------------------------------- */ | /* ---------------------------------------------------------------------- */ | ||||
| /** \name Material Offsets | /** \name Polygons sorted per material | ||||
| * | * | ||||
| * Material offsets contains the offset of a material after sorting tris based on their material. | * Contains polygon indices sorted based on their material. | ||||
| * | * | ||||
| * \{ */ | * \{ */ | ||||
| static void mesh_render_data_mat_offset_load(MeshRenderData *mr, | static void mesh_render_data_polys_sorted_load(MeshRenderData *mr, | ||||
| const MeshBufferExtractionCache *cache); | const MeshBufferExtractionCache *cache); | ||||
| static void mesh_render_data_mat_offset_ensure(MeshRenderData *mr, | static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr, | ||||
| MeshBufferExtractionCache *cache); | MeshBufferExtractionCache *cache); | ||||
| static void mesh_render_data_mat_offset_build(MeshRenderData *mr, | static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, | ||||
| MeshBufferExtractionCache *cache); | |||||
| static void mesh_render_data_mat_offset_build_bm(MeshRenderData *mr, | |||||
| MeshBufferExtractionCache *cache); | |||||
| static void mesh_render_data_mat_offset_build_mesh(MeshRenderData *mr, | |||||
| MeshBufferExtractionCache *cache); | |||||
| static void mesh_render_data_mat_offset_apply_offset(MeshRenderData *mr, | |||||
| MeshBufferExtractionCache *cache); | MeshBufferExtractionCache *cache); | ||||
| static int *mesh_render_data_mat_tri_len_build(MeshRenderData *mr); | |||||
| void mesh_render_data_update_mat_offsets(MeshRenderData *mr, | void mesh_render_data_update_polys_sorted(MeshRenderData *mr, | ||||
| MeshBufferExtractionCache *cache, | MeshBufferExtractionCache *cache, | ||||
| const eMRDataType data_flag) | const eMRDataType data_flag) | ||||
| { | { | ||||
| if (data_flag & MR_DATA_MAT_OFFSETS) { | if (data_flag & MR_DATA_POLYS_SORTED) { | ||||
| mesh_render_data_mat_offset_ensure(mr, cache); | mesh_render_data_polys_sorted_ensure(mr, cache); | ||||
| mesh_render_data_mat_offset_load(mr, cache); | mesh_render_data_polys_sorted_load(mr, cache); | ||||
| } | } | ||||
| } | } | ||||
| static void mesh_render_data_mat_offset_load(MeshRenderData *mr, | static void mesh_render_data_polys_sorted_load(MeshRenderData *mr, | ||||
| const MeshBufferExtractionCache *cache) | const MeshBufferExtractionCache *cache) | ||||
| { | { | ||||
| mr->mat_offsets.tri = cache->mat_offsets.tri; | mr->poly_sorted.tri_first_index = cache->poly_sorted.tri_first_index; | ||||
| mr->mat_offsets.visible_tri_len = cache->mat_offsets.visible_tri_len; | mr->poly_sorted.mat_tri_len = cache->poly_sorted.mat_tri_len; | ||||
| mr->poly_sorted.visible_tri_len = cache->poly_sorted.visible_tri_len; | |||||
| } | } | ||||
| static void mesh_render_data_mat_offset_ensure(MeshRenderData *mr, | static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr, | ||||
| MeshBufferExtractionCache *cache) | MeshBufferExtractionCache *cache) | ||||
| { | { | ||||
| if (cache->mat_offsets.tri) { | if (cache->poly_sorted.tri_first_index) { | ||||
| return; | return; | ||||
| } | } | ||||
| mesh_render_data_mat_offset_build(mr, cache); | mesh_render_data_polys_sorted_build(mr, cache); | ||||
| } | } | ||||
| static void mesh_render_data_mat_offset_build(MeshRenderData *mr, MeshBufferExtractionCache *cache) | static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, | ||||
| MeshBufferExtractionCache *cache) | |||||
| { | { | ||||
| size_t mat_tri_idx_size = sizeof(int) * mr->mat_len; | int *tri_first_index = MEM_mallocN(sizeof(*tri_first_index) * mr->poly_len, __func__); | ||||
| cache->mat_offsets.tri = MEM_callocN(mat_tri_idx_size, __func__); | int *mat_tri_len = mesh_render_data_mat_tri_len_build(mr); | ||||
| /* Count how many triangles for each material. */ | /* Apply offset. */ | ||||
| int visible_tri_len = 0; | |||||
| int *mat_tri_offs = BLI_array_alloca(mat_tri_offs, mr->mat_len); | |||||
| { | |||||
| for (int i = 0; i < mr->mat_len; i++) { | |||||
| mat_tri_offs[i] = visible_tri_len; | |||||
| visible_tri_len += mat_tri_len[i]; | |||||
| } | |||||
| } | |||||
| /* Sort per material. */ | |||||
| int mat_last = mr->mat_len - 1; | |||||
| if (mr->extract_type == MR_EXTRACT_BMESH) { | if (mr->extract_type == MR_EXTRACT_BMESH) { | ||||
| mesh_render_data_mat_offset_build_bm(mr, cache); | BMIter iter; | ||||
| BMFace *f; | |||||
| int i; | |||||
| BM_ITER_MESH_INDEX (f, &iter, mr->bm, BM_FACES_OF_MESH, i) { | |||||
| if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { | |||||
| const int mat = min_ii(f->mat_nr, mat_last); | |||||
| tri_first_index[i] = mat_tri_offs[mat]; | |||||
| mat_tri_offs[mat] += f->len - 2; | |||||
| } | } | ||||
| else { | else { | ||||
| mesh_render_data_mat_offset_build_mesh(mr, cache); | tri_first_index[i] = -1; | ||||
| } | } | ||||
| mesh_render_data_mat_offset_apply_offset(mr, cache); | |||||
| } | } | ||||
| } | |||||
| typedef struct MatOffsetUserData { | else { | ||||
| MeshRenderData *mr; | const MPoly *mp = &mr->mpoly[0]; | ||||
| /** This struct is extended during allocation to hold mat_tri_len for each material. */ | for (int i = 0; i < mr->poly_len; i++, mp++) { | ||||
| int mat_tri_len[0]; | if (!(mr->use_hide && (mp->flag & ME_HIDE))) { | ||||
| } MatOffsetUserData; | const int mat = min_ii(mp->mat_nr, mat_last); | ||||
| tri_first_index[i] = mat_tri_offs[mat]; | |||||
| static void mesh_render_data_mat_offset_reduce(const void *__restrict UNUSED(userdata), | mat_tri_offs[mat] += mp->totloop - 2; | ||||
| void *__restrict chunk_join, | } | ||||
| void *__restrict chunk) | else { | ||||
| { | tri_first_index[i] = -1; | ||||
| MatOffsetUserData *dst = chunk_join; | } | ||||
| MatOffsetUserData *src = chunk; | |||||
| int *dst_mat_len = dst->mat_tri_len; | |||||
| int *src_mat_len = src->mat_tri_len; | |||||
| for (int i = 0; i < dst->mr->mat_len; i++) { | |||||
| dst_mat_len[i] += src_mat_len[i]; | |||||
| } | } | ||||
| } | } | ||||
| static void mesh_render_data_mat_offset_build_threaded(MeshRenderData *mr, | cache->poly_sorted.tri_first_index = tri_first_index; | ||||
| MeshBufferExtractionCache *cache, | cache->poly_sorted.mat_tri_len = mat_tri_len; | ||||
| int face_len, | cache->poly_sorted.visible_tri_len = visible_tri_len; | ||||
| TaskParallelRangeFunc range_func) | |||||
| { | |||||
| /* Extending the #MatOffsetUserData with an int per material slot. */ | |||||
| size_t userdata_size = sizeof(MatOffsetUserData) + | |||||
| (mr->mat_len) * sizeof(*cache->mat_offsets.tri); | |||||
| MatOffsetUserData *userdata = MEM_callocN(userdata_size, __func__); | |||||
| userdata->mr = mr; | |||||
| TaskParallelSettings settings; | |||||
| BLI_parallel_range_settings_defaults(&settings); | |||||
| settings.userdata_chunk = userdata; | |||||
| settings.userdata_chunk_size = userdata_size; | |||||
| settings.min_iter_per_thread = MIN_RANGE_LEN; | |||||
| settings.func_reduce = mesh_render_data_mat_offset_reduce; | |||||
| BLI_task_parallel_range(0, face_len, NULL, range_func, &settings); | |||||
| memcpy(cache->mat_offsets.tri, | |||||
| &userdata->mat_tri_len, | |||||
| (mr->mat_len) * sizeof(*cache->mat_offsets.tri)); | |||||
| MEM_freeN(userdata); | |||||
| } | } | ||||
| static void mesh_render_data_mat_offset_bm_range(void *__restrict UNUSED(userdata), | static void mesh_render_data_mat_tri_len_bm_range_fn(void *__restrict userdata, | ||||
| const int iter, | const int iter, | ||||
| const TaskParallelTLS *__restrict tls) | const TaskParallelTLS *__restrict tls) | ||||
| { | { | ||||
| MatOffsetUserData *mat_offset_userdata = tls->userdata_chunk; | MeshRenderData *mr = userdata; | ||||
| MeshRenderData *mr = mat_offset_userdata->mr; | int *mat_tri_len = tls->userdata_chunk; | ||||
| int *mat_tri_len = mat_offset_userdata->mat_tri_len; | |||||
| BMesh *bm = mr->bm; | BMesh *bm = mr->bm; | ||||
| BMFace *efa = BM_face_at_index(bm, iter); | BMFace *efa = BM_face_at_index(bm, iter); | ||||
| if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { | if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { | ||||
| int mat = min_ii(efa->mat_nr, mr->mat_len - 1); | int mat = min_ii(efa->mat_nr, mr->mat_len - 1); | ||||
| mat_tri_len[mat] += efa->len - 2; | mat_tri_len[mat] += efa->len - 2; | ||||
| } | } | ||||
| } | } | ||||
| static void mesh_render_data_mat_offset_build_bm(MeshRenderData *mr, | static void mesh_render_data_mat_tri_len_mesh_range_fn(void *__restrict userdata, | ||||
| MeshBufferExtractionCache *cache) | |||||
| { | |||||
| BMesh *bm = mr->bm; | |||||
| mesh_render_data_mat_offset_build_threaded( | |||||
| mr, cache, bm->totface, mesh_render_data_mat_offset_bm_range); | |||||
| } | |||||
| static void mesh_render_data_mat_offset_mesh_range(void *__restrict UNUSED(userdata), | |||||
| const int iter, | const int iter, | ||||
| const TaskParallelTLS *__restrict tls) | const TaskParallelTLS *__restrict tls) | ||||
| { | { | ||||
| MatOffsetUserData *mat_offset_userdata = tls->userdata_chunk; | MeshRenderData *mr = userdata; | ||||
| const MeshRenderData *mr = mat_offset_userdata->mr; | int *mat_tri_len = tls->userdata_chunk; | ||||
| int *mat_tri_len = mat_offset_userdata->mat_tri_len; | |||||
| const MPoly *mp = &mr->mpoly[iter]; | const MPoly *mp = &mr->mpoly[iter]; | ||||
| if (!(mr->use_hide && (mp->flag & ME_HIDE))) { | if (!(mr->use_hide && (mp->flag & ME_HIDE))) { | ||||
| int mat = min_ii(mp->mat_nr, mr->mat_len - 1); | int mat = min_ii(mp->mat_nr, mr->mat_len - 1); | ||||
| mat_tri_len[mat] += mp->totloop - 2; | mat_tri_len[mat] += mp->totloop - 2; | ||||
| } | } | ||||
| } | } | ||||
| static void mesh_render_data_mat_offset_build_mesh(MeshRenderData *mr, | static void mesh_render_data_mat_tri_len_reduce_fn(const void *__restrict userdata, | ||||
| MeshBufferExtractionCache *cache) | void *__restrict chunk_join, | ||||
| void *__restrict chunk) | |||||
| { | { | ||||
| mesh_render_data_mat_offset_build_threaded( | const MeshRenderData *mr = userdata; | ||||
| mr, cache, mr->poly_len, mesh_render_data_mat_offset_mesh_range); | int *dst_mat_len = chunk_join; | ||||
| int *src_mat_len = chunk; | |||||
| for (int i = 0; i < mr->mat_len; i++) { | |||||
| dst_mat_len[i] += src_mat_len[i]; | |||||
| } | |||||
| } | } | ||||
| static void mesh_render_data_mat_offset_apply_offset(MeshRenderData *mr, | static int *mesh_render_data_mat_tri_len_build_threaded(MeshRenderData *mr, | ||||
| MeshBufferExtractionCache *cache) | int face_len, | ||||
| TaskParallelRangeFunc range_func) | |||||
| { | { | ||||
| int *mat_tri_len = cache->mat_offsets.tri; | /* Extending the #MatOffsetUserData with an int per material slot. */ | ||||
| int ofs = mat_tri_len[0]; | size_t mat_tri_len_size = sizeof(int) * mr->mat_len; | ||||
| mat_tri_len[0] = 0; | int *mat_tri_len = MEM_callocN(mat_tri_len_size, __func__); | ||||
| for (int i = 1; i < mr->mat_len; i++) { | |||||
| int tmp = mat_tri_len[i]; | TaskParallelSettings settings; | ||||
| mat_tri_len[i] = ofs; | BLI_parallel_range_settings_defaults(&settings); | ||||
| ofs += tmp; | settings.userdata_chunk = mat_tri_len; | ||||
| settings.userdata_chunk_size = mat_tri_len_size; | |||||
| settings.min_iter_per_thread = MIN_RANGE_LEN; | |||||
| settings.func_reduce = mesh_render_data_mat_tri_len_reduce_fn; | |||||
| BLI_task_parallel_range(0, face_len, mr, range_func, &settings); | |||||
| return mat_tri_len; | |||||
| } | } | ||||
| cache->mat_offsets.visible_tri_len = ofs; | |||||
| /* Count how many triangles for each material. */ | |||||
| static int *mesh_render_data_mat_tri_len_build(MeshRenderData *mr) | |||||
| { | |||||
| if (mr->extract_type == MR_EXTRACT_BMESH) { | |||||
| BMesh *bm = mr->bm; | |||||
| return mesh_render_data_mat_tri_len_build_threaded( | |||||
| mr, bm->totface, mesh_render_data_mat_tri_len_bm_range_fn); | |||||
| } | |||||
| return mesh_render_data_mat_tri_len_build_threaded( | |||||
| mr, mr->poly_len, mesh_render_data_mat_tri_len_mesh_range_fn); | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* ---------------------------------------------------------------------- */ | /* ---------------------------------------------------------------------- */ | ||||
| /** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). | /** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). | ||||
| * \{ */ | * \{ */ | ||||
| ▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag) | ||||
| } | } | ||||
| } | } | ||||
| /** | /** | ||||
| * \param is_mode_active: When true, use the modifiers from the edit-data, | * \param is_mode_active: When true, use the modifiers from the edit-data, | ||||
| * otherwise don't use modifiers as they are not from this object. | * otherwise don't use modifiers as they are not from this object. | ||||
| */ | */ | ||||
| MeshRenderData *mesh_render_data_create(Mesh *me, | MeshRenderData *mesh_render_data_create(Mesh *me, | ||||
| MeshBufferExtractionCache *cache, | |||||
| const bool is_editmode, | const bool is_editmode, | ||||
| const bool is_paint_mode, | const bool is_paint_mode, | ||||
| const bool is_mode_active, | const bool is_mode_active, | ||||
| 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 ToolSettings *ts, | const ToolSettings *ts) | ||||
| const eMRIterType iter_type) | |||||
| { | { | ||||
| 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); | ||||
| if (is_editmode) { | if (is_editmode) { | ||||
| ▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | else { | ||||
| mr->vert_len = bm->totvert; | mr->vert_len = bm->totvert; | ||||
| mr->edge_len = bm->totedge; | mr->edge_len = bm->totedge; | ||||
| mr->loop_len = bm->totloop; | mr->loop_len = bm->totloop; | ||||
| mr->poly_len = bm->totface; | mr->poly_len = bm->totface; | ||||
| mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); | mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); | ||||
| } | } | ||||
| if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { | |||||
| mesh_render_data_loose_geom_ensure(mr, cache); | |||||
| mesh_render_data_loose_geom_load(mr, cache); | |||||
| } | |||||
| return mr; | return mr; | ||||
| } | } | ||||
| void mesh_render_data_free(MeshRenderData *mr) | void mesh_render_data_free(MeshRenderData *mr) | ||||
| { | { | ||||
| MEM_SAFE_FREE(mr->mlooptri); | MEM_SAFE_FREE(mr->mlooptri); | ||||
| MEM_SAFE_FREE(mr->loop_normals); | MEM_SAFE_FREE(mr->loop_normals); | ||||
| /* Loose geometry are owned by #MeshBufferExtractionCache. */ | /* Loose geometry are owned by #MeshBufferExtractionCache. */ | ||||
| mr->ledges = NULL; | mr->ledges = NULL; | ||||
| mr->lverts = NULL; | mr->lverts = NULL; | ||||
| MEM_freeN(mr); | MEM_freeN(mr); | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||