yum/gpu_fft

A GPU-friendly FFT

git clone https://git.yummers.dev/yum/gpu_fft

yumAdd gpu DFT code69b4543

master
3.7 KiB120 linesraw
1use anyhow::Context;
2use std::fs;
3use std::fs::File;
4use std::fs::OpenOptions;
5use std::io::Write;
6use std::path::Path;
7use std::process::{Command, Output};
8
9fn say(message: &str) {
10    let file = OpenOptions::new().write(true).open("/dev/tty");
11    if file.is_ok() {
12        let _ = writeln!(file.unwrap(), "gpu_fft: {message}");
13    }
14}
15
16fn run_cmd(cmd: &str, dir: &str) -> anyhow::Result<Output> {
17    println!("cargo::warning=Running {cmd}...");
18    let output = Command::new("sh")
19        .arg("-c")
20        .arg(cmd)
21        .current_dir(dir)
22        .output()
23        .with_context(|| format!("failed to execute `{cmd}`"))?;
24
25    if !output.status.success() {
26        anyhow::bail!(
27            "Command failed `{cmd}`:\nstdout:{}\nstderr:\n{}",
28            String::from_utf8_lossy(&output.stdout),
29            String::from_utf8_lossy(&output.stderr)
30        );
31    }
32
33    Ok(output)
34}
35
36fn ensure_slang() -> Result<(), anyhow::Error> {
37    let sentinel = "./slang/.did_build";
38    if Path::new(sentinel).exists() {
39        // Already built - short-circuit.
40        return Ok(());
41    }
42
43    say(concat!(
44        "The slang compiler is not present, or is missing the\n",
45        "sentinel file. We need the slang compiler in order to translate\n",
46        ".slang shaders into .wgsl, which wgpu (webGPU) uses. We're going to\n",
47        "acquire the slang compiler, then build it. This will take a few\n",
48        "minutes and use a lot of CPU. This only needs to run once."
49    ));
50
51    // Assume we're in some weird intermediate state and start fresh.
52    if Path::new("./slang").exists() {
53        say("Removing old slang compiler...");
54        fs::remove_dir_all("./slang")?;
55    }
56
57    say("cargo::warning=Acquiring slang compiler...");
58    run_cmd(
59        "git clone --recursive https://github.com/shader-slang/slang",
60        ".",
61    )?;
62    run_cmd("mkdir ./slang/build", ".")?;
63
64    say("Building slang compiler...");
65    run_cmd("cmake -B build --preset default", "./slang")?;
66    run_cmd("cmake --build --preset releaseWithDebugInfo", "./slang")?;
67    File::create(sentinel)?;
68    Ok(())
69}
70
71// Convert slang shaders in shaders/ to wgsl. (I refuse to use the term "transpile" - it is
72// redundant and ignores the fact that all compilation is merely a translation from one language to
73// another.)
74fn compile_shaders(slang_dir: &str, wgsl_dir: &str) -> Result<(), anyhow::Error> {
75    let compiler_path = "./slang/build/RelWithDebInfo/bin/slangc";
76
77    if Path::new(wgsl_dir).exists() {
78        fs::remove_dir_all(wgsl_dir)?;
79    }
80    fs::create_dir_all(wgsl_dir)
81        .with_context(|| format!("Failed to create output directory {wgsl_dir}"))?;
82
83    for entry in fs::read_dir(slang_dir)? {
84        let entry = entry?.path();
85        let Some(ext) = entry.extension() else {
86            // No extension.
87            continue;
88        };
89        if ext != "slang" {
90            continue;
91        }
92        let stem = entry
93            .file_stem()
94            .with_context(|| format!("Failed to get file stem for {}", entry.display()))?;
95        let new_path = Path::new(wgsl_dir).join(stem).with_extension("wgsl");
96        let slang_dir = entry.parent().ok_or(anyhow::anyhow!(
97            "Failed to get parent directory for {}",
98            entry.display()
99        ))?;
100        let cmd = format!(
101            "{compiler_path} {} -o {} -no-mangle -I {}",
102            entry.display(),
103            new_path.display(),
104            slang_dir.display(),
105        );
106        run_cmd(&cmd, ".")?;
107    }
108
109    Ok(())
110}
111
112fn main() -> anyhow::Result<()> {
113    println!("cargo:rerun-if-changed=slang");
114    ensure_slang()?;
115
116    println!("cargo:rerun-if-changed=shaders");
117    compile_shaders("./shaders", "./src/generated/shaders")?;
118
119    Ok(())
120}