[WIP] VSE: share ImBufAnim resource between strips #118670

Draft
Richard Antalik wants to merge 14 commits from iss/blender:anim-sharing-2 into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
22 changed files with 509 additions and 211 deletions

View File

@ -2667,7 +2667,7 @@ ImBufAnim *openanim(const char *filepath,
printf("not an anim: %s\n", filepath);
}
else {
printf("anim file doesn't exist: %s\n", filepath);
// xxx printf("anim file doesn't exist: %s\n", filepath);
}
IMB_free_anim(anim);
return nullptr;

View File

@ -1344,6 +1344,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
ed->cache = nullptr;
ed->prefetch_job = nullptr;
ed->runtime.sequence_lookup = nullptr;
ed->runtime.anim_lookup = nullptr;
/* recursive link sequences, lb will be correctly initialized */
link_recurs_seq(reader, &ed->seqbase);

View File

@ -2786,7 +2786,7 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op)
prop = RNA_struct_find_property(&seq_ptr, "filepath");
RNA_property_string_set(&seq_ptr, prop, filepath);
RNA_property_update(C, &seq_ptr, prop);
SEQ_relations_sequence_free_anim(seq);
SEQ_relations_sequence_free_anim(scene, seq);
}
SEQ_relations_invalidate_cache_raw(scene, seq);

View File

@ -153,7 +153,7 @@ static void thumbnail_start_job(void *data, wmJobWorkerStatus *worker_status)
val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, tj->thumb_height, nullptr, nullptr);
SEQ_render_thumbnails(
&tj->context, val->seq_dupli, seq_orig, frame_step, tj->view_area, &worker_status->stop);
SEQ_relations_sequence_free_anim(val->seq_dupli);
SEQ_relations_sequence_free_anim(scene, val->seq_dupli);
}
BLI_ghashIterator_step(&gh_iter);
}
@ -170,7 +170,7 @@ static void thumbnail_start_job(void *data, wmJobWorkerStatus *worker_status)
val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, tj->thumb_height, nullptr, nullptr);
SEQ_render_thumbnails_base_set(
&tj->context, val->seq_dupli, seq_orig, tj->view_area, &worker_status->stop);
SEQ_relations_sequence_free_anim(val->seq_dupli);
SEQ_relations_sequence_free_anim(scene, val->seq_dupli);
}
BLI_ghashIterator_step(&gh_iter);
}

View File

