Blender V4.5
source_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_map.hh"
10#include "BLI_mutex.hh"
11#include "BLI_vector.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 "prefetch.hh"
23#include "source_image_cache.hh"
24
25namespace blender::seq {
26
28
30 struct FrameEntry {
31 ImBuf *image = nullptr;
38 float strip_frame = 0;
39 };
40
45
47
49 {
50 clear();
51 }
52
53 void clear()
54 {
55 for (const auto &item : map_.items()) {
56 for (const auto &frame : item.value.frames.values()) {
57 IMB_freeImBuf(frame.image);
58 }
59 }
60 map_.clear();
61 }
62
63 void remove_entry(const Strip *strip)
64 {
65 StripEntry *entry = map_.lookup_ptr(strip);
66 if (entry == nullptr) {
67 return;
68 }
69 for (const auto &frame : entry->frames.values()) {
70 IMB_freeImBuf(frame.image);
71 }
72 map_.remove_contained(strip);
73 }
74};
75
77{
79 if (*cache == nullptr) {
80 *cache = MEM_new<SourceImageCache>(__func__);
81 }
82 return *cache;
83}
84
86{
87 if (scene == nullptr || scene->ed == nullptr) {
88 return nullptr;
89 }
90 return scene->ed->runtime.source_image_cache;
91}
92
93static float give_cache_frame_index(const Scene *scene, const Strip *strip, float timeline_frame)
94{
95 float frame_index = give_frame_index(scene, strip, timeline_frame);
96 if (strip->type != STRIP_TYPE_SCENE) {
97 /* Scene strips that are slowed down need fractional frame index for animation interpolation;
98 * for others use integer index for better cache hit rates. */
99 frame_index = std::trunc(frame_index);
100 }
101 if (strip->type == STRIP_TYPE_MOVIE) {
102 frame_index += strip->anim_startofs;
103 }
104 return frame_index;
105}
106
107ImBuf *source_image_cache_get(const RenderData *context, const Strip *strip, float timeline_frame)
108{
109 if (context->skip_cache || context->is_proxy_render || strip == nullptr) {
110 return nullptr;
111 }
112
113 Scene *scene = prefetch_get_original_scene_and_strip(context, strip);
114 timeline_frame = math::round(timeline_frame);
115 const float frame_index = give_cache_frame_index(scene, strip, timeline_frame);
116 const int view_id = context->view_id;
117
118 ImBuf *res = nullptr;
119 {
120 std::lock_guard lock(source_image_cache_mutex);
122 if (cache == nullptr) {
123 return nullptr;
124 }
125
126 SourceImageCache::StripEntry *val = cache->map_.lookup_ptr(strip);
127 if (val == nullptr) {
128 /* Nothing in cache for this strip yet. */
129 return nullptr;
130 }
131 /* Search entries for the frame we want. */
132 SourceImageCache::FrameEntry *frame = val->frames.lookup_ptr({frame_index, view_id});
133 if (frame != nullptr) {
134 res = frame->image;
135 }
136
137 /* For effect and scene strips, check if the cached result matches our current
138 * render resolution. If it does not, remove stale source entries for this strip. */
139 if (res != nullptr &&
140 ((strip->type & STRIP_TYPE_EFFECT) != 0 || strip->type == STRIP_TYPE_SCENE))
141 {
142 if (res->x != context->rectx || res->y != context->recty) {
143 cache->remove_entry(strip);
144 return nullptr;
145 }
146 }
147 }
148
149 if (res) {
150 IMB_refImBuf(res);
151 }
152 return res;
153}
154
156 const Strip *strip,
157 float timeline_frame,
158 ImBuf *image)
159{
160 if (context->skip_cache || context->is_proxy_render || strip == nullptr || image == nullptr) {
161 return;
162 }
163
164 Scene *scene = prefetch_get_original_scene_and_strip(context, strip);
165 timeline_frame = math::round(timeline_frame);
166
167 const float frame_index = give_cache_frame_index(scene, strip, timeline_frame);
168 const int view_id = context->view_id;
169
170 IMB_refImBuf(image);
171
172 std::lock_guard lock(source_image_cache_mutex);
174
175 SourceImageCache::StripEntry *val = cache->map_.lookup_ptr(strip);
176
177 if (val == nullptr) {
178 /* Nothing in cache for this strip yet. */
179 cache->map_.add_new(strip, {});
180 val = cache->map_.lookup_ptr(strip);
181 }
182 BLI_assert_msg(val != nullptr, "Source image cache value should never be null here");
183
184 SourceImageCache::FrameEntry &frame = val->frames.lookup_or_add_default({frame_index, view_id});
185 if (frame.image != nullptr) {
186 IMB_freeImBuf(frame.image);
187 }
188 frame.strip_frame = timeline_frame - strip->start;
189 frame.image = image;
190}
191
193{
194 std::lock_guard lock(source_image_cache_mutex);
196 if (cache != nullptr) {
197 cache->remove_entry(strip);
198 }
199}
200
202{
203 std::lock_guard lock(source_image_cache_mutex);
205 if (cache != nullptr) {
207 }
208}
209
211{
212 std::lock_guard lock(source_image_cache_mutex);
214 if (cache != nullptr) {
215 BLI_assert(cache == scene->ed->runtime.source_image_cache);
216 MEM_delete(scene->ed->runtime.source_image_cache);
217 scene->ed->runtime.source_image_cache = nullptr;
218 }
219}
220
222 void *userdata,
223 void callback_iter(void *userdata,
224 const Strip *strip,
225 int timeline_frame))
226{
227 std::lock_guard lock(source_image_cache_mutex);
229 if (cache == nullptr) {
230 return;
231 }
232
233 for (const auto &[strip, frames] : cache->map_.items()) {
234 for (const auto &[frame_key, frame] : frames.frames.items()) {
235 const float timeline_frame = strip->start + frame.strip_frame;
236 callback_iter(userdata, strip, int(timeline_frame));
237 }
238 }
239}
240
242{
243 std::lock_guard lock(source_image_cache_mutex);
245 if (cache == nullptr) {
246 return 0;
247 }
248 size_t size = 0;
249 for (const SourceImageCache::StripEntry &entry : cache->map_.values()) {
250 for (const SourceImageCache::FrameEntry &frame : entry.frames.values()) {
251 size += IMB_get_size_in_memory(frame.image);
252 }
253 }
254 return size;
255}
256
258{
259 std::lock_guard lock(source_image_cache_mutex);
261 if (cache == nullptr) {
262 return 0;
263 }
264 size_t count = 0;
265 for (const SourceImageCache::StripEntry &entry : cache->map_.values()) {
266 count += entry.frames.size();
267 }
268 return count;
269}
270
272{
273 std::lock_guard lock(source_image_cache_mutex);
275 if (cache == nullptr) {
276 return false;
277 }
278
279 /* Find which entry to remove -- we pick the one that is furthest from the current frame,
280 * biasing the ones that are behind the current frame. */
281 const int cur_frame = scene->r.cfra;
282 SourceImageCache::StripEntry *best_strip = nullptr;
283 std::pair<float, int> best_key = {};
284 int best_score = 0;
285 for (const auto &strip : cache->map_.items()) {
286 for (const auto &entry : strip.value.frames.items()) {
287 const int item_frame = int(strip.key->start + entry.value.strip_frame);
288 /* Score for removal is distance to current frame; 2x that if behind current frame. */
289 int score = 0;
290 if (item_frame < cur_frame) {
291 score = (cur_frame - item_frame) * 2;
292 }
293 else if (item_frame > cur_frame) {
294 score = item_frame - cur_frame;
295 }
296 if (score > best_score) {
297 best_strip = &strip.value;
298 best_key = entry.key;
299 best_score = score;
300 }
301 }
302 }
303
304 /* Remove if we found one. */
305 if (best_strip != nullptr) {
306 IMB_freeImBuf(best_strip->frames.lookup(best_key).image);
307 best_strip->frames.remove(best_key);
308 return true;
309 }
310
311 return false;
312}
313
314} // namespace blender::seq
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
@ STRIP_TYPE_SCENE
@ STRIP_TYPE_MOVIE
@ STRIP_TYPE_EFFECT
void IMB_freeImBuf(ImBuf *ibuf)
void IMB_refImBuf(ImBuf *ibuf)
size_t IMB_get_size_in_memory(const ImBuf *ibuf)
volatile int lock
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int count
T round(const T &a)
void source_image_cache_put(const RenderData *context, const Strip *strip, float timeline_frame, ImBuf *image)
static float give_cache_frame_index(const Scene *scene, const Strip *strip, float timeline_frame)
void source_image_cache_iterate(Scene *scene, void *userdata, void callback_iter(void *userdata, const Strip *strip, int timeline_frame))
float give_frame_index(const Scene *scene, const Strip *strip, float timeline_frame)
Definition strip_time.cc:52
bool source_image_cache_evict(Scene *scene)
void source_image_cache_clear(Scene *scene)
void source_image_cache_invalidate_strip(Scene *scene, const Strip *strip)
size_t source_image_cache_get_image_count(const Scene *scene)
static SourceImageCache * query_source_image_cache(const Scene *scene)
ImBuf * source_image_cache_get(const RenderData *context, const Strip *strip, float timeline_frame)
Scene * prefetch_get_original_scene_and_strip(const RenderData *context, const Strip *&strip)
Definition prefetch.cc:170
void source_image_cache_destroy(Scene *scene)
static Mutex source_image_cache_mutex
size_t source_image_cache_calc_memory_size(const Scene *scene)
static SourceImageCache * ensure_source_image_cache(Scene *scene)
std::mutex Mutex
Definition BLI_mutex.hh:47
SourceImageCache * source_image_cache
EditingRuntime runtime
struct Editing * ed
struct RenderData r
Map< std::pair< float, int >, FrameEntry > frames
void remove_entry(const Strip *strip)
Map< const Strip *, StripEntry > map_