mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-28 11:47:21 -06:00
M5: drawcraft-cli bench; canvas keyed by per-document uid (no stale frame from another tab); brushed strokes keep brush art under effects
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
acb2566d89
commit
c06fad7b2b
@@ -4,6 +4,7 @@
|
||||
//! drawcraft-cli mcp [--connect 127.0.0.1:7979 | --headless]
|
||||
//! drawcraft-cli run [--in file.drawcraft|file.svg] [--cmd id [--params '{json}']]... [--export out.svg|.png|.drawcraft]... [--scale 2]
|
||||
//! drawcraft-cli commands
|
||||
//! drawcraft-cli bench FILE [--size 2880x1800] [--iters 5]
|
||||
//! ```
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
@@ -27,6 +28,10 @@ USAGE:
|
||||
|
||||
drawcraft-cli commands
|
||||
Print the command catalogue as JSON.
|
||||
|
||||
drawcraft-cli bench FILE [--size WxH] [--iters N]
|
||||
Render FILE (.drawcraft/.svg) fitted to WxH (default 2880x1800) and print ms per frame
|
||||
(warm), multithreaded and single-threaded.
|
||||
";
|
||||
|
||||
fn main() -> ExitCode {
|
||||
@@ -35,6 +40,7 @@ fn main() -> ExitCode {
|
||||
Some("mcp") => mcp(&args[1..]),
|
||||
Some("run") => run(&args[1..]),
|
||||
Some("commands") => commands(),
|
||||
Some("bench") => bench(&args[1..]),
|
||||
Some("-h" | "--help" | "help") | None => {
|
||||
print!("{USAGE}");
|
||||
Ok(())
|
||||
@@ -150,3 +156,49 @@ fn run(args: &[String]) -> Result<(), String> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bench(args: &[String]) -> Result<(), String> {
|
||||
let mut file = None;
|
||||
let (mut w, mut h, mut iters) = (2880u32, 1800u32, 5u32);
|
||||
let mut it = args.iter();
|
||||
while let Some(a) = it.next() {
|
||||
match a.as_str() {
|
||||
"--size" => {
|
||||
let v = it.next().ok_or("--size needs WxH")?;
|
||||
let (a, b) = v.split_once('x').ok_or("--size needs WxH")?;
|
||||
w = a.parse().map_err(|_| "bad width")?;
|
||||
h = b.parse().map_err(|_| "bad height")?;
|
||||
}
|
||||
"--iters" => iters = it.next().and_then(|v| v.parse().ok()).ok_or("--iters needs a number")?,
|
||||
f if file.is_none() => file = Some(f.to_string()),
|
||||
other => return Err(format!("unknown bench option `{other}`")),
|
||||
}
|
||||
}
|
||||
let file = file.ok_or("bench needs a FILE")?;
|
||||
let mut hl = Headless::new();
|
||||
hl.call("app.open", json!({ "path": file })).map_err(|e| format!("open {file}: {e}"))?;
|
||||
let doc = hl.session.doc().map_err(|e| e.to_string())?.doc.clone();
|
||||
let b = doc.artboards.first().map(|a| a.rect).ok_or("document has no artboard")?;
|
||||
let z = (w as f64 / b.width()).min(h as f64 / b.height()) * 0.95;
|
||||
let view = drawcraft_geom::Affine::translate((w as f64 / 2.0, h as f64 / 2.0))
|
||||
* drawcraft_geom::Affine::scale(z)
|
||||
* drawcraft_geom::Affine::translate(-b.center().to_vec2());
|
||||
let opts = drawcraft_render::RenderOptions::default();
|
||||
println!("{file}: {} nodes, {w}x{h}", doc.layers.iter().map(|l| l.count()).sum::<usize>());
|
||||
for threads in [drawcraft_render::default_threads(), 0] {
|
||||
let mut r = drawcraft_render::Renderer::new();
|
||||
r.threads = threads;
|
||||
r.render(&doc, w, h, view, &opts);
|
||||
let t = std::time::Instant::now();
|
||||
for _ in 0..iters {
|
||||
r.render(&doc, w, h, view, &opts);
|
||||
}
|
||||
println!(
|
||||
" threads {threads}: {:.1} ms/frame (drawn {}, culled {})",
|
||||
t.elapsed().as_secs_f64() * 1000.0 / iters as f64,
|
||||
r.stats.drawn,
|
||||
r.stats.culled
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user