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.