diff options
author | YimingWu <xp8110@outlook.com> | 2021-06-16 17:38:47 +0300 |
---|---|---|
committer | YimingWu <xp8110@outlook.com> | 2021-06-16 17:38:47 +0300 |
commit | 247abdbf4148843daf469285a6a63ab9cd0aeef9 (patch) | |
tree | 78a20d5175b09c83d907aaddef347536bf20aca8 | |
parent | b37093de7b42cf331a4ae5830737d9972d7bb426 (diff) |
LineArt: Cached calculation for modifiers in the same stack.
This allows line art to run only once for each modifier stacks,
with an option to toggle a specific line art modifier should
use cache or re-do their own calculations.
Reviewed By: Sebastian Parborg (zeddb), Hans Goudey (HooglyBoogly)
Differential Revision: https://developer.blender.org/D11291
12 files changed, 322 insertions, 61 deletions
diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h index c6406c8478c..8fbc2112c77 100644 --- a/source/blender/blenkernel/BKE_gpencil_modifier.h +++ b/source/blender/blenkernel/BKE_gpencil_modifier.h @@ -294,6 +294,22 @@ bool BKE_gpencil_has_geometry_modifiers(struct Object *ob); bool BKE_gpencil_has_time_modifiers(struct Object *ob); bool BKE_gpencil_has_transform_modifiers(struct Object *ob); +/* Stores the maximum calculation range in the whole modifier stack for line art so the cache can + * cover everything that will be visible. */ +typedef struct GpencilLineartLimitInfo { + char min_level; + char max_level; + short edge_types; +} GpencilLineartLimitInfo; + +GpencilLineartLimitInfo BKE_gpencil_get_lineart_modifier_limits(const struct Object *ob); + +void BKE_gpencil_set_lineart_modifier_limits(struct GpencilModifierData *md, + const struct GpencilLineartLimitInfo *info, + const bool is_first_lineart); +bool BKE_gpencil_is_first_lineart_in_stack(const struct Object *ob, + const struct GpencilModifierData *md); + void BKE_gpencil_lattice_init(struct Object *ob); void BKE_gpencil_lattice_clear(struct Object *ob); diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 16386cac029..5ac85ad3aaf 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -55,6 +55,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "MOD_gpencil_lineart.h" #include "MOD_gpencil_modifiertypes.h" #include "BLO_read_write.h" @@ -202,6 +203,62 @@ bool BKE_gpencil_has_transform_modifiers(Object *ob) return false; } +GpencilLineartLimitInfo BKE_gpencil_get_lineart_modifier_limits(const Object *ob) +{ + GpencilLineartLimitInfo info = {0}; + bool is_first = true; + LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { + if (md->type == eGpencilModifierType_Lineart) { + LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; + if (is_first || (lmd->flags & LRT_GPENCIL_USE_CACHE)) { + info.min_level = MIN2(info.min_level, lmd->level_start); + info.max_level = MAX2(info.max_level, + (lmd->use_multiple_levels ? lmd->level_end : lmd->level_start)); + info.edge_types |= lmd->edge_types; + } + } + } + return info; +} + +void BKE_gpencil_set_lineart_modifier_limits(GpencilModifierData *md, + const GpencilLineartLimitInfo *info, + const bool is_first_lineart) +{ + BLI_assert(md->type == eGpencilModifierType_Lineart); + LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; + if (is_first_lineart || lmd->flags & LRT_GPENCIL_USE_CACHE) { + lmd->level_start_override = info->min_level; + lmd->level_end_override = info->max_level; + lmd->edge_types_override = info->edge_types; + } + else { + lmd->level_start_override = lmd->level_start; + lmd->level_end_override = lmd->level_end; + lmd->edge_types_override = lmd->edge_types; + } +} + +bool BKE_gpencil_is_first_lineart_in_stack(const Object *ob, const GpencilModifierData *md) +{ + if (md->type != eGpencilModifierType_Lineart) { + return false; + } + LISTBASE_FOREACH (GpencilModifierData *, gmd, &ob->greasepencil_modifiers) { + if (gmd->type == eGpencilModifierType_Lineart) { + if (gmd == md) { + return true; + } + else { + return false; + } + } + } + /* If we reach here it means md is not in ob's modifier stack. */ + BLI_assert(false); + return false; +} + /* apply time modifiers */ static int gpencil_time_modifier( Depsgraph *depsgraph, Scene *scene, Object *ob, bGPDlayer *gpl, int cfra, bool is_render) @@ -771,6 +828,8 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) BKE_gpencil_lattice_init(ob); const bool time_remap = BKE_gpencil_has_time_modifiers(ob); + bool is_first_lineart = true; + GpencilLineartLimitInfo info = BKE_gpencil_get_lineart_modifier_limits(ob); LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { @@ -781,6 +840,11 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) continue; } + if (md->type == eGpencilModifierType_Lineart) { + BKE_gpencil_set_lineart_modifier_limits(md, &info, is_first_lineart); + is_first_lineart = false; + } + /* Apply geometry modifiers (add new geometry). */ if (mti && mti->generateStrokes) { mti->generateStrokes(md, depsgraph, ob); @@ -806,6 +870,8 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) /* Clear any lattice data. */ BKE_gpencil_lattice_clear(ob); + + MOD_lineart_clear_cache(&gpd->runtime.lineart_cache); } void BKE_gpencil_modifier_blend_write(BlendWriter *writer, ListBase *modbase) diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_lineart.h b/source/blender/gpencil_modifiers/MOD_gpencil_lineart.h index 685f0cb36cb..7d75ed5804e 100644 --- a/source/blender/gpencil_modifiers/MOD_gpencil_lineart.h +++ b/source/blender/gpencil_modifiers/MOD_gpencil_lineart.h @@ -29,3 +29,7 @@ void OBJECT_OT_lineart_clear(struct wmOperatorType *ot); void OBJECT_OT_lineart_clear_all(struct wmOperatorType *ot); void WM_operatortypes_lineart(void); + +struct LineartCache; + +void MOD_lineart_clear_cache(struct LineartCache **lc); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 7d2efaebd01..f0aae7e4498 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -38,6 +38,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "MOD_gpencil_lineart.h" #include "lineart/MOD_lineart.h" #include "BKE_collection.h" @@ -88,7 +89,7 @@ static void generate_strokes_actual( } MOD_lineart_gpencil_generate( - lmd->render_buffer, + lmd->cache, depsgraph, ob, gpl, @@ -156,11 +157,31 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec return; } - MOD_lineart_compute_feature_lines(depsgraph, lmd); + LineartCache *local_lc = gpd->runtime.lineart_cache; + if (!gpd->runtime.lineart_cache) { + MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_destroy_render_data(lmd); + } + else { + if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { + MOD_lineart_compute_feature_lines(depsgraph, lmd, &local_lc); + MOD_lineart_destroy_render_data(lmd); + } + MOD_lineart_chain_clear_picked_flag(local_lc); + lmd->cache = local_lc; + } generate_strokes_actual(md, depsgraph, ob, gpl, gpf); - MOD_lineart_destroy_render_data(lmd); + if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { + /* Clear local cache. */ + if (local_lc != gpd->runtime.lineart_cache) { + MOD_lineart_clear_cache(&local_lc); + } + /* Restore the original cache pointer so the modifiers below still have access to the "global" + * cache. */ + lmd->cache = gpd->runtime.lineart_cache; + } WM_main_add_notifier(NA_EDITED | NC_GPENCIL, NULL); } @@ -182,11 +203,12 @@ static void bakeModifier(Main *UNUSED(bmain), return; } - MOD_lineart_compute_feature_lines(depsgraph, lmd); + if (!gpd->runtime.lineart_cache) { + MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_destroy_render_data(lmd); + } generate_strokes_actual(md, depsgraph, ob, gpl, gpf); - - MOD_lineart_destroy_render_data(lmd); } static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) @@ -258,10 +280,16 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) const int source_type = RNA_enum_get(ptr, "source_type"); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_cache = RNA_boolean_get(ptr, "use_cache"); + const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); uiLayoutSetPropSep(layout, true); uiLayoutSetEnabled(layout, !is_baked); + if (!BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data)) { + uiItemR(layout, ptr, "use_cache", 0, NULL, ICON_NONE); + } + uiItemR(layout, ptr, "source_type", 0, NULL, ICON_NONE); if (source_type == LRT_SOURCE_OBJECT) { @@ -280,12 +308,17 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_material", 0, IFACE_("Material Borders"), ICON_NONE); uiItemR(col, ptr, "use_edge_mark", 0, IFACE_("Edge Marks"), ICON_NONE); uiItemR(col, ptr, "use_intersection", 0, IFACE_("Intersections"), ICON_NONE); - uiItemR(col, ptr, "use_crease", 0, IFACE_("Crease"), ICON_NONE); - uiLayout *sub = uiLayoutRow(col, true); - uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_crease")); - uiLayoutSetPropSep(sub, true); - uiItemR(sub, ptr, "crease_threshold", UI_ITEM_R_SLIDER, " ", ICON_NONE); + uiLayout *sub = uiLayoutRowWithHeading(col, false, IFACE_("Crease")); + uiItemR(sub, ptr, "use_crease", 0, "", ICON_NONE); + uiLayout *entry = uiLayoutRow(sub, false); + uiLayoutSetActive(entry, RNA_boolean_get(ptr, "use_crease") || is_first); + if (use_cache && !is_first) { + uiItemL(entry, IFACE_("Angle Cached"), ICON_INFO); + } + else { + uiItemR(entry, ptr, "crease_threshold", UI_ITEM_R_SLIDER, " ", ICON_NONE); + } uiItemPointerR(layout, ptr, "target_layer", &obj_data_ptr, "layers", NULL, ICON_GREASEPENCIL); @@ -308,14 +341,35 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) NULL, material_valid ? ICON_SHADING_TEXTURE : ICON_ERROR); - uiItemR(layout, ptr, "use_remove_doubles", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE); - uiItemR(layout, ptr, "use_object_instances", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); - gpencil_modifier_panel_end(layout, ptr); } +static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); + + const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_cache = RNA_boolean_get(ptr, "use_cache"); + const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetEnabled(layout, !is_baked); + + if (use_cache && !is_first) { + uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + return; + } + + uiLayout *col = uiLayoutColumn(layout, true); + + uiItemR(col, ptr, "use_remove_doubles", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE); + uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); +} + static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; @@ -395,15 +449,23 @@ static void transparency_panel_draw(const bContext *UNUSED(C), Panel *panel) static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel) { - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); uiLayout *layout = panel->layout; const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_cache = RNA_boolean_get(ptr, "use_cache"); + const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); uiLayoutSetPropSep(layout, true); uiLayoutSetEnabled(layout, !is_baked); + if (use_cache && !is_first) { + uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + return; + } + uiLayout *col = uiLayoutColumnWithHeading(layout, true, IFACE_("Chain")); uiItemR(col, ptr, "use_fuzzy_intersections", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_fuzzy_all", 0, NULL, ICON_NONE); @@ -421,13 +483,21 @@ static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *layout = panel->layout; const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_cache = RNA_boolean_get(ptr, "use_cache"); + const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); uiLayoutSetPropSep(layout, true); uiLayoutSetEnabled(layout, !is_baked); + if (use_cache && !is_first) { + uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + return; + } + uiLayout *col = uiLayoutColumn(layout, true); uiLayout *row = uiLayoutRow(col, true); + uiItemR(row, ptr, "source_vertex_group", 0, IFACE_("Filter Source"), ICON_GROUP_VERTEX); uiItemR(row, ptr, "invert_source_vertex_group", UI_ITEM_R_TOGGLE, "", ICON_ARROW_LEFTRIGHT); @@ -474,6 +544,8 @@ static void panelRegister(ARegionType *region_type) region_type, eGpencilModifierType_Lineart, panel_draw); gpencil_modifier_subpanel_register( + region_type, "geometry", "Geometry Processing", NULL, options_panel_draw, panel_type); + gpencil_modifier_subpanel_register( region_type, "style", "Style", NULL, style_panel_draw, panel_type); PanelType *occlusion_panel = gpencil_modifier_subpanel_register( region_type, "occlusion", "Occlusion", NULL, occlusion_panel_draw, panel_type); diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 861085d3e16..b6175762bbe 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -250,6 +250,10 @@ typedef struct LineartRenderBuffer { ListBase wasted_cuts; SpinLock lock_cuts; + /* This is just a pointer to LineartCache::chain_data_pool, which acts as a cache for line + * chains. */ + LineartStaticMemPool *chain_data_pool; + /* Render status */ double view_vector[3]; @@ -309,6 +313,18 @@ typedef struct LineartRenderBuffer { } LineartRenderBuffer; +typedef struct LineartCache { + /** Separate memory pool for chain data, this goes to the cache, so when we free the main pool, + * chains will still be available. */ + LineartStaticMemPool chain_data_pool; + + /** A copy of rb->chains so we have that data available after rb has been destroyed. */ + ListBase chains; + + /** Cache only contains edge types specified in this variable. */ + char rb_edge_types; +} LineartCache; + #define DBL_TRIANGLE_LIM 1e-8 #define DBL_EDGE_LIM 1e-9 @@ -563,10 +579,11 @@ void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float thresh void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); int MOD_lineart_chain_count(const LineartEdgeChain *ec); -void MOD_lineart_chain_clear_picked_flag(struct LineartRenderBuffer *rb); +void MOD_lineart_chain_clear_picked_flag(LineartCache *lc); bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph, - struct LineartGpencilModifierData *lmd); + struct LineartGpencilModifierData *lmd, + LineartCache **cached_result); struct Scene; @@ -579,7 +596,7 @@ LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, doub struct bGPDlayer; struct bGPDframe; -void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb, +void MOD_lineart_gpencil_generate(LineartCache *cache, struct Depsgraph *depsgraph, struct Object *ob, struct bGPDlayer *gpl, diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 25c4e959ac1..f90fbaf4ccf 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -74,7 +74,7 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, static LineartEdgeChain *lineart_chain_create(LineartRenderBuffer *rb) { LineartEdgeChain *ec; - ec = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChain)); + ec = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChain)); BLI_addtail(&rb->chains, ec); @@ -119,7 +119,7 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, return old_rlci; } - eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem)); + eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); copy_v2_v2(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); @@ -149,7 +149,7 @@ static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb return ec->chain.first; } - eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem)); + eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); copy_v2_v2(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); @@ -889,12 +889,12 @@ int MOD_lineart_chain_count(const LineartEdgeChain *ec) return count; } -void MOD_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb) +void MOD_lineart_chain_clear_picked_flag(LineartCache *lc) { - if (rb == NULL) { + if (lc == NULL) { return; } - LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &lc->chains) { ec->picked = 0; } } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 5a979f01bc3..4d25eb438f8 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -21,6 +21,7 @@ * \ingroup editors */ +#include "MOD_gpencil_lineart.h" #include "MOD_lineart.h" #include "BLI_linklist.h" @@ -42,6 +43,7 @@ #include "BKE_gpencil_modifier.h" #include "BKE_material.h" #include "BKE_mesh.h" +#include "BKE_pointcache.h" #include "BKE_scene.h" #include "DEG_depsgraph_query.h" #include "DNA_camera_types.h" @@ -107,6 +109,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl, static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e); +static LineartCache *lineart_init_cache(void); + static void lineart_discard_segment(LineartRenderBuffer *rb, LineartEdgeSegment *es) { BLI_spin_lock(&rb->lock_cuts); @@ -2771,13 +2775,13 @@ static void lineart_destroy_render_data(LineartRenderBuffer *rb) void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) { - LineartRenderBuffer *rb = lmd->render_buffer; + LineartRenderBuffer *rb = lmd->render_buffer_ptr; lineart_destroy_render_data(rb); if (rb) { MEM_freeN(rb); - lmd->render_buffer = NULL; + lmd->render_buffer_ptr = NULL; } if (G.debug_value == 4000) { @@ -2785,14 +2789,33 @@ void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) } } +static LineartCache *lineart_init_cache() +{ + LineartCache *lc = MEM_callocN(sizeof(LineartCache), "Lineart Cache"); + return lc; +} + +void MOD_lineart_clear_cache(struct LineartCache **lc) +{ + if (!(*lc)) { + return; + } + lineart_mem_destroy(&((*lc)->chain_data_pool)); + MEM_freeN(*lc); + (*lc) = NULL; +} + static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, - LineartGpencilModifierData *lmd) + LineartGpencilModifierData *lmd, + LineartCache *lc) { LineartRenderBuffer *rb = MEM_callocN(sizeof(LineartRenderBuffer), "Line Art render buffer"); - lmd->render_buffer = rb; + lmd->cache = lc; + lmd->render_buffer_ptr = rb; + lc->rb_edge_types = lmd->edge_types_override; - if (!scene || !scene->camera) { + if (!scene || !scene->camera || !lc) { return NULL; } Camera *c = scene->camera->data; @@ -2835,11 +2858,15 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, /* See lineart_edge_from_triangle() for how this option may impact performance. */ rb->allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0; - rb->use_contour = (lmd->edge_types & LRT_EDGE_FLAG_CONTOUR) != 0; - rb->use_crease = (lmd->edge_types & LRT_EDGE_FLAG_CREASE) != 0; - rb->use_material = (lmd->edge_types & LRT_EDGE_FLAG_MATERIAL) != 0; - rb->use_edge_marks = (lmd->edge_types & LRT_EDGE_FLAG_EDGE_MARK) != 0; - rb->use_intersections = (lmd->edge_types & LRT_EDGE_FLAG_INTERSECTION) != 0; + int16_t edge_types = lmd->edge_types_override; + + rb->use_contour = (edge_types & LRT_EDGE_FLAG_CONTOUR) != 0; + rb->use_crease = (edge_types & LRT_EDGE_FLAG_CREASE) != 0; + rb->use_material = (edge_types & LRT_EDGE_FLAG_MATERIAL) != 0; + rb->use_edge_marks = (edge_types & LRT_EDGE_FLAG_EDGE_MARK) != 0; + rb->use_intersections = (edge_types & LRT_EDGE_FLAG_INTERSECTION) != 0; + + rb->chain_data_pool = &lc->chain_data_pool; BLI_spin_init(&rb->lock_task); BLI_spin_init(&rb->lock_cuts); @@ -3818,7 +3845,9 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this * * \return True when a change is made. */ -bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModifierData *lmd) +bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, + LineartGpencilModifierData *lmd, + LineartCache **cached_result) { LineartRenderBuffer *rb; Scene *scene = DEG_get_evaluated_scene(depsgraph); @@ -3836,7 +3865,10 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif return false; } - rb = lineart_create_render_buffer(scene, lmd); + LineartCache *lc = lineart_init_cache(); + *cached_result = lc; + + rb = lineart_create_render_buffer(scene, lmd, lc); /* Triangle thread testing data size varies depending on the thread count. * See definition of LineartTriangleThread for details. */ @@ -3844,7 +3876,9 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif /* This is used to limit calculation to a certain level to save time, lines who have higher * occlusion levels will get ignored. */ - rb->max_occlusion_level = MAX2(lmd->level_start, lmd->level_end); + rb->max_occlusion_level = (lmd->flags & LRT_GPENCIL_USE_CACHE) ? + lmd->level_end_override : + (lmd->use_multiple_levels ? lmd->level_end : lmd->level_start); /* FIXME(Yiming): See definition of int #LineartRenderBuffer::_source_type for detailed. */ rb->_source_type = lmd->source_type; @@ -3907,13 +3941,8 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif /* Then we connect chains based on the _proximity_ of their end points in image space, here's * the place threshold value gets involved. */ - - /* do_geometry_space = true. */ MOD_lineart_chain_connect(rb); - /* After chaining, we need to clear flags so we don't confuse GPencil generation calls. */ - MOD_lineart_chain_clear_picked_flag(rb); - float *t_image = &lmd->chaining_image_threshold; /* This configuration ensures there won't be accidental lost of short unchained segments. */ MOD_lineart_chain_discard_short(rb, MIN2(*t_image, 0.001f) - FLT_EPSILON); @@ -3921,6 +3950,12 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif if (rb->angle_splitting_threshold > FLT_EPSILON) { MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold); } + + /* Finally transfer the result list into cache. */ + memcpy(&lc->chains, &rb->chains, sizeof(ListBase)); + + /* At last, we need to clear flags so we don't confuse GPencil generation calls. */ + MOD_lineart_chain_clear_picked_flag(lc); } if (G.debug_value == 4000) { @@ -3944,7 +3979,7 @@ static int lineart_rb_edge_types(LineartRenderBuffer *rb) return types; } -static void lineart_gpencil_generate(LineartRenderBuffer *rb, +static void lineart_gpencil_generate(LineartCache *cache, Depsgraph *depsgraph, Object *gpencil_object, float (*gp_obmat_inverse)[4], @@ -3964,9 +3999,9 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, const char *vgname, int modifier_flags) { - if (rb == NULL) { + if (cache == NULL) { if (G.debug_value == 4000) { - printf("NULL Lineart rb!\n"); + printf("NULL Lineart cache!\n"); } return; } @@ -3990,11 +4025,11 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, float mat[4][4]; unit_m4(mat); - int enabled_types = lineart_rb_edge_types(rb); + int enabled_types = cache->rb_edge_types; bool invert_input = modifier_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP; bool match_output = modifier_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP; - LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &cache->chains) { if (ec->picked) { continue; @@ -4108,7 +4143,7 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, /** * Wrapper for external calls. */ -void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb, +void MOD_lineart_gpencil_generate(LineartCache *cache, Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, @@ -4156,7 +4191,7 @@ void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb, } float gp_obmat_inverse[4][4]; invert_m4_m4(gp_obmat_inverse, ob->obmat); - lineart_gpencil_generate(rb, + lineart_gpencil_generate(cache, depsgraph, ob, gp_obmat_inverse, diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c index c023c63ebc9..7ebb869e955 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c @@ -88,7 +88,12 @@ static void clear_strokes(Object *ob, GpencilModifierData *md, int frame) BKE_gpencil_layer_frame_delete(gpl, gpf); } -static bool bake_strokes(Object *ob, Depsgraph *dg, GpencilModifierData *md, int frame) +static bool bake_strokes(Object *ob, + Depsgraph *dg, + LineartCache **lc, + GpencilModifierData *md, + int frame, + bool is_first) { /* Modifier data sanity check. */ if (lineart_mod_is_disabled(md)) { @@ -111,11 +116,22 @@ static bool bake_strokes(Object *ob, Depsgraph *dg, GpencilModifierData *md, int /* No greasepencil frame created or found. */ return false; } - - MOD_lineart_compute_feature_lines(dg, lmd); + LineartCache *local_lc = *lc; + if (!(*lc)) { + MOD_lineart_compute_feature_lines(dg, lmd, lc); + MOD_lineart_destroy_render_data(lmd); + } + else { + if (is_first || (!(lmd->flags & LRT_GPENCIL_USE_CACHE))) { + MOD_lineart_compute_feature_lines(dg, lmd, &local_lc); + MOD_lineart_destroy_render_data(lmd); + } + MOD_lineart_chain_clear_picked_flag(local_lc); + lmd->cache = local_lc; + } MOD_lineart_gpencil_generate( - lmd->render_buffer, + lmd->cache, dg, ob, gpl, @@ -135,7 +151,15 @@ static bool bake_strokes(Object *ob, Depsgraph *dg, GpencilModifierData *md, int lmd->vgname, lmd->flags); - MOD_lineart_destroy_render_data(lmd); + if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { + /* Clear local cache. */ + if (!is_first) { + MOD_lineart_clear_cache(&local_lc); + } + /* Restore the original cache pointer so the modifiers below still have access to the + * "global" cache. */ + lmd->cache = gpd->runtime.lineart_cache; + } return true; } @@ -174,14 +198,21 @@ static bool lineart_gpencil_bake_single_target(LineartBakeJob *bj, Object *ob, i } } + GpencilLineartLimitInfo info = BKE_gpencil_get_lineart_modifier_limits(ob); + + LineartCache *lc = NULL; + bool is_first = true; LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { if (md->type != eGpencilModifierType_Lineart) { continue; } - if (bake_strokes(ob, bj->dg, md, frame)) { + BKE_gpencil_set_lineart_modifier_limits(md, &info, is_first); + if (bake_strokes(ob, bj->dg, &lc, md, frame, is_first)) { touched = true; + is_first = false; } } + MOD_lineart_clear_cache(&lc); return touched; } diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 410212ce100..3977ad326da 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -878,6 +878,7 @@ typedef enum eLineArtGPencilModifierFlags { LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 1), LRT_GPENCIL_BINARY_WEIGHTS = (1 << 2) /* Deprecated, this is removed for lack of use case. */, LRT_GPENCIL_IS_BAKED = (1 << 3), + LRT_GPENCIL_USE_CACHE = (1 << 4), } eLineArtGPencilModifierFlags; typedef enum eLineartGpencilTransparencyFlags { @@ -886,6 +887,8 @@ typedef enum eLineartGpencilTransparencyFlags { LRT_GPENCIL_TRANSPARENCY_MATCH = (1 << 1), } eLineartGpencilTransparencyFlags; +struct LineartCache; + typedef struct LineartGpencilModifierData { GpencilModifierData modifier; @@ -925,16 +928,24 @@ typedef struct LineartGpencilModifierData { /* CPU mode */ float chaining_image_threshold; - int _pad; - /* Ported from SceneLineArt flags. */ int calculation_flags; /* Additional Switches. */ int flags; - /* Runtime only. */ - void *render_buffer; + /* Runtime data. */ + + /* Because we can potentially only compute features lines once per modifier stack (Use Cache), we + * need to have these override values to ensure that we have the data we need is computed and + * stored in the cache. */ + char level_start_override; + char level_end_override; + short edge_types_override; + + struct LineartCache *cache; + /* Keep a pointer to the render buffer so we can call destroy from ModifierData. */ + struct LineartRenderBuffer *render_buffer_ptr; } LineartGpencilModifierData; diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index ea3c1ff7275..6b7b89e91fa 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -629,6 +629,7 @@ typedef struct bGPdata_Runtime { /** Brush pointer */ Brush *sbuffer_brush; struct GpencilBatchCache *gpencil_cache; + struct LineartCache *lineart_cache; } bGPdata_Runtime; /* grid configuration */ diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 92d65961743..e2cee2836b1 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -2930,6 +2930,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Is Baked", "This modifier has baked data"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_cache", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_USE_CACHE); + RNA_def_property_ui_text(prop, + "Use Cache", + "Use cached scene data from the first line art modifier in the stack. " + "Certain settings will be unavailable"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "thickness", PROP_INT, PROP_NONE); RNA_def_property_ui_text(prop, "Thickness", "The thickness for the generated strokes"); RNA_def_property_ui_range(prop, 1, 100, 1, 1); diff --git a/source/tools b/source/tools -Subproject 2afbb8ec472cac5102eb239f57b006f8c938768 +Subproject 01f51a0e551ab730f0934dc6488613690ac4bf8 |