Blender V5.0
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);
260 wmJob *wm_job = WM_jobs_get(wm,
261 win,
262 scene,
263 "Generating strip thumbnails...",
264 eWM_JobFlag(0),
266 if (!WM_jobs_is_running(wm_job)) {
267 ThumbGenerationJob *tj = MEM_new<ThumbGenerationJob>("ThumbGenerationJob", scene, cache);
268 WM_jobs_customdata_set(wm_job, tj, free_fn);
270 WM_jobs_callbacks(wm_job, run_fn, nullptr, nullptr, end_fn);
271
272 WM_jobs_start(wm, wm_job);
273 }
274}
275
276void ThumbGenerationJob::free_fn(void *customdata)
277{
278 ThumbGenerationJob *job = static_cast<ThumbGenerationJob *>(customdata);
279 MEM_delete(job);
280}
281
282void ThumbGenerationJob::run_fn(void *customdata, wmJobWorkerStatus *worker_status)
283{
284#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
285 clock_t t0 = clock();
286 std::atomic<int> total_thumbs = 0, total_images = 0, total_movies = 0;
287#endif
288
289 ThumbGenerationJob *job = static_cast<ThumbGenerationJob *>(customdata);
291 while (!worker_status->stop) {
292 /* Under cache mutex lock: copy all current requests into a vector for processing.
293 * NOTE: keep the requests set intact! We don't want to add new requests for same
294 * items while we are processing them. They will be removed from the set once
295 * they are finished, one by one. */
296 {
297 std::scoped_lock lock(thumb_cache_mutex);
298 requests.clear();
299 requests.reserve(job->cache_->requests_.size());
300 for (const auto &request : job->cache_->requests_) {
301 requests.append(request);
302 }
303 }
304
305 if (requests.is_empty()) {
306 break;
307 }
308
309 /* Sort requests by file, stream and increasing frame index. */
310 std::sort(requests.begin(),
311 requests.end(),
312 [](const ThumbnailCache::Request &a, const ThumbnailCache::Request &b) {
313 if (a.file_path != b.file_path) {
314 return a.file_path < b.file_path;
315 }
316 if (a.stream_index != b.stream_index) {
317 return a.stream_index < b.stream_index;
318 }
319 return a.frame_index < b.frame_index;
320 });
321
322 /* Note: we could process thumbnail cache requests somewhat in parallel,
323 * but let's not do that so that UI responsiveness is not affected much.
324 * Some of video/image loading code parts are multi-threaded internally already,
325 * and that does provide some parallelism. */
326 {
327 /* Often the same movie file is chopped into multiple strips next to each other.
328 * Since the requests are sorted by file path and frame index, we can reuse MovieReader
329 * objects between them for performance. */
330 MovieReader *cur_anim = nullptr;
331 std::string cur_anim_path;
332 int cur_stream = 0;
333 IMB_Proxy_Size cur_proxy_size = IMB_PROXY_NONE;
334 for (const ThumbnailCache::Request &request : requests) {
335 if (worker_status->stop) {
336 break;
337 }
338
339#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
340 ++total_thumbs;
341#endif
342 ImBuf *thumb = nullptr;
343 if (request.strip_type == STRIP_TYPE_IMAGE) {
344 /* Load thumbnail for an image. */
345#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
346 ++total_images;
347#endif
348 thumb = make_thumb_for_image(job->scene_, request);
349 }
350 else if (request.strip_type == STRIP_TYPE_MOVIE) {
351 /* Load thumbnail for an movie. */
352#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
353 ++total_movies;
354#endif
355
356 /* Are we switching to a different movie file / stream? */
357 if (request.file_path != cur_anim_path || request.stream_index != cur_stream) {
358 if (cur_anim != nullptr) {
359 MOV_close(cur_anim);
360 cur_anim = nullptr;
361 }
362
363 cur_anim_path = request.file_path;
364 cur_stream = request.stream_index;
365 cur_anim = MOV_open_file(
366 cur_anim_path.c_str(), IB_byte_data, cur_stream, true, nullptr);
367 cur_proxy_size = IMB_PROXY_NONE;
368 if (cur_anim != nullptr) {
369 /* Find the lowest proxy resolution available.
370 * `x & -x` leaves only the lowest bit set. */
371 int proxies_mask = MOV_get_existing_proxies(cur_anim);
372 cur_proxy_size = IMB_Proxy_Size(proxies_mask & -proxies_mask);
373 }
374 }
375
376 /* Decode the movie frame. */
377 if (cur_anim != nullptr) {
378 thumb = MOV_decode_frame(cur_anim, request.frame_index, IMB_TC_NONE, cur_proxy_size);
379 if (thumb == nullptr && cur_proxy_size != IMB_PROXY_NONE) {
380 /* Broken proxy file, switch to non-proxy. */
381 cur_proxy_size = IMB_PROXY_NONE;
382 thumb = MOV_decode_frame(cur_anim, request.frame_index, IMB_TC_NONE, cur_proxy_size);
383 }
384 if (thumb != nullptr) {
385 seq_imbuf_assign_spaces(job->scene_, thumb);
386 }
387 }
388 }
389 else {
391 }
392
394
395 /* Add result into the cache (under cache mutex lock). */
396 {
397 std::scoped_lock lock(thumb_cache_mutex);
398 ThumbnailCache::FileEntry *val = job->cache_->map_.lookup_ptr(request.file_path);
399 if (val != nullptr) {
400 val->used_at = math::max(val->used_at, request.requested_at);
401 val->frames.append(
402 {request.frame_index, request.stream_index, thumb, request.requested_at});
403 }
404 else {
405 IMB_freeImBuf(thumb);
406 }
407 /* Remove the request from original set. */
408 job->cache_->requests_.remove(request);
409 }
410
411 if (thumb) {
412 worker_status->do_update = true;
413 }
414 }
415 if (cur_anim != nullptr) {
416 MOV_close(cur_anim);
417 cur_anim = nullptr;
418 }
419 }
420 }
421
422#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
423 clock_t t1 = clock();
424 printf("VSE thumb job: %i thumbs (%i img, %i movie) in %.3f sec\n",
425 total_thumbs.load(),
426 total_images.load(),
427 total_movies.load(),
428 double(t1 - t0) / CLOCKS_PER_SEC);
429#endif
430}
431
432void ThumbGenerationJob::end_fn(void *customdata)
433{
434 ThumbGenerationJob *job = static_cast<ThumbGenerationJob *>(customdata);
436}
437
439 const std::string &key,
440 int frame_index,
441 float timeline_frame,
442 const bContext *C,
443 const Strip *strip)
444{
445 int64_t cur_time = cache.logical_time_;
446 ThumbnailCache::FileEntry *val = cache.map_.lookup_ptr(key);
447
448 if (val == nullptr) {
449 /* Nothing in cache for this path yet. */
450 ThumbnailCache::FileEntry value;
451 value.used_at = cur_time;
452 cache.map_.add_new(key, value);
453 val = cache.map_.lookup_ptr(key);
454 }
455 BLI_assert_msg(val != nullptr, "Thumbnail cache value should never be null here");
456
457 /* Search thumbnail entries of this file for closest match to the frame we want. */
458 int64_t best_index = -1;
459 int best_score = INT_MAX;
460 for (int64_t index = 0; index < val->frames.size(); index++) {
461 if (strip->streamindex != val->frames[index].stream_index) {
462 continue; /* Different video stream than what we need, ignore. */
463 }
464 int score = math::abs(frame_index - val->frames[index].frame_index);
465 if (score < best_score) {
466 best_score = score;
467 best_index = index;
468 if (score == 0) {
469 break;
470 }
471 }
472 }
473
474 if (best_score > 0) {
475 /* We do not have an exact frame match, add a thumb generation request. */
476 const StripElem *se = strip->data->stripdata;
477 int img_width = se->orig_width;
478 int img_height = se->orig_height;
479 ThumbnailCache::Request request(key,
480 frame_index,
481 strip->streamindex,
482 StripType(strip->type),
483 cur_time,
484 timeline_frame,
485 strip->channel,
486 img_width,
487 img_height);
488 cache.requests_.add(request);
490 }
491
492 if (best_index < 0) {
493 return nullptr;
494 }
495
496 /* Return the closest thumbnail fit we have so far. */
497 val->used_at = math::max(val->used_at, cur_time);
498 val->frames[best_index].used_at = math::max(val->frames[best_index].used_at, cur_time);
499 return val->frames[best_index].thumb;
500}
501
503 Scene *scene,
504 const Strip *strip,
505 float timeline_frame)
506{
507 if (!strip_can_have_thumbnail(scene, strip)) {
508 return nullptr;
509 }
510
511 timeline_frame = math::round(timeline_frame);
512
513 const std::string key = get_path_from_strip(scene, strip, timeline_frame);
514 int frame_index = give_frame_index(scene, strip, timeline_frame);
515 if (strip->type == STRIP_TYPE_MOVIE) {
516 frame_index += strip->anim_startofs;
517 }
518
519 ImBuf *res = nullptr;
520 {
521 std::scoped_lock lock(thumb_cache_mutex);
523 res = query_thumbnail(*cache, key, frame_index, timeline_frame, C, strip);
524 }
525
526 if (res) {
527 IMB_refImBuf(res);
528 }
529 return res;
530}
531
533{
534 if (!strip_can_have_thumbnail(scene, strip)) {
535 return;
536 }
537
538 std::scoped_lock lock(thumb_cache_mutex);
540 if (cache != nullptr) {
541 if (ELEM((strip)->type, STRIP_TYPE_MOVIE, STRIP_TYPE_IMAGE)) {
542 const StripElem *elem = strip->data->stripdata;
543 if (elem != nullptr) {
544 int paths_count = 1;
545 if (strip->type == STRIP_TYPE_IMAGE) {
546 /* Image strip has array of file names. */
547 paths_count = int(MEM_allocN_len(elem) / sizeof(*elem));
548 }
549 char filepath[FILE_MAX];
550 const char *basepath = ID_BLEND_PATH_FROM_GLOBAL(&scene->id);
551 for (int i = 0; i < paths_count; i++, elem++) {
552 BLI_path_join(filepath, sizeof(filepath), strip->data->dirpath, elem->filename);
553 BLI_path_abs(filepath, basepath);
554 cache->remove_entry(filepath);
555 }
556 }
557 }
558 }
559}
560
562{
563 std::scoped_lock lock(thumb_cache_mutex);
565 if (cache != nullptr) {
566 cache->logical_time_++;
567
568 /* Count total number of thumbnails, and track which one is the least recently used file. */
569 int64_t entries = 0;
570 std::string oldest_file;
571 /* Do not remove thumbnails for files used within last 10 updates. */
572 int64_t oldest_time = cache->logical_time_ - 10;
573 int64_t oldest_entries = 0;
574 for (const auto &item : cache->map_.items()) {
575 entries += item.value.frames.size();
576 if (item.value.used_at < oldest_time) {
577 oldest_file = item.key;
578 oldest_time = item.value.used_at;
579 oldest_entries = item.value.frames.size();
580 }
581 }
582
583 /* If we're beyond capacity and have a long-unused file, remove that. */
584 if (entries > MAX_THUMBNAILS && !oldest_file.empty()) {
585 cache->remove_entry(oldest_file);
586 entries -= oldest_entries;
587 }
588
589 /* If we're still beyond capacity, remove individual long-unused (but not within
590 * last 100 updates) individual frames. */
591 if (entries > MAX_THUMBNAILS) {
592 for (const auto &item : cache->map_.items()) {
593 for (int64_t i = 0; i < item.value.frames.size(); i++) {
594 if (item.value.frames[i].used_at < cache->logical_time_ - 100) {
595 IMB_freeImBuf(item.value.frames[i].thumb);
596 item.value.frames.remove_and_reorder(i);
597 }
598 }
599 }
600 }
601 }
602}
603
605{
606 std::scoped_lock lock(thumb_cache_mutex);
608 if (cache != nullptr) {
609 cache->requests_.remove_if([&](const ThumbnailCache::Request &request) {
610 return request.timeline_frame < rect.xmin || request.timeline_frame > rect.xmax ||
611 request.channel < rect.ymin || request.channel > rect.ymax;
612 });
613 }
614}
615
617{
618 std::scoped_lock lock(thumb_cache_mutex);
620 if (cache != nullptr) {
621 scene->ed->runtime.thumbnail_cache->clear();
622 }
623}
624
626{
627 std::scoped_lock lock(thumb_cache_mutex);
629 if (cache != nullptr) {
630 BLI_assert(cache == scene->ed->runtime.thumbnail_cache);
631 MEM_delete(scene->ed->runtime.thumbnail_cache);
632 scene->ed->runtime.thumbnail_cache = nullptr;
633 }
634}
635
636} // namespace blender::seq
wmWindow * CTX_wm_window(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
Scene * CTX_data_sequencer_scene(const bContext *C)
#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(...)
#define ID_BLEND_PATH_FROM_GLOBAL(_id)
Definition DNA_ID.h:688
@ 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:214
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:465
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:1807
eWM_JobFlag
Definition WM_api.hh:1759
#define ND_SEQUENCER
Definition WM_types.hh:437
#define NC_SCENE
Definition WM_types.hh:378
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)
nullptr float
#define printf(...)
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:66
MovieReader * MOV_open_file(const char *filepath, const int ib_flags, const int streamindex, const bool keep_original_colorspace, 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:115
void seq_imbuf_assign_spaces(const Scene *scene, ImBuf *ibuf)
Definition render.cc:102
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:238
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
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:341
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:376
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:479
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:211
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:388
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:360