Blender V4.5
thumbnail_cache.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_map.hh"
10#include "BLI_math_base.h"
11#include "BLI_mutex.hh"
12#include "BLI_path_utils.hh"
13#include "BLI_set.hh"
14#include "BLI_task.hh"
15#include "BLI_vector.hh"
16
17#include "BKE_context.hh"
18#include "BKE_library.hh"
19#include "BKE_main.hh"
20
21#include "DNA_scene_types.h"
22#include "DNA_sequence_types.h"
23
24#include "IMB_imbuf.hh"
25
26#include "MOV_read.hh"
27
28#include "SEQ_render.hh"
30#include "SEQ_time.hh"
31
32#include "WM_api.hh"
33
34#include "render.hh"
35
36namespace blender::seq {
37
38static constexpr int MAX_THUMBNAILS = 5000;
39
40// #define DEBUG_PRINT_THUMB_JOB_TIMES
41
43
44/* Thumbnail cache is a map keyed by media file path, with values being
45 * the various thumbnails that are loaded for it (mostly images would contain just
46 * one thumbnail frame, but movies can contain multiple).
47 *
48 * File entries and individual frame entries also record the timestamp when they were
49 * last accessed, so that when the cache is full, some of the old entries can be removed.
50 *
51 * Thumbnails that are requested but do not have an exact match in the cache, are added
52 * to the "requests" set. The requests are processed in the background by a WM job. */
54 struct FrameEntry {
55 int frame_index = 0; /* Frame index (for movies) or image index (for image sequences). */
56 int stream_index = 0; /* Stream index (only for multi-stream movies). */
57 ImBuf *thumb = nullptr;
59 };
60
65
66 struct Request {
67 explicit Request(const std::string &path,
68 int frame,
69 int stream,
70 StripType type,
71 int64_t logical_time,
72 float time_frame,
73 int ch,
74 int width,
75 int height)
76 : file_path(path),
77 frame_index(frame),
78 stream_index(stream),
79 strip_type(type),
80 requested_at(logical_time),
81 timeline_frame(time_frame),
82 channel(ch),
83 full_width(width),
84 full_height(height)
85 {
86 }
87 /* These determine request uniqueness (for equality/hash in a Set). */
88 std::string file_path;
89 int frame_index = 0; /* Frame index (for movies) or image index (for image sequences). */
90 int stream_index = 0; /* Stream index (only for multi-stream movies). */
92
93 /* The following members are payload and do not contribute to uniqueness. */
95 float timeline_frame = 0;
96 int channel = 0;
97 int full_width = 0;
98 int full_height = 0;
99
104 bool operator==(const Request &o) const
105 {
106 return frame_index == o.frame_index && stream_index == o.stream_index &&
108 }
109 };
110
114
116 {
117 clear();
118 }
119
120 void clear()
121 {
122 for (const auto &item : map_.items()) {
123 for (const auto &thumb : item.value.frames) {
124 IMB_freeImBuf(thumb.thumb);
125 }
126 }
127 map_.clear();
128 requests_.clear();
129 logical_time_ = 0;
130 }
131
132 void remove_entry(const std::string &path)
133 {
134 FileEntry *entry = map_.lookup_ptr(path);
135 if (entry == nullptr) {
136 return;
137 }
138 for (const auto &thumb : entry->frames) {
139 IMB_freeImBuf(thumb.thumb);
140 }
141 map_.remove_contained(path);
142 }
143};
144
146{
147 ThumbnailCache **cache = &scene->ed->runtime.thumbnail_cache;
148 if (*cache == nullptr) {
149 *cache = MEM_new<ThumbnailCache>(__func__);
150 }
151 return *cache;
152}
153
155{
156 if (scene == nullptr || scene->ed == nullptr) {
157 return nullptr;
158 }
159 return scene->ed->runtime.thumbnail_cache;
160}
161
162bool strip_can_have_thumbnail(const Scene *scene, const Strip *strip)
163{
164 if (scene == nullptr || scene->ed == nullptr || strip == nullptr) {
165 return false;
166 }
168 return false;
169 }
170 const StripElem *se = strip->data->stripdata;
171 if (se->orig_height == 0 || se->orig_width == 0) {
172 return false;
173 }
174 return true;
175}
176
177static std::string get_path_from_strip(Scene *scene, const Strip *strip, float timeline_frame)
178{
179 char filepath[FILE_MAX];
180 filepath[0] = 0;
181 switch (strip->type) {
182 case STRIP_TYPE_IMAGE: {
183 const StripElem *s_elem = render_give_stripelem(scene, strip, timeline_frame);
184 if (s_elem != nullptr) {
185 BLI_path_join(filepath, sizeof(filepath), strip->data->dirpath, s_elem->filename);
186 BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&scene->id));
187 }
188 } break;
189 case STRIP_TYPE_MOVIE:
191 filepath, sizeof(filepath), strip->data->dirpath, strip->data->stripdata->filename);
192 BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&scene->id));
193 break;
194 }
195 return filepath;
196}
197
198static void image_size_to_thumb_size(int &r_width, int &r_height)
199{
200 float aspect = float(r_width) / float(r_height);
201 if (r_width > r_height) {
202 r_width = THUMB_SIZE;
203 r_height = round_fl_to_int(THUMB_SIZE / aspect);
204 }
205 else {
206 r_height = THUMB_SIZE;
207 r_width = round_fl_to_int(THUMB_SIZE * aspect);
208 }
209}
210
211static ImBuf *make_thumb_for_image(const Scene *scene, const ThumbnailCache::Request &request)
212{
214 request.file_path.c_str(), THUMB_SIZE, nullptr, IMBThumbLoadFlags::LoadLargeFiles);
215 if (ibuf == nullptr) {
216 return nullptr;
217 }
218 /* Keep only float buffer if we have both byte & float. */
219 if (ibuf->float_buffer.data != nullptr && ibuf->byte_buffer.data != nullptr) {
221 }
222
223 seq_imbuf_to_sequencer_space(scene, ibuf, false);
224 seq_imbuf_assign_spaces(scene, ibuf);
225 return ibuf;
226}
227
229{
230 if (ibuf == nullptr) {
231 return;
232 }
233 int width = ibuf->x;
234 int height = ibuf->y;
235 image_size_to_thumb_size(width, height);
236 IMB_scale(ibuf, width, height, IMBScaleFilter::Nearest, false);
237}
238
239/* Background job that processes in-flight thumbnail requests. */
241 Scene *scene_ = nullptr;
242 ThumbnailCache *cache_ = nullptr;
243
244 public:
245 ThumbGenerationJob(Scene *scene, ThumbnailCache *cache) : scene_(scene), cache_(cache) {}
246
247 static void ensure_job(const bContext *C, ThumbnailCache *cache);
248
249 private:
250 static void run_fn(void *customdata, wmJobWorkerStatus *worker_status);
251 static void end_fn(void *customdata);
252 static void free_fn(void *customdata);
253};
254
256{
258 wmWindow *win = CTX_wm_window(C);
259 Scene *scene = CTX_data_scene(C);
260 wmJob *wm_job = WM_jobs_get(
261 wm, win, scene, "Strip Thumbnails", eWM_JobFlag(0), WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL);
262 if (!WM_jobs_is_running(wm_job)) {
263 ThumbGenerationJob *tj = MEM_new<ThumbGenerationJob>("ThumbGenerationJob", scene, cache);
264 WM_jobs_customdata_set(wm_job, tj, free_fn);
266 WM_jobs_callbacks(wm_job, run_fn, nullptr, nullptr, end_fn);
267
268 WM_jobs_start(wm, wm_job);
269 }
270}
271
272void ThumbGenerationJob::free_fn(void *customdata)
273{
274 ThumbGenerationJob *job = static_cast<ThumbGenerationJob *>(customdata);
275 MEM_delete(job);
276}
277
278void ThumbGenerationJob::run_fn(void *customdata, wmJobWorkerStatus *worker_status)
279{
280#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
281 clock_t t0 = clock();
282 std::atomic<int> total_thumbs = 0, total_images = 0, total_movies = 0;
283#endif
284
285 ThumbGenerationJob *job = static_cast<ThumbGenerationJob *>(customdata);
287 while (!worker_status->stop) {
288 /* Under cache mutex lock: copy all current requests into a vector for processing.
289 * NOTE: keep the requests set intact! We don't want to add new requests for same
290 * items while we are processing them. They will be removed from the set once
291 * they are finished, one by one. */
292 {
293 std::scoped_lock lock(thumb_cache_mutex);
294 requests.clear();
295 requests.reserve(job->cache_->requests_.size());
296 for (const auto &request : job->cache_->requests_) {
297 requests.append(request);
298 }
299 }
300
301 if (requests.is_empty()) {
302 break;
303 }
304
305 /* Sort requests by file, stream and increasing frame index. */
306 std::sort(requests.begin(),
307 requests.end(),
308 [](const ThumbnailCache::Request &a, const ThumbnailCache::Request &b) {
309 if (a.file_path != b.file_path) {
310 return a.file_path < b.file_path;
311 }
312 if (a.stream_index != b.stream_index) {
313 return a.stream_index < b.stream_index;
314 }
315 return a.frame_index < b.frame_index;
316 });
317
318 /* Note: we could process thumbnail cache requests somewhat in parallel,
319 * but let's not do that so that UI responsiveness is not affected much.
320 * Some of video/image loading code parts are multi-threaded internally already,
321 * and that does provide some parallelism. */
322 {
323 /* Often the same movie file is chopped into multiple strips next to each other.
324 * Since the requests are sorted by file path and frame index, we can reuse MovieReader
325 * objects between them for performance. */
326 MovieReader *cur_anim = nullptr;
327 std::string cur_anim_path;
328 int cur_stream = 0;
329 IMB_Proxy_Size cur_proxy_size = IMB_PROXY_NONE;
330 for (const ThumbnailCache::Request &request : requests) {
331 if (worker_status->stop) {
332 break;
333 }
334
335#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
336 ++total_thumbs;
337#endif
338 ImBuf *thumb = nullptr;
339 if (request.strip_type == STRIP_TYPE_IMAGE) {
340 /* Load thumbnail for an image. */
341#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
342 ++total_images;
343#endif
344 thumb = make_thumb_for_image(job->scene_, request);
345 }
346 else if (request.strip_type == STRIP_TYPE_MOVIE) {
347 /* Load thumbnail for an movie. */
348#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
349 ++total_movies;
350#endif
351
352 /* Are we switching to a different movie file / stream? */
353 if (request.file_path != cur_anim_path || request.stream_index != cur_stream) {
354 if (cur_anim != nullptr) {
355 MOV_close(cur_anim);
356 cur_anim = nullptr;
357 }
358
359 cur_anim_path = request.file_path;
360 cur_stream = request.stream_index;
361 cur_anim = MOV_open_file(cur_anim_path.c_str(), IB_byte_data, cur_stream, nullptr);
362 cur_proxy_size = IMB_PROXY_NONE;
363 if (cur_anim != nullptr) {
364 /* Find the lowest proxy resolution available.
365 * `x & -x` leaves only the lowest bit set. */
366 int proxies_mask = MOV_get_existing_proxies(cur_anim);
367 cur_proxy_size = IMB_Proxy_Size(proxies_mask & -proxies_mask);
368 }
369 }
370
371 /* Decode the movie frame. */
372 if (cur_anim != nullptr) {
373 thumb = MOV_decode_frame(cur_anim, request.frame_index, IMB_TC_NONE, cur_proxy_size);
374 if (thumb == nullptr && cur_proxy_size != IMB_PROXY_NONE) {
375 /* Broken proxy file, switch to non-proxy. */
376 cur_proxy_size = IMB_PROXY_NONE;
377 thumb = MOV_decode_frame(cur_anim, request.frame_index, IMB_TC_NONE, cur_proxy_size);
378 }
379 if (thumb != nullptr) {
380 seq_imbuf_assign_spaces(job->scene_, thumb);
381 }
382 }
383 }
384 else {
386 }
387
389
390 /* Add result into the cache (under cache mutex lock). */
391 {
392 std::scoped_lock lock(thumb_cache_mutex);
393 ThumbnailCache::FileEntry *val = job->cache_->map_.lookup_ptr(request.file_path);
394 if (val != nullptr) {
395 val->used_at = math::max(val->used_at, request.requested_at);
396 val->frames.append(
397 {request.frame_index, request.stream_index, thumb, request.requested_at});
398 }
399 else {
400 IMB_freeImBuf(thumb);
401 }
402 /* Remove the request from original set. */
403 job->cache_->requests_.remove(request);
404 }
405
406 if (thumb) {
407 worker_status->do_update = true;
408 }
409 }
410 if (cur_anim != nullptr) {
411 MOV_close(cur_anim);
412 cur_anim = nullptr;
413 }
414 }
415 }
416
417#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
418 clock_t t1 = clock();
419 printf("VSE thumb job: %i thumbs (%i img, %i movie) in %.3f sec\n",
420 total_thumbs.load(),
421 total_images.load(),
422 total_movies.load(),
423 double(t1 - t0) / CLOCKS_PER_SEC);
424#endif
425}
426
427void ThumbGenerationJob::end_fn(void *customdata)
428{
429 ThumbGenerationJob *job = static_cast<ThumbGenerationJob *>(customdata);
431}
432
434 const std::string &key,
435 int frame_index,
436 float timeline_frame,
437 const bContext *C,
438 const Strip *strip)
439{
440 int64_t cur_time = cache.logical_time_;
441 ThumbnailCache::FileEntry *val = cache.map_.lookup_ptr(key);
442
443 if (val == nullptr) {
444 /* Nothing in cache for this path yet. */
445 ThumbnailCache::FileEntry value;
446 value.used_at = cur_time;
447 cache.map_.add_new(key, value);
448 val = cache.map_.lookup_ptr(key);
449 }
450 BLI_assert_msg(val != nullptr, "Thumbnail cache value should never be null here");
451
452 /* Search thumbnail entries of this file for closest match to the frame we want. */
453 int64_t best_index = -1;
454 int best_score = INT_MAX;
455 for (int64_t index = 0; index < val->frames.size(); index++) {
456 if (strip->streamindex != val->frames[index].stream_index) {
457 continue; /* Different video stream than what we need, ignore. */
458 }
459 int score = math::abs(frame_index - val->frames[index].frame_index);
460 if (score < best_score) {
461 best_score = score;
462 best_index = index;
463 if (score == 0) {
464 break;
465 }
466 }
467 }
468
469 if (best_score > 0) {
470 /* We do not have an exact frame match, add a thumb generation request. */
471 const StripElem *se = strip->data->stripdata;
472 int img_width = se->orig_width;
473 int img_height = se->orig_height;
474 ThumbnailCache::Request request(key,
475 frame_index,
476 strip->streamindex,
477 StripType(strip->type),
478 cur_time,
479 timeline_frame,
480 strip->channel,
481 img_width,
482 img_height);
483 cache.requests_.add(request);
485 }
486
487 if (best_index < 0) {
488 return nullptr;
489 }
490
491 /* Return the closest thumbnail fit we have so far. */
492 val->used_at = math::max(val->used_at, cur_time);
493 val->frames[best_index].used_at = math::max(val->frames[best_index].used_at, cur_time);
494 return val->frames[best_index].thumb;
495}
496
498 Scene *scene,
499 const Strip *strip,
500 float timeline_frame)
501{
502 if (!strip_can_have_thumbnail(scene, strip)) {
503 return nullptr;
504 }
505
506 timeline_frame = math::round(timeline_frame);
507
508 const std::string key = get_path_from_strip(scene, strip, timeline_frame);
509 int frame_index = give_frame_index(scene, strip, timeline_frame);
510 if (strip->type == STRIP_TYPE_MOVIE) {
511 frame_index += strip->anim_startofs;
512 }
513
514 ImBuf *res = nullptr;
515 {
516 std::scoped_lock lock(thumb_cache_mutex);
518 res = query_thumbnail(*cache, key, frame_index, timeline_frame, C, strip);
519 }
520
521 if (res) {
522 IMB_refImBuf(res);
523 }
524 return res;
525}
526
528{
529 if (!strip_can_have_thumbnail(scene, strip)) {
530 return;
531 }
532
533 std::scoped_lock lock(thumb_cache_mutex);
535 if (cache != nullptr) {
536 if (ELEM((strip)->type, STRIP_TYPE_MOVIE, STRIP_TYPE_IMAGE)) {
537 const StripElem *elem = strip->data->stripdata;
538 if (elem != nullptr) {
539 int paths_count = 1;
540 if (strip->type == STRIP_TYPE_IMAGE) {
541 /* Image strip has array of file names. */
542 paths_count = int(MEM_allocN_len(elem) / sizeof(*elem));
543 }
544 char filepath[FILE_MAX];
545 const char *basepath = strip->scene ? ID_BLEND_PATH_FROM_GLOBAL(&strip->scene->id) :
547 for (int i = 0; i < paths_count; i++, elem++) {
548 BLI_path_join(filepath, sizeof(filepath), strip->data->dirpath, elem->filename);
549 BLI_path_abs(filepath, basepath);
550 cache->remove_entry(filepath);
551 }
552 }
553 }
554 }
555}
556
558{
559 std::scoped_lock lock(thumb_cache_mutex);
561 if (cache != nullptr) {
562 cache->logical_time_++;
563
564 /* Count total number of thumbnails, and track which one is the least recently used file. */
565 int64_t entries = 0;
566 std::string oldest_file;
567 /* Do not remove thumbnails for files used within last 10 updates. */
568 int64_t oldest_time = cache->logical_time_ - 10;
569 int64_t oldest_entries = 0;
570 for (const auto &item : cache->map_.items()) {
571 entries += item.value.frames.size();
572 if (item.value.used_at < oldest_time) {
573 oldest_file = item.key;
574 oldest_time = item.value.used_at;
575 oldest_entries = item.value.frames.size();
576 }
577 }
578
579 /* If we're beyond capacity and have a long-unused file, remove that. */
580 if (entries > MAX_THUMBNAILS && !oldest_file.empty()) {
581 cache->remove_entry(oldest_file);
582 entries -= oldest_entries;
583 }
584
585 /* If we're still beyond capacity, remove individual long-unused (but not within
586 * last 100 updates) individual frames. */
587 if (entries > MAX_THUMBNAILS) {
588 for (const auto &item : cache->map_.items()) {
589 for (int64_t i = 0; i < item.value.frames.size(); i++) {
590 if (item.value.frames[i].used_at < cache->logical_time_ - 100) {
591 IMB_freeImBuf(item.value.frames[i].thumb);
592 item.value.frames.remove_and_reorder(i);
593 }
594 }
595 }
596 }
597 }
598}
599
601{
602 std::scoped_lock lock(thumb_cache_mutex);
604 if (cache != nullptr) {
605 cache->requests_.remove_if([&](const ThumbnailCache::Request &request) {
606 return request.timeline_frame < rect.xmin || request.timeline_frame > rect.xmax ||
607 request.channel < rect.ymin || request.channel > rect.ymax;
608 });
609 }
610}
611
613{
614 std::scoped_lock lock(thumb_cache_mutex);
616 if (cache != nullptr) {
617 scene->ed->runtime.thumbnail_cache->clear();
618 }
619}
620
622{
623 std::scoped_lock lock(thumb_cache_mutex);
625 if (cache != nullptr) {
626 BLI_assert(cache == scene->ed->runtime.thumbnail_cache);
627 MEM_delete(scene->ed->runtime.thumbnail_cache);
628 scene->ed->runtime.thumbnail_cache = nullptr;
629 }
630}
631
632} // namespace blender::seq
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()
Definition main.cc:882
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE int round_fl_to_int(float a)
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define FILE_MAX
#define BLI_path_join(...)
#define ELEM(...)
@ STRIP_TYPE_IMAGE
@ STRIP_TYPE_MOVIE
ImBuf * IMB_thumb_load_image(const char *filepath, const size_t max_thumb_size, char colorspace[IM_MAX_SPACE], const IMBThumbLoadFlags load_flags=IMBThumbLoadFlags::Zero)
Definition readimage.cc:229
void IMB_freeImBuf(ImBuf *ibuf)
void IMB_refImBuf(ImBuf *ibuf)
void IMB_free_byte_pixels(ImBuf *ibuf)
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:777
IMB_Proxy_Size
@ IMB_PROXY_NONE
@ IB_byte_data
@ IMB_TC_NONE
Definition MOV_enums.hh:46
#define C
Definition RandGen.cpp:29
@ WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL
Definition WM_api.hh:1757
eWM_JobFlag
Definition WM_api.hh:1709
#define ND_SEQUENCER
Definition WM_types.hh:434
#define NC_SCENE
Definition WM_types.hh:375
volatile int lock
long long int int64_t
unsigned long long int uint64_t
void append(const T &value)
bool is_empty() const
void reserve(const int64_t min_capacity)
static void ensure_job(const bContext *C, ThumbnailCache *cache)
ThumbGenerationJob(Scene *scene, ThumbnailCache *cache)
#define printf(...)
#define ID_BLEND_PATH_FROM_GLOBAL(_id)
if(state< num_states)
size_t(* MEM_allocN_len)(const void *vmemh)
Definition mallocn.cc:36
int MOV_get_existing_proxies(const MovieReader *anim)
void MOV_close(MovieReader *anim)
Definition movie_read.cc:58
MovieReader * MOV_open_file(const char *filepath, int ib_flags, int streamindex, char colorspace[IM_MAX_SPACE])
ImBuf * MOV_decode_frame(MovieReader *anim, int position, IMB_Timecode_Type tc, IMB_Proxy_Size preview_size)
T max(const T &a, const T &b)
T abs(const T &a)
T round(const T &a)
void seq_imbuf_to_sequencer_space(const Scene *scene, ImBuf *ibuf, bool make_float)
Definition render.cc:110
void seq_imbuf_assign_spaces(const Scene *scene, ImBuf *ibuf)
Definition render.cc:97
void thumbnail_cache_invalidate_strip(Scene *scene, const Strip *strip)
float give_frame_index(const Scene *scene, const Strip *strip, float timeline_frame)
Definition strip_time.cc:52
bool strip_can_have_thumbnail(const Scene *scene, const Strip *strip)
void thumbnail_cache_clear(Scene *scene)
static ImBuf * make_thumb_for_image(const Scene *scene, const ThumbnailCache::Request &request)
ImBuf * thumbnail_cache_get(const bContext *C, Scene *scene, const Strip *strip, float timeline_frame)
static Mutex thumb_cache_mutex
static ImBuf * query_thumbnail(ThumbnailCache &cache, const std::string &key, int frame_index, float timeline_frame, const bContext *C, const Strip *strip)
static constexpr int MAX_THUMBNAILS
static std::string get_path_from_strip(Scene *scene, const Strip *strip, float timeline_frame)
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)
StripElem * render_give_stripelem(const Scene *scene, const Strip *strip, int timeline_frame)
Definition render.cc:233
static constexpr int THUMB_SIZE
void thumbnail_cache_maintain_capacity(Scene *scene)
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
std::mutex Mutex
Definition BLI_mutex.hh:47
ThumbnailCache * thumbnail_cache
EditingRuntime runtime
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
struct Editing * ed
StripElem * stripdata
char dirpath[768]
char filename[256]
StripData * data
struct Scene * scene
short streamindex
bool operator==(const Request &o) const
Request(const std::string &path, int frame, int stream, StripType 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_
float xmax
float xmin
float ymax
float ymin
i
Definition text_draw.cc:230
void WM_main_add_notifier(uint type, void *reference)
bool WM_jobs_is_running(const wmJob *wm_job)
Definition wm_jobs.cc:318
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:353
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:456
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:190
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition wm_jobs.cc:365
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:337