From 9e4c26574a63ce526eddce57c2c10b4ef5ce8f3d Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Wed, 14 May 2025 12:59:46 +0200 Subject: [PATCH] VSE: new cache implementation Rework internals of how VSE caching is done. Primarily to make all the caching logic more understandable from development point of view, but also has several user visible implications (more details in the PR): - Simpler and fewer caching UI options, - Disk cache is gone (primary reason: proxies are kinda the same thing), - VSE cache size set in preferences is actual size used for VSE caches now (previously caching stopped as soon as whole Blender used that much memory, even if some memory usage was not about VSE at all), - Certain scenarios of cache invalidation are faster now. Pull Request: https://projects.blender.org/blender/blender/pulls/137926 --- release/datafiles/userdef/userdef_default.c | 4 - scripts/modules/rna_manual_reference.py | 4 - scripts/startup/bl_ui/space_sequencer.py | 64 +- scripts/startup/bl_ui/space_userpref.py | 10 - source/blender/blenkernel/intern/scene.cc | 4 +- .../blenloader/intern/versioning_280.cc | 1 - .../blenloader/intern/versioning_290.cc | 11 - .../editors/space_outliner/outliner_tools.cc | 4 +- .../editors/space_sequencer/sequencer_edit.cc | 40 +- .../space_sequencer/sequencer_modifier.cc | 10 +- .../sequencer_timeline_draw.cc | 103 +- .../transform/transform_convert_sequencer.cc | 2 +- .../transform_convert_sequencer_image.cc | 4 +- .../transform_convert_sequencer_retiming.cc | 2 +- source/blender/makesdna/DNA_sequence_types.h | 50 +- source/blender/makesdna/DNA_space_types.h | 4 +- source/blender/makesdna/DNA_userdef_types.h | 5 +- source/blender/makesrna/intern/rna_color.cc | 3 +- .../blender/makesrna/intern/rna_sequencer.cc | 104 +-- .../makesrna/intern/rna_sequencer_api.cc | 8 - source/blender/makesrna/intern/rna_space.cc | 10 - source/blender/makesrna/intern/rna_userdef.cc | 43 - source/blender/sequencer/CMakeLists.txt | 12 +- source/blender/sequencer/SEQ_relations.hh | 44 +- .../intern/cache/final_image_cache.cc | 236 +++++ .../intern/cache/final_image_cache.hh | 40 + .../intern/cache/intra_frame_cache.cc | 178 ++++ .../intern/cache/intra_frame_cache.hh | 40 + .../intern/cache/source_image_cache.cc | 287 ++++++ .../intern/cache/source_image_cache.hh | 45 + .../intern/{ => cache}/thumbnail_cache.cc | 0 source/blender/sequencer/intern/disk_cache.cc | 687 -------------- source/blender/sequencer/intern/disk_cache.hh | 36 - .../blender/sequencer/intern/image_cache.cc | 879 ------------------ .../blender/sequencer/intern/image_cache.hh | 56 -- source/blender/sequencer/intern/prefetch.cc | 89 +- source/blender/sequencer/intern/prefetch.hh | 14 +- source/blender/sequencer/intern/render.cc | 88 +- source/blender/sequencer/intern/render.hh | 2 +- source/blender/sequencer/intern/sequencer.cc | 9 +- source/blender/sequencer/intern/strip_add.cc | 2 +- source/blender/sequencer/intern/strip_edit.cc | 2 +- .../sequencer/intern/strip_relations.cc | 137 +-- .../sequencer/intern/strip_transform.cc | 2 +- 44 files changed, 1181 insertions(+), 2194 deletions(-) create mode 100644 source/blender/sequencer/intern/cache/final_image_cache.cc create mode 100644 source/blender/sequencer/intern/cache/final_image_cache.hh create mode 100644 source/blender/sequencer/intern/cache/intra_frame_cache.cc create mode 100644 source/blender/sequencer/intern/cache/intra_frame_cache.hh create mode 100644 source/blender/sequencer/intern/cache/source_image_cache.cc create mode 100644 source/blender/sequencer/intern/cache/source_image_cache.hh rename source/blender/sequencer/intern/{ => cache}/thumbnail_cache.cc (100%) delete mode 100644 source/blender/sequencer/intern/disk_cache.cc delete mode 100644 source/blender/sequencer/intern/disk_cache.hh delete mode 100644 source/blender/sequencer/intern/image_cache.cc delete mode 100644 source/blender/sequencer/intern/image_cache.hh diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index e649511526a..51093c279dc 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -230,10 +230,6 @@ const UserDef U_default = { .temp_win_sizey = 600, }, - .sequencer_disk_cache_dir = "", - .sequencer_disk_cache_compression = 0, - .sequencer_disk_cache_size_limit = 100, - .sequencer_disk_cache_flag = 0, .sequencer_proxy_setup = USER_SEQ_PROXY_SETUP_AUTOMATIC, .collection_instance_empty_size = 1.0f, diff --git a/scripts/modules/rna_manual_reference.py b/scripts/modules/rna_manual_reference.py index cf11d1b45b7..ace20bd2314 100644 --- a/scripts/modules/rna_manual_reference.py +++ b/scripts/modules/rna_manual_reference.py @@ -50,7 +50,6 @@ url_manual_mapping = ( ("bpy.types.preferencesedit.show_only_selected_curve_keyframes*", "editors/preferences/animation.html#bpy-types-preferencesedit-show-only-selected-curve-keyframes"), ("bpy.types.preferencesfilepaths.use_auto_save_temporary_files*", "editors/preferences/save_load.html#bpy-types-preferencesfilepaths-use-auto-save-temporary-files"), ("bpy.types.preferencesinput.view_rotate_sensitivity_turntable*", "editors/preferences/navigation.html#bpy-types-preferencesinput-view-rotate-sensitivity-turntable"), - ("bpy.types.preferencessystem.sequencer_disk_cache_compression*", "editors/preferences/system.html#bpy-types-preferencessystem-sequencer-disk-cache-compression"), ("bpy.types.rigidbodyconstraint.use_override_solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-use-override-solver-iterations"), ("bpy.types.toolsettings.use_gpencil_vertex_select_mask_stroke*", "grease_pencil/modes/vertex_paint/introduction.html#bpy-types-toolsettings-use-gpencil-vertex-select-mask-stroke"), ("bpy.types.toolsettings.use_grease_pencil_multi_frame_editing*", "grease_pencil/multiframe.html#bpy-types-toolsettings-use-grease-pencil-multi-frame-editing"), @@ -63,7 +62,6 @@ url_manual_mapping = ( ("bpy.types.greasepencillineartmodifier.use_invert_collection*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-greasepencillineartmodifier-use-invert-collection"), ("bpy.types.greasepencillineartmodifier.use_invert_silhouette*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-greasepencillineartmodifier-use-invert-silhouette"), ("bpy.types.preferencesfilepaths.show_hidden_files_datablocks*", "editors/preferences/save_load.html#bpy-types-preferencesfilepaths-show-hidden-files-datablocks"), - ("bpy.types.preferencessystem.sequencer_disk_cache_size_limit*", "editors/preferences/system.html#bpy-types-preferencessystem-sequencer-disk-cache-size-limit"), ("bpy.types.rendersettings.compositor_denoise_preview_quality*", "render/eevee/render_settings/performance.html#bpy-types-rendersettings-compositor-denoise-preview-quality"), ("bpy.types.rendersettings.use_sequencer_override_scene_strip*", "editors/video_sequencer/preview/sidebar.html#bpy-types-rendersettings-use-sequencer-override-scene-strip"), ("bpy.types.toolsettings.use_annotation_project_only_selected*", "interface/annotate_tool.html#bpy-types-toolsettings-use-annotation-project-only-selected"), @@ -232,8 +230,6 @@ url_manual_mapping = ( ("bpy.types.preferencesinput.ndof_lock_camera_pan_zoom*", "editors/preferences/input.html#bpy-types-preferencesinput-ndof-lock-camera-pan-zoom"), ("bpy.types.preferencesinput.ndof_view_navigate_method*", "editors/preferences/input.html#bpy-types-preferencesinput-ndof-view-navigate-method"), ("bpy.types.preferencesinput.touchpad_scroll_direction*", "editors/preferences/input.html#bpy-types-preferencesinput-touchpad-scroll-direction"), - ("bpy.types.preferencessystem.sequencer_disk_cache_dir*", "editors/preferences/system.html#bpy-types-preferencessystem-sequencer-disk-cache-dir"), - ("bpy.types.preferencessystem.use_sequencer_disk_cache*", "editors/preferences/system.html#bpy-types-preferencessystem-use-sequencer-disk-cache"), ("bpy.types.preferencesview.use_filter_brushes_by_tool*", "interface/window_system/regions.html#bpy-types-preferencesview-use-filter-brushes-by-tool"), ("bpy.types.preferencesview.use_text_render_subpixelaa*", "editors/preferences/interface.html#bpy-types-preferencesview-use-text-render-subpixelaa"), ("bpy.types.sequencertimelineoverlay.show_strip_offset*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-offset"), diff --git a/scripts/startup/bl_ui/space_sequencer.py b/scripts/startup/bl_ui/space_sequencer.py index 487a74c6ad9..51d3f12f728 100644 --- a/scripts/startup/bl_ui/space_sequencer.py +++ b/scripts/startup/bl_ui/space_sequencer.py @@ -2541,8 +2541,6 @@ class SEQUENCER_PT_cache_settings(SequencerButtonsPanel, Panel): col = layout.column(heading="Cache", align=True) col.prop(ed, "use_cache_raw", text="Raw") - col.prop(ed, "use_cache_preprocessed", text="Preprocessed") - col.prop(ed, "use_cache_composite", text="Composite") col.prop(ed, "use_cache_final", text="Final") @@ -2566,6 +2564,7 @@ class SEQUENCER_PT_cache_view_settings(SequencerButtonsPanel, Panel): layout.use_property_decorate = False cache_settings = context.space_data.cache_overlay + ed = context.scene.sequence_editor layout.active = cache_settings.show_cache col = layout.column(heading="Cache", align=True) @@ -2573,10 +2572,34 @@ class SEQUENCER_PT_cache_view_settings(SequencerButtonsPanel, Panel): show_developer_ui = context.preferences.view.show_developer_ui if show_developer_ui: col.prop(cache_settings, "show_cache_raw", text="Raw") - col.prop(cache_settings, "show_cache_preprocessed", text="Preprocessed") - col.prop(cache_settings, "show_cache_composite", text="Composite") col.prop(cache_settings, "show_cache_final_out", text="Final") + show_cache_size = show_developer_ui and (ed.use_cache_raw or ed.use_cache_final) + if show_cache_size: + cache_raw_size = ed.cache_raw_size + cache_final_size = ed.cache_final_size + + col = layout.box() + col = col.column(align=True) + + split = col.split(factor=0.4, align=True) + split.alignment = 'RIGHT' + split.label(text="Current Cache Size") + split.alignment = 'LEFT' + split.label(text="{:d} MB".format(cache_raw_size + cache_final_size), translate=False) + + split = col.split(factor=0.4, align=True) + split.alignment = 'RIGHT' + split.label(text="Raw") + split.alignment = 'LEFT' + split.label(text="{:d} MB".format(cache_raw_size), translate=False) + + split = col.split(factor=0.4, align=True) + split.alignment = 'RIGHT' + split.label(text="Final") + split.alignment = 'LEFT' + split.label(text="{:d} MB".format(cache_final_size), translate=False) + class SEQUENCER_PT_proxy_settings(SequencerButtonsPanel, Panel): bl_label = "Proxy Settings" @@ -2664,38 +2687,6 @@ class SEQUENCER_PT_strip_proxy(SequencerButtonsPanel, Panel): col.prop(proxy, "timecode", text="Timecode Index") -class SEQUENCER_PT_strip_cache(SequencerButtonsPanel, Panel): - bl_label = "Strip Cache" - bl_category = "Cache" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - show_developer_ui = context.preferences.view.show_developer_ui - if not cls.has_sequencer(context): - return False - if context.active_strip is not None and show_developer_ui: - return True - return False - - def draw_header(self, context): - strip = context.active_strip - self.layout.prop(strip, "override_cache_settings", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - strip = context.active_strip - layout.active = strip.override_cache_settings - - col = layout.column(heading="Cache") - col.prop(strip, "use_cache_raw", text="Raw") - col.prop(strip, "use_cache_preprocessed", text="Preprocessed") - col.prop(strip, "use_cache_composite", text="Composite") - - class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, Panel): bl_label = "Scene Strip Display" bl_space_type = 'SEQUENCE_EDITOR' @@ -3183,7 +3174,6 @@ classes = ( SEQUENCER_PT_cache_settings, SEQUENCER_PT_cache_view_settings, - SEQUENCER_PT_strip_cache, SEQUENCER_PT_proxy_settings, SEQUENCER_PT_strip_proxy, diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index 0aca339ac9f..0e5322de763 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -818,21 +818,11 @@ class USERPREF_PT_system_video_sequencer(SystemPanel, CenterAlignMixIn, Panel): def draw_centered(self, context, layout): prefs = context.preferences system = prefs.system - # edit = prefs.edit layout.prop(system, "memory_cache_limit") layout.separator() - layout.prop(system, "use_sequencer_disk_cache", text="Disk Cache") - col = layout.column() - col.active = system.use_sequencer_disk_cache - col.prop(system, "sequencer_disk_cache_dir", text="Directory") - col.prop(system, "sequencer_disk_cache_size_limit", text="Cache Limit") - col.prop(system, "sequencer_disk_cache_compression", text="Compression") - - layout.separator() - layout.prop(system, "sequencer_proxy_setup") diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 62cc6afe16e..d689d810eb0 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -1284,11 +1284,13 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) ed->act_strip = static_cast( BLO_read_get_new_data_address_no_us(reader, ed->act_strip, sizeof(Strip))); - ed->cache = nullptr; ed->prefetch_job = nullptr; ed->runtime.strip_lookup = nullptr; ed->runtime.media_presence = nullptr; ed->runtime.thumbnail_cache = nullptr; + ed->runtime.intra_frame_cache = nullptr; + ed->runtime.source_image_cache = nullptr; + ed->runtime.final_image_cache = nullptr; /* recursive link sequences, lb will be correctly initialized */ link_recurs_seq(reader, &ed->seqbase); diff --git a/source/blender/blenloader/intern/versioning_280.cc b/source/blender/blenloader/intern/versioning_280.cc index c901d1091dc..a5a884b3c23 100644 --- a/source/blender/blenloader/intern/versioning_280.cc +++ b/source/blender/blenloader/intern/versioning_280.cc @@ -3016,7 +3016,6 @@ static void do_versions_seq_unique_name_all_strips(Scene *sce, ListBase *seqbase static void do_versions_seq_set_cache_defaults(Editing *ed) { ed->cache_flag = SEQ_CACHE_STORE_FINAL_OUT; - ed->recycle_max_cost = 10.0f; } static bool strip_update_flags_cb(Strip *strip, void * /*user_data*/) diff --git a/source/blender/blenloader/intern/versioning_290.cc b/source/blender/blenloader/intern/versioning_290.cc index c1c3a2ef197..820ca0a7b93 100644 --- a/source/blender/blenloader/intern/versioning_290.cc +++ b/source/blender/blenloader/intern/versioning_290.cc @@ -784,16 +784,6 @@ static void do_versions_291_fcurve_handles_limit(FCurve *fcu) } } -static void do_versions_strip_cache_settings_recursive(const ListBase *seqbase) -{ - LISTBASE_FOREACH (Strip *, strip, seqbase) { - strip->cache_flag = 0; - if (strip->type == STRIP_TYPE_META) { - do_versions_strip_cache_settings_recursive(&strip->seqbase); - } - } -} - static void version_node_join_geometry_for_multi_input_socket(bNodeTree *ntree) { LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { @@ -1649,7 +1639,6 @@ void blo_do_versions_290(FileData *fd, Library * /*lib*/, Main *bmain) continue; } ed->cache_flag = (SEQ_CACHE_STORE_RAW | SEQ_CACHE_STORE_FINAL_OUT); - do_versions_strip_cache_settings_recursive(&ed->seqbase); } } diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index ef41a8f7259..b482bee317e 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -2157,13 +2157,13 @@ static void sequence_fn(int event, TreeElement *te, TreeStoreElem * /*tselem*/, else if (event == OL_DOP_HIDE) { if (!(strip->flag & SEQ_MUTE)) { strip->flag |= SEQ_MUTE; - seq::relations_invalidate_dependent(scene, strip); + seq::relations_invalidate_cache(scene, strip); } } else if (event == OL_DOP_UNHIDE) { if (strip->flag & SEQ_MUTE) { strip->flag &= ~SEQ_MUTE; - seq::relations_invalidate_dependent(scene, strip); + seq::relations_invalidate_cache(scene, strip); } } } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.cc b/source/blender/editors/space_sequencer/sequencer_edit.cc index 681dc51514e..338be7749d5 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.cc +++ b/source/blender/editors/space_sequencer/sequencer_edit.cc @@ -381,7 +381,7 @@ static wmOperatorStatus sequencer_snap_exec(bContext *C, wmOperator *op) } } - seq::relations_invalidate_cache_composite(scene, strip); + seq::relations_invalidate_cache(scene, strip); } } @@ -673,7 +673,7 @@ static void slip_strips_delta(wmOperator *op, Scene *scene, SlipData *data, cons bool slip_keyframes = RNA_boolean_get(op->ptr, "slip_keyframes"); for (Strip *strip : data->strips) { seq::time_slip_strip(scene, strip, frame_delta, subframe_delta, slip_keyframes); - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); } RNA_float_set(op->ptr, "offset", new_offset); @@ -918,13 +918,13 @@ static wmOperatorStatus sequencer_mute_exec(bContext *C, wmOperator *op) if (!RNA_boolean_get(op->ptr, "unselected")) { if (strip->flag & SELECT) { strip->flag |= SEQ_MUTE; - seq::relations_invalidate_dependent(scene, strip); + seq::relations_invalidate_cache(scene, strip); } } else { if ((strip->flag & SELECT) == 0) { strip->flag |= SEQ_MUTE; - seq::relations_invalidate_dependent(scene, strip); + seq::relations_invalidate_cache(scene, strip); } } } @@ -972,19 +972,19 @@ static wmOperatorStatus sequencer_unmute_exec(bContext *C, wmOperator *op) strip->type != STRIP_TYPE_SOUND_RAM) { strip->flag &= ~SEQ_MUTE; - seq::relations_invalidate_dependent(scene, strip); + seq::relations_invalidate_cache(scene, strip); } } else if (!RNA_boolean_get(op->ptr, "unselected")) { if (strip->flag & SELECT) { strip->flag &= ~SEQ_MUTE; - seq::relations_invalidate_dependent(scene, strip); + seq::relations_invalidate_cache(scene, strip); } } else { if ((strip->flag & SELECT) == 0) { strip->flag &= ~SEQ_MUTE; - seq::relations_invalidate_dependent(scene, strip); + seq::relations_invalidate_cache(scene, strip); } } } @@ -1243,7 +1243,7 @@ static wmOperatorStatus sequencer_refresh_all_exec(bContext *C, wmOperator * /*o seq::relations_free_imbuf(scene, &ed->seqbase, false); blender::seq::media_presence_free(scene); - blender::seq::thumbnail_cache_clear(scene); + seq::cache_cleanup(scene); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1355,7 +1355,7 @@ static wmOperatorStatus sequencer_reassign_inputs_exec(bContext *C, wmOperator * seq::strip_lookup_invalidate(scene->ed); seq::time_left_handle_frame_set(scene, seq1, seq::time_left_handle_frame_get(scene, seq1)); - seq::relations_invalidate_cache_preprocessed(scene, active_strip); + seq::relations_invalidate_cache(scene, active_strip); seq::offset_animdata(scene, active_strip, (active_strip->start - old_start)); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1413,7 +1413,7 @@ static wmOperatorStatus sequencer_swap_inputs_exec(bContext *C, wmOperator *op) active_strip->seq1 = active_strip->seq2; active_strip->seq2 = strip; - seq::relations_invalidate_cache_preprocessed(scene, active_strip); + seq::relations_invalidate_cache(scene, active_strip); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1907,7 +1907,7 @@ static wmOperatorStatus sequencer_offset_clear_exec(bContext *C, wmOperator * /* /* Update lengths, etc. */ strip = static_cast(ed->seqbasep->first); while (strip) { - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); strip = strip->next; } @@ -2135,7 +2135,7 @@ static wmOperatorStatus sequencer_meta_make_exec(bContext *C, wmOperator * /*op* scene, active_seqbase, strips_to_move, seq::query_strip_connected_and_effect_chain); for (Strip *strip : strips_to_move) { - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); BLI_remlink(active_seqbase, strip); BLI_addtail(&strip_meta->seqbase, strip); channel_max = max_ii(strip->machine, channel_max); @@ -2205,7 +2205,7 @@ static wmOperatorStatus sequencer_meta_separate_exec(bContext *C, wmOperator * / seq::prefetch_stop(scene); LISTBASE_FOREACH (Strip *, strip, &active_strip->seqbase) { - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); } /* Remove all selected from meta, and put in main list. @@ -2340,12 +2340,12 @@ static void swap_strips(Scene *scene, Strip *strip_a, Strip *strip_b) strip_b_start = (strip_b->start - seq::time_left_handle_frame_get(scene, strip_b)) + seq::time_left_handle_frame_get(scene, strip_a); seq::transform_translate_strip(scene, strip_b, strip_b_start - strip_b->start); - seq::relations_invalidate_cache_preprocessed(scene, strip_b); + seq::relations_invalidate_cache(scene, strip_b); strip_a_start = (strip_a->start - seq::time_left_handle_frame_get(scene, strip_a)) + seq::time_right_handle_frame_get(scene, strip_b) + gap; seq::transform_translate_strip(scene, strip_a, strip_a_start - strip_a->start); - seq::relations_invalidate_cache_preprocessed(scene, strip_a); + seq::relations_invalidate_cache(scene, strip_a); } static Strip *find_next_prev_strip(Scene *scene, Strip *test, int lr, int sel) @@ -2531,7 +2531,7 @@ static wmOperatorStatus sequencer_rendersize_exec(bContext *C, wmOperator * /*op active_strip->data->transform->scale_x = active_strip->data->transform->scale_y = 1.0f; active_strip->data->transform->xofs = active_strip->data->transform->yofs = 0.0f; - seq::relations_invalidate_cache_preprocessed(scene, active_strip); + seq::relations_invalidate_cache(scene, active_strip); WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, nullptr); @@ -2706,7 +2706,7 @@ static wmOperatorStatus sequencer_change_effect_input_exec(bContext *C, wmOperat std::swap(*strip_1, *strip_2); - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; @@ -2781,7 +2781,7 @@ static wmOperatorStatus sequencer_change_effect_type_exec(bContext *C, wmOperato sh = seq::effect_handle_get(strip); sh.init(strip); - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; @@ -3376,7 +3376,7 @@ static wmOperatorStatus sequencer_strip_transform_clear_exec(bContext *C, wmOper } break; } - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); } } @@ -3440,7 +3440,7 @@ static wmOperatorStatus sequencer_strip_transform_fit_exec(bContext *C, wmOperat scene->r.xsch, scene->r.ysch, fit_method); - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); } } diff --git a/source/blender/editors/space_sequencer/sequencer_modifier.cc b/source/blender/editors/space_sequencer/sequencer_modifier.cc index 05e923e10ec..df1827d712b 100644 --- a/source/blender/editors/space_sequencer/sequencer_modifier.cc +++ b/source/blender/editors/space_sequencer/sequencer_modifier.cc @@ -44,7 +44,7 @@ static wmOperatorStatus strip_modifier_add_exec(bContext *C, wmOperator *op) seq::modifier_new(strip, nullptr, type); - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; @@ -118,7 +118,7 @@ static wmOperatorStatus strip_modifier_remove_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO); } else { - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); } WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -190,7 +190,7 @@ static wmOperatorStatus strip_modifier_move_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO); } else { - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); } WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -286,7 +286,7 @@ static wmOperatorStatus strip_modifier_copy_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO); } else { - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); } WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -345,7 +345,7 @@ static wmOperatorStatus strip_modifier_equalizer_redefine_exec(bContext *C, wmOp seq::sound_equalizermodifier_set_graphs((SoundEqualizerModifierData *)smd, number); - seq::relations_invalidate_cache_preprocessed(scene, strip); + seq::relations_invalidate_cache(scene, strip); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_sequencer/sequencer_timeline_draw.cc b/source/blender/editors/space_sequencer/sequencer_timeline_draw.cc index b6491707ad3..b4d7439ba2f 100644 --- a/source/blender/editors/space_sequencer/sequencer_timeline_draw.cc +++ b/source/blender/editors/space_sequencer/sequencer_timeline_draw.cc @@ -1673,77 +1673,32 @@ struct CacheDrawData { const View2D *v2d; float stripe_ofs_y; float stripe_ht; - int cache_flag; SeqQuadsBatch *quads; }; -/* Called as a callback. */ -static bool draw_cache_view_init_fn(void * /*userdata*/, size_t item_count) -{ - return item_count == 0; -} - -/* Called as a callback */ -static bool draw_cache_view_iter_fn(void *userdata, - Strip *strip, - int timeline_frame, - int cache_type) +/* Draw final cache entries on top of the timeline. */ +static void draw_cache_final_iter_fn(void *userdata, int timeline_frame) { CacheDrawData *drawdata = static_cast(userdata); + + /* Same as movie clip cache color, see ED_region_cache_draw_cached_segments. */ + const uchar4 col{108, 108, 210, 255}; + const View2D *v2d = drawdata->v2d; - float stripe_top, stripe_bot; - - /* NOTE: Final color is the same as the movie clip cache color. - * See ED_region_cache_draw_cached_segments. - */ - const uchar4 col_final{108, 108, 210, 255}; - const uchar4 col_raw{255, 25, 5, 100}; - const uchar4 col_preproc{25, 25, 191, 100}; - const uchar4 col_composite{255, 153, 0, 100}; - - uchar4 col{0, 0, 0, 0}; - - bool dev_ui = (U.flag & USER_DEVELOPER_UI); - - if ((cache_type & SEQ_CACHE_STORE_FINAL_OUT) && - (drawdata->cache_flag & SEQ_CACHE_SHOW_FINAL_OUT)) - { - /* Draw the final cache on top of the timeline */ - stripe_top = v2d->cur.ymax - (UI_TIME_SCRUB_MARGIN_Y / UI_view2d_scale_get_y(v2d)); - stripe_bot = stripe_top - (UI_TIME_CACHE_MARGIN_Y / UI_view2d_scale_get_y(v2d)); - col = col_final; - } - else { - if (!dev_ui) { - /* Don't show these cache types below unless developer extras is on. */ - return false; - } - if ((cache_type & SEQ_CACHE_STORE_RAW) && (drawdata->cache_flag & SEQ_CACHE_SHOW_RAW)) { - stripe_bot = strip->machine + STRIP_OFSBOTTOM + drawdata->stripe_ofs_y; - col = col_raw; - } - else if ((cache_type & SEQ_CACHE_STORE_PREPROCESSED) && - (drawdata->cache_flag & SEQ_CACHE_SHOW_PREPROCESSED)) - { - stripe_bot = strip->machine + STRIP_OFSBOTTOM + drawdata->stripe_ht + - drawdata->stripe_ofs_y * 2; - col = col_preproc; - } - else if ((cache_type & SEQ_CACHE_STORE_COMPOSITE) && - (drawdata->cache_flag & SEQ_CACHE_SHOW_COMPOSITE)) - { - stripe_bot = strip->machine + STRIP_OFSTOP - drawdata->stripe_ofs_y - drawdata->stripe_ht; - col = col_composite; - } - else { - return false; - } - stripe_top = stripe_bot + drawdata->stripe_ht; - } - + float stripe_top = v2d->cur.ymax - (UI_TIME_SCRUB_MARGIN_Y / UI_view2d_scale_get_y(v2d)); + float stripe_bot = stripe_top - (UI_TIME_CACHE_MARGIN_Y / UI_view2d_scale_get_y(v2d)); drawdata->quads->add_quad(timeline_frame, stripe_bot, timeline_frame + 1, stripe_top, col); +} - return false; +/* Draw source cache entries at bottom of the strips. */ +static void draw_cache_source_iter_fn(void *userdata, const Strip *strip, int timeline_frame) +{ + CacheDrawData *drawdata = static_cast(userdata); + + const uchar4 col{255, 25, 5, 100}; + float stripe_bot = strip->machine + STRIP_OFSBOTTOM + drawdata->stripe_ofs_y; + float stripe_top = stripe_bot + drawdata->stripe_ht; + drawdata->quads->add_quad(timeline_frame, stripe_bot, timeline_frame + 1, stripe_top, col); } static void draw_cache_stripe(const Scene *scene, @@ -1771,8 +1726,6 @@ static void draw_cache_background(const bContext *C, CacheDrawData *draw_data) */ const uchar4 bg_final{78, 78, 145, 255}; const uchar4 bg_raw{255, 25, 5, 25}; - const uchar4 bg_preproc{25, 25, 191, 25}; - const uchar4 bg_composite{255, 153, 0, 25}; float stripe_bot; bool dev_ui = (U.flag & USER_DEVELOPER_UI); @@ -1798,18 +1751,6 @@ static void draw_cache_background(const bContext *C, CacheDrawData *draw_data) if (sseq->cache_overlay.flag & SEQ_CACHE_SHOW_RAW) { draw_cache_stripe(scene, strip, *draw_data->quads, stripe_bot, draw_data->stripe_ht, bg_raw); } - - if (sseq->cache_overlay.flag & SEQ_CACHE_SHOW_PREPROCESSED) { - stripe_bot += draw_data->stripe_ht + draw_data->stripe_ofs_y; - draw_cache_stripe( - scene, strip, *draw_data->quads, stripe_bot, draw_data->stripe_ht, bg_preproc); - } - - if (sseq->cache_overlay.flag & SEQ_CACHE_SHOW_COMPOSITE) { - stripe_bot = strip->machine + STRIP_OFSTOP - draw_data->stripe_ofs_y - draw_data->stripe_ht; - draw_cache_stripe( - scene, strip, *draw_data->quads, stripe_bot, draw_data->stripe_ht, bg_composite); - } } } @@ -1835,13 +1776,17 @@ static void draw_cache_view(const bContext *C) userdata.v2d = v2d; userdata.stripe_ofs_y = stripe_ofs_y; userdata.stripe_ht = stripe_ht; - userdata.cache_flag = sseq->cache_overlay.flag; userdata.quads = &quads; GPU_blend(GPU_BLEND_ALPHA); draw_cache_background(C, &userdata); - seq::cache_iterate(scene, &userdata, draw_cache_view_init_fn, draw_cache_view_iter_fn); + if (sseq->cache_overlay.flag & SEQ_CACHE_SHOW_FINAL_OUT) { + seq::final_image_cache_iterate(scene, &userdata, draw_cache_final_iter_fn); + } + if ((U.flag & USER_DEVELOPER_UI) && (sseq->cache_overlay.flag & SEQ_CACHE_SHOW_RAW)) { + seq::source_image_cache_iterate(scene, &userdata, draw_cache_source_iter_fn); + } quads.draw(); GPU_blend(GPU_BLEND_NONE); diff --git a/source/blender/editors/transform/transform_convert_sequencer.cc b/source/blender/editors/transform/transform_convert_sequencer.cc index d1a44fca114..64c1e9bc46a 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.cc +++ b/source/blender/editors/transform/transform_convert_sequencer.cc @@ -665,7 +665,7 @@ static void recalcData_sequencer(TransInfo *t) Strip *strip = tdsq->strip; if (strip != strip_prev) { - seq::relations_invalidate_cache_composite(t->scene, strip); + seq::relations_invalidate_cache(t->scene, strip); } strip_prev = strip; diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.cc b/source/blender/editors/transform/transform_convert_sequencer_image.cc index 2c70202b7ae..37de1ae6770 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.cc +++ b/source/blender/editors/transform/transform_convert_sequencer_image.cc @@ -308,7 +308,7 @@ static void image_transform_set(TransInfo *t) autokeyframe_sequencer_image(t->context, t->scene, transform, t->mode); } - seq::relations_invalidate_cache_preprocessed(t->scene, strip); + seq::relations_invalidate_cache(t->scene, strip); } } @@ -373,7 +373,7 @@ static void image_origin_set(TransInfo *t) transform->xofs = tdseq->orig_translation.x - delta_translation.x; transform->yofs = tdseq->orig_translation.y - delta_translation.y; - seq::relations_invalidate_cache_preprocessed(t->scene, strip); + seq::relations_invalidate_cache(t->scene, strip); } } diff --git a/source/blender/editors/transform/transform_convert_sequencer_retiming.cc b/source/blender/editors/transform/transform_convert_sequencer_retiming.cc index b412c4c2d79..2ef994fc52d 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_retiming.cc +++ b/source/blender/editors/transform/transform_convert_sequencer_retiming.cc @@ -154,7 +154,7 @@ static void recalcData_sequencer_retiming(TransInfo *t) seq::retiming_key_timeline_frame_set(t->scene, strip, key, td2d->loc[0]); } - seq::relations_invalidate_cache_preprocessed(t->scene, strip); + seq::relations_invalidate_cache(t->scene, strip); } /* Test overlap, displays red outline. */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 721415043ec..a875d0b17b1 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -29,26 +29,32 @@ struct bSound; #ifdef __cplusplus namespace blender::seq { +struct FinalImageCache; +struct IntraFrameCache; struct MediaPresence; struct ThumbnailCache; struct TextVarsRuntime; struct PrefetchJob; +struct SourceImageCache; struct StripLookup; -struct SeqCache; } // namespace blender::seq +using FinalImageCache = blender::seq::FinalImageCache; +using IntraFrameCache = blender::seq::IntraFrameCache; using MediaPresence = blender::seq::MediaPresence; using ThumbnailCache = blender::seq::ThumbnailCache; using TextVarsRuntime = blender::seq::TextVarsRuntime; using PrefetchJob = blender::seq::PrefetchJob; +using SourceImageCache = blender::seq::SourceImageCache; using StripLookup = blender::seq::StripLookup; -using SeqCache = blender::seq::SeqCache; #else +typedef struct FinalImageCache FinalImageCache; +typedef struct IntraFrameCache IntraFrameCache; typedef struct MediaPresence MediaPresence; typedef struct ThumbnailCache ThumbnailCache; typedef struct TextVarsRuntime TextVarsRuntime; typedef struct PrefetchJob PrefetchJob; +typedef struct SourceImageCache SourceImageCache; typedef struct StripLookup StripLookup; -typedef struct SeqCache SeqCache; #endif /* -------------------------------------------------------------------- */ @@ -280,8 +286,7 @@ typedef struct Strip { char alpha_mode; char _pad2[2]; - - int cache_flag; + int _pad9; /* is sfra needed anymore? - it looks like its only used in one place */ /** Starting frame according to the timeline of the scene. */ @@ -334,7 +339,9 @@ typedef struct EditingRuntime { StripLookup *strip_lookup; MediaPresence *media_presence; ThumbnailCache *thumbnail_cache; - void *_pad; + IntraFrameCache *intra_frame_cache; + SourceImageCache *source_image_cache; + FinalImageCache *final_image_cache; } EditingRuntime; typedef struct Editing { @@ -363,19 +370,10 @@ typedef struct Editing { rctf overlay_frame_rect; int show_missing_media_flag; - int _pad1; - - SeqCache *cache; - - /* Cache control */ - float recycle_max_cost; /* UNUSED only for versioning. */ int cache_flag; PrefetchJob *prefetch_job; - /* Must be initialized only by seq_cache_create() */ - int64_t disk_cache_timestamp; - EditingRuntime runtime; } Editing; @@ -845,28 +843,16 @@ enum { SEQUENCE_MASK_TIME_ABSOLUTE = 1, }; -/** - * #Strip.cache_flag - * - #SEQ_CACHE_STORE_RAW - * - #SEQ_CACHE_STORE_PREPROCESSED - * - #SEQ_CACHE_STORE_COMPOSITE - * - #FINAL_OUT is ignored - * - * #Editing.cache_flag - * all entries - */ enum { SEQ_CACHE_STORE_RAW = (1 << 0), - SEQ_CACHE_STORE_PREPROCESSED = (1 << 1), - SEQ_CACHE_STORE_COMPOSITE = (1 << 2), + SEQ_CACHE_UNUSED_1 = (1 << 1), /* Was SEQ_CACHE_STORE_PREPROCESSED */ + SEQ_CACHE_UNUSED_2 = (1 << 2), /* Was SEQ_CACHE_STORE_COMPOSITE */ SEQ_CACHE_STORE_FINAL_OUT = (1 << 3), /* For lookup purposes */ - SEQ_CACHE_ALL_TYPES = SEQ_CACHE_STORE_RAW | SEQ_CACHE_STORE_PREPROCESSED | - SEQ_CACHE_STORE_COMPOSITE | SEQ_CACHE_STORE_FINAL_OUT, - - SEQ_CACHE_OVERRIDE = (1 << 4), + SEQ_CACHE_ALL_TYPES = SEQ_CACHE_STORE_RAW | SEQ_CACHE_STORE_FINAL_OUT, + SEQ_CACHE_UNUSED_4 = (1 << 4), /* Was SEQ_CACHE_OVERRIDE */ SEQ_CACHE_UNUSED_5 = (1 << 5), SEQ_CACHE_UNUSED_6 = (1 << 6), SEQ_CACHE_UNUSED_7 = (1 << 7), @@ -874,7 +860,7 @@ enum { SEQ_CACHE_UNUSED_9 = (1 << 9), SEQ_CACHE_PREFETCH_ENABLE = (1 << 10), - SEQ_CACHE_DISK_CACHE_ENABLE = (1 << 11), + SEQ_CACHE_UNUSED_11 = (1 << 11), /* Was SEQ_CACHE_DISK_CACHE_ENABLE */ }; /** #Strip.color_tag. */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 7b0e91bde4f..2366dec7bf2 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -659,8 +659,8 @@ typedef struct SequencerCacheOverlay { typedef enum eSpaceSeq_SequencerCacheOverlay_Flag { SEQ_CACHE_SHOW = (1 << 1), SEQ_CACHE_SHOW_RAW = (1 << 2), - SEQ_CACHE_SHOW_PREPROCESSED = (1 << 3), - SEQ_CACHE_SHOW_COMPOSITE = (1 << 4), + /* Was SEQ_CACHE_SHOW_PREPROCESSED = (1 << 3) */ + /* Was SEQ_CACHE_SHOW_COMPOSITE = (1 << 4) */ SEQ_CACHE_SHOW_FINAL_OUT = (1 << 5), } eSpaceSeq_SequencerCacheOverlay_Flag; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index fcc68942d17..153502cea4d 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -1140,11 +1140,8 @@ typedef struct UserDef { char render_display_type; /* eUserpref_RenderDisplayType */ char filebrowser_display_type; /* eUserpref_TempSpaceDisplayType */ - char sequencer_disk_cache_dir[1024]; - int sequencer_disk_cache_compression; /* eUserpref_DiskCacheCompression */ - int sequencer_disk_cache_size_limit; - short sequencer_disk_cache_flag; short sequencer_proxy_setup; /* eUserpref_SeqProxySetup */ + short _pad1; float collection_instance_empty_size; char text_flag; diff --git a/source/blender/makesrna/intern/rna_color.cc b/source/blender/makesrna/intern/rna_color.cc index e80234ce9d2..484eee0d319 100644 --- a/source/blender/makesrna/intern/rna_color.cc +++ b/source/blender/makesrna/intern/rna_color.cc @@ -84,7 +84,7 @@ static bool seq_update_modifier_curve(Strip *strip, void *user_data) if (smd->type == seqModifierType_Curves) { CurvesModifierData *cmd = reinterpret_cast(smd); if (&cmd->curve_mapping == data->curve) { - blender::seq::relations_invalidate_cache_preprocessed(data->scene, strip); + blender::seq::relations_invalidate_cache(data->scene, strip); } } } @@ -716,7 +716,6 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, if (&scene->sequencer_colorspace_settings == colorspace_settings) { /* Scene colorspace was changed. */ blender::seq::cache_cleanup(scene); - blender::seq::thumbnail_cache_clear(scene); } else { /* Strip colorspace was likely changed. */ diff --git a/source/blender/makesrna/intern/rna_sequencer.cc b/source/blender/makesrna/intern/rna_sequencer.cc index e0e8cae0c79..9dd5e3f8957 100644 --- a/source/blender/makesrna/intern/rna_sequencer.cc +++ b/source/blender/makesrna/intern/rna_sequencer.cc @@ -174,7 +174,7 @@ static void rna_Strip_invalidate_preprocessed_update(Main * /*bmain*/, if (ed) { Strip *strip = (Strip *)ptr->data; - blender::seq::relations_invalidate_cache_preprocessed(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } } @@ -188,7 +188,7 @@ static void UNUSED_FUNCTION(rna_Strip_invalidate_composite_update)(Main * /*bmai if (ed) { Strip *strip = (Strip *)ptr->data; - blender::seq::relations_invalidate_cache_composite(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } } @@ -463,7 +463,7 @@ static void rna_Strip_start_frame_final_set(PointerRNA *ptr, int value) blender::seq::time_left_handle_frame_set(scene, strip, value); do_strip_frame_change_update(scene, strip); - blender::seq::relations_invalidate_cache_composite(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } static void rna_Strip_end_frame_final_set(PointerRNA *ptr, int value) @@ -473,7 +473,7 @@ static void rna_Strip_end_frame_final_set(PointerRNA *ptr, int value) blender::seq::time_right_handle_frame_set(scene, strip, value); do_strip_frame_change_update(scene, strip); - blender::seq::relations_invalidate_cache_composite(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } static void rna_Strip_start_frame_set(PointerRNA *ptr, float value) @@ -483,7 +483,7 @@ static void rna_Strip_start_frame_set(PointerRNA *ptr, float value) blender::seq::transform_translate_strip(scene, strip, value - strip->start); do_strip_frame_change_update(scene, strip); - blender::seq::relations_invalidate_cache_composite(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } static void rna_Strip_frame_offset_start_set(PointerRNA *ptr, float value) @@ -491,7 +491,7 @@ static void rna_Strip_frame_offset_start_set(PointerRNA *ptr, float value) Strip *strip = (Strip *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; - blender::seq::relations_invalidate_cache_composite(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); strip->startofs = value; } @@ -500,7 +500,7 @@ static void rna_Strip_frame_offset_end_set(PointerRNA *ptr, float value) Strip *strip = (Strip *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; - blender::seq::relations_invalidate_cache_composite(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); strip->endofs = value; } @@ -568,7 +568,7 @@ static void rna_Strip_frame_length_set(PointerRNA *ptr, int value) blender::seq::time_right_handle_frame_set( scene, strip, blender::seq::time_left_handle_frame_get(scene, strip) + value); do_strip_frame_change_update(scene, strip); - blender::seq::relations_invalidate_cache_composite(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } static int rna_Strip_frame_length_get(PointerRNA *ptr) @@ -606,7 +606,7 @@ static void rna_Strip_channel_set(PointerRNA *ptr, int value) if (blender::seq::transform_test_overlap(scene, seqbase, strip)) { blender::seq::transform_seqbase_shuffle_ex(seqbase, strip, scene, channel_delta); } - blender::seq::relations_invalidate_cache_composite(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } static bool rna_Strip_lock_get(PointerRNA *ptr) @@ -668,7 +668,7 @@ static void rna_StripTransform_update(Main * /*bmain*/, Scene * /*scene*/, Point Editing *ed = blender::seq::editing_get(scene); Strip *strip = strip_get_by_transform(ed, static_cast(ptr->data)); - blender::seq::relations_invalidate_cache_preprocessed(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } static bool crop_strip_cmp_fn(Strip *strip, void *arg_pt) @@ -715,7 +715,7 @@ static void rna_StripCrop_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA Editing *ed = blender::seq::editing_get(scene); Strip *strip = strip_get_by_crop(ed, static_cast(ptr->data)); - blender::seq::relations_invalidate_cache_preprocessed(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } static void rna_Strip_text_font_set(PointerRNA *ptr, @@ -1101,7 +1101,7 @@ static void rna_StripProxy_update(Main * /*bmain*/, Scene * /*scene*/, PointerRN Scene *scene = (Scene *)ptr->owner_id; Editing *ed = blender::seq::editing_get(scene); Strip *strip = strip_get_by_proxy(ed, static_cast(ptr->data)); - blender::seq::relations_invalidate_cache_preprocessed(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } /* do_versions? */ @@ -1202,7 +1202,7 @@ static void rna_StripColorBalance_update(Main * /*bmain*/, Scene * /*scene*/, Po StripModifierData *smd; Strip *strip = strip_get_by_colorbalance(ed, static_cast(ptr->data), &smd); - blender::seq::relations_invalidate_cache_preprocessed(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } static void rna_SequenceEditor_overlay_lock_set(PointerRNA *ptr, bool value) @@ -1259,6 +1259,24 @@ static void rna_SequenceEditor_overlay_frame_set(PointerRNA *ptr, int value) } } +static int rna_SequenceEditor_get_cache_raw_size(PointerRNA *ptr) +{ + Scene *scene = (Scene *)ptr->owner_id; + if (scene == nullptr) { + return 0; + } + return int(blender::seq::source_image_cache_calc_memory_size(scene) / 1024 / 1024); +} + +static int rna_SequenceEditor_get_cache_final_size(PointerRNA *ptr) +{ + Scene *scene = (Scene *)ptr->owner_id; + if (scene == nullptr) { + return 0; + } + return int(blender::seq::final_image_cache_calc_memory_size(scene) / 1024 / 1024); +} + static void rna_SequenceEditor_display_stack(ID *id, Editing *ed, ReportList *reports, @@ -1393,7 +1411,7 @@ static void rna_StripModifier_update(Main *bmain, Scene * /*scene*/, PointerRNA DEG_relations_tag_update(bmain); } else { - blender::seq::relations_invalidate_cache_preprocessed(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } } @@ -1439,7 +1457,7 @@ static StripModifierData *rna_Strip_modifier_new( smd = blender::seq::modifier_new(strip, name, type); - blender::seq::relations_invalidate_cache_preprocessed(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, nullptr); @@ -1461,7 +1479,7 @@ static void rna_Strip_modifier_remove(Strip *strip, } smd_ptr->invalidate(); - blender::seq::relations_invalidate_cache_preprocessed(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, nullptr); } @@ -1472,7 +1490,7 @@ static void rna_Strip_modifier_clear(Strip *strip, bContext *C) blender::seq::modifier_clear(strip); - blender::seq::relations_invalidate_cache_preprocessed(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, nullptr); } @@ -1561,7 +1579,7 @@ static void rna_SequenceTimelineChannel_mute_update(Main *bmain, } LISTBASE_FOREACH (Strip *, strip, seqbase) { - blender::seq::relations_invalidate_cache_composite(scene, strip); + blender::seq::relations_invalidate_cache(scene, strip); } rna_Strip_sound_update(bmain, active_scene, ptr); @@ -2305,31 +2323,6 @@ static void rna_def_strip(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Modifiers", "Modifiers affecting this strip"); rna_def_strip_modifiers(brna, prop); - prop = RNA_def_property(srna, "use_cache_raw", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "cache_flag", SEQ_CACHE_STORE_RAW); - RNA_def_property_ui_text(prop, - "Cache Raw", - "Cache raw images read from disk, for faster tweaking of strip " - "parameters at the cost of memory usage"); - - prop = RNA_def_property(srna, "use_cache_preprocessed", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "cache_flag", SEQ_CACHE_STORE_PREPROCESSED); - RNA_def_property_ui_text( - prop, - "Cache Preprocessed", - "Cache preprocessed images, for faster tweaking of effects at the cost of memory usage"); - - prop = RNA_def_property(srna, "use_cache_composite", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "cache_flag", SEQ_CACHE_STORE_COMPOSITE); - RNA_def_property_ui_text(prop, - "Cache Composite", - "Cache intermediate composited images, for faster tweaking of stacked " - "strips at the cost of memory usage"); - - prop = RNA_def_property(srna, "override_cache_settings", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "cache_flag", SEQ_CACHE_OVERRIDE); - RNA_def_property_ui_text(prop, "Override Cache Settings", "Override global cache settings"); - prop = RNA_def_property(srna, "show_retiming_keys", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", SEQ_SHOW_RETIMING); RNA_def_property_ui_text(prop, "Show Retiming Keys", "Show retiming keys, so they can be moved"); @@ -2527,20 +2520,6 @@ static void rna_def_editor(BlenderRNA *brna) "Cache raw images read from disk, for faster tweaking of strip " "parameters at the cost of memory usage"); - prop = RNA_def_property(srna, "use_cache_preprocessed", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "cache_flag", SEQ_CACHE_STORE_PREPROCESSED); - RNA_def_property_ui_text( - prop, - "Cache Preprocessed", - "Cache preprocessed images, for faster tweaking of effects at the cost of memory usage"); - - prop = RNA_def_property(srna, "use_cache_composite", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "cache_flag", SEQ_CACHE_STORE_COMPOSITE); - RNA_def_property_ui_text(prop, - "Cache Composite", - "Cache intermediate composited images, for faster tweaking of stacked " - "strips at the cost of memory usage"); - prop = RNA_def_property(srna, "use_cache_final", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "cache_flag", SEQ_CACHE_STORE_FINAL_OUT); RNA_def_property_ui_text(prop, "Cache Final", "Cache final image for each frame"); @@ -2553,6 +2532,17 @@ static void rna_def_editor(BlenderRNA *brna) "Render frames ahead of current frame in the background for faster playback"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, nullptr); + prop = RNA_def_property(srna, "cache_raw_size", PROP_INT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE | PROP_ANIMATABLE); + RNA_def_property_int_funcs(prop, "rna_SequenceEditor_get_cache_raw_size", nullptr, nullptr); + RNA_def_property_ui_text(prop, "Raw Cache Size", "Size of raw source images cache in megabytes"); + + prop = RNA_def_property(srna, "cache_final_size", PROP_INT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE | PROP_ANIMATABLE); + RNA_def_property_int_funcs(prop, "rna_SequenceEditor_get_cache_final_size", nullptr, nullptr); + RNA_def_property_ui_text( + prop, "Final Cache Size", "Size of final rendered images cache in megabytes"); + /* functions */ func = RNA_def_function(srna, "display_stack", "rna_SequenceEditor_display_stack"); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.cc b/source/blender/makesrna/intern/rna_sequencer_api.cc index 7ca6e94ecf7..f68a12f5cec 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.cc +++ b/source/blender/makesrna/intern/rna_sequencer_api.cc @@ -656,12 +656,6 @@ static void rna_Strip_invalidate_cache_rnafunc(ID *id, Strip *self, int type) case SEQ_CACHE_STORE_RAW: blender::seq::relations_invalidate_cache_raw((Scene *)id, self); break; - case SEQ_CACHE_STORE_PREPROCESSED: - blender::seq::relations_invalidate_cache_preprocessed((Scene *)id, self); - break; - case SEQ_CACHE_STORE_COMPOSITE: - blender::seq::relations_invalidate_cache_composite((Scene *)id, self); - break; } } @@ -695,8 +689,6 @@ void RNA_api_strip(StructRNA *srna) static const EnumPropertyItem strip_cache_type_items[] = { {SEQ_CACHE_STORE_RAW, "RAW", 0, "Raw", ""}, - {SEQ_CACHE_STORE_PREPROCESSED, "PREPROCESSED", 0, "Preprocessed", ""}, - {SEQ_CACHE_STORE_COMPOSITE, "COMPOSITE", 0, "Composite", ""}, {0, nullptr, 0, nullptr, nullptr}, }; diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index 1a9ab014a28..a4322768ffb 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -6233,16 +6233,6 @@ static void rna_def_space_sequencer_cache_overlay(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, nullptr, "cache_overlay.flag", SEQ_CACHE_SHOW_RAW); RNA_def_property_ui_text(prop, "Raw Images", "Visualize cached raw images"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, nullptr); - - prop = RNA_def_property(srna, "show_cache_preprocessed", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "cache_overlay.flag", SEQ_CACHE_SHOW_PREPROCESSED); - RNA_def_property_ui_text(prop, "Preprocessed Images", "Visualize cached pre-processed images"); - RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, nullptr); - - prop = RNA_def_property(srna, "show_cache_composite", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "cache_overlay.flag", SEQ_CACHE_SHOW_COMPOSITE); - RNA_def_property_ui_text(prop, "Composite Images", "Visualize cached composite images"); - RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, nullptr); } static void rna_def_space_sequencer(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index b27e6e3114a..586d59b24fb 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -6125,25 +6125,6 @@ static void rna_def_userdef_system(BlenderRNA *brna) {0, nullptr, 0, nullptr, nullptr}, }; - static const EnumPropertyItem seq_disk_cache_compression_levels[] = { - {USER_SEQ_DISK_CACHE_COMPRESSION_NONE, - "NONE", - 0, - "None", - "Requires fast storage, but uses minimum CPU resources"}, - {USER_SEQ_DISK_CACHE_COMPRESSION_LOW, - "LOW", - 0, - "Low", - "Doesn't require fast storage and uses less CPU resources"}, - {USER_SEQ_DISK_CACHE_COMPRESSION_HIGH, - "HIGH", - 0, - "High", - "Works on slower storage devices and uses most CPU resources"}, - {0, nullptr, 0, nullptr, nullptr}, - }; - static const EnumPropertyItem seq_proxy_setup_options[] = { {USER_SEQ_PROXY_SETUP_MANUAL, "MANUAL", 0, "Manual", "Set up proxies manually"}, {USER_SEQ_PROXY_SETUP_AUTOMATIC, @@ -6195,30 +6176,6 @@ static void rna_def_userdef_system(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Memory Cache Limit", "Memory cache limit (in megabytes)"); RNA_def_property_update(prop, 0, "rna_Userdef_memcache_update"); - /* Sequencer disk cache */ - - prop = RNA_def_property(srna, "use_sequencer_disk_cache", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna( - prop, nullptr, "sequencer_disk_cache_flag", SEQ_CACHE_DISK_CACHE_ENABLE); - RNA_def_property_ui_text(prop, "Use Disk Cache", "Store cached images to disk"); - - prop = RNA_def_property(srna, "sequencer_disk_cache_dir", PROP_STRING, PROP_DIRPATH); - RNA_def_property_string_sdna(prop, nullptr, "sequencer_disk_cache_dir"); - RNA_def_property_ui_text(prop, "Disk Cache Directory", "Override default directory"); - - prop = RNA_def_property(srna, "sequencer_disk_cache_size_limit", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, nullptr, "sequencer_disk_cache_size_limit"); - RNA_def_property_range(prop, 0, INT_MAX); - RNA_def_property_ui_text(prop, "Disk Cache Limit", "Disk cache limit (in gigabytes)"); - - prop = RNA_def_property(srna, "sequencer_disk_cache_compression", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, seq_disk_cache_compression_levels); - RNA_def_property_enum_sdna(prop, nullptr, "sequencer_disk_cache_compression"); - RNA_def_property_ui_text( - prop, - "Disk Cache Compression Level", - "Smaller compression will result in larger files, but less decoding overhead"); - /* Sequencer proxy setup */ prop = RNA_def_property(srna, "sequencer_proxy_setup", PROP_ENUM, PROP_NONE); diff --git a/source/blender/sequencer/CMakeLists.txt b/source/blender/sequencer/CMakeLists.txt index d01062e6a31..03e489d1bf3 100644 --- a/source/blender/sequencer/CMakeLists.txt +++ b/source/blender/sequencer/CMakeLists.txt @@ -40,9 +40,14 @@ set(SRC SEQ_utils.hh intern/animation.cc + intern/cache/final_image_cache.cc + intern/cache/final_image_cache.hh + intern/cache/intra_frame_cache.cc + intern/cache/intra_frame_cache.hh + intern/cache/source_image_cache.cc + intern/cache/source_image_cache.hh + intern/cache/thumbnail_cache.cc intern/channels.cc - intern/disk_cache.cc - intern/disk_cache.hh intern/effects/effects.cc intern/effects/effects.hh intern/effects/vse_effect_add_sub_mul.cc @@ -57,8 +62,6 @@ set(SRC intern/effects/vse_effect_text.cc intern/effects/vse_effect_transform.cc intern/effects/vse_effect_wipe.cc - intern/image_cache.cc - intern/image_cache.hh intern/iterator.cc intern/media_presence.cc intern/modifier.cc @@ -84,7 +87,6 @@ set(SRC intern/strip_time.cc intern/strip_time.hh intern/strip_transform.cc - intern/thumbnail_cache.cc intern/utils.cc intern/utils.hh ) diff --git a/source/blender/sequencer/SEQ_relations.hh b/source/blender/sequencer/SEQ_relations.hh index 9cf60dd2b7e..f13ffd4c827 100644 --- a/source/blender/sequencer/SEQ_relations.hh +++ b/source/blender/sequencer/SEQ_relations.hh @@ -33,16 +33,27 @@ bool relations_check_scene_recursion(Scene *scene, ReportList *reports); */ bool relations_render_loop_check(Strip *strip_main, Strip *strip); void relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render); + +/** + * Invalidates various caches related to a given strip: + * - Final cached frames over the length of the strip, + * - Intra-frame caches of the current frame, + * - Source/raw caches of the meta strip that contains this strip, if any, + * - Media presence cache of the strip, + * - Rebuilds speed index map if this is a speed effect strip, + * - Tags DEG for strip recalculation, + * - Stops prefetching job, if any. + */ +void relations_invalidate_cache(Scene *scene, Strip *strip); + +/** + * Does everything #relations_invalidate_cache does, plus invalidates cached raw source + * images of the strip. + */ void relations_invalidate_cache_raw(Scene *scene, Strip *strip); -void relations_invalidate_cache_preprocessed(Scene *scene, Strip *strip); -void relations_invalidate_cache_composite(Scene *scene, Strip *strip); -void relations_invalidate_dependent(Scene *scene, Strip *strip); + void relations_invalidate_scene_strips(Main *bmain, Scene *scene_target); void relations_invalidate_movieclip_strips(Main *bmain, MovieClip *clip_target); -void relations_invalidate_cache_in_range(Scene *scene, - Strip *strip, - Strip *range_mask, - int invalidate_types); /** * Release FFmpeg handles of strips that are not currently displayed to minimize memory usage. */ @@ -58,11 +69,20 @@ void relations_check_uids_unique_and_report(const Scene *scene); void relations_session_uid_generate(Strip *strip); void cache_cleanup(Scene *scene); -void cache_iterate( - Scene *scene, - void *userdata, - bool callback_init(void *userdata, size_t item_count), - bool callback_iter(void *userdata, Strip *strip, int timeline_frame, int cache_type)); +bool is_cache_full(const Scene *scene); + +void source_image_cache_iterate(Scene *scene, + void *userdata, + void callback_iter(void *userdata, + const Strip *strip, + int timeline_frame)); +void final_image_cache_iterate(Scene *scene, + void *userdata, + void callback_iter(void *userdata, int timeline_frame)); + +size_t source_image_cache_calc_memory_size(const Scene *scene); +size_t final_image_cache_calc_memory_size(const Scene *scene); + bool exists_in_seqbase(const Strip *strip, const ListBase *seqbase); } // namespace blender::seq diff --git a/source/blender/sequencer/intern/cache/final_image_cache.cc b/source/blender/sequencer/intern/cache/final_image_cache.cc new file mode 100644 index 00000000000..a53ffa7f5d9 --- /dev/null +++ b/source/blender/sequencer/intern/cache/final_image_cache.cc @@ -0,0 +1,236 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup sequencer + */ + +#include "BLI_map.hh" +#include "BLI_mutex.hh" + +#include "DNA_scene_types.h" +#include "DNA_sequence_types.h" + +#include "IMB_imbuf.hh" + +#include "SEQ_relations.hh" +#include "SEQ_render.hh" +#include "SEQ_time.hh" + +#include "final_image_cache.hh" +#include "prefetch.hh" + +namespace blender::seq { + +static Mutex final_image_cache_mutex; + +struct FinalImageCache { + /* Key is {timeline frame, view ID}. */ + Map, ImBuf *> map_; + + ~FinalImageCache() + { + clear(); + } + + void clear() + { + for (ImBuf *item : map_.values()) { + IMB_freeImBuf(item); + } + map_.clear(); + } +}; + +static FinalImageCache *ensure_final_image_cache(Scene *scene) +{ + FinalImageCache **cache = &scene->ed->runtime.final_image_cache; + if (*cache == nullptr) { + *cache = MEM_new(__func__); + } + return *cache; +} + +static FinalImageCache *query_final_image_cache(const Scene *scene) +{ + if (scene == nullptr || scene->ed == nullptr) { + return nullptr; + } + return scene->ed->runtime.final_image_cache; +} + +ImBuf *final_image_cache_get(Scene *scene, float timeline_frame, int view_id) +{ + const std::pair key = {int(math::round(timeline_frame)), view_id}; + + ImBuf *res = nullptr; + { + std::lock_guard lock(final_image_cache_mutex); + FinalImageCache *cache = query_final_image_cache(scene); + if (cache == nullptr) { + return nullptr; + } + res = cache->map_.lookup_default(key, nullptr); + } + + if (res) { + IMB_refImBuf(res); + } + return res; +} + +void final_image_cache_put(Scene *scene, float timeline_frame, int view_id, ImBuf *image) +{ + const std::pair key = {int(math::round(timeline_frame)), view_id}; + + IMB_refImBuf(image); + + std::lock_guard lock(final_image_cache_mutex); + FinalImageCache *cache = ensure_final_image_cache(scene); + + cache->map_.add_or_modify( + key, + [&](ImBuf **value) { *value = image; }, + [&](ImBuf **existing) { + if (*existing) { + IMB_freeImBuf(*existing); + } + *existing = image; + }); +} + +void final_image_cache_invalidate_frame_range(Scene *scene, + const float timeline_frame_start, + const float timeline_frame_end) +{ + std::lock_guard lock(final_image_cache_mutex); + FinalImageCache *cache = query_final_image_cache(scene); + if (cache == nullptr) { + return; + } + + const int key_start = int(math::floor(timeline_frame_start)); + const int key_end = int(math::ceil(timeline_frame_end)); + + for (auto it = cache->map_.items().begin(); it != cache->map_.items().end(); it++) { + const int key = (*it).key.first; + if (key >= key_start && key <= key_end) { + IMB_freeImBuf((*it).value); + cache->map_.remove(it); + } + } +} + +void final_image_cache_clear(Scene *scene) +{ + std::lock_guard lock(final_image_cache_mutex); + FinalImageCache *cache = query_final_image_cache(scene); + if (cache != nullptr) { + scene->ed->runtime.final_image_cache->clear(); + } +} + +void final_image_cache_destroy(Scene *scene) +{ + std::lock_guard lock(final_image_cache_mutex); + FinalImageCache *cache = query_final_image_cache(scene); + if (cache != nullptr) { + BLI_assert(cache == scene->ed->runtime.final_image_cache); + MEM_delete(scene->ed->runtime.final_image_cache); + scene->ed->runtime.final_image_cache = nullptr; + } +} + +void final_image_cache_iterate(Scene *scene, + void *userdata, + void callback_iter(void *userdata, int timeline_frame)) +{ + std::lock_guard lock(final_image_cache_mutex); + FinalImageCache *cache = query_final_image_cache(scene); + if (cache == nullptr) { + return; + } + for (std::pair frame_view : cache->map_.keys()) { + callback_iter(userdata, frame_view.first); + } +} + +size_t final_image_cache_calc_memory_size(const Scene *scene) +{ + std::lock_guard lock(final_image_cache_mutex); + FinalImageCache *cache = query_final_image_cache(scene); + if (cache == nullptr) { + return 0; + } + size_t size = 0; + for (ImBuf *frame : cache->map_.values()) { + size += IMB_get_size_in_memory(frame); + } + return size; +} + +size_t final_image_cache_get_image_count(const Scene *scene) +{ + std::lock_guard lock(final_image_cache_mutex); + FinalImageCache *cache = query_final_image_cache(scene); + if (cache == nullptr) { + return 0; + } + return cache->map_.size(); +} + +bool final_image_cache_evict(Scene *scene) +{ + std::lock_guard lock(final_image_cache_mutex); + FinalImageCache *cache = query_final_image_cache(scene); + if (cache == nullptr) { + return false; + } + + /* Find which entry to remove -- we pick the one that is furthest from the current frame, + * biasing the ones that are behind the current frame. + * + * However, do not try to evict entries from the current prefetch job range -- we need to + * be able to fully fill the cache from prefetching, and then actually stop the job when it + * is full and no longer can evict anything. */ + int cur_prefetch_start = INT_MIN, cur_prefetch_end = INT_MIN; + seq_prefetch_get_time_range(scene, &cur_prefetch_start, &cur_prefetch_end); + + const int cur_frame = scene->r.cfra; + std::pair best_key = {}; + ImBuf *best_item = nullptr; + int best_score = 0; + for (const auto &item : cache->map_.items()) { + const int item_frame = item.key.first; + if (item_frame >= cur_prefetch_start && item_frame <= cur_prefetch_end) { + continue; /* Within active prefetch range, do not try to remove it. */ + } + + /* Score for removal is distance to current frame; 2x that if behind current frame. */ + int score = 0; + if (item_frame < cur_frame) { + score = (cur_frame - item_frame) * 2; + } + else if (item_frame > cur_frame) { + score = item_frame - cur_frame; + } + if (score > best_score) { + best_key = item.key; + best_item = item.value; + best_score = score; + } + } + + /* Remove if we found one. */ + if (best_item != nullptr) { + IMB_freeImBuf(best_item); + cache->map_.remove(best_key); + return true; + } + + /* Did not find anything to remove. */ + return false; +} + +} // namespace blender::seq diff --git a/source/blender/sequencer/intern/cache/final_image_cache.hh b/source/blender/sequencer/intern/cache/final_image_cache.hh new file mode 100644 index 00000000000..c576fe3876f --- /dev/null +++ b/source/blender/sequencer/intern/cache/final_image_cache.hh @@ -0,0 +1,40 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup sequencer + * + * Cache of final rendered frames. + * - Keyed by (timeline frame, view_id). + * - When full, cache eviction policy is to remove frames furthest + * from the current playhead, biasing towards removal of + * frames behind the playhead. + * - Invalidated fairly often while editing, basically whenever any + * strip overlapping that frame changes. + */ + +#pragma once + +struct ImBuf; +struct Strip; +struct Scene; + +namespace blender::seq { + +void final_image_cache_put(Scene *scene, float timeline_frame, int view_id, ImBuf *image); + +ImBuf *final_image_cache_get(Scene *scene, float timeline_frame, int view_id); + +void final_image_cache_invalidate_frame_range(Scene *scene, + const float timeline_frame_start, + const float timeline_frame_end); + +void final_image_cache_clear(Scene *scene); +void final_image_cache_destroy(Scene *scene); + +bool final_image_cache_evict(Scene *scene); + +size_t final_image_cache_get_image_count(const Scene *scene); + +} // namespace blender::seq diff --git a/source/blender/sequencer/intern/cache/intra_frame_cache.cc b/source/blender/sequencer/intern/cache/intra_frame_cache.cc new file mode 100644 index 00000000000..a6b90026a07 --- /dev/null +++ b/source/blender/sequencer/intern/cache/intra_frame_cache.cc @@ -0,0 +1,178 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup sequencer + */ + +#include "BLI_map.hh" + +#include "DNA_scene_types.h" +#include "DNA_sequence_types.h" + +#include "IMB_imbuf.hh" + +#include "intra_frame_cache.hh" + +namespace blender::seq { + +struct StripImageMap { + Map map_; + ImBuf *get(const Strip *strip) const; + void put(const Strip *strip, ImBuf *image); + void invalidate(const Strip *strip); + void clear(); +}; + +struct IntraFrameCache { + StripImageMap preprocessed; + StripImageMap composite; + float timeline_frame = -1.0f; + int view_id = -1; + + ~IntraFrameCache() + { + preprocessed.clear(); + composite.clear(); + } +}; + +static IntraFrameCache *query_intra_frame_cache(Scene *scene) +{ + if (scene == nullptr || scene->ed == nullptr) { + return nullptr; + } + return scene->ed->runtime.intra_frame_cache; +} + +void intra_frame_cache_invalidate(Scene *scene) +{ + IntraFrameCache *cache = query_intra_frame_cache(scene); + if (cache != nullptr) { + cache->preprocessed.clear(); + cache->composite.clear(); + cache->timeline_frame = -1.0f; + cache->view_id = -1; + } +} + +void intra_frame_cache_invalidate(Scene *scene, const Strip *strip) +{ + if (strip == nullptr) { + return; + } + IntraFrameCache *cache = query_intra_frame_cache(scene); + if (cache != nullptr) { + cache->preprocessed.invalidate(strip); + cache->composite.invalidate(strip); + } +} + +void StripImageMap::invalidate(const Strip *strip) +{ + /* Invalidate this strip, and all strips that are above it. */ + for (auto it = this->map_.items().begin(); it != this->map_.items().end(); it++) { + const Strip *key = (*it).key; + if (key == strip || key->machine >= strip->machine) { + IMB_freeImBuf((*it).value); + this->map_.remove(it); + } + } +} + +ImBuf *StripImageMap::get(const Strip *strip) const +{ + ImBuf *image = this->map_.lookup_default(strip, nullptr); + if (image != nullptr) { + IMB_refImBuf(image); + } + return image; +} + +void StripImageMap::put(const Strip *strip, ImBuf *image) +{ + BLI_assert(strip != nullptr); + if (image == nullptr) { + return; + } + ImBuf *existing = this->map_.lookup_default(strip, nullptr); + if (existing != nullptr) { + IMB_freeImBuf(existing); + } + this->map_.add_overwrite(strip, image); + IMB_refImBuf(image); +} + +void StripImageMap::clear() +{ + for (const auto &item : this->map_.items()) { + IMB_freeImBuf(item.value); + } + this->map_.clear(); +} + +ImBuf *intra_frame_cache_get_preprocessed(Scene *scene, const Strip *strip) +{ + IntraFrameCache *cache = query_intra_frame_cache(scene); + if (strip == nullptr || cache == nullptr) { + return nullptr; + } + return cache->preprocessed.get(strip); +} + +ImBuf *intra_frame_cache_get_composite(Scene *scene, const Strip *strip) +{ + IntraFrameCache *cache = query_intra_frame_cache(scene); + if (strip == nullptr || cache == nullptr) { + return nullptr; + } + return cache->composite.get(strip); +} + +void intra_frame_cache_put_preprocessed(Scene *scene, const Strip *strip, ImBuf *image) +{ + if (scene == nullptr || scene->ed == nullptr || strip == nullptr || image == nullptr) { + return; + } + IntraFrameCache *&cache = scene->ed->runtime.intra_frame_cache; + if (cache == nullptr) { + cache = MEM_new(__func__); + } + cache->preprocessed.put(strip, image); +} + +void intra_frame_cache_put_composite(Scene *scene, const Strip *strip, ImBuf *image) +{ + if (scene == nullptr || scene->ed == nullptr || strip == nullptr || image == nullptr) { + return; + } + IntraFrameCache *&cache = scene->ed->runtime.intra_frame_cache; + if (cache == nullptr) { + cache = MEM_new(__func__); + } + cache->composite.put(strip, image); +} + +void intra_frame_cache_destroy(Scene *scene) +{ + IntraFrameCache *cache = query_intra_frame_cache(scene); + if (cache != nullptr) { + MEM_SAFE_DELETE(scene->ed->runtime.intra_frame_cache); + } +} + +void intra_frame_cache_set_cur_frame(Scene *scene, float frame, int view_id) +{ + IntraFrameCache *cache = query_intra_frame_cache(scene); + if (cache != nullptr) { + if (cache->timeline_frame != frame || cache->view_id != view_id) { + cache->timeline_frame = frame; + cache->view_id = view_id; + cache->preprocessed.clear(); + cache->composite.clear(); + } + } +} + +} // namespace blender::seq diff --git a/source/blender/sequencer/intern/cache/intra_frame_cache.hh b/source/blender/sequencer/intern/cache/intra_frame_cache.hh new file mode 100644 index 00000000000..62cc9add56a --- /dev/null +++ b/source/blender/sequencer/intern/cache/intra_frame_cache.hh @@ -0,0 +1,40 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup sequencer + * + * Cached intermediate images used while rendering one sequencer frame. + * - For each strip, "preprocessed" (strip source, possibly + * transformed, with modifiers applied) and "composite" (result of + * blending this strip with image underneath) images are cached. + * - Whenever going to a different frame, the cached content of previous + * frame is cleared. + * - Primary reason for having this cache at all, is when the whole frame + * is a complex stack of things, and you want to tweak settings of one + * of the involved strips. You don't want to be re-calculating all the + * strips that are "below" your tweaked strip, for better interactivity. + */ + +#pragma once + +struct ImBuf; +struct Strip; +struct Scene; + +namespace blender::seq { + +ImBuf *intra_frame_cache_get_preprocessed(Scene *scene, const Strip *strip); +ImBuf *intra_frame_cache_get_composite(Scene *scene, const Strip *strip); +void intra_frame_cache_put_preprocessed(Scene *scene, const Strip *strip, ImBuf *image); +void intra_frame_cache_put_composite(Scene *scene, const Strip *strip, ImBuf *image); + +void intra_frame_cache_destroy(Scene *scene); + +void intra_frame_cache_invalidate(Scene *scene, const Strip *strip); +void intra_frame_cache_invalidate(Scene *scene); + +void intra_frame_cache_set_cur_frame(Scene *scene, float frame, int view_id); + +} // namespace blender::seq diff --git a/source/blender/sequencer/intern/cache/source_image_cache.cc b/source/blender/sequencer/intern/cache/source_image_cache.cc new file mode 100644 index 00000000000..40d55e4d262 --- /dev/null +++ b/source/blender/sequencer/intern/cache/source_image_cache.cc @@ -0,0 +1,287 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup sequencer + */ + +#include "BLI_map.hh" +#include "BLI_mutex.hh" +#include "BLI_vector.hh" + +#include "DNA_scene_types.h" +#include "DNA_sequence_types.h" + +#include "IMB_imbuf.hh" + +#include "SEQ_relations.hh" +#include "SEQ_render.hh" +#include "SEQ_time.hh" + +#include "prefetch.hh" +#include "source_image_cache.hh" + +namespace blender::seq { + +static Mutex source_image_cache_mutex; + +struct SourceImageCache { + struct StripEntry { + /* Map key is {source media frame index (i.e. movie frame), view ID}. */ + Map, ImBuf *> frames; + }; + + Map map_; + + ~SourceImageCache() + { + clear(); + } + + void clear() + { + for (const auto &item : map_.items()) { + for (ImBuf *image : item.value.frames.values()) { + IMB_freeImBuf(image); + } + } + map_.clear(); + } + + void remove_entry(const Strip *strip) + { + StripEntry *entry = map_.lookup_ptr(strip); + if (entry == nullptr) { + return; + } + for (ImBuf *image : entry->frames.values()) { + IMB_freeImBuf(image); + } + map_.remove_contained(strip); + } +}; + +static SourceImageCache *ensure_source_image_cache(Scene *scene) +{ + SourceImageCache **cache = &scene->ed->runtime.source_image_cache; + if (*cache == nullptr) { + *cache = MEM_new(__func__); + } + return *cache; +} + +static SourceImageCache *query_source_image_cache(const Scene *scene) +{ + if (scene == nullptr || scene->ed == nullptr) { + return nullptr; + } + return scene->ed->runtime.source_image_cache; +} + +ImBuf *source_image_cache_get(const RenderData *context, const Strip *strip, float timeline_frame) +{ + if (context->skip_cache || context->is_proxy_render || strip == nullptr) { + return nullptr; + } + + Scene *scene = prefetch_get_original_scene_and_strip(context, strip); + timeline_frame = math::round(timeline_frame); + int frame_index = give_frame_index(scene, strip, timeline_frame); + if (strip->type == STRIP_TYPE_MOVIE) { + frame_index += strip->anim_startofs; + } + const int view_id = context->view_id; + + ImBuf *res = nullptr; + { + std::lock_guard lock(source_image_cache_mutex); + SourceImageCache *cache = query_source_image_cache(scene); + if (cache == nullptr) { + return nullptr; + } + + SourceImageCache::StripEntry *val = cache->map_.lookup_ptr(strip); + if (val == nullptr) { + /* Nothing in cache for this strip yet. */ + return nullptr; + } + /* Search entries for the frame we want. */ + res = val->frames.lookup_default({frame_index, view_id}, nullptr); + } + + if (res) { + IMB_refImBuf(res); + } + return res; +} + +void source_image_cache_put(const RenderData *context, + const Strip *strip, + float timeline_frame, + ImBuf *image) +{ + if (context->skip_cache || context->is_proxy_render || strip == nullptr || image == nullptr) { + return; + } + + Scene *scene = prefetch_get_original_scene_and_strip(context, strip); + timeline_frame = math::round(timeline_frame); + + int frame_index = give_frame_index(scene, strip, timeline_frame); + if (strip->type == STRIP_TYPE_MOVIE) { + frame_index += strip->anim_startofs; + } + const int view_id = context->view_id; + + IMB_refImBuf(image); + + std::lock_guard lock(source_image_cache_mutex); + SourceImageCache *cache = ensure_source_image_cache(scene); + + SourceImageCache::StripEntry *val = cache->map_.lookup_ptr(strip); + + if (val == nullptr) { + /* Nothing in cache for this strip yet. */ + cache->map_.add_new(strip, {}); + val = cache->map_.lookup_ptr(strip); + } + BLI_assert_msg(val != nullptr, "Source image cache value should never be null here"); + + ImBuf *&item = val->frames.lookup_or_add_default({frame_index, view_id}); + if (item != nullptr) { + IMB_freeImBuf(item); + } + item = image; +} + +void source_image_cache_invalidate_strip(Scene *scene, const Strip *strip) +{ + std::lock_guard lock(source_image_cache_mutex); + SourceImageCache *cache = query_source_image_cache(scene); + if (cache != nullptr) { + cache->remove_entry(strip); + } +} + +void source_image_cache_clear(Scene *scene) +{ + std::lock_guard lock(source_image_cache_mutex); + SourceImageCache *cache = query_source_image_cache(scene); + if (cache != nullptr) { + scene->ed->runtime.source_image_cache->clear(); + } +} + +void source_image_cache_destroy(Scene *scene) +{ + std::lock_guard lock(source_image_cache_mutex); + SourceImageCache *cache = query_source_image_cache(scene); + if (cache != nullptr) { + BLI_assert(cache == scene->ed->runtime.source_image_cache); + MEM_delete(scene->ed->runtime.source_image_cache); + scene->ed->runtime.source_image_cache = nullptr; + } +} + +void source_image_cache_iterate(Scene *scene, + void *userdata, + void callback_iter(void *userdata, + const Strip *strip, + int timeline_frame)) +{ + std::lock_guard lock(source_image_cache_mutex); + SourceImageCache *cache = query_source_image_cache(scene); + if (cache == nullptr) { + return; + } + + const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base); + + for (const auto &[key, value] : cache->map_.items()) { + for (std::pair frame_view : value.frames.keys()) { + /* We have frame index of source media, try to guesstimate the timeline frame. + * Note that this will be not correct when retiming, strobing etc. are used. + * However, factor in playback rate difference. */ + float frame_fl = frame_view.first / time_media_playback_rate_factor_get(key, scene_fps); + float timeline_frame = frame_fl + time_start_frame_get(key); + + callback_iter(userdata, key, int(timeline_frame)); + } + } +} + +size_t source_image_cache_calc_memory_size(const Scene *scene) +{ + std::lock_guard lock(source_image_cache_mutex); + SourceImageCache *cache = query_source_image_cache(scene); + if (cache == nullptr) { + return 0; + } + size_t size = 0; + for (const SourceImageCache::StripEntry &entry : cache->map_.values()) { + for (const ImBuf *image : entry.frames.values()) { + size += IMB_get_size_in_memory(image); + } + } + return size; +} + +size_t source_image_cache_get_image_count(const Scene *scene) +{ + std::lock_guard lock(source_image_cache_mutex); + SourceImageCache *cache = query_source_image_cache(scene); + if (cache == nullptr) { + return 0; + } + size_t count = 0; + for (const SourceImageCache::StripEntry &entry : cache->map_.values()) { + count += entry.frames.size(); + } + return count; +} + +bool source_image_cache_evict(Scene *scene) +{ + std::lock_guard lock(source_image_cache_mutex); + SourceImageCache *cache = query_source_image_cache(scene); + if (cache == nullptr) { + return false; + } + + /* Find which entry to remove -- we pick the one that is furthest from the current frame, + * biasing the ones that are behind the current frame. */ + const int cur_frame = scene->r.cfra; + SourceImageCache::StripEntry *best_strip = nullptr; + std::pair best_key = {}; + int best_score = 0; + for (const auto &strip : cache->map_.items()) { + for (const auto &entry : strip.value.frames.items()) { + const int item_frame = entry.key.first; + /* Score for removal is distance to current frame; 2x that if behind current frame. */ + int score = 0; + if (item_frame < cur_frame) { + score = (cur_frame - item_frame) * 2; + } + else if (item_frame > cur_frame) { + score = item_frame - cur_frame; + } + if (score > best_score) { + best_strip = &strip.value; + best_key = entry.key; + best_score = score; + } + } + } + + /* Remove if we found one. */ + if (best_strip != nullptr) { + IMB_freeImBuf(best_strip->frames.lookup(best_key)); + best_strip->frames.remove(best_key); + return true; + } + + return false; +} + +} // namespace blender::seq diff --git a/source/blender/sequencer/intern/cache/source_image_cache.hh b/source/blender/sequencer/intern/cache/source_image_cache.hh new file mode 100644 index 00000000000..6a2318b1e73 --- /dev/null +++ b/source/blender/sequencer/intern/cache/source_image_cache.hh @@ -0,0 +1,45 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup sequencer + * + * Cache source images for strips. + * - Keyed by (strip + frame index within strip media + view ID). + * - Caching is only done for strips that are independent of + * any other strips (images, movies, no-input effect strips like + * Text and Color). + * - When full, cache eviction policy is to remove frames furthest + * from the current playhead, biasing towards removal of + * frames behind the playhead. + * - Invalidated fairly rarely, since the cached items only change + * when the source content changes. + */ + +#pragma once + +struct ImBuf; +struct Strip; +struct Scene; +struct RenderData; + +namespace blender::seq { + +void source_image_cache_put(const RenderData *context, + const Strip *strip, + float timeline_frame, + ImBuf *image); + +ImBuf *source_image_cache_get(const RenderData *context, const Strip *strip, float timeline_frame); + +void source_image_cache_invalidate_strip(Scene *scene, const Strip *strip); + +void source_image_cache_clear(Scene *scene); +void source_image_cache_destroy(Scene *scene); + +bool source_image_cache_evict(Scene *scene); + +size_t source_image_cache_get_image_count(const Scene *scene); + +} // namespace blender::seq diff --git a/source/blender/sequencer/intern/thumbnail_cache.cc b/source/blender/sequencer/intern/cache/thumbnail_cache.cc similarity index 100% rename from source/blender/sequencer/intern/thumbnail_cache.cc rename to source/blender/sequencer/intern/cache/thumbnail_cache.cc diff --git a/source/blender/sequencer/intern/disk_cache.cc b/source/blender/sequencer/intern/disk_cache.cc deleted file mode 100644 index aad0802df9f..00000000000 --- a/source/blender/sequencer/intern/disk_cache.cc +++ /dev/null @@ -1,687 +0,0 @@ -/* SPDX-FileCopyrightText: 2021 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup sequencer - */ - -#include -#include -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_scene_types.h" -#include "DNA_sequence_types.h" -#include "DNA_userdef_types.h" - -#include "IMB_colormanagement.hh" -#include "IMB_imbuf.hh" -#include "IMB_imbuf_types.hh" - -#include "BLI_endian_defines.h" -#include "BLI_endian_switch.h" -#include "BLI_fileops.h" -#include "BLI_fileops_types.h" -#include "BLI_listbase.h" -#include "BLI_path_utils.hh" -#include "BLI_string.h" -#include "BLI_threads.h" - -#include "BKE_main.hh" - -#include "SEQ_render.hh" -#include "SEQ_time.hh" - -#include "disk_cache.hh" -#include "image_cache.hh" - -/** - * Disk Cache Design Notes - * ======================= - * - * Disk cache uses directory specified in user preferences - * For each cached non-temp image, image data and supplementary info are written to HDD. - * Multiple(DCACHE_IMAGES_PER_FILE) images share the same file. - * Each of these files contains header DiskCacheHeader followed by image data. - * ZLIB compression with user definable level can be used to compress image data(per image) - * Images are written in order in which they are rendered. - * Overwriting of individual entry is not possible. - * Stored images are deleted by invalidation, or when size of all files exceeds maximum - * size specified in user preferences. - * To distinguish 2 blend files with same name, scene->ed->disk_cache_timestamp - * is used as UID. Blend file can still be copied manually which may cause conflict. - */ - -namespace blender::seq { - -/* Format string: - * `-x-%()-.dcf`. */ -#define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf" -#define DCACHE_IMAGES_PER_FILE 100 -#define DCACHE_CURRENT_VERSION 2 -#define COLORSPACE_NAME_MAX 64 /* XXX: defined in IMB intern. */ - -struct DiskCacheHeaderEntry { - uchar encoding; - uint64_t frameno; - uint64_t size_compressed; - uint64_t size_raw; - uint64_t offset; - char colorspace_name[COLORSPACE_NAME_MAX]; -}; - -struct DiskCacheHeader { - DiskCacheHeaderEntry entry[DCACHE_IMAGES_PER_FILE]; -}; - -struct SeqDiskCache { - Main *bmain; - int64_t timestamp; - ListBase files; - ThreadMutex read_write_mutex; - size_t size_total; -}; - -struct DiskCacheFile { - DiskCacheFile *next, *prev; - char filepath[FILE_MAX]; - char dir[FILE_MAXDIR]; - char file[FILE_MAX]; - BLI_stat_t fstat; - int cache_type; - int rectx; - int recty; - int render_size; - int view_id; - int start_frame; -}; - -static ThreadMutex cache_create_lock = BLI_MUTEX_INITIALIZER; - -static const char *seq_disk_cache_base_dir() -{ - return U.sequencer_disk_cache_dir; -} - -static int seq_disk_cache_compression_level() -{ - switch (U.sequencer_disk_cache_compression) { - case USER_SEQ_DISK_CACHE_COMPRESSION_NONE: - return 0; - case USER_SEQ_DISK_CACHE_COMPRESSION_LOW: - return 1; - case USER_SEQ_DISK_CACHE_COMPRESSION_HIGH: - return 9; - } - - return U.sequencer_disk_cache_compression; -} - -static size_t seq_disk_cache_size_limit() -{ - return size_t(U.sequencer_disk_cache_size_limit) * (1024 * 1024 * 1024); -} - -bool seq_disk_cache_is_enabled(Main *bmain) -{ - return (U.sequencer_disk_cache_dir[0] != '\0' && U.sequencer_disk_cache_size_limit != 0 && - (U.sequencer_disk_cache_flag & SEQ_CACHE_DISK_CACHE_ENABLE) != 0 && - bmain->filepath[0] != '\0'); -} - -static DiskCacheFile *seq_disk_cache_add_file_to_list(SeqDiskCache *disk_cache, - const char *filepath) -{ - - DiskCacheFile *cache_file = MEM_callocN("SeqDiskCacheFile"); - char dir[FILE_MAXDIR], file[FILE_MAX]; - BLI_path_split_dir_file(filepath, dir, sizeof(dir), file, sizeof(file)); - STRNCPY(cache_file->filepath, filepath); - STRNCPY(cache_file->dir, dir); - STRNCPY(cache_file->file, file); - sscanf(file, - DCACHE_FNAME_FORMAT, - &cache_file->cache_type, - &cache_file->rectx, - &cache_file->recty, - &cache_file->render_size, - &cache_file->view_id, - &cache_file->start_frame); - cache_file->start_frame *= DCACHE_IMAGES_PER_FILE; - BLI_addtail(&disk_cache->files, cache_file); - return cache_file; -} - -static void seq_disk_cache_get_files(SeqDiskCache *disk_cache, const char *dirpath) -{ - direntry *filelist, *fl; - uint i; - disk_cache->size_total = 0; - - const int filelist_num = BLI_filelist_dir_contents(dirpath, &filelist); - i = filelist_num; - fl = filelist; - while (i--) { - /* Don't follow links. */ - const eFileAttributes file_attrs = BLI_file_attributes(fl->path); - if (file_attrs & FILE_ATTR_ANY_LINK) { - fl++; - continue; - } - - char file[FILE_MAX]; - BLI_path_split_file_part(fl->path, file, sizeof(file)); - - bool is_dir = BLI_is_dir(fl->path); - if (is_dir && !FILENAME_IS_CURRPAR(file)) { - char subpath[FILE_MAX]; - STRNCPY(subpath, fl->path); - BLI_path_slash_ensure(subpath, sizeof(subpath)); - seq_disk_cache_get_files(disk_cache, subpath); - } - - if (!is_dir) { - const char *ext = BLI_path_extension(fl->path); - if (ext && ext[1] == 'd' && ext[2] == 'c' && ext[3] == 'f') { - DiskCacheFile *cache_file = seq_disk_cache_add_file_to_list(disk_cache, fl->path); - cache_file->fstat = fl->s; - disk_cache->size_total += cache_file->fstat.st_size; - } - } - fl++; - } - BLI_filelist_free(filelist, filelist_num); -} - -static DiskCacheFile *seq_disk_cache_get_oldest_file(SeqDiskCache *disk_cache) -{ - DiskCacheFile *oldest_file = static_cast(disk_cache->files.first); - if (oldest_file == nullptr) { - return nullptr; - } - for (DiskCacheFile *cache_file = oldest_file->next; cache_file; cache_file = cache_file->next) { - if (cache_file->fstat.st_mtime < oldest_file->fstat.st_mtime) { - oldest_file = cache_file; - } - } - - return oldest_file; -} - -static void seq_disk_cache_delete_file(SeqDiskCache *disk_cache, DiskCacheFile *file) -{ - disk_cache->size_total -= file->fstat.st_size; - BLI_delete(file->filepath, false, false); - BLI_remlink(&disk_cache->files, file); - MEM_freeN(file); -} - -bool seq_disk_cache_enforce_limits(SeqDiskCache *disk_cache) -{ - BLI_mutex_lock(&disk_cache->read_write_mutex); - while (disk_cache->size_total > seq_disk_cache_size_limit()) { - DiskCacheFile *oldest_file = seq_disk_cache_get_oldest_file(disk_cache); - - if (!oldest_file) { - /* We shouldn't enforce limits with no files, do re-scan. */ - seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); - continue; - } - - if (BLI_exists(oldest_file->filepath) == 0) { - /* File may have been manually deleted during runtime, do re-scan. */ - BLI_freelistN(&disk_cache->files); - seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); - continue; - } - - seq_disk_cache_delete_file(disk_cache, oldest_file); - } - BLI_mutex_unlock(&disk_cache->read_write_mutex); - - return true; -} - -static DiskCacheFile *seq_disk_cache_get_file_entry_by_path(SeqDiskCache *disk_cache, - const char *filepath) -{ - DiskCacheFile *cache_file = static_cast(disk_cache->files.first); - - for (; cache_file; cache_file = cache_file->next) { - if (BLI_strcasecmp(cache_file->filepath, filepath) == 0) { - return cache_file; - } - } - - return nullptr; -} - -/* Update file size and timestamp. */ -static void seq_disk_cache_update_file(SeqDiskCache *disk_cache, const char *filepath) -{ - DiskCacheFile *cache_file; - int64_t size_before; - int64_t size_after; - - cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, filepath); - size_before = cache_file->fstat.st_size; - - if (BLI_stat(filepath, &cache_file->fstat) == -1) { - BLI_assert(false); - memset(&cache_file->fstat, 0, sizeof(BLI_stat_t)); - } - - size_after = cache_file->fstat.st_size; - disk_cache->size_total += size_after - size_before; -} - -/* Path format: - * /_seq_cache/-//DCACHE_FNAME_FORMAT - */ - -static void seq_disk_cache_get_project_dir(SeqDiskCache *disk_cache, - char *dirpath, - size_t dirpath_maxncpy) -{ - char cache_dir[FILE_MAX]; - const char *blendfile_path = BKE_main_blendfile_path(disk_cache->bmain); - /* Use suffix, so that the cache directory name does not conflict with the bmain's blend file. */ - SNPRINTF(cache_dir, "%s_seq_cache", BLI_path_basename(blendfile_path)); - BLI_path_join(dirpath, dirpath_maxncpy, seq_disk_cache_base_dir(), cache_dir); -} - -static void seq_disk_cache_get_dir( - SeqDiskCache *disk_cache, Scene *scene, Strip *strip, char *dirpath, size_t dirpath_maxncpy) -{ - char scene_name[MAX_ID_NAME + 22]; /* + -%PRId64 */ - char strip_name[STRIP_NAME_MAXSTR]; - char project_dir[FILE_MAX]; - - seq_disk_cache_get_project_dir(disk_cache, project_dir, sizeof(project_dir)); - SNPRINTF(scene_name, "%s-%" PRId64, scene->id.name, disk_cache->timestamp); - STRNCPY(strip_name, strip->name); - BLI_path_make_safe_filename(scene_name); - BLI_path_make_safe_filename(strip_name); - - BLI_path_join(dirpath, dirpath_maxncpy, project_dir, scene_name, strip_name); -} - -static void seq_disk_cache_get_file_path(SeqDiskCache *disk_cache, - SeqCacheKey *key, - char *filepath, - size_t filepath_maxncpy) -{ - seq_disk_cache_get_dir(disk_cache, key->context.scene, key->strip, filepath, filepath_maxncpy); - int frameno = int(key->frame_index) / DCACHE_IMAGES_PER_FILE; - char cache_filename[FILE_MAXFILE]; - SNPRINTF(cache_filename, - DCACHE_FNAME_FORMAT, - key->type, - key->context.rectx, - key->context.recty, - key->context.preview_render_size, - key->context.view_id, - frameno); - - BLI_path_append(filepath, filepath_maxncpy, cache_filename); -} - -static void seq_disk_cache_create_version_file(const char *filepath) -{ - BLI_file_ensure_parent_dir_exists(filepath); - - FILE *file = BLI_fopen(filepath, "w"); - if (file) { - fprintf(file, "%d", DCACHE_CURRENT_VERSION); - fclose(file); - } -} - -static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache) -{ - char dirpath[FILE_MAX]; - char path_version_file[FILE_MAX]; - int version = 0; - - seq_disk_cache_get_project_dir(disk_cache, dirpath, sizeof(dirpath)); - BLI_path_join(path_version_file, sizeof(path_version_file), dirpath, "cache_version"); - - if (BLI_exists(dirpath) && BLI_is_dir(dirpath)) { - FILE *file = BLI_fopen(path_version_file, "r"); - - if (file) { - const int num_items_read = fscanf(file, "%d", &version); - if (num_items_read == 0) { - version = -1; - } - fclose(file); - } - - if (version != DCACHE_CURRENT_VERSION) { - BLI_delete(dirpath, true, true); - seq_disk_cache_create_version_file(path_version_file); - } - } - else { - seq_disk_cache_create_version_file(path_version_file); - } -} - -static void seq_disk_cache_delete_invalid_files(SeqDiskCache *disk_cache, - Scene *scene, - Strip *strip, - int invalidate_types, - int range_start, - int range_end) -{ - DiskCacheFile *next_file, *cache_file = static_cast(disk_cache->files.first); - char cache_dir[FILE_MAX]; - seq_disk_cache_get_dir(disk_cache, scene, strip, cache_dir, sizeof(cache_dir)); - BLI_path_slash_ensure(cache_dir, sizeof(cache_dir)); - - while (cache_file) { - next_file = cache_file->next; - if (cache_file->cache_type & invalidate_types) { - if (STREQ(cache_dir, cache_file->dir)) { - const int timeline_frame_start = cache_file->start_frame + time_start_frame_get(strip); - if (timeline_frame_start > range_start && timeline_frame_start <= range_end) { - seq_disk_cache_delete_file(disk_cache, cache_file); - } - } - } - cache_file = next_file; - } -} - -void seq_disk_cache_invalidate(SeqDiskCache *disk_cache, - Scene *scene, - Strip *strip, - Strip *strip_changed, - int invalidate_types) -{ - int start; - int end; - - BLI_mutex_lock(&disk_cache->read_write_mutex); - - start = time_left_handle_frame_get(scene, strip_changed) - DCACHE_IMAGES_PER_FILE; - end = time_right_handle_frame_get(scene, strip_changed); - - seq_disk_cache_delete_invalid_files(disk_cache, scene, strip, invalidate_types, start, end); - - BLI_mutex_unlock(&disk_cache->read_write_mutex); -} - -static size_t deflate_imbuf_to_file(ImBuf *ibuf, - FILE *file, - int level, - DiskCacheHeaderEntry *header_entry) -{ - void *data = (ibuf->byte_buffer.data != nullptr) ? (void *)ibuf->byte_buffer.data : - (void *)ibuf->float_buffer.data; - - /* Apply compression if wanted, otherwise just write directly to the file. */ - if (level > 0) { - return BLI_file_zstd_from_mem_at_pos( - data, header_entry->size_raw, file, header_entry->offset, level); - } - - fseek(file, header_entry->offset, SEEK_SET); - return fwrite(data, 1, header_entry->size_raw, file); -} - -static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntry *header_entry) -{ - void *data = (ibuf->byte_buffer.data != nullptr) ? (void *)ibuf->byte_buffer.data : - (void *)ibuf->float_buffer.data; - char header[4]; - fseek(file, header_entry->offset, SEEK_SET); - if (fread(header, 1, sizeof(header), file) != sizeof(header)) { - return 0; - } - - /* Check if the data is compressed or raw. */ - if (BLI_file_magic_is_zstd(header)) { - return BLI_file_unzstd_to_mem_at_pos(data, header_entry->size_raw, file, header_entry->offset); - } - - fseek(file, header_entry->offset, SEEK_SET); - return fread(data, 1, header_entry->size_raw, file); -} - -static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) -{ - BLI_fseek(file, 0LL, SEEK_SET); - const size_t num_items_read = fread(header, sizeof(*header), 1, file); - if (num_items_read < 1) { - BLI_assert_msg(0, "unable to read disk cache header"); - perror("unable to read disk cache header"); - return false; - } - - for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { - if ((ENDIAN_ORDER == B_ENDIAN) && header->entry[i].encoding == 0) { - BLI_endian_switch_uint64(&header->entry[i].frameno); - BLI_endian_switch_uint64(&header->entry[i].offset); - BLI_endian_switch_uint64(&header->entry[i].size_compressed); - BLI_endian_switch_uint64(&header->entry[i].size_raw); - } - } - - return true; -} - -static size_t seq_disk_cache_write_header(FILE *file, const DiskCacheHeader *header) -{ - BLI_fseek(file, 0LL, SEEK_SET); - return fwrite(header, sizeof(*header), 1, file); -} - -static int seq_disk_cache_add_header_entry(const SeqCacheKey *key, - ImBuf *ibuf, - DiskCacheHeader *header) -{ - int i; - uint64_t offset = sizeof(*header); - - /* Lookup free entry, get offset for new data. */ - for (i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { - if (header->entry[i].size_compressed == 0) { - break; - } - } - - /* Attempt to write beyond set entry limit. - * Reset file header and start writing from beginning. - */ - if (i == DCACHE_IMAGES_PER_FILE) { - i = 0; - memset(header, 0, sizeof(*header)); - } - - /* Calculate offset for image data. */ - if (i > 0) { - offset = header->entry[i - 1].offset + header->entry[i - 1].size_compressed; - } - - if (ENDIAN_ORDER == B_ENDIAN) { - header->entry[i].encoding = 255; - } - else { - header->entry[i].encoding = 0; - } - - header->entry[i].offset = offset; - header->entry[i].frameno = key->frame_index; - - /* Store colorspace name of ibuf. */ - const char *colorspace_name; - if (ibuf->byte_buffer.data) { - header->entry[i].size_raw = int64_t(ibuf->x) * ibuf->y * ibuf->channels; - colorspace_name = IMB_colormanagement_get_rect_colorspace(ibuf); - } - else { - header->entry[i].size_raw = int64_t(ibuf->x) * ibuf->y * ibuf->channels * 4; - colorspace_name = IMB_colormanagement_get_float_colorspace(ibuf); - } - STRNCPY(header->entry[i].colorspace_name, colorspace_name); - - return i; -} - -static int seq_disk_cache_get_header_entry(const SeqCacheKey *key, const DiskCacheHeader *header) -{ - for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { - if (header->entry[i].frameno == key->frame_index) { - return i; - } - } - - return -1; -} - -bool seq_disk_cache_write_file(SeqDiskCache *disk_cache, SeqCacheKey *key, ImBuf *ibuf) -{ - BLI_mutex_lock(&disk_cache->read_write_mutex); - - char filepath[FILE_MAX]; - - seq_disk_cache_get_file_path(disk_cache, key, filepath, sizeof(filepath)); - BLI_file_ensure_parent_dir_exists(filepath); - - /* Touch the file. */ - FILE *file = BLI_fopen(filepath, "rb+"); - if (!file) { - file = BLI_fopen(filepath, "wb+"); - if (!file) { - BLI_mutex_unlock(&disk_cache->read_write_mutex); - return false; - } - seq_disk_cache_add_file_to_list(disk_cache, filepath); - } - - DiskCacheFile *cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, filepath); - DiskCacheHeader header; - memset(&header, 0, sizeof(header)); - /* The file may be empty when touched (above). - * This is fine, don't attempt reading the header in that case. */ - if (cache_file->fstat.st_size != 0 && !seq_disk_cache_read_header(file, &header)) { - fclose(file); - seq_disk_cache_delete_file(disk_cache, cache_file); - BLI_mutex_unlock(&disk_cache->read_write_mutex); - return false; - } - int entry_index = seq_disk_cache_add_header_entry(key, ibuf, &header); - - size_t bytes_written = deflate_imbuf_to_file( - ibuf, file, seq_disk_cache_compression_level(), &header.entry[entry_index]); - - if (bytes_written != 0) { - /* Last step is writing header, as image data can be overwritten, - * but missing data would cause problems. - */ - header.entry[entry_index].size_compressed = bytes_written; - seq_disk_cache_write_header(file, &header); - seq_disk_cache_update_file(disk_cache, filepath); - fclose(file); - - BLI_mutex_unlock(&disk_cache->read_write_mutex); - return true; - } - - BLI_mutex_unlock(&disk_cache->read_write_mutex); - return false; -} - -ImBuf *seq_disk_cache_read_file(SeqDiskCache *disk_cache, SeqCacheKey *key) -{ - BLI_mutex_lock(&disk_cache->read_write_mutex); - - char filepath[FILE_MAX]; - DiskCacheHeader header; - - seq_disk_cache_get_file_path(disk_cache, key, filepath, sizeof(filepath)); - BLI_file_ensure_parent_dir_exists(filepath); - - FILE *file = BLI_fopen(filepath, "rb"); - if (!file) { - BLI_mutex_unlock(&disk_cache->read_write_mutex); - return nullptr; - } - - if (!seq_disk_cache_read_header(file, &header)) { - fclose(file); - BLI_mutex_unlock(&disk_cache->read_write_mutex); - return nullptr; - } - int entry_index = seq_disk_cache_get_header_entry(key, &header); - - /* Item not found. */ - if (entry_index < 0) { - fclose(file); - BLI_mutex_unlock(&disk_cache->read_write_mutex); - return nullptr; - } - - ImBuf *ibuf; - uint64_t size_char = uint64_t(key->context.rectx) * key->context.recty * 4; - uint64_t size_float = uint64_t(key->context.rectx) * key->context.recty * 16; - size_t expected_size; - - if (header.entry[entry_index].size_raw == size_char) { - expected_size = size_char; - ibuf = IMB_allocImBuf( - key->context.rectx, key->context.recty, 32, IB_byte_data | IB_uninitialized_pixels); - IMB_colormanagement_assign_byte_colorspace(ibuf, header.entry[entry_index].colorspace_name); - } - else if (header.entry[entry_index].size_raw == size_float) { - expected_size = size_float; - ibuf = IMB_allocImBuf( - key->context.rectx, key->context.recty, 32, IB_float_data | IB_uninitialized_pixels); - IMB_colormanagement_assign_float_colorspace(ibuf, header.entry[entry_index].colorspace_name); - } - else { - fclose(file); - BLI_mutex_unlock(&disk_cache->read_write_mutex); - return nullptr; - } - - size_t bytes_read = inflate_file_to_imbuf(ibuf, file, &header.entry[entry_index]); - - /* Sanity check. */ - if (bytes_read != expected_size) { - fclose(file); - IMB_freeImBuf(ibuf); - BLI_mutex_unlock(&disk_cache->read_write_mutex); - return nullptr; - } - BLI_file_touch(filepath); - seq_disk_cache_update_file(disk_cache, filepath); - fclose(file); - - BLI_mutex_unlock(&disk_cache->read_write_mutex); - return ibuf; -} - -SeqDiskCache *seq_disk_cache_create(Main *bmain, Scene *scene) -{ - SeqDiskCache *disk_cache = MEM_callocN("SeqDiskCache"); - disk_cache->bmain = bmain; - BLI_mutex_init(&disk_cache->read_write_mutex); - seq_disk_cache_handle_versioning(disk_cache); - seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); - disk_cache->timestamp = scene->ed->disk_cache_timestamp; - BLI_mutex_unlock(&cache_create_lock); - return disk_cache; -} - -void seq_disk_cache_free(SeqDiskCache *disk_cache) -{ - BLI_freelistN(&disk_cache->files); - BLI_mutex_end(&disk_cache->read_write_mutex); - MEM_freeN(disk_cache); -} - -} // namespace blender::seq diff --git a/source/blender/sequencer/intern/disk_cache.hh b/source/blender/sequencer/intern/disk_cache.hh deleted file mode 100644 index 17239e1a52b..00000000000 --- a/source/blender/sequencer/intern/disk_cache.hh +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-FileCopyrightText: 2021 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup sequencer - */ - -#pragma once - -/** \file - * \ingroup sequencer - */ - -struct ImBuf; -struct Main; -struct Scene; -struct Strip; - -namespace blender::seq { -struct SeqCacheKey; -struct SeqDiskCache; - -SeqDiskCache *seq_disk_cache_create(Main *bmain, Scene *scene); -void seq_disk_cache_free(SeqDiskCache *disk_cache); -bool seq_disk_cache_is_enabled(Main *bmain); -ImBuf *seq_disk_cache_read_file(SeqDiskCache *disk_cache, SeqCacheKey *key); -bool seq_disk_cache_write_file(SeqDiskCache *disk_cache, SeqCacheKey *key, ImBuf *ibuf); -bool seq_disk_cache_enforce_limits(SeqDiskCache *disk_cache); -void seq_disk_cache_invalidate(SeqDiskCache *disk_cache, - Scene *scene, - Strip *strip, - Strip *strip_changed, - int invalidate_types); - -} // namespace blender::seq diff --git a/source/blender/sequencer/intern/image_cache.cc b/source/blender/sequencer/intern/image_cache.cc deleted file mode 100644 index 98bf4d3687a..00000000000 --- a/source/blender/sequencer/intern/image_cache.cc +++ /dev/null @@ -1,879 +0,0 @@ -/* SPDX-FileCopyrightText: 2010 Peter Schlaile . - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup bke - */ - -#include -#include -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_scene_types.h" -#include "DNA_sequence_types.h" -#include "DNA_userdef_types.h" - -#include "IMB_imbuf.hh" -#include "IMB_imbuf_types.hh" - -#include "BLI_ghash.h" -#include "BLI_math_base.h" -#include "BLI_mempool.h" -#include "BLI_mutex.hh" -#include "BLI_threads.h" - -#include "BKE_main.hh" - -#include "SEQ_prefetch.hh" -#include "SEQ_relations.hh" -#include "SEQ_render.hh" -#include "SEQ_time.hh" - -#include "disk_cache.hh" -#include "image_cache.hh" -#include "prefetch.hh" - -/** - * Sequencer Cache Design Notes - * ============================ - * - * Function: - * All images created during rendering are added to cache, even if the cache is already full. - * This is because: - * - One image may be needed multiple times during rendering. - * - Keeping the last rendered frame allows us for faster re-render when user edits strip in stack. - * - We can decide if we keep frame only when it's completely rendered. Otherwise we risk having - * "holes" in the cache, which can be annoying. - * - * If the cache is full all entries for pending frame will have is_temp_cache set. - * - * Linking: We use links to reduce number of iterations over entries needed to manage cache. - * Entries are linked in order as they are put into cache. - * Only permanent (is_temp_cache = 0) cache entries are linked. - * Putting #SEQ_CACHE_STORE_FINAL_OUT will reset linking - * - * Only entire frame can be freed to release resources for new entries (recycling). - * Once again, this is to reduce number of iterations, but also more controllable than removing - * entries one by one in reverse order to their creation. - * - * User can exclude caching of some images. Such entries will have is_temp_cache set. - */ - -namespace blender::seq { - -struct SeqCache { - Main *bmain; - GHash *hash; - ThreadMutex iterator_mutex; - BLI_mempool *keys_pool; - BLI_mempool *items_pool; - SeqCacheKey *last_key; - SeqDiskCache *disk_cache; -}; - -struct SeqCacheItem { - SeqCache *cache_owner; - ImBuf *ibuf; -}; - -static blender::Mutex cache_create_lock; - -static bool seq_cmp_render_data(const RenderData *a, const RenderData *b) -{ - return ((a->preview_render_size != b->preview_render_size) || (a->rectx != b->rectx) || - (a->recty != b->recty) || (a->bmain != b->bmain) || (a->scene != b->scene) || - (a->motion_blur_shutter != b->motion_blur_shutter) || - (a->motion_blur_samples != b->motion_blur_samples) || - (a->scene->r.views_format != b->scene->r.views_format) || (a->view_id != b->view_id)); -} - -static uint seq_hash_render_data(const RenderData *a) -{ - uint rval = a->rectx + a->recty; - - rval ^= a->preview_render_size; - rval ^= intptr_t(a->bmain) << 6; - rval ^= intptr_t(a->scene) << 6; - rval ^= int(a->motion_blur_shutter * 100.0f) << 10; - rval ^= a->motion_blur_samples << 16; - rval ^= ((a->scene->r.views_format * 2) + a->view_id) << 24; - - return rval; -} - -static uint seq_cache_hashhash(const void *key_) -{ - const SeqCacheKey *key = static_cast(key_); - uint rval = seq_hash_render_data(&key->context); - - rval ^= *(const uint *)&key->frame_index; - rval += key->type; - rval ^= intptr_t(key->strip) << 6; - - return rval; -} - -static bool seq_cache_hashcmp(const void *a_, const void *b_) -{ - const SeqCacheKey *a = static_cast(a_); - const SeqCacheKey *b = static_cast(b_); - - return ((a->strip != b->strip) || (a->frame_index != b->frame_index) || (a->type != b->type) || - seq_cmp_render_data(&a->context, &b->context)); -} - -static float seq_cache_timeline_frame_to_frame_index(const Scene *scene, - const Strip *strip, - const float timeline_frame, - const int type) -{ - /* With raw images, map timeline_frame to strip input media frame range. This means that static - * images or extended frame range of movies will only generate one cache entry. No special - * treatment in converting frame index to timeline_frame is needed. */ - bool is_effect = strip->type & STRIP_TYPE_EFFECT; - if (!is_effect && type == SEQ_CACHE_STORE_RAW) { - return give_frame_index(scene, strip, timeline_frame); - } - - return timeline_frame - time_start_frame_get(strip); -} - -static int seq_cache_key_timeline_frame_get(const SeqCacheKey *key) -{ - return key->frame_index + time_start_frame_get(key->strip); -} - -static SeqCache *seq_cache_get_from_scene(Scene *scene) -{ - if (scene && scene->ed && scene->ed->cache) { - return scene->ed->cache; - } - - return nullptr; -} - -static void seq_cache_lock(Scene *scene) -{ - SeqCache *cache = seq_cache_get_from_scene(scene); - - if (cache) { - BLI_mutex_lock(&cache->iterator_mutex); - } -} - -static void seq_cache_unlock(Scene *scene) -{ - SeqCache *cache = seq_cache_get_from_scene(scene); - - if (cache) { - BLI_mutex_unlock(&cache->iterator_mutex); - } -} - -static size_t seq_cache_get_mem_total() -{ - return size_t(U.memcachelimit) * 1024 * 1024; -} - -static void seq_cache_keyfree(void *val) -{ - SeqCacheKey *key = static_cast(val); - BLI_mempool_free(key->cache_owner->keys_pool, key); -} - -static void seq_cache_valfree(void *val) -{ - SeqCacheItem *item = (SeqCacheItem *)val; - - if (item->ibuf) { - IMB_freeImBuf(item->ibuf); - } - - BLI_mempool_free(item->cache_owner->items_pool, item); -} - -static int get_stored_types_flag(Scene *scene, SeqCacheKey *key) -{ - int flag; - if (key->strip->cache_flag & SEQ_CACHE_OVERRIDE) { - flag = key->strip->cache_flag; - } - else { - flag = scene->ed->cache_flag; - } - - /* SEQ_CACHE_STORE_FINAL_OUT can not be overridden by strip cache */ - flag |= (scene->ed->cache_flag & SEQ_CACHE_STORE_FINAL_OUT); - - return flag; -} - -static void seq_cache_put_ex(Scene *scene, SeqCacheKey *key, ImBuf *ibuf) -{ - SeqCache *cache = seq_cache_get_from_scene(scene); - SeqCacheItem *item; - item = static_cast(BLI_mempool_alloc(cache->items_pool)); - item->cache_owner = cache; - item->ibuf = ibuf; - - const int stored_types_flag = get_stored_types_flag(scene, key); - - /* Item stored for later use. */ - if (stored_types_flag & key->type) { - key->is_temp_cache = false; - key->link_prev = cache->last_key; - } - - BLI_assert(!BLI_ghash_haskey(cache->hash, key)); - BLI_ghash_insert(cache->hash, key, item); - IMB_refImBuf(ibuf); - - /* Store pointer to last cached key. */ - SeqCacheKey *temp_last_key = cache->last_key; - cache->last_key = key; - - /* Set last_key's reference to this key so we can look up chain backwards. - * Item is already put in cache, so cache->last_key points to current key. - */ - if (!key->is_temp_cache && temp_last_key) { - temp_last_key->link_next = cache->last_key; - } - - /* Reset linking. */ - if (key->type == SEQ_CACHE_STORE_FINAL_OUT) { - cache->last_key = nullptr; - } -} - -static ImBuf *seq_cache_get_ex(SeqCache *cache, SeqCacheKey *key) -{ - SeqCacheItem *item = static_cast(BLI_ghash_lookup(cache->hash, key)); - - if (item && item->ibuf) { - IMB_refImBuf(item->ibuf); - - return item->ibuf; - } - - return nullptr; -} - -static void seq_cache_key_unlink(SeqCacheKey *key) -{ - if (key->link_next) { - BLI_assert(key == key->link_next->link_prev); - key->link_next->link_prev = key->link_prev; - } - if (key->link_prev) { - BLI_assert(key == key->link_prev->link_next); - key->link_prev->link_next = key->link_next; - } -} - -/* Choose a key out of 2 candidates(leftmost and rightmost items) - * to recycle based on currently used strategy */ -static SeqCacheKey *seq_cache_choose_key(Scene *scene, SeqCacheKey *lkey, SeqCacheKey *rkey) -{ - SeqCacheKey *finalkey = nullptr; - const int lkey_tml_frame = seq_cache_key_timeline_frame_get(lkey); - const int rkey_tml_frame = seq_cache_key_timeline_frame_get(rkey); - - /* Ideally, cache would not need to check the state of prefetching task - * that is tricky to do however, because prefetch would need to know, - * if a key, that is about to be created would be removed by itself. - * - * This can happen because only FINAL_OUT item insertion will trigger recycling - * but that is also the point, where prefetch can be suspended. - * - * We could use temp cache as a shield and later make it a non-temporary entry, - * but it is not worth of increasing system complexity. - */ - if (scene->ed->cache_flag & SEQ_CACHE_PREFETCH_ENABLE && seq_prefetch_job_is_running(scene)) { - int pfjob_start, pfjob_end; - seq_prefetch_get_time_range(scene, &pfjob_start, &pfjob_end); - - if (lkey) { - if (lkey_tml_frame < pfjob_start || lkey_tml_frame > pfjob_end) { - return lkey; - } - } - - if (rkey) { - if (rkey_tml_frame < pfjob_start || rkey_tml_frame > pfjob_end) { - return rkey; - } - } - - return nullptr; - } - - if (rkey && lkey) { - if (lkey_tml_frame > rkey_tml_frame) { - SeqCacheKey *swapkey = lkey; - lkey = rkey; - rkey = swapkey; - } - - int l_diff = scene->r.cfra - lkey_tml_frame; - int r_diff = rkey_tml_frame - scene->r.cfra; - - if (l_diff > r_diff) { - finalkey = lkey; - } - else { - finalkey = rkey; - } - } - else { - if (lkey) { - finalkey = lkey; - } - else { - finalkey = rkey; - } - } - return finalkey; -} - -static void seq_cache_recycle_linked(Scene *scene, SeqCacheKey *base) -{ - SeqCache *cache = seq_cache_get_from_scene(scene); - if (!cache) { - return; - } - - SeqCacheKey *next = base->link_next; - - while (base) { - if (!BLI_ghash_haskey(cache->hash, base)) { - break; /* Key has already been removed from cache. */ - } - - SeqCacheKey *prev = base->link_prev; - if (prev != nullptr && prev->link_next != base) { - /* Key has been removed and replaced and doesn't belong to this chain anymore. */ - base->link_prev = nullptr; - break; - } - - seq_cache_key_unlink(base); - BLI_ghash_remove(cache->hash, base, seq_cache_keyfree, seq_cache_valfree); - BLI_assert(base != cache->last_key); - base = prev; - } - - base = next; - while (base) { - if (!BLI_ghash_haskey(cache->hash, base)) { - break; /* Key has already been removed from cache. */ - } - - next = base->link_next; - if (next != nullptr && next->link_prev != base) { - /* Key has been removed and replaced and doesn't belong to this chain anymore. */ - base->link_next = nullptr; - break; - } - - seq_cache_key_unlink(base); - BLI_ghash_remove(cache->hash, base, seq_cache_keyfree, seq_cache_valfree); - BLI_assert(base != cache->last_key); - base = next; - } -} - -static SeqCacheKey *seq_cache_get_item_for_removal(Scene *scene) -{ - SeqCache *cache = seq_cache_get_from_scene(scene); - SeqCacheKey *finalkey = nullptr; - /* Leftmost key. */ - SeqCacheKey *lkey = nullptr; - /* Rightmost key. */ - SeqCacheKey *rkey = nullptr; - SeqCacheKey *key = nullptr; - - GHashIterator gh_iter; - BLI_ghashIterator_init(&gh_iter, cache->hash); - int total_count = 0; - - while (!BLI_ghashIterator_done(&gh_iter)) { - key = static_cast(BLI_ghashIterator_getKey(&gh_iter)); - SeqCacheItem *item = static_cast(BLI_ghashIterator_getValue(&gh_iter)); - BLI_ghashIterator_step(&gh_iter); - BLI_assert(key->cache_owner == cache); - - /* This shouldn't happen, but better be safe than sorry. */ - if (!item->ibuf) { - seq_cache_recycle_linked(scene, key); - /* Can not continue iterating after linked remove. */ - BLI_ghashIterator_init(&gh_iter, cache->hash); - continue; - } - - if (key->is_temp_cache || key->link_next != nullptr) { - continue; - } - - total_count++; - - if (lkey) { - if (seq_cache_key_timeline_frame_get(key) < seq_cache_key_timeline_frame_get(lkey)) { - lkey = key; - } - } - else { - lkey = key; - } - if (rkey) { - if (seq_cache_key_timeline_frame_get(key) > seq_cache_key_timeline_frame_get(rkey)) { - rkey = key; - } - } - else { - rkey = key; - } - } - (void)total_count; /* Quiet set-but-unused warning (may be removed). */ - - finalkey = seq_cache_choose_key(scene, lkey, rkey); - - return finalkey; -} - -bool seq_cache_recycle_item(Scene *scene) -{ - SeqCache *cache = seq_cache_get_from_scene(scene); - if (!cache) { - return false; - } - - seq_cache_lock(scene); - - while (seq_cache_is_full()) { - SeqCacheKey *finalkey = seq_cache_get_item_for_removal(scene); - - if (finalkey) { - seq_cache_recycle_linked(scene, finalkey); - } - else { - seq_cache_unlock(scene); - return false; - } - } - seq_cache_unlock(scene); - return true; -} - -static void seq_cache_set_temp_cache_linked(Scene *scene, SeqCacheKey *base) -{ - SeqCache *cache = seq_cache_get_from_scene(scene); - - if (!cache || !base) { - return; - } - - SeqCacheKey *next = base->link_next; - - while (base) { - SeqCacheKey *prev = base->link_prev; - base->is_temp_cache = true; - base = prev; - } - - base = next; - while (base) { - next = base->link_next; - base->is_temp_cache = true; - base = next; - } -} - -static void seq_cache_create(Main *bmain, Scene *scene) -{ - std::scoped_lock lock(cache_create_lock); - if (scene->ed->cache == nullptr) { - SeqCache *cache = MEM_callocN("SeqCache"); - cache->keys_pool = BLI_mempool_create(sizeof(SeqCacheKey), 0, 64, BLI_MEMPOOL_NOP); - cache->items_pool = BLI_mempool_create(sizeof(SeqCacheItem), 0, 64, BLI_MEMPOOL_NOP); - cache->hash = BLI_ghash_new(seq_cache_hashhash, seq_cache_hashcmp, "SeqCache hash"); - cache->last_key = nullptr; - cache->bmain = bmain; - BLI_mutex_init(&cache->iterator_mutex); - scene->ed->cache = cache; - - if (scene->ed->disk_cache_timestamp == 0) { - scene->ed->disk_cache_timestamp = time(nullptr); - } - } -} - -static void seq_cache_populate_key(SeqCacheKey *key, - const RenderData *context, - Strip *strip, - const float timeline_frame, - const int type) -{ - key->cache_owner = seq_cache_get_from_scene(context->scene); - key->strip = strip; - key->context = *context; - key->frame_index = seq_cache_timeline_frame_to_frame_index( - context->scene, strip, timeline_frame, type); - key->type = type; - key->link_prev = nullptr; - key->link_next = nullptr; - key->is_temp_cache = true; - key->task_id = context->task_id; -} - -static SeqCacheKey *seq_cache_allocate_key(SeqCache *cache, - const RenderData *context, - Strip *strip, - const float timeline_frame, - const int type) -{ - SeqCacheKey *key = static_cast(BLI_mempool_alloc(cache->keys_pool)); - seq_cache_populate_key(key, context, strip, timeline_frame, type); - return key; -} - -/* ***************************** API ****************************** */ - -void seq_cache_free_temp_cache(Scene *scene, short id, int timeline_frame) -{ - SeqCache *cache = seq_cache_get_from_scene(scene); - if (!cache) { - return; - } - - seq_cache_lock(scene); - - GHashIterator gh_iter; - BLI_ghashIterator_init(&gh_iter, cache->hash); - while (!BLI_ghashIterator_done(&gh_iter)) { - SeqCacheKey *key = static_cast(BLI_ghashIterator_getKey(&gh_iter)); - BLI_ghashIterator_step(&gh_iter); - BLI_assert(key->cache_owner == cache); - - if (key->is_temp_cache && key->task_id == id) { - /* Use frame_index here to avoid freeing raw images if they are used for multiple frames. */ - float frame_index = seq_cache_timeline_frame_to_frame_index( - scene, key->strip, timeline_frame, key->type); - if (frame_index != key->frame_index || - timeline_frame > time_right_handle_frame_get(scene, key->strip) || - timeline_frame < time_left_handle_frame_get(scene, key->strip)) - { - seq_cache_key_unlink(key); - BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree); - if (key == cache->last_key) { - cache->last_key = nullptr; - } - } - } - } - seq_cache_unlock(scene); -} - -void seq_cache_destruct(Scene *scene) -{ - SeqCache *cache = seq_cache_get_from_scene(scene); - if (!cache) { - return; - } - - BLI_ghash_free(cache->hash, seq_cache_keyfree, seq_cache_valfree); - BLI_mempool_destroy(cache->keys_pool); - BLI_mempool_destroy(cache->items_pool); - BLI_mutex_end(&cache->iterator_mutex); - - if (cache->disk_cache != nullptr) { - seq_disk_cache_free(cache->disk_cache); - } - - MEM_freeN(cache); - scene->ed->cache = nullptr; -} - -void cache_cleanup(Scene *scene) -{ - prefetch_stop(scene); - - SeqCache *cache = seq_cache_get_from_scene(scene); - if (!cache) { - return; - } - - seq_cache_lock(scene); - - GHashIterator gh_iter; - BLI_ghashIterator_init(&gh_iter, cache->hash); - while (!BLI_ghashIterator_done(&gh_iter)) { - SeqCacheKey *key = static_cast(BLI_ghashIterator_getKey(&gh_iter)); - BLI_assert(key->cache_owner == cache); - - BLI_ghashIterator_step(&gh_iter); - - /* NOTE: no need to call #seq_cache_key_unlink as all keys are removed. */ - BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree); - } - cache->last_key = nullptr; - seq_cache_unlock(scene); -} - -void seq_cache_cleanup_strip(Scene *scene, - Strip *strip, - Strip *strip_changed, - int invalidate_types, - bool force_strip_changed_range) -{ - SeqCache *cache = seq_cache_get_from_scene(scene); - if (!cache) { - return; - } - - if (seq_disk_cache_is_enabled(cache->bmain) && cache->disk_cache != nullptr) { - seq_disk_cache_invalidate(cache->disk_cache, scene, strip, strip_changed, invalidate_types); - } - - seq_cache_lock(scene); - - const int range_start_strip_changed = time_left_handle_frame_get(scene, strip_changed); - const int range_end_strip_changed = time_right_handle_frame_get(scene, strip_changed); - - int range_start = range_start_strip_changed; - int range_end = range_end_strip_changed; - - if (!force_strip_changed_range) { - const int range_start_strip = time_left_handle_frame_get(scene, strip); - const int range_end_strip = time_right_handle_frame_get(scene, strip); - - range_start = max_ii(range_start, range_start_strip); - range_end = min_ii(range_end, range_end_strip); - } - - int invalidate_composite = invalidate_types & SEQ_CACHE_STORE_FINAL_OUT; - int invalidate_source = invalidate_types & (SEQ_CACHE_STORE_RAW | SEQ_CACHE_STORE_PREPROCESSED | - SEQ_CACHE_STORE_COMPOSITE); - - GHashIterator gh_iter; - BLI_ghashIterator_init(&gh_iter, cache->hash); - while (!BLI_ghashIterator_done(&gh_iter)) { - SeqCacheKey *key = static_cast(BLI_ghashIterator_getKey(&gh_iter)); - BLI_ghashIterator_step(&gh_iter); - BLI_assert(key->cache_owner == cache); - - const int key_timeline_frame = seq_cache_key_timeline_frame_get(key); - /* Clean all final and composite in intersection of strip and strip_changed. */ - if (key->type & invalidate_composite && key_timeline_frame >= range_start && - key_timeline_frame <= range_end) - { - seq_cache_key_unlink(key); - BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree); - } - else if (key->type & invalidate_source && key->strip == strip && - key_timeline_frame >= time_left_handle_frame_get(scene, strip_changed) && - key_timeline_frame <= time_right_handle_frame_get(scene, strip_changed)) - { - seq_cache_key_unlink(key); - BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree); - } - } - cache->last_key = nullptr; - seq_cache_unlock(scene); -} - -ImBuf *seq_cache_get(const RenderData *context, Strip *strip, float timeline_frame, int type) -{ - - if (context->skip_cache || context->is_proxy_render || !strip) { - return nullptr; - } - - Scene *scene = context->scene; - - if (context->is_prefetch_render) { - context = seq_prefetch_original_context_get(context); - scene = context->scene; - strip = seq_prefetch_original_strip_get(strip, scene); - } - - if (!strip) { - return nullptr; - } - - if (!scene->ed->cache) { - seq_cache_create(context->bmain, scene); - } - - seq_cache_lock(scene); - SeqCache *cache = seq_cache_get_from_scene(scene); - ImBuf *ibuf = nullptr; - SeqCacheKey key; - - /* Try RAM cache: */ - if (cache && strip) { - seq_cache_populate_key(&key, context, strip, timeline_frame, type); - ibuf = seq_cache_get_ex(cache, &key); - } - seq_cache_unlock(scene); - - if (ibuf) { - return ibuf; - } - - if (context->for_render) { - return nullptr; - } - - /* Try disk cache: */ - if (seq_disk_cache_is_enabled(context->bmain)) { - if (cache->disk_cache == nullptr) { - cache->disk_cache = seq_disk_cache_create(context->bmain, context->scene); - } - - ibuf = seq_disk_cache_read_file(cache->disk_cache, &key); - - if (ibuf == nullptr) { - return nullptr; - } - - /* Store read image in RAM. Only recycle item for final type. */ - if (key.type != SEQ_CACHE_STORE_FINAL_OUT || seq_cache_recycle_item(scene)) { - SeqCacheKey *new_key = seq_cache_allocate_key(cache, context, strip, timeline_frame, type); - seq_cache_put_ex(scene, new_key, ibuf); - } - } - - return ibuf; -} - -bool seq_cache_put_if_possible( - const RenderData *context, Strip *strip, float timeline_frame, int type, ImBuf *ibuf) -{ - Scene *scene = context->scene; - - if (context->is_prefetch_render) { - context = seq_prefetch_original_context_get(context); - scene = context->scene; - strip = seq_prefetch_original_strip_get(strip, scene); - } - - if (!strip) { - return false; - } - - if (seq_cache_recycle_item(scene)) { - seq_cache_put(context, strip, timeline_frame, type, ibuf); - return true; - } - - if (scene->ed->cache) { - seq_cache_set_temp_cache_linked(scene, scene->ed->cache->last_key); - scene->ed->cache->last_key = nullptr; - } - - return false; -} - -void seq_cache_put( - const RenderData *context, Strip *strip, float timeline_frame, int type, ImBuf *i) -{ - if (i == nullptr || context->skip_cache || context->is_proxy_render || !strip) { - return; - } - - Scene *scene = context->scene; - - if (context->is_prefetch_render) { - context = seq_prefetch_original_context_get(context); - scene = context->scene; - strip = seq_prefetch_original_strip_get(strip, scene); - BLI_assert(strip != nullptr); - } - - /* Prevent reinserting, it breaks cache key linking. */ - ImBuf *test = seq_cache_get(context, strip, timeline_frame, type); - if (test) { - IMB_freeImBuf(test); - return; - } - - if (!scene->ed->cache) { - seq_cache_create(context->bmain, scene); - } - - seq_cache_lock(scene); - SeqCache *cache = seq_cache_get_from_scene(scene); - SeqCacheKey *key = seq_cache_allocate_key(cache, context, strip, timeline_frame, type); - seq_cache_put_ex(scene, key, i); - seq_cache_unlock(scene); - - if (context->for_render) { - key->is_temp_cache = true; - } - - if (!key->is_temp_cache) { - if (seq_disk_cache_is_enabled(context->bmain)) { - if (cache->disk_cache == nullptr) { - seq_disk_cache_create(context->bmain, context->scene); - } - - seq_disk_cache_write_file(cache->disk_cache, key, i); - seq_disk_cache_enforce_limits(cache->disk_cache); - } - } -} - -void cache_iterate( - Scene *scene, - void *userdata, - bool callback_init(void *userdata, size_t item_count), - bool callback_iter(void *userdata, Strip *strip, int timeline_frame, int cache_type)) -{ - SeqCache *cache = seq_cache_get_from_scene(scene); - if (!cache) { - return; - } - - seq_cache_lock(scene); - bool interrupt = callback_init(userdata, BLI_ghash_len(cache->hash)); - - GHashIterator gh_iter; - BLI_ghashIterator_init(&gh_iter, cache->hash); - - while (!BLI_ghashIterator_done(&gh_iter) && !interrupt) { - SeqCacheKey *key = static_cast(BLI_ghashIterator_getKey(&gh_iter)); - BLI_ghashIterator_step(&gh_iter); - BLI_assert(key->cache_owner == cache); - int timeline_frame; - if (key->type & SEQ_CACHE_STORE_FINAL_OUT) { - timeline_frame = seq_cache_key_timeline_frame_get(key); - } - else { - /* This is not a final cache image. The cached frame is relative to where the strip is - * currently and where it was when it was cached. We can't use the timeline_frame, we need to - * derive the timeline frame from key->frame_index. - * - * NOTE This will not work for RAW caches if they have retiming, strobing, or different - * playback rate than the scene. Because it would take quite a bit of effort to properly - * convert RAW frames like that to a timeline frame, we skip doing this as visualizing these - * are a developer option that not many people will see. - */ - timeline_frame = key->frame_index + time_start_frame_get(key->strip); - } - - interrupt = callback_iter(userdata, key->strip, timeline_frame, key->type); - } - - cache->last_key = nullptr; - seq_cache_unlock(scene); -} - -bool seq_cache_is_full() -{ - return seq_cache_get_mem_total() < MEM_get_memory_in_use(); -} - -} // namespace blender::seq diff --git a/source/blender/sequencer/intern/image_cache.hh b/source/blender/sequencer/intern/image_cache.hh deleted file mode 100644 index 04fcc35778f..00000000000 --- a/source/blender/sequencer/intern/image_cache.hh +++ /dev/null @@ -1,56 +0,0 @@ -/* SPDX-FileCopyrightText: 2004 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -/** \file - * \ingroup sequencer - */ - -#include "SEQ_render.hh" /* Needed for #eSeqTaskId. */ - -struct ImBuf; -struct Scene; -struct SeqRenderData; -struct Strip; - -namespace blender::seq { - -struct SeqCache; - -struct SeqCacheKey { - SeqCache *cache_owner; - void *userkey; - SeqCacheKey *link_prev; /* Used for linking intermediate items to final frame. */ - SeqCacheKey *link_next; /* Used for linking intermediate items to final frame. */ - Strip *strip; - RenderData context; - float frame_index; /* Usually same as timeline_frame. Mapped to media for RAW entries. */ - float cost; /* In short: render time(s) divided by playback frame duration(s) */ - bool is_temp_cache; /* this cache entry will be freed before rendering next frame */ - /* ID of task for assigning temp cache entries to particular task(thread, etc.) */ - eTaskId task_id; - int type; -}; - -ImBuf *seq_cache_get(const RenderData *context, Strip *strip, float timeline_frame, int type); -void seq_cache_put( - const RenderData *context, Strip *strip, float timeline_frame, int type, ImBuf *i); -bool seq_cache_put_if_possible( - const RenderData *context, Strip *strip, float timeline_frame, int type, ImBuf *ibuf); -/** - * Find only "base" keys. - * Sources(other types) for a frame must be freed all at once. - */ -bool seq_cache_recycle_item(Scene *scene); -void seq_cache_free_temp_cache(Scene *scene, short id, int timeline_frame); -void seq_cache_destruct(Scene *scene); -void seq_cache_cleanup_strip(Scene *scene, - Strip *strip, - Strip *strip_changed, - int invalidate_types, - bool force_seq_changed_range); -bool seq_cache_is_full(); - -} // namespace blender::seq diff --git a/source/blender/sequencer/intern/prefetch.cc b/source/blender/sequencer/intern/prefetch.cc index 52efdd5354d..218ed72f1a3 100644 --- a/source/blender/sequencer/intern/prefetch.cc +++ b/source/blender/sequencer/intern/prefetch.cc @@ -43,7 +43,8 @@ #include "SEQ_render.hh" #include "SEQ_sequencer.hh" -#include "image_cache.hh" +#include "cache/final_image_cache.hh" +#include "cache/source_image_cache.hh" #include "prefetch.hh" #include "render.hh" @@ -124,7 +125,7 @@ static bool seq_prefetch_job_is_waiting(Scene *scene) return pfjob->waiting; } -static Strip *seq_prefetch_original_strip_get_impl(Strip *strip, ListBase *seqbase) +static Strip *original_strip_get(const Strip *strip, ListBase *seqbase) { LISTBASE_FOREACH (Strip *, strip_orig, seqbase) { if (STREQ(strip->name, strip_orig->name)) { @@ -132,7 +133,7 @@ static Strip *seq_prefetch_original_strip_get_impl(Strip *strip, ListBase *seqba } if (strip_orig->type == STRIP_TYPE_META) { - Strip *match = seq_prefetch_original_strip_get_impl(strip, &strip_orig->seqbase); + Strip *match = original_strip_get(strip, &strip_orig->seqbase); if (match != nullptr) { return match; } @@ -142,28 +143,48 @@ static Strip *seq_prefetch_original_strip_get_impl(Strip *strip, ListBase *seqba return nullptr; } -Strip *seq_prefetch_original_strip_get(Strip *strip, Scene *scene) +static Strip *original_strip_get(const Strip *strip, Scene *scene) { Editing *ed = scene->ed; - return seq_prefetch_original_strip_get_impl(strip, &ed->seqbase); + return original_strip_get(strip, &ed->seqbase); } -RenderData *seq_prefetch_original_context_get(const RenderData *context) +static RenderData *get_original_context(const RenderData *context) { PrefetchJob *pfjob = seq_prefetch_job_get(context->scene); return &pfjob->context; } +Scene *prefetch_get_original_scene(const RenderData *context) +{ + Scene *scene = context->scene; + if (context->is_prefetch_render) { + context = get_original_context(context); + scene = context->scene; + } + return scene; +} + +Scene *prefetch_get_original_scene_and_strip(const RenderData *context, const Strip *&strip) +{ + Scene *scene = context->scene; + if (context->is_prefetch_render) { + context = get_original_context(context); + scene = context->scene; + strip = original_strip_get(strip, scene); + } + return scene; +} + static bool seq_prefetch_is_cache_full(Scene *scene) { - PrefetchJob *pfjob = seq_prefetch_job_get(scene); - - if (!seq_cache_is_full()) { + bool full = is_cache_full(scene); + if (!full) { return false; } - - return seq_cache_recycle_item(pfjob->scene) == false; + bool evicted = final_image_cache_evict(scene); + return !evicted; } static float seq_prefetch_cfra(PrefetchJob *pfjob) @@ -177,7 +198,17 @@ static AnimationEvalContext seq_prefetch_anim_eval_context(PrefetchJob *pfjob) void seq_prefetch_get_time_range(Scene *scene, int *r_start, int *r_end) { + /* When there is no prefetch job, return "impossible" negative values. */ + *r_start = INT_MIN; + *r_end = INT_MIN; + PrefetchJob *pfjob = seq_prefetch_job_get(scene); + if (pfjob == nullptr) { + return; + } + if ((scene->ed->cache_flag & SEQ_CACHE_PREFETCH_ENABLE) == 0 || !pfjob->running) { + return; + } *r_start = pfjob->cfra; *r_end = seq_prefetch_cfra(pfjob); @@ -314,7 +345,7 @@ static void seq_prefetch_update_active_seqbase(PrefetchJob *pfjob) Editing *ed_eval = editing_get(pfjob->scene_eval); if (ms_orig != nullptr) { - Strip *meta_eval = seq_prefetch_original_strip_get(ms_orig->parent_strip, pfjob->scene_eval); + Strip *meta_eval = original_strip_get(ms_orig->parent_strip, pfjob->scene_eval); active_seqbase_set(ed_eval, &meta_eval->seqbase); } else { @@ -350,33 +381,23 @@ void seq_prefetch_free(Scene *scene) MEM_delete(pfjob); } -static bool seq_prefetch_strip_has_disk_cache(PrefetchJob *pfjob, - Strip *strip, - bool can_have_final_image) +static bool strip_is_cached(PrefetchJob *pfjob, Strip *strip, bool can_have_final_image) { RenderData *ctx = &pfjob->context_cpy; float cfra = seq_prefetch_cfra(pfjob); - ImBuf *ibuf = seq_cache_get(ctx, strip, cfra, SEQ_CACHE_STORE_PREPROCESSED); + ImBuf *ibuf = source_image_cache_get(ctx, strip, cfra); if (ibuf != nullptr) { IMB_freeImBuf(ibuf); return true; } - ibuf = seq_cache_get(ctx, strip, cfra, SEQ_CACHE_STORE_RAW); - if (ibuf != nullptr) { - IMB_freeImBuf(ibuf); - return true; - } - - if (!can_have_final_image) { - return false; - } - - ibuf = seq_cache_get(ctx, strip, cfra, SEQ_CACHE_STORE_FINAL_OUT); - if (ibuf != nullptr) { - IMB_freeImBuf(ibuf); - return true; + if (can_have_final_image) { + ibuf = final_image_cache_get(pfjob->context.scene, cfra, pfjob->context.view_id); + if (ibuf != nullptr) { + IMB_freeImBuf(ibuf); + return true; + } } return false; @@ -401,9 +422,9 @@ static bool seq_prefetch_scene_strip_is_rendered(PrefetchJob *pfjob, return true; } - /* Disable prefetching 3D scene strips, but check for disk cache. */ + /* A scene strip would be rendered, if it has no cached image for it. */ if (strip->type == STRIP_TYPE_SCENE && (strip->flag & SEQ_SCENE_STRIPS) == 0 && - !seq_prefetch_strip_has_disk_cache(pfjob, strip, !is_recursive_check)) + !strip_is_cached(pfjob, strip, !is_recursive_check)) { return true; } @@ -493,7 +514,6 @@ static void *seq_prefetch_frames(void *job) } ImBuf *ibuf = render_give_ibuf(&pfjob->context_cpy, seq_prefetch_cfra(pfjob), 0); - seq_cache_free_temp_cache(pfjob->scene, pfjob->context.task_id, seq_prefetch_cfra(pfjob)); IMB_freeImBuf(ibuf); /* Suspend thread if there is nothing to be prefetched. */ @@ -513,7 +533,6 @@ static void *seq_prefetch_frames(void *job) pfjob->num_frames_prefetched++; } - seq_cache_free_temp_cache(pfjob->scene, pfjob->context.task_id, seq_prefetch_cfra(pfjob)); pfjob->running = false; pfjob->scene_eval->ed->prefetch_job = nullptr; @@ -576,7 +595,7 @@ void seq_prefetch_start(const RenderData *context, float timeline_frame) * cache storage enabled, has strips to render, not rendering, not doing modal transform - * important, see D7820. */ if ((ed->cache_flag & SEQ_CACHE_PREFETCH_ENABLE) && !running && !scrubbing && !playing && - ed->cache_flag & SEQ_CACHE_ALL_TYPES && has_strips && !G.is_rendering && !G.moving) + (ed->cache_flag & SEQ_CACHE_ALL_TYPES) && has_strips && !G.is_rendering && !G.moving) { seq_prefetch_start_ex(context, timeline_frame); } diff --git a/source/blender/sequencer/intern/prefetch.hh b/source/blender/sequencer/intern/prefetch.hh index e23502747cf..dc420c715b6 100644 --- a/source/blender/sequencer/intern/prefetch.hh +++ b/source/blender/sequencer/intern/prefetch.hh @@ -9,11 +9,12 @@ */ struct Scene; -struct SeqRenderData; struct Strip; namespace blender::seq { +struct RenderData; + /** * Start or resume prefetching. */ @@ -21,13 +22,8 @@ void seq_prefetch_start(const RenderData *context, float timeline_frame); void seq_prefetch_free(Scene *scene); bool seq_prefetch_job_is_running(Scene *scene); void seq_prefetch_get_time_range(Scene *scene, int *r_start, int *r_end); -/** - * For cache context swapping. - */ -RenderData *seq_prefetch_original_context_get(const RenderData *context); -/** - * For cache context swapping. - */ -Strip *seq_prefetch_original_strip_get(Strip *strip, Scene *scene); + +Scene *prefetch_get_original_scene(const RenderData *context); +Scene *prefetch_get_original_scene_and_strip(const RenderData *context, const Strip *&strip); } // namespace blender::seq diff --git a/source/blender/sequencer/intern/render.cc b/source/blender/sequencer/intern/render.cc index daf70588ac1..e3a2965830f 100644 --- a/source/blender/sequencer/intern/render.cc +++ b/source/blender/sequencer/intern/render.cc @@ -67,8 +67,10 @@ #include "SEQ_transform.hh" #include "SEQ_utils.hh" +#include "cache/final_image_cache.hh" +#include "cache/intra_frame_cache.hh" +#include "cache/source_image_cache.hh" #include "effects/effects.hh" -#include "image_cache.hh" #include "multiview.hh" #include "prefetch.hh" #include "proxy.hh" @@ -710,16 +712,19 @@ static ImBuf *seq_render_preprocess_ibuf(const RenderData *context, /* Proxies and non-generator effect strips are not stored in cache. */ const bool is_effect_with_inputs = (strip->type & STRIP_TYPE_EFFECT) != 0 && - effect_get_num_inputs(strip->type) != 0; + (effect_get_num_inputs(strip->type) != 0 || + (strip->type == STRIP_TYPE_ADJUSTMENT)); if (!is_proxy_image && !is_effect_with_inputs) { - seq_cache_put(context, strip, timeline_frame, SEQ_CACHE_STORE_RAW, ibuf); + Scene *orig_scene = prefetch_get_original_scene(context); + if (orig_scene->ed->cache_flag & SEQ_CACHE_STORE_RAW) { + source_image_cache_put(context, strip, timeline_frame, ibuf); + } } if (use_preprocess) { ibuf = input_preprocess(context, strip, timeline_frame, ibuf, is_proxy_image); } - seq_cache_put(context, strip, timeline_frame, SEQ_CACHE_STORE_PREPROCESSED, ibuf); return ibuf; } @@ -1561,8 +1566,10 @@ static ImBuf *seq_render_scene_strip(const RenderData *context, } if (view_id != context->view_id) { - seq_cache_put( - &localcontext, strip, timeline_frame, SEQ_CACHE_STORE_RAW, ibufs_arr[view_id]); + Scene *orig_scene = prefetch_get_original_scene(context); + if (orig_scene->ed->cache_flag & SEQ_CACHE_STORE_RAW) { + source_image_cache_put(&localcontext, strip, timeline_frame, ibufs_arr[view_id]); + } } RE_ReleaseResultImage(re); @@ -1735,18 +1742,17 @@ ImBuf *seq_render_strip(const RenderData *context, Strip *strip, float timeline_frame) { - ImBuf *ibuf = nullptr; bool use_preprocess = false; bool is_proxy_image = false; - ibuf = seq_cache_get(context, strip, timeline_frame, SEQ_CACHE_STORE_PREPROCESSED); + ImBuf *ibuf = intra_frame_cache_get_preprocessed(context->scene, strip); if (ibuf != nullptr) { return ibuf; } /* Proxies are not stored in cache. */ if (!can_use_proxy(context, strip, rendersize_to_proxysize(context->preview_render_size))) { - ibuf = seq_cache_get(context, strip, timeline_frame, SEQ_CACHE_STORE_RAW); + ibuf = seq::source_image_cache_get(context, strip, timeline_frame); } if (ibuf == nullptr) { @@ -1757,6 +1763,7 @@ ImBuf *seq_render_strip(const RenderData *context, use_preprocess = seq_input_have_to_preprocess(context, strip, timeline_frame); ibuf = seq_render_preprocess_ibuf( context, strip, ibuf, timeline_frame, use_preprocess, is_proxy_image); + intra_frame_cache_put_preprocessed(context->scene, strip, ibuf); } if (ibuf == nullptr) { @@ -1852,8 +1859,7 @@ static ImBuf *seq_render_strip_stack(const RenderData *context, for (i = strips.size() - 1; i >= 0; i--) { Strip *strip = strips[i]; - out = seq_cache_get(context, strip, timeline_frame, SEQ_CACHE_STORE_COMPOSITE); - + out = intra_frame_cache_get_composite(context->scene, strip); if (out) { break; } @@ -1886,7 +1892,7 @@ static ImBuf *seq_render_strip_stack(const RenderData *context, /* Check whether the raw (before preprocessing, which can add alpha) strip content * was opaque. */ - ImBuf *ibuf_raw = seq_cache_get(context, strip, timeline_frame, SEQ_CACHE_STORE_RAW); + ImBuf *ibuf_raw = seq::source_image_cache_get(context, strip, timeline_frame); if (ibuf_raw != nullptr) { if (ibuf_raw->planes != R_IMF_PLANES_RGBA) { opaques.add_occluder(context, strip, i); @@ -1920,7 +1926,7 @@ static ImBuf *seq_render_strip_stack(const RenderData *context, out = seq_render_strip_stack_apply_effect(context, strip, timeline_frame, ibuf1, ibuf2); IMB_metadata_copy(out, ibuf2); - seq_cache_put(context, strips[i], timeline_frame, SEQ_CACHE_STORE_COMPOSITE, out); + intra_frame_cache_put_composite(context->scene, strip, out); IMB_freeImBuf(ibuf1); IMB_freeImBuf(ibuf2); @@ -1951,12 +1957,40 @@ static ImBuf *seq_render_strip_stack(const RenderData *context, IMB_freeImBuf(ibuf2); } - seq_cache_put(context, strips[i], timeline_frame, SEQ_CACHE_STORE_COMPOSITE, out); + intra_frame_cache_put_composite(context->scene, strip, out); } return out; } +static void evict_caches_if_full(Scene *scene) +{ + if (!is_cache_full(scene)) { + return; + } + + /* Cache is full, so we want to remove some images. We always try to remove one final image, + * and some amount of source images for each final image, so that ratio of cached images + * stays the same. Depending on the frame composition complexity, there can be lots of + * source images cached for a single final frame; if we only removed one source image + * we'd eventually have the cache still filled only with source images. */ + const size_t count_final = final_image_cache_get_image_count(scene); + const size_t count_source = source_image_cache_get_image_count(scene); + const size_t source_per_final = std::max( + divide_ceil_ul(count_source, std::max(count_final, 1)), 1); + + do { + bool evicted_final = final_image_cache_evict(scene); + bool evicted_source = false; + for (size_t i = 0; i < source_per_final; i++) { + evicted_source |= source_image_cache_evict(scene); + } + if (!evicted_final && !evicted_source) { + break; /* Can't evict no more. */ + } + } while (is_cache_full(scene)); +} + ImBuf *render_give_ibuf(const RenderData *context, float timeline_frame, int chanshown) { Scene *scene = context->scene; @@ -1979,30 +2013,32 @@ ImBuf *render_give_ibuf(const RenderData *context, float timeline_frame, int cha channels = ed->displayed_channels; } - SeqRenderState state; + intra_frame_cache_set_cur_frame(scene, timeline_frame, context->view_id); + + Scene *orig_scene = prefetch_get_original_scene(context); ImBuf *out = nullptr; + if (!context->skip_cache && !context->is_proxy_render) { + out = final_image_cache_get(orig_scene, timeline_frame, context->view_id); + } Vector strips = seq_shown_strips_get( scene, channels, seqbasep, timeline_frame, chanshown); - if (!strips.is_empty()) { - out = seq_cache_get(context, strips.last(), timeline_frame, SEQ_CACHE_STORE_FINAL_OUT); - } - - seq_cache_free_temp_cache(context->scene, context->task_id, timeline_frame); /* Make sure we only keep the `anim` data for strips that are in view. */ relations_free_all_anim_ibufs(context->scene, timeline_frame); + SeqRenderState state; + if (!strips.is_empty() && !out) { std::scoped_lock lock(seq_render_mutex); out = seq_render_strip_stack(context, &state, channels, seqbasep, timeline_frame, chanshown); - if (context->is_prefetch_render) { - seq_cache_put(context, strips.last(), timeline_frame, SEQ_CACHE_STORE_FINAL_OUT, out); - } - else { - seq_cache_put_if_possible( - context, strips.last(), timeline_frame, SEQ_CACHE_STORE_FINAL_OUT, out); + evict_caches_if_full(orig_scene); + + if (out && (orig_scene->ed->cache_flag & SEQ_CACHE_STORE_FINAL_OUT) && !context->skip_cache && + !context->is_proxy_render) + { + final_image_cache_put(orig_scene, timeline_frame, context->view_id, out); } } diff --git a/source/blender/sequencer/intern/render.hh b/source/blender/sequencer/intern/render.hh index 1e88f3e1bea..437377825eb 100644 --- a/source/blender/sequencer/intern/render.hh +++ b/source/blender/sequencer/intern/render.hh @@ -21,7 +21,7 @@ struct Strip; namespace blender::seq { -/* mutable state for sequencer */ +/* Mutable state while rendering one sequencer frame. */ struct SeqRenderState { LinkNode *scene_parents = nullptr; }; diff --git a/source/blender/sequencer/intern/sequencer.cc b/source/blender/sequencer/intern/sequencer.cc index 2765084f681..f0a5767a54c 100644 --- a/source/blender/sequencer/intern/sequencer.cc +++ b/source/blender/sequencer/intern/sequencer.cc @@ -55,7 +55,9 @@ #include "BLO_read_write.hh" -#include "image_cache.hh" +#include "cache/final_image_cache.hh" +#include "cache/intra_frame_cache.hh" +#include "cache/source_image_cache.hh" #include "prefetch.hh" #include "sequencer.hh" #include "utils.hh" @@ -275,7 +277,6 @@ Editing *editing_ensure(Scene *scene) ed = scene->ed = MEM_callocN("addseq"); ed->seqbasep = &ed->seqbase; - ed->cache = nullptr; ed->cache_flag = (SEQ_CACHE_STORE_FINAL_OUT | SEQ_CACHE_STORE_RAW); ed->show_missing_media_flag = SEQ_EDIT_SHOW_MISSING_MEDIA; ed->displayed_channels = &ed->channels; @@ -294,7 +295,6 @@ void editing_free(Scene *scene, const bool do_id_user) } seq_prefetch_free(scene); - seq_cache_destruct(scene); /* handle cache freeing above */ LISTBASE_FOREACH_MUTABLE (Strip *, strip, &ed->seqbase) { @@ -305,6 +305,9 @@ void editing_free(Scene *scene, const bool do_id_user) strip_lookup_free(ed); blender::seq::media_presence_free(scene); blender::seq::thumbnail_cache_destroy(scene); + blender::seq::intra_frame_cache_destroy(scene); + blender::seq::source_image_cache_destroy(scene); + blender::seq::final_image_cache_destroy(scene); channels_free(&ed->channels); MEM_freeN(ed); diff --git a/source/blender/sequencer/intern/strip_add.cc b/source/blender/sequencer/intern/strip_add.cc index 154e0c6ffad..a8273dd214f 100644 --- a/source/blender/sequencer/intern/strip_add.cc +++ b/source/blender/sequencer/intern/strip_add.cc @@ -78,7 +78,7 @@ void add_load_data_init(LoadData *load_data, static void strip_add_generic_update(Scene *scene, Strip *strip) { strip_unique_name_set(scene, &scene->ed->seqbase, strip); - relations_invalidate_cache_composite(scene, strip); + relations_invalidate_cache(scene, strip); strip_lookup_invalidate(scene->ed); strip_time_effect_range_set(scene, strip); time_update_meta_strip_range(scene, lookup_meta_by_strip(scene->ed, strip)); diff --git a/source/blender/sequencer/intern/strip_edit.cc b/source/blender/sequencer/intern/strip_edit.cc index 3fe177a4b8a..a609b46d6f7 100644 --- a/source/blender/sequencer/intern/strip_edit.cc +++ b/source/blender/sequencer/intern/strip_edit.cc @@ -200,7 +200,7 @@ bool edit_move_strip_to_seqbase(Scene *scene, /* Move to meta. */ BLI_remlink(seqbase, strip); BLI_addtail(dst_seqbase, strip); - relations_invalidate_cache_preprocessed(scene, strip); + relations_invalidate_cache(scene, strip); /* Update meta. */ if (transform_test_overlap(scene, dst_seqbase, strip)) { diff --git a/source/blender/sequencer/intern/strip_relations.cc b/source/blender/sequencer/intern/strip_relations.cc index ed9067ee0e8..123e16ba000 100644 --- a/source/blender/sequencer/intern/strip_relations.cc +++ b/source/blender/sequencer/intern/strip_relations.cc @@ -27,11 +27,14 @@ #include "SEQ_prefetch.hh" #include "SEQ_relations.hh" #include "SEQ_sequencer.hh" +#include "SEQ_thumbnail_cache.hh" #include "SEQ_time.hh" #include "SEQ_utils.hh" +#include "cache/final_image_cache.hh" +#include "cache/intra_frame_cache.hh" +#include "cache/source_image_cache.hh" #include "effects/effects.hh" -#include "image_cache.hh" #include "sequencer.hh" #include "utils.hh" @@ -42,86 +45,29 @@ bool relation_is_effect_of_strip(const Strip *effect, const Strip *input) return ELEM(input, effect->seq1, effect->seq2); } -/* check whether cur depends on strip */ -static bool strip_relations_check_depend(const Scene *scene, Strip *strip, Strip *cur) +void cache_cleanup(Scene *scene) { - if (relation_is_effect_of_strip(cur, strip)) { - return true; - } - - /* strips are not intersecting in time, assume no dependency exists between them */ - if (time_right_handle_frame_get(scene, cur) < time_left_handle_frame_get(scene, strip) || - time_left_handle_frame_get(scene, cur) > time_right_handle_frame_get(scene, strip)) - { - return false; - } - - /* checking strip is below reference one, not dependent on it */ - if (cur->machine < strip->machine) { - return false; - } - - /* strip is not blending with lower machines, no dependency here occurs - * check for non-effects only since effect could use lower machines as input - */ - if ((cur->type & STRIP_TYPE_EFFECT) == 0 && - ((cur->blend_mode == SEQ_BLEND_REPLACE) || - (cur->blend_mode == STRIP_TYPE_CROSS && cur->blend_opacity == 100.0f))) - { - return false; - } - - return true; + thumbnail_cache_clear(scene); + source_image_cache_clear(scene); + final_image_cache_clear(scene); + intra_frame_cache_invalidate(scene); } -static void strip_do_invalidate_dependent(Scene *scene, Strip *strip, ListBase *seqbase) +bool is_cache_full(const Scene *scene) { - LISTBASE_FOREACH (Strip *, cur, seqbase) { - if (cur == strip) { - continue; - } - - if (strip_relations_check_depend(scene, strip, cur)) { - /* Effect must be invalidated completely if they depend on invalidated strip. */ - if ((cur->type & STRIP_TYPE_EFFECT) != 0) { - seq_cache_cleanup_strip(scene, cur, strip, SEQ_CACHE_ALL_TYPES, false); - } - else { - /* In case of alpha over for example only invalidate composite image */ - seq_cache_cleanup_strip( - scene, cur, strip, SEQ_CACHE_STORE_COMPOSITE | SEQ_CACHE_STORE_FINAL_OUT, false); - } - } - - if (cur->seqbase.first) { - strip_do_invalidate_dependent(scene, strip, &cur->seqbase); - } - } + size_t cache_limit = size_t(U.memcachelimit) * 1024 * 1024; + return source_image_cache_calc_memory_size(scene) + final_image_cache_calc_memory_size(scene) > + cache_limit; } -static void strip_invalidate_cache(Scene *scene, - Strip *strip, - bool invalidate_self, - int invalidate_types) +static void invalidate_final_cache_strip_range(Scene *scene, const Strip *strip) { - Editing *ed = scene->ed; - - if (invalidate_self) { - seq_cache_cleanup_strip(scene, strip, strip, invalidate_types, false); - } - - if (strip->effectdata && strip->type == STRIP_TYPE_SPEED) { - strip_effect_speed_rebuild_map(scene, strip); - } - - blender::seq::media_presence_invalidate_strip(scene, strip); - strip_do_invalidate_dependent(scene, strip, &ed->seqbase); - DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); - prefetch_stop(scene); + const int strip_left = time_left_handle_frame_get(scene, strip); + const int strip_right = time_right_handle_frame_get(scene, strip); + final_image_cache_invalidate_frame_range(scene, strip_left, strip_right); } -/* Find meta-strips that contain invalidated_seq and invalidate them. */ -static void strip_relations_find_and_invalidate_metas(Scene *scene, Strip *strip) +static void invalidate_raw_cache_of_parent_meta(Scene *scene, Strip *strip) { Strip *meta = lookup_meta_by_strip(editing_get(scene), strip); if (meta == nullptr) { @@ -131,51 +77,30 @@ static void strip_relations_find_and_invalidate_metas(Scene *scene, Strip *strip relations_invalidate_cache_raw(scene, meta); } -void relations_invalidate_cache_in_range(Scene *scene, - Strip *strip, - Strip *range_mask, - int invalidate_types) -{ - seq_cache_cleanup_strip(scene, strip, range_mask, invalidate_types, true); - strip_relations_find_and_invalidate_metas(scene, strip); -} - void relations_invalidate_cache_raw(Scene *scene, Strip *strip) { - strip_invalidate_cache(scene, strip, true, SEQ_CACHE_ALL_TYPES); - strip_relations_find_and_invalidate_metas(scene, strip); + source_image_cache_invalidate_strip(scene, strip); + relations_invalidate_cache(scene, strip); } -void relations_invalidate_cache_preprocessed(Scene *scene, Strip *strip) -{ - strip_invalidate_cache(scene, - strip, - true, - SEQ_CACHE_STORE_PREPROCESSED | SEQ_CACHE_STORE_COMPOSITE | - SEQ_CACHE_STORE_FINAL_OUT); - strip_relations_find_and_invalidate_metas(scene, strip); -} - -void relations_invalidate_cache_composite(Scene *scene, Strip *strip) +void relations_invalidate_cache(Scene *scene, Strip *strip) { if (strip->type == STRIP_TYPE_SOUND_RAM) { return; } - strip_invalidate_cache( - scene, strip, true, SEQ_CACHE_STORE_COMPOSITE | SEQ_CACHE_STORE_FINAL_OUT); - strip_relations_find_and_invalidate_metas(scene, strip); -} - -void relations_invalidate_dependent(Scene *scene, Strip *strip) -{ - if (strip->type == STRIP_TYPE_SOUND_RAM) { - return; + if (strip->effectdata && strip->type == STRIP_TYPE_SPEED) { + strip_effect_speed_rebuild_map(scene, strip); } - strip_invalidate_cache( - scene, strip, false, SEQ_CACHE_STORE_COMPOSITE | SEQ_CACHE_STORE_FINAL_OUT); - strip_relations_find_and_invalidate_metas(scene, strip); + media_presence_invalidate_strip(scene, strip); + + invalidate_final_cache_strip_range(scene, strip); + intra_frame_cache_invalidate(scene, strip); + invalidate_raw_cache_of_parent_meta(scene, strip); + + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + prefetch_stop(scene); } static void invalidate_scene_strips(Scene *scene, Scene *scene_target, ListBase *seqbase) diff --git a/source/blender/sequencer/intern/strip_transform.cc b/source/blender/sequencer/intern/strip_transform.cc index a68ed573ff1..6dc6b3872d6 100644 --- a/source/blender/sequencer/intern/strip_transform.cc +++ b/source/blender/sequencer/intern/strip_transform.cc @@ -542,7 +542,7 @@ void transform_offset_after_frame(Scene *scene, LISTBASE_FOREACH (Strip *, strip, seqbase) { if (time_left_handle_frame_get(scene, strip) >= timeline_frame) { transform_translate_strip(scene, strip, delta); - relations_invalidate_cache_preprocessed(scene, strip); + relations_invalidate_cache(scene, strip); } }