@ -298,6 +298,8 @@ typedef struct SeqTimelineChannel {
typedef struct EditingRuntime {
struct SequenceLookup *sequence_lookup;
struct AnimManager *anim_lookup;
void *_pad0;
} EditingRuntime;
typedef struct Editing {

View File

@ -607,9 +607,9 @@ static bool seq_find_colorspace_settings_cb(Sequence *seq, void *user_data)
return true;
}
static bool seq_free_anim_cb(Sequence *seq, void * /*user_data*/)
static bool seq_free_anim_cb(Sequence *seq, void *user_data)
{
SEQ_relations_sequence_free_anim(seq);
SEQ_relations_sequence_free_anim(static_cast<Scene *>(user_data), seq);
return true;
}
@ -659,7 +659,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain,
Sequence *seq = cb_data.r_seq;
if (seq) {
SEQ_relations_sequence_free_anim(seq);
SEQ_relations_sequence_free_anim(scene, seq);
if (seq->strip->proxy && seq->strip->proxy->anim) {
IMB_free_anim(seq->strip->proxy->anim);
@ -669,7 +669,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain,
SEQ_relations_invalidate_cache_raw(scene, seq);
}
else {
SEQ_for_each_callback(&scene->ed->seqbase, seq_free_anim_cb, nullptr);
SEQ_for_each_callback(&scene->ed->seqbase, seq_free_anim_cb, scene);
}
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, nullptr);

View File

@ -41,6 +41,8 @@ set(SRC
SEQ_transform.hh
SEQ_utils.hh
intern/anim_manager.hh
intern/anim_manager.cc
intern/animation.cc
intern/channels.cc
intern/disk_cache.cc

View File

@ -22,7 +22,7 @@ bool SEQ_relation_is_effect_of_strip(const Sequence *effect, const Sequence *inp
/**
* Function to free imbuf and anim data on changes.
*/
void SEQ_relations_sequence_free_anim(Sequence *seq);
void SEQ_relations_sequence_free_anim(const Scene *scene, Sequence *seq);
bool SEQ_relations_check_scene_recursion(Scene *scene, ReportList *reports);
/**
* Check if "seq_main" (indirectly) uses strip "seq".
@ -39,10 +39,6 @@ void SEQ_relations_invalidate_cache_in_range(Scene *scene,
Sequence *seq,
Sequence *range_mask,
int invalidate_types);
/**
* Release FFmpeg handles of strips that are not currently displayed to minimize memory usage.
*/
void SEQ_relations_free_all_anim_ibufs(Scene *scene, int timeline_frame);
/**
* A debug and development function which checks whether sequences have unique UIDs.
* Errors will be reported to the console.

View File

@ -0,0 +1,388 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup sequencer
*/
#include "BLI_blenlib.h"
#include "BLI_hash_mm3.hh"
#include "BLI_index_range.hh"
#include "BLI_math_base.h"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
#include "BKE_image.h"
#include "BKE_main.hh"
#include "BKE_scene.hh"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "SEQ_iterator.hh"
#include "SEQ_relations.hh"
#include "SEQ_sequencer.hh"
#include "SEQ_utils.hh"
#include "anim_manager.hh"
#include "multiview.hh"
#include "proxy.hh"
#include "render.hh"
#include "strip_time.hh"
#define PREFETCH_DIST 512
static void anim_filepath_get(const Scene *scene,
Sequence *seq,
size_t filepath_size,
char *r_filepath)
{
BLI_path_join(r_filepath, filepath_size, seq->strip->dirpath, seq->strip->stripdata->filename);
BLI_path_abs(r_filepath, ID_BLEND_PATH_FROM_GLOBAL(&scene->id));
}
static bool use_proxy(Editing *ed, Sequence *seq)
{
StripProxy *proxy = seq->strip->proxy;
return proxy && ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) != 0 ||
(ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE));
}
static void proxy_dir_get(Editing *ed, Sequence *seq, size_t str_len, char *r_proxy_dirpath)
{
if (use_proxy(ed, seq)) {
if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) {
if (ed->proxy_dir[0] == 0) {
BLI_strncpy(r_proxy_dirpath, "//BL_proxy", str_len);
}
else {
BLI_strncpy(r_proxy_dirpath, ed->proxy_dir, str_len);
}
}
else {
BLI_strncpy(r_proxy_dirpath, seq->strip->proxy->dirpath, str_len);
}
BLI_path_abs(r_proxy_dirpath, BKE_main_blendfile_path_from_global());
}
}
static void index_dir_set(Editing *ed, Sequence *seq, StripAnim *sanim)
{
if (sanim->anim == nullptr || !use_proxy(ed, seq)) {
return;
}
char proxy_dirpath[FILE_MAX];
proxy_dir_get(ed, seq, sizeof(proxy_dirpath), proxy_dirpath);
seq_proxy_index_dir_set(sanim->anim, proxy_dirpath);
}
static ImBufAnim *anim_get(Sequence *seq, const char *filepath, bool openfile)
{
ImBufAnim *anim = nullptr;
if (openfile) {
anim = openanim(filepath,
IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
seq->streamindex,
seq->strip->colorspace_settings.name);
}
else {
anim = openanim_noload(filepath,
IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
seq->streamindex,
seq->strip->colorspace_settings.name);
}
return anim;
}
static bool is_multiview(const Scene *scene, Sequence *seq, const char *filepath)
{
bool use_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0;
char prefix[FILE_MAX];
const char *ext = nullptr;
BKE_scene_multiview_view_prefix_get(const_cast<Scene *>(scene), filepath, prefix, &ext);
return use_multiview && seq->views_format == R_IMF_VIEWS_INDIVIDUAL && prefix[0] != '\0';
}
static blender::Vector<ImBufAnim *> multiview_anims_get(const Scene *scene,
Sequence *seq,
const char *filepath)
{
int totfiles = seq_num_files(scene, seq->views_format, true);
char prefix[FILE_MAX];
const char *ext = nullptr;
BKE_scene_multiview_view_prefix_get(const_cast<Scene *>(scene), filepath, prefix, &ext);
blender::Vector<ImBufAnim *> anims;
for (int i = 0; i < totfiles; i++) {
const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i);
char filepath_view[FILE_MAX];
SNPRINTF(filepath_view, "%s%s%s", prefix, suffix, ext);
/* Multiview files must be loaded, otherwise it is not possible to detect failure. */
ImBufAnim *anim = anim_get(seq, filepath_view, true);
if (anim != nullptr) {
anims.append(anim);
}
}
return anims;
}
void ShareableAnim::release_from_strip(Sequence *seq)
{
if (anims.size() == 0 || BLI_listbase_is_empty(&seq->anims)) {
return;
}
BLI_mutex_lock(mutex);
LISTBASE_FOREACH (StripAnim *, sanim, &seq->anims) {
MEM_freeN(sanim);
}
BLI_listbase_clear(&seq->anims);
users.remove_if([seq](Sequence *seq_user) { return seq == seq_user; });
if (users.size() == 0) {
for (ImBufAnim *anim : anims) {
IMB_free_anim(anim);
}
anims.clear();
}
BLI_mutex_unlock(mutex);
};
void ShareableAnim::release_from_all_strips(void)
{
for (Sequence *user : users) {
release_from_strip(user);
}
}
void ShareableAnim::assign_to_strip(const Scene *scene, Sequence *seq)
{
// XXX check this out
// BLI_assert(BLI_listbase_is_empty(&seq->anims));
Editing *ed = SEQ_editing_get(scene);
for (int i = 0; i < anims.size(); i++) {
ImBufAnim *anim = anims[i];
StripAnim *sanim = static_cast<StripAnim *>(MEM_mallocN(sizeof(StripAnim), "Strip Anim"));
sanim->anim = anim;
BLI_addtail(&seq->anims, sanim);
index_dir_set(ed, seq, sanim);
if (is_multiview(scene, seq, filepath)) {
const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i);
IMB_suffix_anim(sanim->anim, suffix);
}
}
users.append(seq);
};
// check if needed.
ThreadMutex acq_anim_mutex = BLI_MUTEX_INITIALIZER;
void ShareableAnim::acquire_anims(const Scene *scene, Sequence *seq, bool openfile)
{
if (is_multiview(scene, seq, filepath)) {
anims = multiview_anims_get(scene, seq, filepath);
multiview_loaded = true;
return;
}
BLI_mutex_lock(&acq_anim_mutex);
ImBufAnim *anim = anim_get(seq, filepath, openfile);
BLI_mutex_unlock(&acq_anim_mutex);
if (anim != nullptr) {
anims.append(anim);
}
}
bool ShareableAnim::has_anim(const Scene *scene, Sequence *seq)
{
if (is_multiview(scene, seq, filepath) && !multiview_loaded) {
return false;
}
return !anims.is_empty();
}
ShareableAnim::ShareableAnim()
{
mutex = BLI_mutex_alloc();
}
static ShareableAnim &anim_lookup_by_filepath(Editing *ed, const char *filepath)
{
blender::Map<std::string, ShareableAnim> &anims = ed->runtime.anim_lookup->anims;
BLI_mutex_lock(ed->runtime.anim_lookup->mutex);
// XXX How does this free sh_anim.anims???
ShareableAnim &sh_anim = anims.lookup_or_add_default(std::string(filepath));
memcpy(sh_anim.filepath, filepath, sizeof(sh_anim.filepath));
BLI_mutex_unlock(ed->runtime.anim_lookup->mutex);
return sh_anim;
}
static ShareableAnim &anim_lookup_by_seq(const Scene *scene, Sequence *seq)
{
Editing *ed = SEQ_editing_get(scene);
seq_anim_lookup_ensure(ed);
char filepath[FILE_MAX];
anim_filepath_get(scene, seq, sizeof(filepath), filepath);
ShareableAnim &sh_anim = anim_lookup_by_filepath(ed, filepath);
return sh_anim;
}
void seq_open_anim_file(const Scene *scene, Sequence *seq, bool openfile)
{
if ((seq->anims.first != nullptr) && (((StripAnim *)seq->anims.first)->anim != nullptr)) {
return;
}
Editing *ed = SEQ_editing_get(scene);
seq_anim_lookup_ensure(ed);
char filepath[FILE_MAX];
anim_filepath_get(scene, seq, sizeof(filepath), filepath);
ShareableAnim &sh_anim = anim_lookup_by_filepath(ed, filepath);
BLI_mutex_lock(sh_anim.mutex);
if (!sh_anim.has_anim(scene, seq)) {
sh_anim.acquire_anims(scene, seq, openfile);
}
sh_anim.assign_to_strip(scene, seq);
BLI_mutex_unlock(sh_anim.mutex);
}
void SEQ_relations_sequence_free_anim(const Scene *scene, Sequence *seq)
{
ShareableAnim &sh_anim = anim_lookup_by_seq(scene, seq);
sh_anim.release_from_strip(seq);
}
static blender::Vector<Sequence *> strips_to_prefetch_get(const Scene *scene)
{
Editing *ed = SEQ_editing_get(scene);
ListBase *seqbase = SEQ_active_seqbase_get(ed);
blender::VectorSet<Sequence *> strips = SEQ_query_all_strips_recursive(seqbase);
const int timeline_frame = BKE_scene_frame_get(scene);
strips.remove_if([scene, timeline_frame](Sequence *seq) {
if (seq->type != SEQ_TYPE_MOVIE) {
return true;
}
if (seq_time_distance_from_frame(scene, seq, timeline_frame) > PREFETCH_DIST) {
return true;
}
return false;
});
blender::Vector<Sequence *> strips_2;
for (Sequence *seq : strips) {
strips_2.append(seq);
}
// XXX is VectorSet not sortable?
std::sort(strips_2.begin(), strips_2.end(), [&](const Sequence *a, const Sequence *b) {
return seq_time_distance_from_frame(scene, a, timeline_frame) <
seq_time_distance_from_frame(scene, b, timeline_frame);
});
return strips_2;
}
static void free_unused_anims(const Editing *ed, blender::Vector<Sequence *> &strips)
{
seq_render_mutex_lock();
blender::Map<std::string, ShareableAnim> &anims = ed->runtime.anim_lookup->anims;
for (ShareableAnim &sh_anim : anims.values()) {
bool strips_use_anim = false;
for (Sequence *user : sh_anim.users) {
if (strips.contains(user)) {
strips_use_anim = true;
break;
}
}
// TODO has users func? could be nicer anyway...
if (!strips_use_anim && sh_anim.users.size() > 0) {
sh_anim.release_from_all_strips();
}
}
seq_render_mutex_unlock();
}
/* This function single-handedly manages all* anims VSE could ever need.
* Well, not all, but all for playback purposes, not counting proxies, which is TODO...
*/
static void *manage_anims_thread(void *data)
{
const Scene *scene = static_cast<const Scene *>(data);
Editing *ed = SEQ_editing_get(scene);
blender::Vector<Sequence *> strips = strips_to_prefetch_get(scene);
free_unused_anims(ed, strips);
// TODO why is this not working?
/*using namespace blender;
threading::parallel_for(strips.index_range(), 1, [&](const IndexRange range) {
for (int i : range) {
Sequence *seq = strips[i];
seq_open_anim_file(scene, seq, true);
}
});*/
for (Sequence *seq : strips) {
seq_open_anim_file(scene, seq, true);
}
ed->runtime.anim_lookup->working = false;
return 0;
}
void seq_anim_lookup_ensure(Editing *ed)
{
if (ed->runtime.anim_lookup == nullptr) {
ed->runtime.anim_lookup = MEM_new<AnimManager>(__func__);
}
}
AnimManager::AnimManager()
{
BLI_threadpool_init(&threads, manage_anims_thread, 1);
mutex = BLI_mutex_alloc();
}
void AnimManager::manage_anims(Scene *scene)
{
if (working) {
return;
}
working = true;
BLI_threadpool_clear(&threads);
BLI_threadpool_insert(&threads, static_cast<void *>(scene));
}
void AnimManager::load_set(const Scene *scene, blender::Vector<Sequence *> &strips)
{
blender::Map<std::string, ShareableAnim> &anims = scene->ed->runtime.anim_lookup->anims;
anims.reserve(strips.size() * 50);
using namespace blender;
threading::parallel_for(strips.index_range(), 1, [&](const IndexRange range) {
for (int i : range) {
Sequence *seq = strips[i];
seq_open_anim_file(scene, seq, true);
}
});
}

