Compare commits

..
Author SHA1 Message Date
bherbruck 22d1cd7032 Release 0.13.0 2026-10-15 20:34:13 +00:00
bherbruck 6a0dc18655 Fix the Windows 32-bit and macOS release builds
On 32-bit Windows SetWindowLongPtrW is SetWindowLongW, which takes and
returns i32, so the window-procedure hook didn't compile for i686.
The macOS app bundle now signs the bundled solidcraft-cli before the
bundle itself; codesign refuses a bundle with an unsigned helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 94e76cd97e)
2026-10-15 20:34:12 +00:00
bherbruck e006dbf1af Release 0.13.0
The tree is the 0.13.0 version commit (8241a750fa) on main exactly; the previous release is kept as a parent so release only moves forward.
2026-10-15 19:59:50 +00:00
bherbruckandClaude Opus 5.5 8241a750fa Release 0.13.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 19:59:42 +00:00
bherbruck 41779cf7bb Release 0.12.0: release branch follows main again after the 0.11.1 patch
The tree is main at c36a6f9f exactly; the 0.11.1 patch commit is kept as a parent so release only moves forward.
2026-10-15 13:32:01 +00:00
bherbruckandClaude Opus 5.5 b01b9689c8 Release 0.11.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:41:41 +00:00
bherbruck 4426da6f5d Windows: start Direct3D 12 first, and Vulkan only without a D3D12 GPU (#1190)
* Windows: start Direct3D 12 first, and Vulkan only without a D3D12 GPU

wgpu started Vulkan, D3D12 and OpenGL together and preferred Vulkan, so
every Windows start loaded all installed Vulkan drivers. An old Intel
Vulkan driver (igvk64.dll) crashed 0.10.1 at startup inside the driver.

The app now probes D3D12, then Vulkan, then OpenGL, one backend at a
time, and starts only the first with a hardware adapter (WARP when none
has one). WGPU_BACKEND still wins; graphics.json's "backend" sets the
order by hand; the software renderer (safe mode, the retry) uses D3D12
alone. A new Windows CI job starts the real app under several settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* CI: a test filter YAML parses

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* CI: the startup smoke test writes graphics.json where the app reads it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* CI: run the WARP shader tests one at a time

WARP's shader JIT (d3d10warp.dll) crashed twice in 124 runs of the gpu
tests with four test threads, with a null read inside its own optimizer
while several devices compiled compute pipelines at once. Serial runs
did not crash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 95ea05e7c8)
2026-10-14 20:41:37 +00:00
bherbruck 5ead82e627 Release 0.11.0: release branch follows main again after the 0.10.1 patch
The tree is main at cccbe125 exactly; c29b0d2f (0.10.1) is kept as a parent so release only moves forward.
2026-10-14 20:07:41 +00:00
bherbruck c29b0d2f13 Release 0.10.1 2026-10-14 14:41:19 +00:00
bherbruckandClaude Opus 5.5 480fd8d517 Fix the startup crash on D3D12 with FXC: CPU fallback for the tessellation pipeline, FXC-friendly shader (#1098) (#1099)
* GPU tessellation: a pipeline the device cannot build falls back to CPU meshing instead of crashing (#1098)

Build the tessellation compute pipeline inside error scopes; TessGpu::new returns an error,
and the viewport logs it and meshes bodies on the CPU. A Windows CI job builds the pipeline
with D3D12's FXC on the WARP adapter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tessellation shader: basis functions in one-dimensional local arrays, so D3D12's FXC compiles it (#1098)

FXC has to unroll loops that write an array inside a struct (Basis.n0/n1) or a 2-D array
(ndu) by a counter, and cannot with run-time trip counts (X3511). A2.2 basis functions in a
local 1-D array returned whole; derivatives from the degree p-1 functions; the pole step
offsets computed rather than indexed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:41:16 +00:00
bherbruckandClaude Opus 5.5 4c09fa5355 Hovered and selected faces light solidly again over GPU-drawn bodies (#1064)
Since the model's mesh went back to the reference mesher (#940), the GPU viewport drew
opaque bodies from their display plans while hover and selection lit faces with the
reference mesh's triangles. Two triangulations of the same face at equal depth fought, so
a hovered face shimmered in wedges instead of lighting whole.

Faces lit over a body drawn from its plan now use the plan's own triangles
(ModelBody::plan_mesh, evaluated once on the CPU), and the highlight pass gets a small
depth bias toward the eye so f32 rounding between the GPU and CPU evaluations of the same
triangles cannot speckle it either.

Test: every face of a box with a pocket and a round boss, hovered and rendered offscreen
through the viewport's real pipelines on the GPU path, lights the same pixels as on the
CPU path (skipped without a GPU adapter).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:56:47 +00:00
4 changed files with 20 additions and 59 deletions

No files matched your search

Generated
+15 -15
View File
@@ -3704,7 +3704,7 @@ dependencies = [
[[package]]
name = "solidcraft"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"block2 0.6.2",
"eframe",
@@ -3733,7 +3733,7 @@ dependencies = [
[[package]]
name = "solidcraft-cli"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"serde_json",
"solidcraft-engine",
@@ -3742,7 +3742,7 @@ dependencies = [
[[package]]
name = "solidcraft-derive"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"proc-macro2",
"quote",
@@ -3751,7 +3751,7 @@ dependencies = [
[[package]]
name = "solidcraft-doc"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"log",
"serde",
@@ -3768,7 +3768,7 @@ dependencies = [
[[package]]
name = "solidcraft-engine"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"log",
"serde",
@@ -3784,7 +3784,7 @@ dependencies = [
[[package]]
name = "solidcraft-geom"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"serde",
"serde_json",
@@ -3793,7 +3793,7 @@ dependencies = [
[[package]]
name = "solidcraft-gpu"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"log",
"naga",
@@ -3806,7 +3806,7 @@ dependencies = [
[[package]]
name = "solidcraft-io"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"quick-xml",
"serde",
@@ -3821,7 +3821,7 @@ dependencies = [
[[package]]
name = "solidcraft-kernel"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"log",
"rayon",
@@ -3839,7 +3839,7 @@ dependencies = [
[[package]]
name = "solidcraft-mcp"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"serde_json",
"solidcraft-engine",
@@ -3847,7 +3847,7 @@ dependencies = [
[[package]]
name = "solidcraft-render"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"image",
"moxcms",
@@ -3860,7 +3860,7 @@ dependencies = [
[[package]]
name = "solidcraft-sketch"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"serde",
"serde_json",
@@ -3871,7 +3871,7 @@ dependencies = [
[[package]]
name = "solidcraft-ui-egui"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"egui",
"egui-wgpu",
@@ -3892,7 +3892,7 @@ dependencies = [
[[package]]
name = "solidcraft-web"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"eframe",
"egui",
@@ -5381,7 +5381,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
[[package]]
name = "xtask"
version = "0.13.1"
version = "0.13.0"
dependencies = [
"serde_json",
"sha2",
+1 -1
View File
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"]
exclude = ["vendor"]
[workspace.package]
version = "0.13.1"
version = "0.13.0"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.95"
+3 -39
View File
@@ -184,30 +184,13 @@ pub fn search_dirs() -> Vec<PathBuf> {
dirs
}
/// The first `program` in `dirs` (on Windows with `.exe`/`.cmd`/`.bat`).
/// The first `program` in `dirs` (on Windows also with `.exe`/`.cmd`).
pub fn find_in(program: &str, dirs: &[PathBuf]) -> Option<PathBuf> {
let names = names_for(program, cfg!(windows));
let names: Vec<String> =
if cfg!(windows) { ["", ".exe", ".cmd", ".bat"].iter().map(|e| format!("{program}{e}")).collect() } else { vec![program.to_string()] };
dirs.iter().flat_map(|d| names.iter().map(move |n| d.join(n))).find(|p| p.is_file())
}
/// The file names `program` can have. On Windows only ones it can run: npm puts a Unix shell
/// script with the bare name next to the `.cmd` shim, and starting that fails with "not a valid
/// Win32 application".
fn names_for(program: &str, windows: bool) -> Vec<String> {
if windows { [".exe", ".cmd", ".bat"].iter().map(|e| format!("{program}{e}")).collect() } else { vec![program.to_string()] }
}
/// A Windows command saved without an extension (`…\npm\codex-acp`, from an older version's
/// lookup): the `.exe`/`.cmd`/`.bat` beside it, if there is one.
pub fn runnable_sibling(program: &Path) -> Option<PathBuf> {
if program.extension().is_some() {
return None;
}
let dir = program.parent()?;
let name = program.file_name()?.to_str()?;
names_for(name, true).into_iter().map(|n| dir.join(n)).find(|p| p.is_file())
}
/// `program --version`'s first line, or `None` after `limit` (the probe is then stopped).
fn version(program: &Path, limit: Duration) -> Option<String> {
let mut cmd = if cfg!(windows) && program.extension().is_some_and(|e| e.eq_ignore_ascii_case("cmd") || e.eq_ignore_ascii_case("bat")) {
@@ -296,25 +279,6 @@ mod tests {
let _ = std::fs::remove_dir_all(root);
}
#[test]
fn on_windows_only_runnable_shims_are_candidates() {
// npm's bare `codex-acp` is a shell script; Windows can't start it (os error 193).
assert_eq!(names_for("codex-acp", true), ["codex-acp.exe", "codex-acp.cmd", "codex-acp.bat"]);
assert_eq!(names_for("codex-acp", false), ["codex-acp"]);
}
#[test]
fn a_saved_bare_npm_path_runs_its_cmd_shim() {
let root = std::env::temp_dir().join(format!("sc-agent-shim-{}", std::process::id()));
std::fs::create_dir_all(&root).unwrap();
std::fs::write(root.join("codex-acp"), "#!/bin/sh").unwrap();
std::fs::write(root.join("codex-acp.cmd"), "@echo off").unwrap();
assert_eq!(runnable_sibling(&root.join("codex-acp")), Some(root.join("codex-acp.cmd")));
assert_eq!(runnable_sibling(&root.join("codex-acp.cmd")), None);
assert_eq!(runnable_sibling(&root.join("gemini")), None);
let _ = std::fs::remove_dir_all(root);
}
#[test]
fn the_version_number_is_picked_out_of_the_line() {
assert_eq!(version_of("@agentclientprotocol/codex-acp 2.2.2"), "2.2.2");
+1 -4
View File
@@ -51,11 +51,8 @@ impl ProcessHandle {
}
}
/// On Windows, npm installs `.cmd` shims, which only `cmd.exe` runs. A command saved as the bare
/// npm path (a shell script there) runs the shim beside it.
/// On Windows, npm installs `.cmd` shims, which only `cmd.exe` runs.
fn command_for(program: &str) -> Command {
let shim = if cfg!(windows) { super::discover::runnable_sibling(std::path::Path::new(program)) } else { None };
let program = shim.as_deref().and_then(|p| p.to_str()).unwrap_or(program);
let lower = program.to_ascii_lowercase();
if cfg!(windows) && (lower.ends_with(".cmd") || lower.ends_with(".bat")) {
let mut c = Command::new("cmd");