Headless pictures draw on the GPU, with the viewport's pipelines (CPU rasterizer as last tier) (#1555)
Thanks @jyviko.
This commit is contained in:
1 parent
48322f6464
commit
1a0b33fcf3
54 files changed
+3525
-1998
No files matched your search
+44
-9
@@ -10,6 +10,25 @@ fn server() -> Server {
|
||||
Server::new(Box::new(Headless::default()))
|
||||
}
|
||||
|
||||
/// Screenshots are drawn on the GPU by the renderer the host installs (`solidcraft-cli mcp`); the
|
||||
/// tests install the same one. `false`, with the reason printed, on a machine without a GPU
|
||||
/// adapter: the screenshots are then left out of the test.
|
||||
fn renders() -> bool {
|
||||
match solidcraft_gpu::capture::shared() {
|
||||
Ok(c) => {
|
||||
solidcraft_engine::capture::install(std::sync::Arc::new(move |scene, cam, w, h| {
|
||||
c.rgba(scene, cam, w, h).map(|rgba| solidcraft_engine::capture::Picture { rgba, renderer: c.renderer() }).map_err(|e| e.to_string())
|
||||
}));
|
||||
true
|
||||
}
|
||||
Err(e @ solidcraft_gpu::GpuError::NoAdapter(_)) if std::env::var_os("SOLIDCRAFT_REQUIRE_GPU").is_none() => {
|
||||
eprintln!("screenshots left out: {e}");
|
||||
false
|
||||
}
|
||||
Err(e) => panic!("{e}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(s: &mut Server, id: u64, method: &str, params: Value) -> Value {
|
||||
let line = json!({"jsonrpc": "2.0", "id": id, "method": method, "params": params}).to_string();
|
||||
let r = s.handle_line(&line).expect("reply");
|
||||
@@ -166,10 +185,12 @@ fn agent_builds_box_fillet_hole() {
|
||||
assert!(rel(volume(&mut s), expect) < 5e-4);
|
||||
|
||||
// Look at it from the top: a real PNG comes back as an image block.
|
||||
let img = tool(&mut s, "screenshot", json!({"view": "top", "width": 320, "height": 200}));
|
||||
assert_eq!(img["type"], "image");
|
||||
assert_eq!(img["mimeType"], "image/png");
|
||||
assert!(img["data"].as_str().unwrap().starts_with("iVBORw0KGgo"), "not a PNG");
|
||||
if renders() {
|
||||
let img = tool(&mut s, "screenshot", json!({"view": "top", "width": 320, "height": 200}));
|
||||
assert_eq!(img["type"], "image");
|
||||
assert_eq!(img["mimeType"], "image/png");
|
||||
assert!(img["data"].as_str().unwrap().starts_with("iVBORw0KGgo"), "not a PNG");
|
||||
}
|
||||
|
||||
// Export, save, start over, open.
|
||||
let dir = std::env::temp_dir().join(format!("solidcraft-mcp-test-{}", std::process::id()));
|
||||
@@ -181,8 +202,10 @@ fn agent_builds_box_fillet_hole() {
|
||||
tool(&mut s, "export", json!({"path": stl.to_string_lossy(), "format": "stl"}));
|
||||
assert!(std::fs::metadata(&stl).unwrap().len() > 1000);
|
||||
let shot = dir.join("shot.png");
|
||||
tool(&mut s, "screenshot", json!({"path": shot.to_string_lossy(), "width": 64, "height": 64}));
|
||||
assert!(std::fs::metadata(&shot).unwrap().len() > 100);
|
||||
if renders() {
|
||||
tool(&mut s, "screenshot", json!({"path": shot.to_string_lossy(), "width": 64, "height": 64}));
|
||||
assert!(std::fs::metadata(&shot).unwrap().len() > 100);
|
||||
}
|
||||
let design = dir.join("part.solidcraft");
|
||||
tool(&mut s, "save", json!({"path": design.to_string_lossy()}));
|
||||
tool(&mut s, "new_design", json!({"name": "Empty"}));
|
||||
@@ -524,7 +547,9 @@ fn the_default_folders_are_the_designs_the_recovery_folder_and_the_scratch_folde
|
||||
tool(&mut s, "execute", json!({"command": "parameters.export", "params": {"path": at(&proj, "params.csv")}}));
|
||||
tool(&mut s, "save", json!({"path": at(&proj, "copy.solidcraft")}));
|
||||
tool(&mut s, "open", json!({"path": at(&proj, "part.solidcraft")}));
|
||||
tool(&mut s, "screenshot", json!({"width": 32, "height": 32}));
|
||||
if renders() {
|
||||
tool(&mut s, "screenshot", json!({"width": 32, "height": 32}));
|
||||
}
|
||||
tool(&mut s, "execute", json!({"command": "doc.recovery_list", "params": {"dir": recovery.to_string_lossy()}}));
|
||||
assert!(proj.join("part.step").exists() && proj.join("copy.solidcraft").exists());
|
||||
// Hidden files in it, and folders beside it, stay out of reach.
|
||||
@@ -563,7 +588,9 @@ fn tool_calls_stay_inside_the_agents_folders() {
|
||||
tool(&mut s, "save", json!({"path": at(&root, "part.solidcraft")}));
|
||||
tool(&mut s, "export", json!({"path": at(&root, "part.step")}));
|
||||
tool(&mut s, "open", json!({"path": at(&root, "part.solidcraft")}));
|
||||
tool(&mut s, "screenshot", json!({"path": at(&root, "shot.png"), "width": 64, "height": 64}));
|
||||
if renders() {
|
||||
tool(&mut s, "screenshot", json!({"path": at(&root, "shot.png"), "width": 64, "height": 64}));
|
||||
}
|
||||
let escapes = vec![at(&out, "x.step"), at(&root, "../out/x.step")];
|
||||
#[cfg(unix)]
|
||||
let mut escapes = escapes;
|
||||
@@ -644,7 +671,9 @@ fn short_names_child() {
|
||||
assert!(windows_refusal(&temp.to_string_lossy()).is_some_and(|why| why.contains("8.3")), "{}", temp.display());
|
||||
let mut s = Server::new(Box::new(Headless::default())).with_path_scope(Some(crate::agent_scope([])));
|
||||
tool(&mut s, "execute", json!({"command": "solid.box", "params": {"length": 10, "width": 10, "height": 10}}));
|
||||
tool(&mut s, "screenshot", json!({"width": 32, "height": 32}));
|
||||
if renders() {
|
||||
tool(&mut s, "screenshot", json!({"width": 32, "height": 32}));
|
||||
}
|
||||
tool(&mut s, "execute", json!({"command": "doc.recovery_list", "params": {}}));
|
||||
// An agent's own short spelling of the scratch folder stays refused.
|
||||
let scratch = crate::scratch_dir().unwrap();
|
||||
@@ -687,6 +716,9 @@ fn a_bridged_request_carries_the_agents_folders() {
|
||||
/// standard view.
|
||||
#[test]
|
||||
fn screenshot_renders_a_saved_named_view() {
|
||||
if !renders() {
|
||||
return;
|
||||
}
|
||||
let mut s = server();
|
||||
tool(&mut s, "execute", json!({"command": "solid.box", "params": {"length": 40, "width": 20, "height": 10}}));
|
||||
let camera = json!({"target": [20, 10, 5], "yaw": 0.5, "pitch": -0.6, "distance": 150});
|
||||
@@ -710,6 +742,9 @@ fn screenshot_renders_a_saved_named_view() {
|
||||
/// `browser.visibility` stays out whatever `axes` says.
|
||||
#[test]
|
||||
fn screenshot_can_leave_the_grid_and_the_axes_out() {
|
||||
if !renders() {
|
||||
return;
|
||||
}
|
||||
let mut s = server();
|
||||
tool(&mut s, "execute", json!({"command": "solid.box", "params": {"length": 40, "width": 30, "height": 20, "corner": [10, -15, -10]}}));
|
||||
let shot = |s: &mut Server, extra: Value| {
|
||||
|
||||
Reference in new issue
Block a user