Files
effectcraft/crates/ui-egui/tests/ui_ram_preview.rs
T
bflatasticandClaude Opus 5.5 26c714db7f M4.14: content-keyed RAM preview frames; proxies in the disk cache's content key
- FrameKey's identity is the comp's content (frames::comp_content), not the project revision:
  an edit keeps the cached frames of comps it doesn't touch and undo finds the frames of the
  state it returns to. The identity hashes the addresses of the comps a comp draws (~0.1 ms on
  the 1500-layer test project; the full content hash took ~117 ms), by value the footage, solids,
  proxies and project settings; with expressions, every item. Frames::content_of keeps a
  project snapshot per identity with frames (or in flight), so Arc::make_mut always copies an
  edited comp and its address can't be reused meanwhile; past 64 identities the least recently
  used go with their frames (long drags).
- drop_stale is gone (frames of other contents are evicted least-recently-shown first).
- disk_cache::comp_content_key covers items' proxies and Use Proxy (it served stale frames).
- Tests: frames (content keys, edits elsewhere and undo, bounded drags, in-place edit safety),
  ui_ram_preview (end to end), the disk cache proxy key. Docs: roadmap (M4 done), parity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 14:39:17 +07:00

54 lines
2.6 KiB
Rust

//! RAM preview frames are keyed by content: editing one comp keeps the cached frames of another,
//! and undo finds the frames of the state it returns to.
use effectcraft_engine::Session;
use effectcraft_engine::project::ItemId;
use effectcraft_ui_egui::EffectcraftApp;
use egui_kittest::Harness;
use serde_json::json;
fn settle(h: &mut Harness<'_, EffectcraftApp>) {
for _ in 0..600 {
h.step();
if h.state().frames.inflight() == 0 && h.state().frames.last_ms.lock().map(|v| *v > 0.0).unwrap_or(false) {
break;
}
std::thread::sleep(std::time::Duration::from_millis(10));
}
h.run_steps(2);
}
fn shown_cached(h: &Harness<'_, EffectcraftApp>, comp: u64) -> bool {
let app = h.state();
let key = effectcraft_ui_egui::frames::FrameKey { frame: 0, ..app.shown_series(ItemId(comp)) };
app.frames.is_cached(&key)
}
#[test]
fn an_edit_in_another_comp_keeps_the_cached_frames_and_undo_finds_them_again() {
let mut s = Session::default();
let b = s.execute("comp.new", json!({"name": "B", "width": 64, "height": 36, "duration": 1})).unwrap()["comp"].as_u64().unwrap();
let a = s.execute("comp.new", json!({"name": "A", "width": 64, "height": 36, "duration": 1})).unwrap()["comp"].as_u64().unwrap();
s.execute("layer.newSolid", json!({"color": "#3080ff"})).unwrap();
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_| EffectcraftApp::new(s));
settle(&mut h);
assert!(shown_cached(&h, a), "A's frame rendered");
// Edit B (open it, add a layer), then come back to A: its frame is still in RAM.
h.state_mut().session.execute("comp.open", json!({"comp": b})).unwrap();
h.state_mut().session.execute("layer.newSolid", json!({"color": "#ff8030"})).unwrap();
settle(&mut h);
let b_frame = h.state().shown_series(ItemId(b));
h.state_mut().session.execute("comp.open", json!({"comp": a})).unwrap();
assert!(shown_cached(&h, a), "A's frame survived the edit in B");
// Edit A, then undo: the frame of the state undo returns to is found again.
let before = h.state().shown_series(ItemId(a));
h.state_mut().session.execute("layer.newSolid", json!({"color": "#20c040"})).unwrap();
settle(&mut h);
assert_ne!(h.state().shown_series(ItemId(a)), before, "A's content changed");
h.state_mut().session.execute("edit.undo", json!({})).unwrap();
assert_eq!(h.state().shown_series(ItemId(a)), before);
assert!(shown_cached(&h, a), "undo finds the earlier frame");
// B's frame is still there too.
assert!(h.state().frames.is_cached(&effectcraft_ui_egui::frames::FrameKey { frame: 0, ..b_frame }));
}