Skip to content

Prefetching

Prefetching can be used to improve consistency of playback speed if image processing demands are unevenly spread. This is done by rendering frames in advance in background job and filling cache up to its limit. This way overhead of drawing code is eliminated too, even though it's usually not significant.

Startup and stop procedure

When prefetching is enabled, function SEQ_render_give_ibuf() which is responsible for rendering timeline content will try to start prefetching by calling function seq_prefetch_start. If prefetch thread is running, but it is suspended, it is resumed. On any update done to sequencer data that affects image output, prefetch must be stopped by calling SEQ_prefetch_stop(). This is done as a part of cache invalidation, but there are cases when it has to be done explicitly, for example on undo. When image is re-rendered after change, prefetching will restart with new data.

Rendering loop

Because DNA structures may contain runtime data necessary for operation, prefetch operates on Scene copy, which contains sequencer data as well. It hosts its own dependency graph to evaluate data for each frame and a copy of SeqRenderData rendering context. This context is marked as for prefetching(is_prefetch_render), so another prefetching job is not created from prefetching thread. Finally function seq_prefetch_frames() is executed in a thread. Rendering loop uses dependency loop to evaluate all datablocks. There is call to BKE_animsys_evaluate_animdata which I am not entirely sure why it is needed as it shouldn't be. Function SEQ_render_give_ibuf() is called on copy of scene and rendering context. Rendering loop can be suspended by timeline scrubbing or cache being full. When there are no more frames to render or prefetching is disabled, job is terminated. Loop resumes or starts, when cache is freed again.

Cache handling

Prefetching job has no prior information on where it should stop. This is because each frame may have different size in memory or frames may be missing. Further it does not know about total memory usage since it may change outside of sequencer. Because of this function seq_prefetch_do_suspend() polls function seq_prefetch_is_cache_full() on every iteration of rendering loop. If cache is full, thread will be suspended. Once cache is freed through startup procedure signal is sent to prefetching thread to check memory usage and resume render loop if possible. Unlike normal sequencer rendering procedure, prefetch job must store SEQ_CACHE_STORE_FINAL_OUT image regardless of memory usage. This is, because function seq_cache_put_if_possible would not store image if it's size is greater than available memory and therefore function seq_prefetch_is_cache_full() would never return true. Prefetch job relies on disregarding this limit, but since this will cause thread being suspended, it will never be crossed by large amount.

Another important fact is, that each scene with sequencer data has it's own cache. This means, that calling function SEQ_render_give_ibuf() with copy of original data would store image with different context and it wouldn't be possible to get it from context that is used to draw UI. This is resolved by referencing original prefetch job in copied scene - pfjob->scene_eval->ed->prefetch_job = pfjob; In cache code original data can be accessed with function seq_prefetch_get_original_context(). This is not great way to resolve the issue though.

Finally, cache freeing strategy is changed from furthest cached frame to "ideally leftmost cached frame outside of prefetching area, otherwise rightmost frame outside of prefetching area". This is, because user can move current frame few frames before, inside or after prefetched area. This can also happen frequently, so freeing strategy should be least disruptive and preserve continuous flow of cached images as much as it can.

To debug prefetching process, cache overlay is useful tool. To continuously update cache overlay, function SEQ_prefetch_need_redraw is polled from sequencer drawing code and adds notifier to redraw itself which creates event loop. Event loop is terminated when prefetching is suspended.

Limitations

Currently 3D scene strips can't be prefetched. This is for 2 reasons - They use offscreen rendering, which requires interlock with main frame, which is not implemented. Even if background rendering would be used, problem is that dependency graph used by prefetch job can't be used to create dependency graph for render engine.