From 1b07b7a068e9c81274d36ff5b21703b25fe68bfa Mon Sep 17 00:00:00 2001 From: YimingWu Date: Tue, 8 Jun 2021 13:03:37 +0800 Subject: LineArt: Threaded Object Loading. Move BMesh conversion and all loading code into worker. Reviewed By: Sebastian Parborg (zeddb) Differential Revision: https://developer.blender.org/D11288 --- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- .../gpencil_modifiers/intern/lineart/MOD_lineart.h | 39 +- .../gpencil_modifiers/intern/lineart/lineart_cpu.c | 558 +++++++++++++-------- .../intern/lineart/lineart_intern.h | 7 + .../intern/lineart/lineart_util.c | 33 +- source/tools | 2 +- 7 files changed, 425 insertions(+), 218 deletions(-) diff --git a/release/scripts/addons b/release/scripts/addons index 27fe7f3a4f9..4fcdbfe7c20 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 27fe7f3a4f964b53af436c4da4ddea337eff0c7e +Subproject commit 4fcdbfe7c20edfc1204c0aa46c98ea25354abcd9 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 5a82baad9f9..7d78c8a63f2 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 5a82baad9f986722104280e8354a4427d8e9eab1 +Subproject commit 7d78c8a63f2f4b146f9327ddc0d567a5921b94ea diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 712e92d017d..861085d3e16 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -23,6 +23,7 @@ #pragma once +#include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_math.h" /* Needed here for inline functions. */ #include "BLI_threads.h" @@ -226,6 +227,7 @@ typedef struct LineartRenderBuffer { int tile_count_x, tile_count_y; double width_per_tile, height_per_tile; double view_projection[4][4]; + double view[4][4]; struct LineartBoundingArea *initial_bounding_areas; unsigned int bounding_area_count; @@ -310,7 +312,7 @@ typedef struct LineartRenderBuffer { #define DBL_TRIANGLE_LIM 1e-8 #define DBL_EDGE_LIM 1e-9 -#define LRT_MEMORY_POOL_64MB (1 << 26) +#define LRT_MEMORY_POOL_1MB (1 << 20) typedef enum eLineartTriangleFlags { LRT_CULL_DONT_CARE = 0, @@ -343,6 +345,41 @@ typedef struct LineartRenderTaskInfo { } LineartRenderTaskInfo; +struct BMesh; + +typedef struct LineartObjectInfo { + struct LineartObjectInfo *next; + struct Object *original_ob; + struct Mesh *original_me; + double model_view_proj[4][4]; + double model_view[4][4]; + double normal[4][4]; + LineartElementLinkNode *v_reln; + int usage; + int global_i_offset; + + bool free_use_mesh; + + /* Threads will add lines inside here, when all threads are done, we combine those into the + * ones in LineartRenderBuffer. */ + ListBase contour; + ListBase intersection; + ListBase crease; + ListBase material; + ListBase edge_mark; + ListBase floating; + +} LineartObjectInfo; + +typedef struct LineartObjectLoadTaskInfo { + struct LineartRenderBuffer *rb; + struct Depsgraph *dg; + /* LinkNode styled list */ + LineartObjectInfo *pending; + /* Used to spread the load across several threads. This can not overflow. */ + long unsigned int total_faces; +} LineartObjectLoadTaskInfo; + /** * Bounding area diagram: * \code{.txt} diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 82ca1131cc1..d623a553ab8 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -29,6 +29,8 @@ #include "BLI_task.h" #include "BLI_utildefines.h" +#include "PIL_time.h" + #include "BKE_camera.h" #include "BKE_collection.h" #include "BKE_customdata.h" @@ -1478,6 +1480,49 @@ static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e) } } +static void lineart_add_edge_to_list_thread(LineartObjectInfo *obi, LineartEdge *e) +{ + +#define LRT_ASSIGN_EDGE(name) \ + lineart_prepend_edge_direct(&obi->name.first, e); \ + if (!obi->name.last) { \ + obi->name.last = e; \ + } + switch (e->flags) { + case LRT_EDGE_FLAG_CONTOUR: + LRT_ASSIGN_EDGE(contour); + break; + case LRT_EDGE_FLAG_CREASE: + LRT_ASSIGN_EDGE(crease); + break; + case LRT_EDGE_FLAG_MATERIAL: + LRT_ASSIGN_EDGE(material); + break; + case LRT_EDGE_FLAG_EDGE_MARK: + LRT_ASSIGN_EDGE(edge_mark); + break; + case LRT_EDGE_FLAG_INTERSECTION: + LRT_ASSIGN_EDGE(intersection); + break; + } +#undef LRT_ASSIGN_EDGE +} + +static void lineart_finalize_object_edge_list(LineartRenderBuffer *rb, LineartObjectInfo *obi) +{ +#define LRT_OBI_TO_RB(name) \ + if (obi->name.last) { \ + ((LineartEdge *)obi->name.last)->next = rb->name.first; \ + rb->name.first = obi->name.first; \ + } + LRT_OBI_TO_RB(contour); + LRT_OBI_TO_RB(crease); + LRT_OBI_TO_RB(material); + LRT_OBI_TO_RB(edge_mark); + LRT_OBI_TO_RB(intersection); +#undef LRT_OBI_TO_RB +} + static void lineart_triangle_adjacent_assign(LineartTriangle *tri, LineartTriangleAdjacent *ta, LineartEdge *e) @@ -1493,13 +1538,7 @@ static void lineart_triangle_adjacent_assign(LineartTriangle *tri, } } -static void lineart_geometry_object_load(Depsgraph *dg, - Object *ob, - double (*mv_mat)[4], - double (*mvp_mat)[4], - LineartRenderBuffer *rb, - int override_usage, - int *global_vindex) +static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb) { BMesh *bm; BMVert *v; @@ -1507,250 +1546,244 @@ static void lineart_geometry_object_load(Depsgraph *dg, BMEdge *e; BMLoop *loop; LineartEdge *la_e; + LineartEdgeSegment *la_s; LineartTriangle *tri; LineartTriangleAdjacent *orta; - double new_mvp[4][4], new_mv[4][4], normal[4][4]; - float imat[4][4]; + double(*model_view_proj)[4] = obi->model_view_proj, (*model_view)[4] = obi->model_view, + (*normal)[4] = obi->normal; LineartElementLinkNode *eln; LineartVert *orv; LineartEdge *o_la_e; + LineartEdgeSegment *o_la_s; LineartTriangle *ort; Object *orig_ob; int CanFindFreestyle = 0; - int i, global_i = (*global_vindex); - Mesh *use_mesh; + int i; float use_crease = 0; - int usage = override_usage ? override_usage : ob->lineart.usage; + int usage = obi->usage; -#define LRT_MESH_FINISH \ - BM_mesh_free(bm); \ - if (ob->type != OB_MESH) { \ - BKE_mesh_free(use_mesh); \ - MEM_freeN(use_mesh); \ + if (obi->original_me->edit_mesh) { + /* Do not use edit_mesh directly because we will modify it, so create a copy. */ + bm = BM_mesh_copy(obi->original_me->edit_mesh->bm); + } + else { + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(obi->original_me))); + bm = BM_mesh_create(&allocsize, + &((struct BMeshCreateParams){ + .use_toolflags = true, + })); + BM_mesh_bm_from_me(bm, + obi->original_me, + &((struct BMeshFromMeshParams){ + .calc_face_normal = true, + })); } - if (usage == OBJECT_LRT_EXCLUDE) { - return; + if (obi->free_use_mesh) { + BKE_mesh_free(obi->original_me); + MEM_freeN(obi->original_me); } - if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) { + if (rb->remove_doubles) { + BMEditMesh *em = BKE_editmesh_create(bm, false); + BMOperator findop, weldop; - if (ob->type == OB_MESH) { - use_mesh = DEG_get_evaluated_object(dg, ob)->data; - } - else { - use_mesh = BKE_mesh_new_from_object(NULL, ob, false, false); - } + /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */ + BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001); - /* In case we can not get any mesh geometry data from the object */ - if (!use_mesh) { - return; - } + BMO_op_exec(bm, &findop); - /* First we need to prepare the matrix used for transforming this specific object. */ - mul_m4db_m4db_m4fl_uniq(new_mvp, mvp_mat, ob->obmat); - mul_m4db_m4db_m4fl_uniq(new_mv, mv_mat, ob->obmat); + /* Weld the vertices. */ + BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts"); + BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap"); + BMO_op_exec(bm, &weldop); - invert_m4_m4(imat, ob->obmat); - transpose_m4(imat); - copy_m4d_m4(normal, imat); + BMO_op_finish(bm, &findop); + BMO_op_finish(bm, &weldop); - if (use_mesh->edit_mesh) { - /* Do not use edit_mesh directly because we will modify it, so create a copy. */ - bm = BM_mesh_copy(use_mesh->edit_mesh->bm); - } - else { - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(use_mesh))); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - BM_mesh_bm_from_me(bm, - use_mesh, - &((struct BMeshFromMeshParams){ - .calc_face_normal = true, - })); - } + MEM_freeN(em); + } - if (rb->remove_doubles) { - BMEditMesh *em = BKE_editmesh_create(bm, false); - BMOperator findop, weldop; + BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false); + BM_mesh_triangulate( + bm, MOD_TRIANGULATE_QUAD_FIXED, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL); + BM_mesh_normals_update(bm); + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */ - BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001); + if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) { + CanFindFreestyle = 1; + } - BMO_op_exec(bm, &findop); + /* Only allocate memory for verts and tris as we don't know how many lines we will generate + * yet. */ + orv = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert); + ort = lineart_mem_acquire_thread(&rb->render_data_pool, bm->totface * rb->triangle_size); - /* Weld the vertices. */ - BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts"); - BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap"); - BMO_op_exec(bm, &weldop); + orig_ob = obi->original_ob; - BMO_op_finish(bm, &findop); - BMO_op_finish(bm, &weldop); + BLI_spin_lock(&rb->lock_task); + eln = lineart_list_append_pointer_pool_sized_thread( + &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&rb->lock_task); - MEM_freeN(em); - } + eln->element_count = bm->totvert; + eln->object_ref = orig_ob; + obi->v_reln = eln; - BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false); - BM_mesh_triangulate( - bm, MOD_TRIANGULATE_QUAD_FIXED, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL); - BM_mesh_normals_update(bm); - BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { + use_crease = cosf(M_PI - orig_ob->lineart.crease_threshold); + } + else { + use_crease = rb->crease_threshold; + } - if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) { - CanFindFreestyle = 1; - } + /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates + * erroneous detection on creases. Future configuration should allow options. */ + if (orig_ob->type == OB_FONT) { + eln->flags |= LRT_ELEMENT_BORDER_ONLY; + } - /* Only allocate memory for verts and tris as we don't know how many lines we will generate - * yet. */ - orv = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert); - ort = lineart_mem_acquire(&rb->render_data_pool, bm->totface * rb->triangle_size); + BLI_spin_lock(&rb->lock_task); + eln = lineart_list_append_pointer_pool_sized_thread( + &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&rb->lock_task); - orig_ob = ob->id.orig_id ? (Object *)ob->id.orig_id : ob; + eln->element_count = bm->totface; + eln->object_ref = orig_ob; + eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); - eln = lineart_list_append_pointer_pool_sized( - &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode)); - eln->element_count = bm->totvert; - eln->object_ref = orig_ob; + /* Note this memory is not from pool, will be deleted after culling. */ + orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent"); + /* Link is minimal so we use pool anyway. */ + BLI_spin_lock(&rb->lock_task); + lineart_list_append_pointer_pool_thread( + &rb->triangle_adjacent_pointers, &rb->render_data_pool, orta); + BLI_spin_unlock(&rb->lock_task); - if (ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { - use_crease = cosf(M_PI - ob->lineart.crease_threshold); - } - else { - use_crease = rb->crease_threshold; - } - - /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates - * erroneous detection on creases. Future configuration should allow options. */ - if (ob->type == OB_FONT) { - eln->flags |= LRT_ELEMENT_BORDER_ONLY; - } - - eln = lineart_list_append_pointer_pool_sized( - &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode)); - eln->element_count = bm->totface; - eln->object_ref = orig_ob; - eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); - - /* Note this memory is not from pool, will be deleted after culling. */ - orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent"); - /* Link is minimal so we use pool anyway. */ - lineart_list_append_pointer_pool(&rb->triangle_adjacent_pointers, &rb->render_data_pool, orta); - - for (i = 0; i < bm->totvert; i++) { - v = BM_vert_at_index(bm, i); - lineart_vert_transform(v, i, orv, new_mv, new_mvp); - orv[i].index = i + global_i; - } - /* Register a global index increment. See #lineart_triangle_share_edge() and - * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually - * overflow, in such large scene it's virtually impossible for two vertex of the same numeric - * index to come close together. */ - (*global_vindex) += bm->totvert; - - tri = ort; - for (i = 0; i < bm->totface; i++) { - f = BM_face_at_index(bm, i); - - loop = f->l_first; - tri->v[0] = &orv[BM_elem_index_get(loop->v)]; - loop = loop->next; - tri->v[1] = &orv[BM_elem_index_get(loop->v)]; - loop = loop->next; - tri->v[2] = &orv[BM_elem_index_get(loop->v)]; - - /* Transparency bit assignment. */ - Material *mat = BKE_object_material_get(ob, f->mat_nr + 1); - tri->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ? - mat->lineart.transparency_mask : - 0); - - double gn[3]; - copy_v3db_v3fl(gn, f->no); - mul_v3_mat3_m4v3_db(tri->gn, normal, gn); - normalize_v3_db(tri->gn); - - if (usage == OBJECT_LRT_INTERSECTION_ONLY) { - tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; - } - else if (ELEM(usage, OBJECT_LRT_NO_INTERSECTION, OBJECT_LRT_OCCLUSION_ONLY)) { - tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; - } + for (i = 0; i < bm->totvert; i++) { + v = BM_vert_at_index(bm, i); + lineart_vert_transform(v, i, orv, model_view, model_view_proj); + orv[i].index = i; + } + /* Register a global index increment. See #lineart_triangle_share_edge() and + * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually + * overflow, in such large scene it's virtually impossible for two vertex of the same numeric + * index to come close together. */ + obi->global_i_offset = bm->totvert; - /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ - tri->intersecting_verts = (void *)&orta[i]; + tri = ort; + for (i = 0; i < bm->totface; i++) { + f = BM_face_at_index(bm, i); - tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); + loop = f->l_first; + tri->v[0] = &orv[BM_elem_index_get(loop->v)]; + loop = loop->next; + tri->v[1] = &orv[BM_elem_index_get(loop->v)]; + loop = loop->next; + tri->v[2] = &orv[BM_elem_index_get(loop->v)]; + + /* Transparency bit assignment. */ + Material *mat = BKE_object_material_get(orig_ob, f->mat_nr + 1); + tri->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ? + mat->lineart.transparency_mask : + 0); + + double gn[3]; + copy_v3db_v3fl(gn, f->no); + mul_v3_mat3_m4v3_db(tri->gn, normal, gn); + normalize_v3_db(tri->gn); + + if (usage == OBJECT_LRT_INTERSECTION_ONLY) { + tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; + } + else if (usage == OBJECT_LRT_NO_INTERSECTION || usage == OBJECT_LRT_OCCLUSION_ONLY) { + tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; } - /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */ + /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ + tri->intersecting_verts = (void *)&orta[i]; - int allocate_la_e = 0; - for (i = 0; i < bm->totedge; i++) { - e = BM_edge_at_index(bm, i); + tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); + } - /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */ - char eflag = lineart_identify_feature_line( - rb, e, ort, orv, use_crease, ob->type == OB_FONT, CanFindFreestyle, bm); - if (eflag) { - /* Only allocate for feature lines (instead of all lines) to save memory. */ - allocate_la_e++; - } - /* Here we just use bm's flag for when loading actual lines, then we don't need to call - * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always - * set the flag, so hflag stays 0 for lines that are not feature lines. */ - e->head.hflag = eflag; + /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */ + + int allocate_la_e = 0; + for (i = 0; i < bm->totedge; i++) { + e = BM_edge_at_index(bm, i); + + /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */ + char eflag = lineart_identify_feature_line( + rb, e, ort, orv, use_crease, orig_ob->type == OB_FONT, CanFindFreestyle, bm); + if (eflag) { + /* Only allocate for feature lines (instead of all lines) to save memory. */ + allocate_la_e++; } + /* Here we just use bm's flag for when loading actual lines, then we don't need to call + * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always + * set the flag, so hflag stays 0 for lines that are not feature lines. */ + e->head.hflag = eflag; + } - o_la_e = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e); - eln = lineart_list_append_pointer_pool_sized( - &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode)); - eln->element_count = allocate_la_e; - eln->object_ref = orig_ob; + o_la_e = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e); + o_la_s = lineart_mem_acquire_thread(&rb->render_data_pool, + sizeof(LineartEdgeSegment) * allocate_la_e); + BLI_spin_lock(&rb->lock_task); + eln = lineart_list_append_pointer_pool_sized_thread( + &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&rb->lock_task); + eln->element_count = allocate_la_e; + eln->object_ref = orig_ob; - la_e = o_la_e; - for (i = 0; i < bm->totedge; i++) { - e = BM_edge_at_index(bm, i); + la_e = o_la_e; + la_s = o_la_s; + for (i = 0; i < bm->totedge; i++) { + e = BM_edge_at_index(bm, i); - /* Not a feature line, so we skip. */ - if (!e->head.hflag) { - continue; - } + /* Not a feature line, so we skip. */ + if (!e->head.hflag) { + continue; + } - la_e->v1 = &orv[BM_elem_index_get(e->v1)]; - la_e->v2 = &orv[BM_elem_index_get(e->v2)]; - la_e->v1_obindex = la_e->v1->index - global_i; - la_e->v2_obindex = la_e->v2->index - global_i; - if (e->l) { - int findex = BM_elem_index_get(e->l->f); - la_e->t1 = lineart_triangle_from_index(rb, ort, findex); - lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e); - if (e->l->radial_next && e->l->radial_next != e->l) { - findex = BM_elem_index_get(e->l->radial_next->f); - la_e->t2 = lineart_triangle_from_index(rb, ort, findex); - lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e); - } - } - la_e->flags = e->head.hflag; - la_e->object_ref = orig_ob; - - LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool, - sizeof(LineartEdgeSegment)); - BLI_addtail(&la_e->segments, es); - if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) { - lineart_add_edge_to_list(rb, la_e); + la_e->v1 = &orv[BM_elem_index_get(e->v1)]; + la_e->v2 = &orv[BM_elem_index_get(e->v2)]; + la_e->v1_obindex = la_e->v1->index; + la_e->v2_obindex = la_e->v2->index; + if (e->l) { + int findex = BM_elem_index_get(e->l->f); + la_e->t1 = lineart_triangle_from_index(rb, ort, findex); + lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e); + if (e->l->radial_next && e->l->radial_next != e->l) { + findex = BM_elem_index_get(e->l->radial_next->f); + la_e->t2 = lineart_triangle_from_index(rb, ort, findex); + lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e); } - - la_e++; + } + la_e->flags = e->head.hflag; + la_e->object_ref = orig_ob; + BLI_addtail(&la_e->segments, la_s); + if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || + usage == OBJECT_LRT_NO_INTERSECTION) { + lineart_add_edge_to_list_thread(obi, la_e); } - LRT_MESH_FINISH + la_e++; + la_s++; } -#undef LRT_MESH_FINISH + /* always free bm as it's a copy from before threading */ + BM_mesh_free(bm); +} + +static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool), + LineartObjectLoadTaskInfo *olti) +{ + LineartRenderBuffer *rb = olti->rb; + for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) { + lineart_geometry_object_load(obi, rb); + } } static bool _lineart_object_not_in_source_collection(Collection *source, Object *ob) @@ -1830,6 +1863,24 @@ static int lineart_usage_check(Collection *c, Object *ob, LineartRenderBuffer *_ return OBJECT_LRT_INHERIT; } +static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_list, + LineartObjectInfo *obi, + int thread_count, + int this_face_count) +{ + LineartObjectLoadTaskInfo *use_olti = olti_list; + long unsigned int min_face = use_olti->total_faces; + for (int i = 0; i < thread_count; i++) { + if (olti_list[i].total_faces < min_face) { + min_face = olti_list[i].total_faces; + use_olti = &olti_list[i]; + } + } + use_olti->total_faces += this_face_count; + obi->next = use_olti->pending; + use_olti->pending = obi; +} + static void lineart_main_load_geometries( Depsgraph *depsgraph, Scene *scene, @@ -1845,6 +1896,12 @@ static void lineart_main_load_geometries( int fit = BKE_camera_sensor_fit(cam->sensor_fit, rb->w, rb->h); double asp = ((double)rb->w / (double)rb->h); + double t_start; + + if (G.debug_value == 4000) { + t_start = PIL_check_seconds_timer(); + } + if (cam->type == CAM_PERSP) { if (fit == CAMERA_SENSOR_FIT_VERT && asp > 1) { sensor *= asp; @@ -1866,6 +1923,7 @@ static void lineart_main_load_geometries( copy_m4_m4_db(rb->view_projection, proj); unit_m4_db(view); + copy_m4_m4_db(rb->view, view); BLI_listbase_clear(&rb->triangle_buffer_pointers); BLI_listbase_clear(&rb->vertex_buffer_pointers); @@ -1878,16 +1936,90 @@ static void lineart_main_load_geometries( flags |= DEG_ITER_OBJECT_FLAG_DUPLI; } - /* This is to serialize vertex index in the whole scene, so lineart_triangle_share_edge() can - * work properly from the lack of triangle adjacent info. */ - int global_i = 0; + int thread_count = rb->thread_count; + + /* This memory is in render buffer memory pool. so we don't need to free those after loading. */ + LineartObjectLoadTaskInfo *olti = lineart_mem_acquire( + &rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count); DEG_OBJECT_ITER_BEGIN (depsgraph, ob, flags) { - int usage = lineart_usage_check(scene->master_collection, ob, rb); + LineartObjectInfo *obi = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartObjectInfo)); + obi->usage = lineart_usage_check(scene->master_collection, ob, rb); + Mesh *use_mesh; + + if (obi->usage == OBJECT_LRT_EXCLUDE) { + continue; + } + + Object *use_ob = DEG_get_evaluated_object(depsgraph, ob); + + if (!(use_ob->type == OB_MESH || use_ob->type == OB_MBALL || use_ob->type == OB_CURVE || + use_ob->type == OB_SURF || use_ob->type == OB_FONT)) { + continue; + } + if (use_ob->type == OB_MESH) { + use_mesh = use_ob->data; + } + else { + use_mesh = BKE_mesh_new_from_object(NULL, use_ob, false, true); + } - lineart_geometry_object_load(depsgraph, ob, view, proj, rb, usage, &global_i); + /* In case we still can not get any mesh geometry data from the object */ + if (!use_mesh) { + continue; + } + + if (ob->type != OB_MESH) { + obi->free_use_mesh = true; + } + + /* Prepare the matrix used for transforming this specific object (instance). */ + mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, ob->obmat); + mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, ob->obmat); + float imat[4][4]; + invert_m4_m4(imat, ob->obmat); + transpose_m4(imat); + copy_m4d_m4(obi->normal, imat); + + obi->original_me = use_mesh; + obi->original_ob = (ob->id.orig_id ? (Object *)ob->id.orig_id : (Object *)ob); + lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->totpoly); } DEG_OBJECT_ITER_END; + + TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); + + for (int i = 0; i < thread_count; i++) { + olti[i].rb = rb; + olti[i].dg = depsgraph; + BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], 0, NULL); + } + BLI_task_pool_work_and_wait(tp); + BLI_task_pool_free(tp); + + /* The step below is to serialize vertex index in the whole scene, so + * lineart_triangle_share_edge() can work properly from the lack of triangle adjacent info. */ + int global_i = 0; + + for (int i = 0; i < thread_count; i++) { + for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) { + if (!obi->v_reln) { + continue; + } + LineartVert *v = (LineartVert *)obi->v_reln->pointer; + int v_count = obi->v_reln->element_count; + for (int vi = 0; vi < v_count; vi++) { + v[vi].index += global_i; + } + global_i += v_count; + lineart_finalize_object_edge_list(rb, obi); + } + } + + if (G.debug_value == 4000) { + double t_elapsed = PIL_check_seconds_timer() - t_start; + printf("Line art loading time: %lf\n", t_elapsed); + } } /** @@ -2531,7 +2663,6 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, } } } - return result; } @@ -3693,7 +3824,11 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif Scene *scene = DEG_get_evaluated_scene(depsgraph); int intersections_only = 0; /* Not used right now, but preserve for future. */ - BKE_scene_camera_switch_update(scene); + double t_start; + + if (G.debug_value == 4000) { + t_start = PIL_check_seconds_timer(); + } if (!scene->camera) { return false; @@ -3788,6 +3923,9 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif if (G.debug_value == 4000) { lineart_count_and_print_render_buffer_memory(rb); + + double t_elapsed = PIL_check_seconds_timer() - t_start; + printf("Line art total time: %lf\n", t_elapsed); } return true; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h index 9ed98b38f07..e457d4a83a0 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h @@ -43,6 +43,13 @@ void *lineart_list_append_pointer_pool_sized(ListBase *h, struct LineartStaticMemPool *smp, void *data, int size); +void *lineart_list_append_pointer_pool_thread(ListBase *h, + struct LineartStaticMemPool *smp, + void *data); +void *lineart_list_append_pointer_pool_sized_thread(ListBase *h, + LineartStaticMemPool *smp, + void *data, + int size); void *list_push_pointer_static(ListBase *h, struct LineartStaticMemPool *smp, void *p); void *list_push_pointer_static_sized(ListBase *h, struct LineartStaticMemPool *smp, diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c index d05f931f75d..47cca0ecd61 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c @@ -62,6 +62,31 @@ void *lineart_list_append_pointer_pool_sized(ListBase *h, BLI_addtail(h, lip); return lip; } +void *lineart_list_append_pointer_pool_thread(ListBase *h, LineartStaticMemPool *smp, void *data) +{ + LinkData *lip; + if (h == NULL) { + return 0; + } + lip = lineart_mem_acquire_thread(smp, sizeof(LinkData)); + lip->data = data; + BLI_addtail(h, lip); + return lip; +} +void *lineart_list_append_pointer_pool_sized_thread(ListBase *h, + LineartStaticMemPool *smp, + void *data, + int size) +{ + LinkData *lip; + if (h == NULL) { + return 0; + } + lip = lineart_mem_acquire_thread(smp, size); + lip->data = data; + BLI_addtail(h, lip); + return lip; +} void *lineart_list_pop_pointer_no_free(ListBase *h) { @@ -82,10 +107,10 @@ void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip) LineartStaticMemPoolNode *lineart_mem_new_static_pool(LineartStaticMemPool *smp, size_t size) { size_t set_size = size; - if (set_size < LRT_MEMORY_POOL_64MB) { - set_size = LRT_MEMORY_POOL_64MB; /* Prevent too many small allocations. */ + if (set_size < LRT_MEMORY_POOL_1MB) { + set_size = LRT_MEMORY_POOL_1MB; /* Prevent too many small allocations. */ } - size_t total_size = size + sizeof(LineartStaticMemPoolNode); + size_t total_size = set_size + sizeof(LineartStaticMemPoolNode); LineartStaticMemPoolNode *smpn = MEM_callocN(total_size, "mempool"); smpn->size = total_size; smpn->used_byte = sizeof(LineartStaticMemPoolNode); @@ -211,7 +236,7 @@ void lineart_count_and_print_render_buffer_memory(LineartRenderBuffer *rb) LISTBASE_FOREACH (LineartStaticMemPoolNode *, smpn, &rb->render_data_pool.pools) { count_this++; - sum_this += LRT_MEMORY_POOL_64MB; + sum_this += LRT_MEMORY_POOL_1MB; } printf("LANPR Memory allocated %zu Standalone nodes, total %zu Bytes.\n", count_this, sum_this); total += sum_this; diff --git a/source/tools b/source/tools index 01f51a0e551..f99d29ae3e6 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 01f51a0e551ab730f0934dc6488613690ac4bf8f +Subproject commit f99d29ae3e6ad44d45d79309454c45f8088781a4 -- cgit v1.2.3