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
This commit is contained in:
YimingWu 2021-06-08 13:03:37 +08:00
parent 0abce91940
commit 1b07b7a068
Notes: blender-bot 2023-02-14 05:01:20 +01:00
Referenced by issue #96781, Regression: Blender crash when using a particle system + grease pencil line art modifier
Referenced by issue #87739, Line Art further improvement list
7 changed files with 451 additions and 244 deletions

@ -1 +1 @@
Subproject commit 27fe7f3a4f964b53af436c4da4ddea337eff0c7e
Subproject commit 4fcdbfe7c20edfc1204c0aa46c98ea25354abcd9

@ -1 +1 @@
Subproject commit 5a82baad9f986722104280e8354a4427d8e9eab1
Subproject commit 7d78c8a63f2f4b146f9327ddc0d567a5921b94ea

View File

@ -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}

View File

@ -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,
}));
}
if (rb->remove_doubles) {
BMEditMesh *em = BKE_editmesh_create(bm, false);
BMOperator findop, weldop;
/* 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);
BMO_op_exec(bm, &findop);
/* 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);
BMO_op_finish(bm, &findop);
BMO_op_finish(bm, &weldop);
MEM_freeN(em);
}
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 (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
CanFindFreestyle = 1;
}
/* 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);
orig_ob = ob->id.orig_id ? (Object *)ob->id.orig_id : ob;
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;
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;
}
/* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
tri->intersecting_verts = (void *)&orta[i];
tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size);
}
/* 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, 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;
la_e = o_la_e;
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;
}
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++;
}
LRT_MESH_FINISH
MEM_freeN(em);
}
#undef LRT_MESH_FINISH
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 (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
CanFindFreestyle = 1;
}
/* 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);
orig_ob = obi->original_ob;
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);
eln->element_count = bm->totvert;
eln->object_ref = orig_ob;
obi->v_reln = eln;
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;
}
/* 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;
}
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);
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. */
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);
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;
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(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;
}
/* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
tri->intersecting_verts = (void *)&orta[i];
tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size);
}
/* 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_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;
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;
}
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->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);
}
la_e++;
la_s++;
}
/* 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;
lineart_geometry_object_load(depsgraph, ob, view, proj, rb, usage, &global_i);
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);
}
/* 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;

View File

@ -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,

View File

@ -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;

@ -1 +1 @@
Subproject commit 01f51a0e551ab730f0934dc6488613690ac4bf8f
Subproject commit f99d29ae3e6ad44d45d79309454c45f8088781a4