View File

@ -0,0 +1,51 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup sequencer
*/
struct Scene;
struct Sequence;
#include "BLI_map.hh"
#include "BLI_struct_equality_utils.hh"
#include "BLI_threads.h"
class ShareableAnim {
public:
blender::Vector<ImBufAnim *> anims; /* In same order as strip views (`StripAnim` order). */
blender::Vector<Sequence *> users;
bool multiview_loaded = false;
ThreadMutex *mutex;
char filepath[1024]; // XXX
void release_from_strip(Sequence *seq);
void release_from_all_strips(void);
void assign_to_strip(const Scene *scene, Sequence *seq);
void acquire_anims(const Scene *scene, Sequence *seq, bool openfile);
bool has_anim(const Scene *scene, Sequence *seq);
ShareableAnim();
};
// This needs to be lockable class and has "working" property
class AnimManager {
public:
blender::Map<std::string, ShareableAnim> anims;
ListBase threads{nullptr, nullptr};
bool working{false};
ThreadMutex *mutex;
/* TODO: this will be tricky function - it must know where anims were prefetched last time and if
current playhead position is within this range. It very well may not be...
*/
// bool anims_are_prefetched()
AnimManager();
void manage_anims(Scene *scene);
void load_set(const Scene *scene, blender::Vector<Sequence *> &strips);
};
void seq_open_anim_file(const Scene *scene, Sequence *seq, bool openfile);
void seq_anim_lookup_ensure(Editing *ed);

