M9: TailwindCSS + shadcn/ui frontend redesign
M10: Dynamic plugin system with shared library loading
M11: Macro recording, playback, and batch processing
M12: 3D volume visualization, marching cubes, STL/OBJ export, rivol:// protocol
M13: Additional GPU shaders, enhanced auto-threshold, merge channels
M14: Binary image processing (EDT, watershed, skeleton, connected components,
voronoi), segmentation & analysis (particles, colocalization, find maxima),
math/noise/filter/transform ops (60+ total), scientific LUTs (Fire, Ice,
Spectrum, Jet, Phase, HiLo + .lut file I/O), Apple Metal GPU optimization
(metal-only feature, Apple Silicon detection, lower dispatch threshold),
native Metal compute crate (ri-metal with MSL shaders via objc2-metal),
7 new WGSL GPU shaders (bilateral, variance, mean, math_ops, outline,
minmax_filter, affine transform). 25 crates, 162 tests, 75+ IPC commands.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
133 lines
3.9 KiB
Rust
133 lines
3.9 KiB
Rust
use std::env;
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
|
|
fn main() {
|
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
|
let shader_dir = Path::new("shaders");
|
|
let metallib_path = out_dir.join("default.metallib");
|
|
|
|
println!("cargo:rerun-if-changed=shaders/");
|
|
|
|
// Only compile Metal shaders on macOS
|
|
if !cfg!(target_os = "macos") {
|
|
// Write empty dummy metallib for non-macOS builds
|
|
fs::write(&metallib_path, &[]).unwrap();
|
|
println!(
|
|
"cargo:warning=Not on macOS; Metal shaders will not be compiled. \
|
|
Using empty metallib stub."
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Check if xcrun is available
|
|
let xcrun_check = Command::new("xcrun").arg("--version").output();
|
|
if xcrun_check.is_err() || !xcrun_check.unwrap().status.success() {
|
|
fs::write(&metallib_path, &[]).unwrap();
|
|
println!(
|
|
"cargo:warning=xcrun not found; Metal shaders will not be compiled. \
|
|
Using empty metallib stub."
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Find all .metal shader files
|
|
let metal_files: Vec<PathBuf> = if shader_dir.exists() {
|
|
fs::read_dir(shader_dir)
|
|
.unwrap()
|
|
.filter_map(|entry| {
|
|
let entry = entry.ok()?;
|
|
let path = entry.path();
|
|
if path.extension().and_then(|e| e.to_str()) == Some("metal") {
|
|
Some(path)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect()
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
if metal_files.is_empty() {
|
|
fs::write(&metallib_path, &[]).unwrap();
|
|
println!("cargo:warning=No .metal shader files found in shaders/");
|
|
return;
|
|
}
|
|
|
|
// Compile each .metal file to .air
|
|
let mut air_files = Vec::new();
|
|
for metal_file in &metal_files {
|
|
let stem = metal_file.file_stem().unwrap().to_str().unwrap();
|
|
let air_path = out_dir.join(format!("{}.air", stem));
|
|
|
|
println!("cargo:rerun-if-changed={}", metal_file.display());
|
|
|
|
let status = Command::new("xcrun")
|
|
.args([
|
|
"metal",
|
|
"-c",
|
|
"-target",
|
|
"air64-apple-macos14.0",
|
|
"-o",
|
|
])
|
|
.arg(&air_path)
|
|
.arg(metal_file)
|
|
.status();
|
|
|
|
match status {
|
|
Ok(s) if s.success() => {
|
|
air_files.push(air_path);
|
|
}
|
|
Ok(s) => {
|
|
println!(
|
|
"cargo:warning=Failed to compile {} (exit code: {:?})",
|
|
metal_file.display(),
|
|
s.code()
|
|
);
|
|
}
|
|
Err(e) => {
|
|
println!(
|
|
"cargo:warning=Failed to run xcrun metal for {}: {}",
|
|
metal_file.display(),
|
|
e
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if air_files.is_empty() {
|
|
fs::write(&metallib_path, &[]).unwrap();
|
|
println!("cargo:warning=No .air files produced; using empty metallib stub.");
|
|
return;
|
|
}
|
|
|
|
// Link all .air files into a single .metallib
|
|
let mut cmd = Command::new("xcrun");
|
|
cmd.arg("metallib").arg("-o").arg(&metallib_path);
|
|
for air in &air_files {
|
|
cmd.arg(air);
|
|
}
|
|
|
|
match cmd.status() {
|
|
Ok(s) if s.success() => {
|
|
println!("cargo:note=Successfully compiled Metal shaders into default.metallib");
|
|
}
|
|
Ok(s) => {
|
|
fs::write(&metallib_path, &[]).unwrap();
|
|
println!(
|
|
"cargo:warning=metallib linking failed (exit code: {:?}); using empty stub.",
|
|
s.code()
|
|
);
|
|
}
|
|
Err(e) => {
|
|
fs::write(&metallib_path, &[]).unwrap();
|
|
println!(
|
|
"cargo:warning=Failed to run xcrun metallib: {}; using empty stub.",
|
|
e
|
|
);
|
|
}
|
|
}
|
|
}
|