Phase 4: prefetch upcoming images during playback
`assets_needed_at(document, time)` (core) enumerates the image asset ids referenced by the visible vector layers' active keyframes at a time (top-level + group children). During playback the stage decodes the images needed ~0.5s ahead into the bounded ImageCache, so a keyframe that swaps image fills doesn't hitch when the playhead reaches it. Gated on is_playing; nested clip-instance recursion + background decode are refinements. Completes Phase 4 (image asset paging: Tier 2 decoded-cache LRU, Tier 1 lazy bytes, playback prefetch).
This commit is contained in:
parent
3d0a334014
commit
0883c77e2b
|
|
@ -158,6 +158,28 @@ impl Default for ImageCache {
|
|||
}
|
||||
}
|
||||
|
||||
/// Image asset ids referenced by the visible vector layers' active keyframes at `time`
|
||||
/// (top-level + group children). Used to prefetch/decode images ahead during playback.
|
||||
/// (Recursing into nested clip instances is a refinement.)
|
||||
pub fn assets_needed_at(document: &Document, time: f64) -> Vec<Uuid> {
|
||||
let mut ids = Vec::new();
|
||||
for layer in document.all_layers() {
|
||||
if let crate::layer::AnyLayer::Vector(vl) = layer {
|
||||
if !vl.layer.visible {
|
||||
continue;
|
||||
}
|
||||
if let Some(kf) = vl.keyframe_at(time) {
|
||||
for fill in &kf.graph.fills {
|
||||
if let Some(id) = fill.image_fill {
|
||||
ids.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ids
|
||||
}
|
||||
|
||||
/// Decode image bytes to a premultiplied tiny-skia Pixmap (CPU render path).
|
||||
fn decode_image_to_pixmap(data: &[u8]) -> Option<tiny_skia::Pixmap> {
|
||||
let img = image::load_from_memory(data).ok()?;
|
||||
|
|
|
|||
|
|
@ -439,6 +439,8 @@ struct VelloRenderContext {
|
|||
onion: crate::panes::OnionSkinSettings,
|
||||
/// `.beam` container path for lazily paging image-asset bytes in the ImageCache.
|
||||
container_path: Option<std::path::PathBuf>,
|
||||
/// Whether playback is active (gates image prefetch).
|
||||
is_playing: bool,
|
||||
/// Active layer for tool operations
|
||||
active_layer_id: Option<uuid::Uuid>,
|
||||
/// Delta for drag preview (world space)
|
||||
|
|
@ -1047,6 +1049,22 @@ impl egui_wgpu::CallbackTrait for VelloCallback {
|
|||
}
|
||||
}
|
||||
|
||||
// Prefetch: during playback, decode images the upcoming frames will need
|
||||
// (a short lookahead) into the bounded cache, so a keyframe that swaps image
|
||||
// fills doesn't hitch when the playhead reaches it.
|
||||
if self.ctx.is_playing {
|
||||
const PREFETCH_LOOKAHEAD: f64 = 0.5;
|
||||
let ahead = lightningbeam_core::renderer::assets_needed_at(
|
||||
&self.ctx.document,
|
||||
self.ctx.playback_time + PREFETCH_LOOKAHEAD,
|
||||
);
|
||||
for id in ahead {
|
||||
if let Some(asset) = self.ctx.document.get_image_asset(&id) {
|
||||
let _ = image_cache.get_or_decode(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drop(image_cache);
|
||||
let _t_after_scene_build = std::time::Instant::now();
|
||||
|
||||
|
|
@ -12078,6 +12096,7 @@ impl PaneRenderer for StagePane {
|
|||
tool_state: shared.tool_state.clone(),
|
||||
onion: shared.onion,
|
||||
container_path: shared.container_path.clone(),
|
||||
is_playing: *shared.is_playing,
|
||||
active_layer_id: *shared.active_layer_id,
|
||||
drag_delta,
|
||||
selection: shared.selection.clone(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue