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 {
35
40
41 bool operator==(const Key &other) const
42 {
43 return seqbasep == other.seqbasep && timeline_frame == other.timeline_frame &&
45 }
46 };
48
50 {
51 clear();
52 }
53
54 void clear()
55 {
56 for (ImBuf *item : map_.values()) {
57 IMB_freeImBuf(item);
58 }
59 map_.clear();
60 }
61};
62
64{
66 if (*cache == nullptr) {
67 *cache = MEM_new<FinalImageCache>(__func__);
68 }
69 return *cache;
70}
71
73{
74 if (scene == nullptr || scene->ed == nullptr) {
75 return nullptr;
76 }
77 return scene->ed->runtime.final_image_cache;
78}
79
81 const ListBase *seqbasep,
82 const float timeline_frame,
83 const int view_id,
84 const int display_channel)
85{
86 const FinalImageCache::Key key = {
87 seqbasep, int(math::round(timeline_frame)), view_id, display_channel};
88
89 ImBuf *res = nullptr;
90 {
91 std::lock_guard lock(final_image_cache_mutex);
93 if (cache == nullptr) {
94 return nullptr;
95 }
96 res = cache->map_.lookup_default(key, nullptr);
97 }
98
99 if (res) {
100 IMB_refImBuf(res);
101 }
102 return res;
103}
104
106 const ListBase *seqbasep,
107 const float timeline_frame,
108 const int view_id,
109 const int display_channel,
110 ImBuf *image)
111{
112 const FinalImageCache::Key key = {
113 seqbasep, int(math::round(timeline_frame)), view_id, display_channel};
114
115 IMB_refImBuf(image);
116
117 std::lock_guard lock(final_image_cache_mutex);
119
120 cache->map_.add_or_modify(
121 key,
122 [&](ImBuf **value) { *value = image; },
123 [&](ImBuf **existing) {
124 if (*existing) {
125 IMB_freeImBuf(*existing);
126 }
127 *existing = image;
128 });
129}
130
132 const float timeline_frame_start,
133 const float timeline_frame_end)
134{
135 std::lock_guard lock(final_image_cache_mutex);
137 if (cache == nullptr) {
138 return;
139 }
140
141 const int key_start = int(math::floor(timeline_frame_start));
142 const int key_end = int(math::ceil(timeline_frame_end));
143
144 for (auto it = cache->map_.items().begin(); it != cache->map_.items().end(); it++) {
145 const int key = (*it).key.timeline_frame;
146 if (key >= key_start && key <= key_end) {
147 IMB_freeImBuf((*it).value);
148 cache->map_.remove(it);
149 }
150 }
151}
152
154{
155 std::lock_guard lock(final_image_cache_mutex);
157 if (cache != nullptr) {
159 }
160}
161
163{
164 std::lock_guard lock(final_image_cache_mutex);
166 if (cache != nullptr) {
167 BLI_assert(cache == scene->ed->runtime.final_image_cache);
168 MEM_delete(scene->ed->runtime.final_image_cache);
169 scene->ed->runtime.final_image_cache = nullptr;
170 }
171}
172
174 void *userdata,
175 void callback_iter(void *userdata, int timeline_frame))
176{
177 std::lock_guard lock(final_image_cache_mutex);
179 if (cache == nullptr) {
180 return;
181 }
182 for (const FinalImageCache::Key &frame_view : cache->map_.keys()) {
183 callback_iter(userdata, frame_view.timeline_frame);
184 }
185}
186
188{
189 std::lock_guard lock(final_image_cache_mutex);
191 if (cache == nullptr) {
192 return 0;
193 }
194 size_t size = 0;
195 for (ImBuf *frame : cache->map_.values()) {
197 }
198 return size;
199}
200
202{
203 std::lock_guard lock(final_image_cache_mutex);
205 if (cache == nullptr) {
206 return 0;
207 }
208 return cache->map_.size();
209}
210
212{
213 std::lock_guard lock(final_image_cache_mutex);
215 if (cache == nullptr) {
216 return false;
217 }
218
219 /* Find which entry to remove -- we pick the one that is furthest from the current frame,
220 * biasing the ones that are behind the current frame.
221 *
222 * However, do not try to evict entries from the current prefetch job range -- we need to
223 * be able to fully fill the cache from prefetching, and then actually stop the job when it
224 * is full and no longer can evict anything. */
225 int cur_prefetch_start = std::numeric_limits<int>::min();
226 int cur_prefetch_end = std::numeric_limits<int>::min();
227 seq_prefetch_get_time_range(scene, &cur_prefetch_start, &cur_prefetch_end);
228
229 const int cur_frame = scene->r.cfra;
230 FinalImageCache::Key best_key = {};
231 ImBuf *best_item = nullptr;
232 int best_score = 0;
233 for (const auto &item : cache->map_.items()) {
234 const int item_frame = item.key.timeline_frame;
235 if (item_frame >= cur_prefetch_start && item_frame <= cur_prefetch_end) {
236 continue; /* Within active prefetch range, do not try to remove it. */
237 }
238
239 /* Score for removal is distance to current frame; 2x that if behind current frame. */
240 int score = 0;
241 if (item_frame < cur_frame) {
242 score = (cur_frame - item_frame) * 2;
243 }
244 else if (item_frame > cur_frame) {
245 score = item_frame - cur_frame;
246 }
247 if (score > best_score) {
248 best_key = item.key;
249 best_item = item.value;
250 best_score = score;
251 }
252 }
253
254 /* Remove if we found one. */
255 if (best_item != nullptr) {
256 IMB_freeImBuf(best_item);
257 cache->map_.remove(best_key);
258 return true;
259 }
260
261 /* Did not find anything to remove. */
262 return false;
263}
264
265} // 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)
void final_image_cache_put(Scene *scene, const ListBase *seqbasep, const float timeline_frame, const int view_id, const int display_channel, ImBuf *image)
bool final_image_cache_evict(Scene *scene)
ImBuf * final_image_cache_get(Scene *scene, const ListBase *seqbasep, const float timeline_frame, const int view_id, const int display_channel)
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
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