View File

@ -24,7 +24,7 @@ void seq_anim_add_suffix(Scene *scene, ImBufAnim *anim, const int view_id)
IMB_suffix_anim(anim, suffix);
}
int seq_num_files(Scene *scene, char views_format, const bool is_multiview)
int seq_num_files(const Scene *scene, char views_format, const bool is_multiview)
{
if (!is_multiview) {
return 1;

View File

@ -23,4 +23,4 @@ void seq_multiview_name(
/**
* The number of files will vary according to the stereo format.
*/
int seq_num_files(Scene *scene, char views_format, bool is_multiview);
int seq_num_files(const Scene *scene, char views_format, bool is_multiview);

View File

@ -42,11 +42,11 @@
#include "SEQ_sequencer.hh"
#include "SEQ_time.hh"
#include "anim_manager.hh"
#include "multiview.hh"
#include "proxy.hh"
#include "render.hh"
#include "sequencer.hh"
#include "utils.hh"
struct SeqIndexBuildContext {
IndexBuildContext *index_context;
@ -455,7 +455,7 @@ bool SEQ_proxy_rebuild_context(Main *bmain,
continue;
}
SEQ_relations_sequence_free_anim(seq);
SEQ_relations_sequence_free_anim(scene, seq);
context = static_cast<SeqIndexBuildContext *>(
MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context"));

View File

@ -64,28 +64,36 @@
#include "SEQ_transform.hh"
#include "SEQ_utils.hh"
#include "anim_manager.hh"
#include "effects.hh"
#include "image_cache.hh"
#include "multiview.hh"
#include "prefetch.hh"
#include "proxy.hh"
#include "render.hh"
#include "utils.hh"
#include <algorithm>
using namespace blender;
ThreadMutex seq_render_mutex = BLI_MUTEX_INITIALIZER;
static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
SeqRenderState *state,
ListBase *channels,
ListBase *seqbasep,
float timeline_frame,
int chanshown);
static ThreadMutex seq_render_mutex = BLI_MUTEX_INITIALIZER;
SequencerDrawView sequencer_view3d_fn = nullptr; /* nullptr in background mode */
void seq_render_mutex_lock(void)
{
BLI_mutex_lock(&seq_render_mutex);
}
void seq_render_mutex_unlock(void)
{
BLI_mutex_unlock(&seq_render_mutex);
}
/* -------------------------------------------------------------------- */
/** \name Color-space utility functions
* \{ */
@ -1123,6 +1131,7 @@ static ImBuf *seq_render_movie_strip_custom_file_proxy(const SeqRenderData *cont
if (proxy->anim == nullptr) {
if (seq_proxy_get_custom_file_filepath(seq, filepath, context->view_id)) {
// eeeh probably should be managed by manager as well
proxy->anim = openanim(filepath, IB_rect, 0, seq->strip->colorspace_settings.name);
}
if (proxy->anim == nullptr) {
@ -1167,7 +1176,7 @@ static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context,
{
ibuf = seq_render_movie_strip_custom_file_proxy(context, seq, timeline_frame);
}
else {
else if (sanim != nullptr) {
ibuf = IMB_anim_absolute(sanim->anim,
frame_index + seq->anim_startofs,
seq_render_movie_strip_timecode_get(seq),
@ -1180,7 +1189,7 @@ static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context,
}
/* Fetching for requested proxy size failed, try fetching the original instead. */
if (ibuf == nullptr) {
if (ibuf == nullptr && sanim != nullptr) {
ibuf = IMB_anim_absolute(sanim->anim,
frame_index + seq->anim_startofs,
seq_render_movie_strip_timecode_get(seq),
@ -1206,7 +1215,7 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
bool *r_is_proxy_image)
{
/* Load all the videos. */
seq_open_anim_file(context->scene, seq, false);
// seq_open_anim_file(context->scene, seq, false);
ImBuf *ibuf = nullptr;
StripAnim *sanim = static_cast<StripAnim *>(seq->anims.first);
@ -1245,7 +1254,7 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
SeqRenderData localcontext = *context;
localcontext.view_id = view_id;
if (view_id != context->view_id) {
if (view_id != context->view_id && ibuf_arr[view_id]) {
ibuf_arr[view_id] = seq_render_preprocess_ibuf(
&localcontext, seq, ibuf_arr[view_id], timeline_frame, true, false);
}
@ -2096,11 +2105,18 @@ ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame,
}
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. */
SEQ_relations_free_all_anim_ibufs(context->scene, timeline_frame);
seq_anim_lookup_ensure(ed);
AnimManager *lookup = ed->runtime.anim_lookup;
if (lookup->working) {
printf("Oh noooooooo, prefetch is blocking main!!!!!\n");
}
if (!strips.is_empty() && !out) {
BLI_mutex_lock(&seq_render_mutex);
/* Load anims in main thread for the first time and lock them, so they can't be freed. */
lookup->load_set(scene, strips);
out = seq_render_strip_stack(context, &state, channels, seqbasep, timeline_frame, chanshown);
if (context->is_prefetch_render) {
@ -2110,10 +2126,12 @@ ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame,
seq_cache_put_if_possible(
context, strips.last(), timeline_frame, SEQ_CACHE_STORE_FINAL_OUT, out);
}
// printf("main is unlocking strips\n");
BLI_mutex_unlock(&seq_render_mutex);
}
seq_prefetch_start(context, timeline_frame);
// lookup->manage_anims(scene);
return out;
}

View File

@ -48,3 +48,5 @@ ImBuf *seq_render_mask(const SeqRenderData *context,
float frame_index,
bool make_float);
void seq_imbuf_assign_spaces(const Scene *scene, ImBuf *ibuf);
void seq_render_mutex_lock(void);
void seq_render_mutex_unlock(void);

View File

@ -171,7 +171,7 @@ static void seq_sequence_free_ex(Scene *scene,
seq_free_strip(seq->strip);
}
SEQ_relations_sequence_free_anim(seq);
SEQ_relations_sequence_free_anim(scene, seq);
if (seq->type & SEQ_TYPE_EFFECT) {
SeqEffectHandle sh = SEQ_effect_handle_get(seq);

View File

@ -560,7 +560,7 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo
filepath, sizeof(filepath), seq->strip->dirpath, seq->strip->stripdata->filename);
BLI_path_abs(filepath, BKE_main_blendfile_path_from_global());
SEQ_relations_sequence_free_anim(seq);
SEQ_relations_sequence_free_anim(scene, seq);
if (is_multiview && (seq->views_format == R_IMF_VIEWS_INDIVIDUAL)) {
char prefix[FILE_MAX];

View File

@ -256,7 +256,7 @@ void SEQ_relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
if (seq->strip) {
if (seq->type == SEQ_TYPE_MOVIE) {
SEQ_relations_sequence_free_anim(seq);
SEQ_relations_sequence_free_anim(scene, seq);
}
if (seq->type == SEQ_TYPE_SPEED) {
seq_effect_speed_rebuild_map(scene, seq);
@ -272,48 +272,6 @@ void SEQ_relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
}
}
static void sequencer_all_free_anim_ibufs(const Scene *scene,
ListBase *seqbase,
int timeline_frame,
const int frame_range[2])
{
Editing *ed = SEQ_editing_get(scene);
for (Sequence *seq = static_cast<Sequence *>(seqbase->first); seq != nullptr; seq = seq->next) {
if (!SEQ_time_strip_intersects_frame(scene, seq, timeline_frame) ||
!((frame_range[0] <= timeline_frame) && (frame_range[1] > timeline_frame)))
{
SEQ_relations_sequence_free_anim(seq);
}
if (seq->type == SEQ_TYPE_META) {
int meta_range[2];
MetaStack *ms = SEQ_meta_stack_active_get(ed);
if (ms != nullptr && ms->parseq == seq) {
meta_range[0] = -MAXFRAME;
meta_range[1] = MAXFRAME;
}
else {
/* Limit frame range to meta strip. */
meta_range[0] = max_ii(frame_range[0], SEQ_time_left_handle_frame_get(scene, seq));
meta_range[1] = min_ii(frame_range[1], SEQ_time_right_handle_frame_get(scene, seq));
}
sequencer_all_free_anim_ibufs(scene, &seq->seqbase, timeline_frame, meta_range);
}
}
}
void SEQ_relations_free_all_anim_ibufs(Scene *scene, int timeline_frame)
{
Editing *ed = SEQ_editing_get(scene);
if (ed == nullptr) {
return;
}
const int frame_range[2] = {-MAXFRAME, MAXFRAME};
sequencer_all_free_anim_ibufs(scene, &ed->seqbase, timeline_frame, frame_range);
}
static Sequence *sequencer_check_scene_recursion(Scene *scene, ListBase *seqbase)
{
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
@ -390,21 +348,6 @@ bool SEQ_relations_render_loop_check(Sequence *seq_main, Sequence *seq)
return false;
}
void SEQ_relations_sequence_free_anim(Sequence *seq)
{
while (seq->anims.last) {
StripAnim *sanim = static_cast<StripAnim *>(seq->anims.last);
if (sanim->anim) {
IMB_free_anim(sanim->anim);
sanim->anim = nullptr;
}
BLI_freelinkN(&seq->anims, sanim);
}
BLI_listbase_clear(&seq->anims);
}
void SEQ_relations_session_uid_generate(Sequence *sequence)
{
sequence->runtime.session_uid = BLI_session_uid_generate();

View File

@ -31,9 +31,9 @@
#include "SEQ_time.hh"
#include "SEQ_transform.hh"
#include "anim_manager.hh"
#include "sequencer.hh"
#include "strip_time.hh"
#include "utils.hh"
float SEQ_time_media_playback_rate_factor_get(const Scene *scene, const Sequence *seq)
{
@ -326,7 +326,8 @@ float SEQ_time_sequence_get_fps(Scene *scene, Sequence *seq)
{
switch (seq->type) {
case SEQ_TYPE_MOVIE: {
seq_open_anim_file(scene, seq, true);
seq_open_anim_file(scene, seq, true); // This is bit annoying, but think it is legal to have
// API to open anim on demand anyway...
if (BLI_listbase_is_empty(&seq->anims)) {
return 0.0f;
}
@ -618,3 +619,19 @@ void SEQ_time_slip_strip(const Scene *scene, Sequence *seq, int delta)
{
seq_time_slip_strip_ex(scene, seq, delta, false);
}
int seq_time_distance_from_frame(const Scene *scene, const Sequence *seq, int timeline_frame)
{
const int left_handle = SEQ_time_left_handle_frame_get(scene, seq);
const int right_handle = SEQ_time_right_handle_frame_get(scene, seq);
if (SEQ_time_strip_intersects_frame(scene, seq, timeline_frame)) {
return 0;
}
if (timeline_frame < left_handle) {
return left_handle - timeline_frame;
}
return timeline_frame - right_handle;
}

View File

@ -45,3 +45,4 @@ void seq_time_translate_handles(const Scene *scene, Sequence *seq, const int off
float seq_time_media_playback_rate_factor_get(const Scene *scene, const Sequence *seq);
int seq_time_strip_original_content_length_get(const Scene *scene, const Sequence *seq);
float seq_retiming_evaluate(const Sequence *seq, const float frame_index);
int seq_time_distance_from_frame(const Scene *scene, const Sequence *seq, int timeline_frame);

View File

@ -202,128 +202,6 @@ ListBase *SEQ_get_seqbase_from_sequence(Sequence *seq, ListBase **r_channels, in
return seqbase;
}
static void open_anim_filepath(Sequence *seq,
StripAnim *sanim,
const char *filepath,
bool openfile)
{
if (openfile) {
sanim->anim = openanim(filepath,
IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
seq->streamindex,
seq->strip->colorspace_settings.name);
}
else {
sanim->anim = openanim_noload(filepath,
IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
seq->streamindex,
seq->strip->colorspace_settings.name);
}
}
static bool use_proxy(Editing *ed, Sequence *seq)
{
StripProxy *proxy = seq->strip->proxy;
return proxy && ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) != 0 ||
(ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE));
}
static void proxy_dir_get(Editing *ed, Sequence *seq, size_t str_len, char *r_proxy_dirpath)
{
if (use_proxy(ed, seq)) {
if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) {
if (ed->proxy_dir[0] == 0) {
BLI_strncpy(r_proxy_dirpath, "//BL_proxy", str_len);
}
else {
BLI_strncpy(r_proxy_dirpath, ed->proxy_dir, str_len);
}
}
else {
BLI_strncpy(r_proxy_dirpath, seq->strip->proxy->dirpath, str_len);
}
BLI_path_abs(r_proxy_dirpath, BKE_main_blendfile_path_from_global());
}
}
static void index_dir_set(Editing *ed, Sequence *seq, StripAnim *sanim)
{
if (sanim->anim == nullptr || !use_proxy(ed, seq)) {
return;
}
char proxy_dirpath[FILE_MAX];
proxy_dir_get(ed, seq, sizeof(proxy_dirpath), proxy_dirpath);
seq_proxy_index_dir_set(sanim->anim, proxy_dirpath);
}
static bool open_anim_file_multiview(Scene *scene, Sequence *seq, char *filepath, bool openfile)
{
char prefix[FILE_MAX];
const char *ext = nullptr;
BKE_scene_multiview_view_prefix_get(scene, filepath, prefix, &ext);
if (seq->views_format != R_IMF_VIEWS_INDIVIDUAL || prefix[0] == '\0') {
return false;
}
Editing *ed = scene->ed;
bool is_multiview_loaded = false;
int totfiles = seq_num_files(scene, seq->views_format, true);
for (int i = 0; i < totfiles; i++) {
const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i);
char filepath_view[FILE_MAX];
SNPRINTF(filepath_view, "%s%s%s", prefix, suffix, ext);
StripAnim *sanim = static_cast<StripAnim *>(MEM_mallocN(sizeof(StripAnim), "Strip Anim"));
open_anim_filepath(seq, sanim, filepath_view, openfile);
if (sanim->anim == nullptr) {
MEM_freeN(sanim);
return false; /* Multiview render failed. */
}
index_dir_set(ed, seq, sanim);
BLI_addtail(&seq->anims, sanim);
IMB_suffix_anim(sanim->anim, suffix);
is_multiview_loaded = true;
}
return is_multiview_loaded;
}
void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile)
{
if ((seq->anims.first != nullptr) && (((StripAnim *)seq->anims.first)->anim != nullptr) &&
!openfile)
{
return;
}
/* Reset all the previously created anims. */
SEQ_relations_sequence_free_anim(seq);
Editing *ed = scene->ed;
char filepath[FILE_MAX];
BLI_path_join(filepath, sizeof(filepath), seq->strip->dirpath, seq->strip->stripdata->filename);
BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&scene->id));
bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0;
bool multiview_is_loaded = false;
if (is_multiview) {
multiview_is_loaded = open_anim_file_multiview(scene, seq, filepath, openfile);
}
if (!is_multiview || !multiview_is_loaded) {
StripAnim *sanim = static_cast<StripAnim *>(MEM_mallocN(sizeof(StripAnim), "Strip Anim"));
BLI_addtail(&seq->anims, sanim);
open_anim_filepath(seq, sanim, filepath, openfile);
index_dir_set(ed, seq, sanim);
}
}
const Sequence *SEQ_get_topmost_sequence(const Scene *scene, int frame)
{
Editing *ed = scene->ed;

View File

@ -12,5 +12,4 @@ struct ListBase;
struct Scene;
bool sequencer_seq_generates_image(Sequence *seq);
void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile);
Sequence *SEQ_get_meta_by_seqbase(ListBase *seqbase_main, ListBase *meta_seqbase);