[WIP] VSE: share ImBufAnim resource between strips #118670
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -298,6 +298,8 @@ typedef struct SeqTimelineChannel {
|
|||
|
||||
typedef struct EditingRuntime {
|
||||
struct SequenceLookup *sequence_lookup;
|
||||
struct AnimManager *anim_lookup;
|
||||
void *_pad0;
|
||||
} EditingRuntime;
|
||||
|
||||
typedef struct Editing {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue