Blender V5.0
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 {
34
39
40 bool operator==(const Key &other) const
41 {
42 return timeline_frame == other.timeline_frame && view_id == other.view_id &&
44 }
45 };
47
49 {
50 clear();
51 }
52
53 void clear()
54 {
55 for (ImBuf *item : map_.values()) {
56 IMB_freeImBuf(item);
57 }
58 map_.clear();
59 }
60};
61
63{
65 if (*cache == nullptr) {
66 *cache = MEM_new<FinalImageCache>(__func__);
67 }
68 return *cache;
69}
70
72{
73 if (scene == nullptr || scene->ed == nullptr) {
74 return nullptr;
75 }
76 return scene->ed->runtime.final_image_cache;
77}
78
79ImBuf *final_image_cache_get(Scene *scene, float timeline_frame, int view_id, int display_channel)
80{
81 const FinalImageCache::Key key = {int(math::round(timeline_frame)), view_id, display_channel};
82
83 ImBuf *res = nullptr;
84 {
85 std::lock_guard lock(final_image_cache_mutex);
87 if (cache == nullptr) {
88 return nullptr;
89 }
90 res = cache->map_.lookup_default(key, nullptr);
91 }
92
93 if (res) {
94 IMB_refImBuf(res);
95 }
96 return res;
97}
98
100 Scene *scene, float timeline_frame, int view_id, int display_channel, ImBuf *image)
101{
102 const FinalImageCache::Key key = {int(math::round(timeline_frame)), view_id, display_channel};
103
104 IMB_refImBuf(image);
105
106 std::lock_guard lock(final_image_cache_mutex);
108
109 cache->map_.add_or_modify(
110 key,
111 [&](ImBuf **value) { *value = image; },
112 [&](ImBuf **existing) {
113 if (*existing) {
114 IMB_freeImBuf(*existing);
115 }
116 *existing = image;
117 });
118}
119
121 const float timeline_frame_start,
122 const float timeline_frame_end)
123{
124 std::lock_guard lock(final_image_cache_mutex);
126 if (cache == nullptr) {
127 return;
128 }
129
130 const int key_start = int(math::floor(timeline_frame_start));
131 const int key_end = int(math::ceil(timeline_frame_end));
132
133 for (auto it = cache->map_.items().begin(); it != cache->map_.items().end(); it++) {
134 const int key = (*it).key.timeline_frame;
135 if (key >= key_start && key <= key_end) {
136 IMB_freeImBuf((*it).value);
137 cache->map_.remove(it);
138 }
139 }
140}
141
143{
144 std::lock_guard lock(final_image_cache_mutex);
146 if (cache != nullptr) {
148 }
149}
150
152{
153 std::lock_guard lock(final_image_cache_mutex);
155 if (cache != nullptr) {
156 BLI_assert(cache == scene->ed->runtime.final_image_cache);
157 MEM_delete(scene->ed->runtime.final_image_cache);
158 scene->ed->runtime.final_image_cache = nullptr;
159 }
160}
161
163 void *userdata,
164 void callback_iter(void *userdata, int timeline_frame))
165{
166 std::lock_guard lock(final_image_cache_mutex);
168 if (cache == nullptr) {
169 return;
170 }
171 for (const FinalImageCache::Key &frame_view : cache->map_.keys()) {
172 callback_iter(userdata, frame_view.timeline_frame);
173 }
174}
175
177{
178 std::lock_guard lock(final_image_cache_mutex);
180 if (cache == nullptr) {
181 return 0;
182 }
183 size_t size = 0;
184 for (ImBuf *frame : cache->map_.values()) {
186 }
187 return size;
188}
189
191{
192 std::lock_guard lock(final_image_cache_mutex);
194 if (cache == nullptr) {
195 return 0;
196 }
197 return cache->map_.size();
198}
199
201{
202 std::lock_guard lock(final_image_cache_mutex);
204 if (cache == nullptr) {
205 return false;
206 }
207
208 /* Find which entry to remove -- we pick the one that is furthest from the current frame,
209 * biasing the ones that are behind the current frame.
210 *
211 * However, do not try to evict entries from the current prefetch job range -- we need to
212 * be able to fully fill the cache from prefetching, and then actually stop the job when it
213 * is full and no longer can evict anything. */
214 int cur_prefetch_start = std::numeric_limits<int>::min();
215 int cur_prefetch_end = std::numeric_limits<int>::min();
217 /* Only activate the prefetch guards if the cache is active. */
218 seq_prefetch_get_time_range(scene, &cur_prefetch_start, &cur_prefetch_end);
219 }
220 const bool prefetch_loops_around = cur_prefetch_start > cur_prefetch_end;
221
222 const int timeline_start = PSFRA;
223 const int timeline_end = PEFRA;
224 /* If we wrap around, treat the timeline start as the playback head position.
225 * This is to try to mitigate un-needed cache evictions. */
226 const int cur_frame = prefetch_loops_around ? timeline_start : scene->r.cfra;
227
228 FinalImageCache::Key best_key = {};
229 ImBuf *best_item = nullptr;
230 int best_score = 0;
231 for (const auto &item : cache->map_.items()) {
232 const int item_frame = item.key.timeline_frame;
233 if (prefetch_loops_around) {
234 if (item_frame >= timeline_start && item_frame <= cur_prefetch_end) {
235 continue; /* Within active prefetch range, do not try to remove it. */
236 }
237 if (item_frame >= cur_prefetch_start && item_frame <= timeline_end) {
238 continue; /* Within active prefetch range, do not try to remove it. */
239 }
240 }
241 else if (item_frame >= cur_prefetch_start && item_frame <= cur_prefetch_end) {
242 continue; /* Within active prefetch range, do not try to remove it. */
243 }
244
245 /* Score for removal is distance to current frame; 2x that if behind current frame. */
246 int score = 0;
247 if (item_frame < cur_frame) {
248 score = (cur_frame - item_frame) * 2;
249 }
250 else if (item_frame > cur_frame) {
251 score = item_frame - cur_frame;
252 }
253 if (score > best_score) {
254 best_key = item.key;
255 best_item = item.value;
256 best_score = score;
257 }
258 }
259
260 /* Remove if we found one. */
261 if (best_item != nullptr) {
262 IMB_freeImBuf(best_item);
263 cache->map_.remove(best_key);
264 return true;
265 }
266
267 /* Did not find anything to remove. */
268 return false;
269}
270
271} // namespace blender::seq
#define BLI_assert(a)
Definition BLI_assert.h:46
#define PSFRA
#define PEFRA
@ SEQ_CACHE_STORE_FINAL_OUT
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)
ImBuf * final_image_cache_get(Scene *scene, float timeline_frame, int view_id, int display_channel)
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)
bool final_image_cache_evict(Scene *scene)
size_t final_image_cache_get_image_count(const Scene *scene)
void final_image_cache_put(Scene *scene, float timeline_frame, int view_id, int display_channel, ImBuf *image)
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:209
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
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