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); } }