Blender V4.5
final_image_cache.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_hash.hh"
10#include "BLI_map.hh"
11#include "BLI_mutex.hh"
12
13#include "DNA_scene_types.h"
14#include "DNA_sequence_types.h"
15
16#include "IMB_imbuf.hh"
17
18#include "SEQ_relations.hh"
19#include "SEQ_render.hh"
20#include "SEQ_time.hh"
21
22#include "final_image_cache.hh"
23#include "prefetch.hh"
24
25namespace blender::seq {
26
28
30 struct Key {
36
42
43 bool operator==(const Key &other) const
44 {
45 return seqbasep == other.seqbasep && timeline_frame == other.timeline_frame &&
46 view_id == other.view_id && display_channel == other.display_channel &&
48 }
49 };
51
53 {
54 clear();
55 }
56
57 void clear()
58 {
59 for (ImBuf *item : map_.values()) {
60 IMB_freeImBuf(item);
61 }
62 map_.clear();
63 }
64};
65
67{
69 if (*cache == nullptr) {
70 *cache = MEM_new<FinalImageCache>(__func__);
71 }
72 return *cache;
73}
74
76{
77 if (scene == nullptr || scene->ed == nullptr) {
78 return nullptr;
79 }
80 return scene->ed->runtime.final_image_cache;
81}
82
84 const ListBase *seqbasep,
85 float timeline_frame,
86 int view_id,
87 int display_channel,
88 int2 image_size)
89{
90 const FinalImageCache::Key key = {
91 seqbasep, int(math::round(timeline_frame)), view_id, display_channel, image_size};
92
93 ImBuf *res = nullptr;
94 {
95 std::lock_guard lock(final_image_cache_mutex);
97 if (cache == nullptr) {
98 return nullptr;
99 }
100 res = cache->map_.lookup_default(key, nullptr);
101 }
102
103 if (res) {
104 IMB_refImBuf(res);
105 }
106 return res;
107}
108
110 const ListBase *seqbasep,
111 const float timeline_frame,
112 const int view_id,
113 const int display_channel,
114 int2 image_size,
115 ImBuf *image)
116{
117 const FinalImageCache::Key key = {
118 seqbasep, int(math::round(timeline_frame)), view_id, display_channel, image_size};
119
120 IMB_refImBuf(image);
121
122 std::lock_guard lock(final_image_cache_mutex);
124
125 cache->map_.add_or_modify(
126 key,
127 [&](ImBuf **value) { *value = image; },
128 [&](ImBuf **existing) {
129 if (*existing) {
130 IMB_freeImBuf(*existing);
131 }
132 *existing = image;
133 });
134}
135
137 const float timeline_frame_start,
138 const float timeline_frame_end)
139{
140 std::lock_guard lock(final_image_cache_mutex);
142 if (cache == nullptr) {
143 return;
144 }
145
146 const int key_start = int(math::floor(timeline_frame_start));
147 const int key_end = int(math::ceil(timeline_frame_end));
148
149 for (auto it = cache->map_.items().begin(); it != cache->map_.items().end(); it++) {
150 const int key = (*it).key.timeline_frame;
151 if (key >= key_start && key <= key_end) {
152 IMB_freeImBuf((*it).value);
153 cache->map_.remove(it);
154 }
155 }
156}
157
159{
160 std::lock_guard lock(final_image_cache_mutex);
162 if (cache != nullptr) {
164 }
165}
166
168{
169 std::lock_guard lock(final_image_cache_mutex);
171 if (cache != nullptr) {
172 BLI_assert(cache == scene->ed->runtime.final_image_cache);
173 MEM_delete(scene->ed->runtime.final_image_cache);
174 scene->ed->runtime.final_image_cache = nullptr;
175 }
176}
177
179 void *userdata,
180 void callback_iter(void *userdata, int timeline_frame))
181{
182 std::lock_guard lock(final_image_cache_mutex);
184 if (cache == nullptr) {
185 return;
186 }
187 for (const FinalImageCache::Key &frame_view : cache->map_.keys()) {
188 callback_iter(userdata, frame_view.timeline_frame);
189 }
190}
191
193{
194 std::lock_guard lock(final_image_cache_mutex);
196 if (cache == nullptr) {
197 return 0;
198 }
199 size_t size = 0;
200 for (ImBuf *frame : cache->map_.values()) {
202 }
203 return size;
204}
205
207{
208 std::lock_guard lock(final_image_cache_mutex);
210 if (cache == nullptr) {
211 return 0;
212 }
213 return cache->map_.size();
214}
215
217{
218 std::lock_guard lock(final_image_cache_mutex);
220 if (cache == nullptr) {
221 return false;
222 }
223
224 /* Find which entry to remove -- we pick the one that is furthest from the current frame,
225 * biasing the ones that are behind the current frame.
226 *
227 * However, do not try to evict entries from the current prefetch job range -- we need to
228 * be able to fully fill the cache from prefetching, and then actually stop the job when it
229 * is full and no longer can evict anything. */
230 int cur_prefetch_start = std::numeric_limits<int>::min();
231 int cur_prefetch_end = std::numeric_limits<int>::min();
232 seq_prefetch_get_time_range(scene, &cur_prefetch_start, &cur_prefetch_end);
233
234 const int cur_frame = scene->r.cfra;
235 FinalImageCache::Key best_key = {};
236 ImBuf *best_item = nullptr;
237 int best_score = 0;
238 for (const auto &item : cache->map_.items()) {
239 const int item_frame = item.key.timeline_frame;
240 if (item_frame >= cur_prefetch_start && item_frame <= cur_prefetch_end) {
241 continue; /* Within active prefetch range, do not try to remove it. */
242 }
243
244 /* Score for removal is distance to current frame; 2x that if behind current frame. */
245 int score = 0;
246 if (item_frame < cur_frame) {
247 score = (cur_frame - item_frame) * 2;
248 }
249 else if (item_frame > cur_frame) {
250 score = item_frame - cur_frame;
251 }
252 if (score > best_score) {
253 best_key = item.key;
254 best_item = item.value;
255 best_score = score;
256 }
257 }
258
259 /* Remove if we found one. */
260 if (best_item != nullptr) {
261 IMB_freeImBuf(best_item);
262 cache->map_.remove(best_key);
263 return true;
264 }
265
266 /* Did not find anything to remove. */
267 return false;
268}
269
270} // namespace blender::seq
#define BLI_assert(a)
Definition BLI_assert.h:46
void IMB_freeImBuf(ImBuf *ibuf)
void IMB_refImBuf(ImBuf *ibuf)
size_t IMB_get_size_in_memory(const ImBuf *ibuf)
volatile int lock
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
T floor(const T &a)
T ceil(const T &a)
T round(const T &a)
static Mutex final_image_cache_mutex
size_t final_image_cache_calc_memory_size(const Scene *scene)
static FinalImageCache * ensure_final_image_cache(Scene *scene)
void final_image_cache_invalidate_frame_range(Scene *scene, const float timeline_frame_start, const float timeline_frame_end)
void final_image_cache_iterate(Scene *scene, void *userdata, void callback_iter(void *userdata, int timeline_frame))
void final_image_cache_destroy(Scene *scene)
void final_image_cache_clear(Scene *scene)
ImBuf * final_image_cache_get(Scene *scene, const ListBase *seqbasep, float timeline_frame, int view_id, int display_channel, int2 image_size)
bool final_image_cache_evict(Scene *scene)
void final_image_cache_put(Scene *scene, const ListBase *seqbasep, const float timeline_frame, const int view_id, const int display_channel, int2 image_size, ImBuf *image)
size_t final_image_cache_get_image_count(const Scene *scene)
static FinalImageCache * query_final_image_cache(const Scene *scene)
void seq_prefetch_get_time_range(Scene *scene, int *r_start, int *r_end)
Definition prefetch.cc:202
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
VecBase< int32_t, 2 > int2
std::mutex Mutex
Definition BLI_mutex.hh:47
FinalImageCache * final_image_cache
EditingRuntime runtime
struct Editing * ed
struct RenderData r
bool operator==(const Key &other) const