118 for (
const auto &item :
map_.items()) {
119 for (
const auto &thumb : item.value.frames) {
123 map_.clear_and_shrink();
131 if (entry ==
nullptr) {
134 for (
const auto &thumb : entry->
frames) {
137 map_.remove_contained(path);
144 if (*cache ==
nullptr) {
145 *cache = MEM_new<ThumbnailCache>(__func__);
152 if (scene ==
nullptr || scene->ed ==
nullptr) {
155 return scene->ed->runtime.thumbnail_cache;
160 if (scene ==
nullptr || scene->ed ==
nullptr || seq ==
nullptr) {
180 if (s_elem !=
nullptr) {
196 float aspect =
float(r_width) /
float(r_height);
197 if (r_width > r_height) {
211 if (ibuf ==
nullptr) {
226 if (ibuf ==
nullptr) {
230 int height = ibuf->
y;
237 Scene *scene_ =
nullptr;
247 static void end_fn(
void *customdata);
248 static void free_fn(
void *customdata);
259 ThumbGenerationJob *tj = MEM_new<ThumbGenerationJob>(
"ThumbGenerationJob", scene, cache);
268void ThumbGenerationJob::free_fn(
void *customdata)
274void ThumbGenerationJob::run_fn(
void *customdata,
wmJobWorkerStatus *worker_status)
276#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
277 clock_t t0 = clock();
278 std::atomic<int> total_thumbs = 0, total_images = 0, total_movies = 0;
283 while (!worker_status->
stop) {
291 requests.
reserve(job->cache_->requests_.size());
292 for (
const auto &request : job->cache_->requests_) {
302 std::sort(requests.
begin(),
304 [](
const ThumbnailCache::Request &a,
const ThumbnailCache::Request &
b) {
305 if (a.file_path != b.file_path) {
306 return a.file_path < b.file_path;
308 if (a.stream_index !=
b.stream_index) {
309 return a.stream_index < b.stream_index;
311 return a.frame_index <
b.frame_index;
323 ImBufAnim *cur_anim = nullptr;
324 std::string cur_anim_path;
326 for (const int i : range) {
327 const ThumbnailCache::Request &request = requests[i];
328 if (worker_status->stop) {
332#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
335 ImBuf *thumb = nullptr;
336 if (request.seq_type == SEQ_TYPE_IMAGE) {
338#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
341 thumb = make_thumb_for_image(job->scene_, request);
343 else if (request.seq_type == SEQ_TYPE_MOVIE) {
345#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
350 if (request.file_path != cur_anim_path || request.stream_index != cur_stream) {
351 if (cur_anim != nullptr) {
352 IMB_free_anim(cur_anim);
356 cur_anim_path = request.file_path;
357 cur_stream = request.stream_index;
358 cur_anim = IMB_open_anim(cur_anim_path.c_str(), IB_rect, cur_stream, nullptr);
362 if (cur_anim != nullptr) {
363 thumb = IMB_anim_absolute(cur_anim, request.frame_index, IMB_TC_NONE, IMB_PROXY_NONE);
364 if (thumb != nullptr) {
365 seq_imbuf_assign_spaces(job->scene_, thumb);
370 BLI_assert_unreachable();
373 scale_to_thumbnail_size(thumb);
377 std::scoped_lock lock(thumb_cache_mutex);
378 ThumbnailCache::FileEntry *val = job->cache_->map_.lookup_ptr(request.file_path);
379 if (val != nullptr) {
380 val->used_at = math::max(val->used_at, request.requested_at);
382 {request.frame_index, request.stream_index, thumb, request.requested_at});
385 IMB_freeImBuf(thumb);
388 job->cache_->requests_.remove(request);
392 worker_status->do_update = true;
395 if (cur_anim !=
nullptr) {
402#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
403 clock_t t1 = clock();
404 printf(
"VSE thumb job: %i thumbs (%i img, %i movie) in %.3f sec\n",
408 double(t1 - t0) / CLOCKS_PER_SEC);
412void ThumbGenerationJob::end_fn(
void *customdata)
414 ThumbGenerationJob *job =
static_cast<ThumbGenerationJob *
>(customdata);
419 const std::string &key,
421 float timeline_frame,
426 ThumbnailCache::FileEntry *val = cache.
map_.lookup_ptr(key);
428 if (val ==
nullptr) {
430 ThumbnailCache::FileEntry value;
431 value.used_at = cur_time;
432 cache.
map_.add_new(key, value);
433 val = cache.
map_.lookup_ptr(key);
435 BLI_assert_msg(val !=
nullptr,
"Thumbnail cache value should never be null here");
439 int best_score = INT_MAX;
440 for (
int64_t index = 0; index < val->frames.size(); index++) {
441 if (seq->
streamindex != val->frames[index].stream_index) {
444 int score =
math::abs(frame_index - val->frames[index].frame_index);
445 if (score < best_score) {
454 if (best_score > 0) {
459 ThumbnailCache::Request request(key,
469 ThumbGenerationJob::ensure_job(C, &cache);
472 if (best_index < 0) {
477 val->used_at =
math::max(val->used_at, cur_time);
478 val->frames[best_index].used_at =
math::max(val->frames[best_index].used_at, cur_time);
479 return val->frames[best_index].thumb;
485 float timeline_frame)
499 ImBuf *res =
nullptr;
503 res =
query_thumbnail(*cache, key, frame_index, timeline_frame, C, seq);
520 if (cache !=
nullptr) {
523 if (elem !=
nullptr) {
532 for (
int i = 0; i < paths_count; i++, elem++) {
546 if (cache !=
nullptr) {
551 std::string oldest_file;
555 for (
const auto &item : cache->
map_.items()) {
556 entries += item.value.frames.size();
557 if (item.value.used_at < oldest_time) {
558 oldest_file = item.key;
559 oldest_time = item.value.used_at;
560 oldest_entries = item.value.frames.size();
567 entries -= oldest_entries;
573 for (
const auto &item : cache->
map_.items()) {
574 for (
int64_t i = 0; i < item.value.frames.size(); i++) {
575 if (item.value.frames[i].used_at < cache->
logical_time_ - 100) {
577 item.value.frames.remove_and_reorder(i);
589 if (cache !=
nullptr) {
590 cache->
requests_.remove_if([&](
const ThumbnailCache::Request &request) {
591 return request.timeline_frame < rect.
xmin || request.timeline_frame > rect.
xmax ||
592 request.channel < rect.
ymin || request.channel > rect.
ymax;
601 if (cache !=
nullptr) {
602 scene->ed->runtime.thumbnail_cache->clear();
610 if (cache !=
nullptr) {
611 BLI_assert(cache == scene->ed->runtime.thumbnail_cache);
612 MEM_delete(scene->ed->runtime.thumbnail_cache);
613 scene->ed->runtime.thumbnail_cache =
nullptr;
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
const char * BKE_main_blendfile_path_from_global()
#define BLI_assert_msg(a, msg)
MINLINE int round_fl_to_int(float a)
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define BLI_path_join(...)
#define ID_BLEND_PATH_FROM_GLOBAL(_id)
void imb_freerectImBuf(ImBuf *ibuf)
void IMB_free_anim(ImBufAnim *anim)
void IMB_refImBuf(ImBuf *ibuf)
ImBuf * IMB_thumb_load_image(const char *filepath, const size_t max_thumb_size, char colorspace[IM_MAX_SPACE], IMBThumbLoadFlags load_flags=IMBThumbLoadFlags::Zero)
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
@ WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL
void append(const T &value)
IndexRange index_range() const
void reserve(const int64_t min_capacity)
static void ensure_job(const bContext *C, ThumbnailCache *cache)
ThumbGenerationJob(Scene *scene, ThumbnailCache *cache)
local_group_size(16, 16) .push_constant(Type b
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void IMB_freeImBuf(ImBuf *)
size_t(* MEM_allocN_len)(const void *vmemh)
T max(const T &a, const T &b)
static std::string get_path_from_seq(Scene *scene, const Sequence *seq, float timeline_frame)
void thumbnail_cache_invalidate_strip(Scene *scene, const Sequence *seq)
static constexpr int SEQ_THUMB_SIZE
static ImBuf * query_thumbnail(ThumbnailCache &cache, const std::string &key, int frame_index, float timeline_frame, const bContext *C, const Sequence *seq)
ImBuf * thumbnail_cache_get(const bContext *C, Scene *scene, const Sequence *seq, float timeline_frame)
void thumbnail_cache_clear(Scene *scene)
static ImBuf * make_thumb_for_image(const Scene *scene, const ThumbnailCache::Request &request)
static constexpr int MAX_THUMBNAILS
bool strip_can_have_thumbnail(const Scene *scene, const Sequence *seq)
static void image_size_to_thumb_size(int &r_width, int &r_height)
static void scale_to_thumbnail_size(ImBuf *ibuf)
void thumbnail_cache_discard_requests_outside(Scene *scene, const rctf &rect)
static ThumbnailCache * query_thumbnail_cache(Scene *scene)
static ThumbnailCache * ensure_thumbnail_cache(Scene *scene)
void thumbnail_cache_destroy(Scene *scene)
static std::mutex thumb_cache_mutex
void thumbnail_cache_maintain_capacity(Scene *scene)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
uint64_t get_default_hash(const T &v)
void seq_imbuf_to_sequencer_space(const Scene *scene, ImBuf *ibuf, bool make_float)
void seq_imbuf_assign_spaces(const Scene *scene, ImBuf *ibuf)
StripElem * SEQ_render_give_stripelem(const Scene *scene, const Sequence *seq, int timeline_frame)
unsigned __int64 uint64_t
float SEQ_give_frame_index(const Scene *scene, const Sequence *seq, float timeline_frame)
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
Vector< FrameEntry > frames
bool operator==(const Request &o) const
Request(const std::string &path, int frame, int stream, SequenceType type, int64_t logical_time, float time_frame, int ch, int width, int height)
void remove_entry(const std::string &path)
Map< std::string, FileEntry > map_
void WM_main_add_notifier(uint type, void *reference)
bool WM_jobs_is_running(const wmJob *wm_job)
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))