mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-25 10:36:56 -06:00
- crates/export (effectcraft-export): render a comp's span to H.264 MP4 (+AAC), ProRes 422/4444 MOV (+PCM, alpha), PNG/JPEG/TIFF/EXR sequences and animated GIF via FilmCraft's pure-Rust encoders and MP4/MOV muxer; parallel batched frame rendering, progress callback with cancellation. - project: Render Queue model (Render Settings, Output Module, output path templates, status) saved with the project. - render: FootageSource::audio hook + comp audio mixdown (levels, in/out, solo, precomps); media implements it. - engine: Exporter hook, background/blocking render job, renderQueue.* commands (add Cmd+M, remove, setRender, setRenderSettings, setOutputModule, setOutput, move, duplicate, render, stop, list, formats). - host wires the exporter; CLI `render` subcommand. - ui-egui: After Effects-style Render Queue panel with automation ids. - tests: encode/decode round trips for every format (FilmCraft decoders, ffmpeg/ffprobe oracle when installed), audio, cancel, engine commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
44 lines
2.3 KiB
Rust
44 lines
2.3 KiB
Rust
//! Headless look at the Render Queue panel (wgpu offscreen). Ignored by default (needs a GPU
|
|
//! adapter); run with
|
|
//! `RQ_SNAPSHOT=/abs/out.png cargo test -p effectcraft-ui-egui --test render_queue_snapshot -- --ignored`.
|
|
|
|
use effectcraft_engine::Session;
|
|
use effectcraft_engine::project::render_queue::RenderStatus;
|
|
use effectcraft_ui_egui::EffectcraftApp;
|
|
use egui_kittest::Harness;
|
|
use serde_json::json;
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn render_queue_panel_snapshot() {
|
|
let mut s = Session::default();
|
|
s.execute("file.openDemoProject", json!({})).unwrap();
|
|
s.execute("renderQueue.add", json!({"output": "/renders/[compName].[fileExtension]", "resolution": "half", "timeSpan": "comp"})).unwrap();
|
|
s.execute("renderQueue.add", json!({"format": "prores", "channels": "rgba", "output": "/renders/[compName]_alpha.mov"})).unwrap();
|
|
s.execute("renderQueue.add", json!({"format": "png", "output": "/renders/frames/[compName]_[#####].png", "timeSpan": "workArea"})).unwrap();
|
|
s.execute("renderQueue.add", json!({"format": "gif", "resolution": "quarter"})).unwrap();
|
|
{
|
|
let p = std::sync::Arc::make_mut(&mut s.project);
|
|
p.render_queue[0].status = RenderStatus::Done;
|
|
p.render_queue[0].started = Some(1_790_000_000);
|
|
p.render_queue[0].render_time = Some(12.4);
|
|
p.render_queue[1].status = RenderStatus::Failed("disk full".into());
|
|
p.render_queue[1].started = Some(1_790_000_013);
|
|
p.render_queue[1].render_time = Some(0.8);
|
|
p.render_queue[3].render = false;
|
|
p.render_queue[3].status = RenderStatus::Unqueued;
|
|
}
|
|
let ids: Vec<u64> = s.project.render_queue.iter().take(3).map(|i| i.id).collect();
|
|
let mut app = Some(EffectcraftApp::new(s));
|
|
let mut h = Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).build_eframe(|_| app.take().expect("app"));
|
|
h.ctx.data_mut(|d| d.insert_temp(egui::Id::new("rq-ui"), (ids, None::<u64>)));
|
|
h.state_mut().show_panel(effectcraft_ui_egui::dock::PanelKind::RenderQueue);
|
|
h.run_steps(4);
|
|
let img = h.render().expect("render");
|
|
let out = std::env::var("RQ_SNAPSHOT").unwrap_or_else(|_| concat!(env!("CARGO_MANIFEST_DIR"), "/../../target/test-out/render_queue.png").into());
|
|
if let Some(d) = std::path::Path::new(&out).parent() {
|
|
std::fs::create_dir_all(d).unwrap();
|
|
}
|
|
img.save(&out).unwrap();
|
|
}
|