From 52110f4e2929fbf86b275ea2d8d2c72ca18580fd Mon Sep 17 00:00:00 2001 From: Bradlee Speice Date: Wed, 1 Jan 2025 17:32:11 -0500 Subject: [PATCH] Initial xtask check-in --- .cargo/config.toml | 4 ++ .devcontainer/devcontainer.json | 13 +++++ .gitignore | 4 ++ Cargo.lock | 48 ++++++++++++++++ Cargo.toml | 12 ++++ crates/flare/Cargo.toml | 10 ++++ crates/flare/build.rs | 29 ++++++++++ crates/flare/src/lib.rs | 14 +++++ crates/xtask/Cargo.toml | 10 ++++ crates/xtask/src/flags.rs | 56 +++++++++++++++++++ crates/xtask/src/main.rs | 21 +++++++ crates/xtask/src/shader_compile.rs | 19 +++++++ crates/xtask/src/shader_test.rs | 34 ++++++++++++ crates/xtask/src/slang_build.rs | 83 ++++++++++++++++++++++++++++ shader-tests/enum.slang | 67 ++++++++++++++++++++++ shader-tests/enum.slang.expected.txt | 4 ++ shader/hello-world.slang | 11 ++++ 17 files changed, 439 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 crates/flare/Cargo.toml create mode 100644 crates/flare/build.rs create mode 100644 crates/flare/src/lib.rs create mode 100644 crates/xtask/Cargo.toml create mode 100644 crates/xtask/src/flags.rs create mode 100644 crates/xtask/src/main.rs create mode 100644 crates/xtask/src/shader_compile.rs create mode 100644 crates/xtask/src/shader_test.rs create mode 100644 crates/xtask/src/slang_build.rs create mode 100644 shader-tests/enum.slang create mode 100644 shader-tests/enum.slang.expected.txt create mode 100644 shader/hello-world.slang diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..6f16c7f --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,4 @@ +[alias] +shader-compile = "run --package xtask -- shader-compile" +shader-test = "run --package xtask -- shader-test" +xtask = "run --package xtask --" \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..33e1504 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,13 @@ +{ + "name": "Rust", + "image": "mcr.microsoft.com/devcontainers/rust:1-1-bookworm", + "runArgs": ["--userns=keep-id"], + "containerUser": "vscode", + "customizations": { + "vscode": { + "extensions": [ + "shader-slang.slang-language-extension" + ] + } + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08403ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target + +.idea/ +cmake-build-* diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..19b1642 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,48 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "xflags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d9e15fbb3de55454b0106e314b28e671279009b363e6f1d8e39fdc3bf048944" +dependencies = [ + "xflags-macros", +] + +[[package]] +name = "xflags-macros" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "672423d4fea7ffa2f6c25ba60031ea13dc6258070556f125cc4d790007d4a155" + +[[package]] +name = "xshell" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d" +dependencies = [ + "xshell-macros", +] + +[[package]] +name = "xshell-macros" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547" + +[[package]] +name = "xtask" +version = "0.1.0" +dependencies = [ + "anyhow", + "xflags", + "xshell", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4905c5c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[workspace] +members = [ + "crates/xtask" +] +resolver = "2" + +[workspace.package] +version = "0.1.0" +edition = "2021" + +[workspace.dependencies] +anyhow = "1.0" diff --git a/crates/flare/Cargo.toml b/crates/flare/Cargo.toml new file mode 100644 index 0000000..04e00df --- /dev/null +++ b/crates/flare/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "flare" +version.workspace = true +edition.workspace = true + +[dependencies] + +[build-dependencies] +reqwest.workspace = true +zip-extract.workspace = true \ No newline at end of file diff --git a/crates/flare/build.rs b/crates/flare/build.rs new file mode 100644 index 0000000..a153c87 --- /dev/null +++ b/crates/flare/build.rs @@ -0,0 +1,29 @@ +use std::env; +use std::fs::{File}; +use std::io::{BufReader, Write}; +use std::path::PathBuf; +use std::process::Command; + +pub fn main() { + /* + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=../shader"); + + let url = "https://github.com/shader-slang/slang/releases/download/v2024.17/slang-2024.17-linux-x86_64.zip"; + let output_path = PathBuf::from(format!("{}/slang.zip", env::var("OUT_DIR").unwrap())); + let output_dir = PathBuf::from(format!("{}/slang", env::var("OUT_DIR").unwrap())); + + let mut response = reqwest::blocking::get(url).expect("Unable to fetch shader compiler"); + + let mut response_out = File::create(&output_path).unwrap(); + response.copy_to(&mut response_out).expect("Unable to copy file"); + response_out.flush().expect("Unable to flush output file"); + + let response_out = File::open(output_path).expect("Unable to create output file"); + let output_reader = BufReader::new(response_out); + zip_extract::extract(output_reader, output_dir.as_path(), true).expect("Unable to extract shader compiler"); + + let slangc_path = output_dir.join("bin/slangc"); + let shader_path= PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("../../shader"); + */ +} \ No newline at end of file diff --git a/crates/flare/src/lib.rs b/crates/flare/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/crates/flare/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/xtask/Cargo.toml b/crates/xtask/Cargo.toml new file mode 100644 index 0000000..1f86d2c --- /dev/null +++ b/crates/xtask/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "xtask" +edition.workspace = true +version.workspace = true +publish = false + +[dependencies] +anyhow.workspace = true +xflags = "0.3" +xshell = "0.2" \ No newline at end of file diff --git a/crates/xtask/src/flags.rs b/crates/xtask/src/flags.rs new file mode 100644 index 0000000..bf2ba5a --- /dev/null +++ b/crates/xtask/src/flags.rs @@ -0,0 +1,56 @@ +use std::path::PathBuf; + +xflags::xflags! { + src "./src/flags.rs" + + cmd xtask { + cmd shader-compile { + optional -S, --source source: PathBuf + } + + cmd shader-test { + optional -S, --source source: PathBuf + } + } +} +// generated start +// The following code is generated by `xflags` macro. +// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate. +#[derive(Debug)] +pub struct Xtask { + pub subcommand: XtaskCmd, +} + +#[derive(Debug)] +pub enum XtaskCmd { + ShaderCompile(ShaderCompile), + ShaderTest(ShaderTest), +} + +#[derive(Debug)] +pub struct ShaderCompile { + pub source: Option, +} + +#[derive(Debug)] +pub struct ShaderTest { + pub source: Option, +} + +impl Xtask { + #[allow(dead_code)] + pub fn from_env_or_exit() -> Self { + Self::from_env_or_exit_() + } + + #[allow(dead_code)] + pub fn from_env() -> xflags::Result { + Self::from_env_() + } + + #[allow(dead_code)] + pub fn from_vec(args: Vec) -> xflags::Result { + Self::from_vec_(args) + } +} +// generated end diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs new file mode 100644 index 0000000..4d79889 --- /dev/null +++ b/crates/xtask/src/main.rs @@ -0,0 +1,21 @@ +mod flags; +mod shader_compile; +mod shader_test; +mod slang_build; + +use std::path::PathBuf; +use xshell::{Shell}; +use crate::flags::XtaskCmd; + +fn main() -> anyhow::Result<()> { + let project_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../.."); + + let sh = Shell::new()?; + sh.change_dir(project_root); + + let flags = flags::Xtask::from_env_or_exit(); + match flags.subcommand { + XtaskCmd::ShaderCompile(cmd) => cmd.run(&sh), + XtaskCmd::ShaderTest(cmd) => cmd.run(&sh), + } +} diff --git a/crates/xtask/src/shader_compile.rs b/crates/xtask/src/shader_compile.rs new file mode 100644 index 0000000..1c77424 --- /dev/null +++ b/crates/xtask/src/shader_compile.rs @@ -0,0 +1,19 @@ +use crate::flags; +use crate::slang_build::SlangBuild; +use xshell::Shell; + +impl flags::ShaderCompile { + pub(crate) fn run(&self, sh: &Shell) -> anyhow::Result<()> { + let targets = ["slangc", "slang-glslang"]; + let build_path = SlangBuild::new(self.source.as_ref()).build_targets(&sh, &targets)?; + let slangc_path = format!("{}/Release/bin/slangc", build_path.display()); + + sh.create_dir("target/xtask/shader")?; + sh.cmd(&slangc_path) + .args(["-target", "spirv"]) + .args(["-o", "target/xtask/shader/hello-world.spv"]) + .arg("shader/hello-world.slang") + .run()?; + Ok(()) + } +} diff --git a/crates/xtask/src/shader_test.rs b/crates/xtask/src/shader_test.rs new file mode 100644 index 0000000..259f284 --- /dev/null +++ b/crates/xtask/src/shader_test.rs @@ -0,0 +1,34 @@ +use crate::flags::ShaderTest; +use crate::slang_build::SlangBuild; +use xshell::Shell; + +impl ShaderTest { + pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { + // Pending resolution of https://github.com/shader-slang/slang/issues/5979 + /* + sh.cmd(format!("{}/Release/bin/slang-test", build_dir.display())) + .args(["-category", "compute"]) + .args(["-test-dir", "shader-tests"]) + .run()?; + Ok(()) + */ + + let targets = ["slang-test"]; + let build_dir = SlangBuild::new(self.source.as_ref()).build_targets(&sh, &targets)?; + let build_dir = build_dir.to_str().unwrap(); + + // slang-test currently relies on a folder named `tests/` in the current directory + sh.cmd("cp") + .arg("-r") + .arg("shader-tests") + .arg(format!("{build_dir}/tests")) + .run()?; + + let _dir_guard = sh.push_dir(&build_dir); + sh.cmd(format!("Release/bin/slang-test")) + .args(["-category", "compute"]) + .run()?; + + Ok(()) + } +} diff --git a/crates/xtask/src/slang_build.rs b/crates/xtask/src/slang_build.rs new file mode 100644 index 0000000..baa4812 --- /dev/null +++ b/crates/xtask/src/slang_build.rs @@ -0,0 +1,83 @@ +use anyhow::Context; +use std::path::{Path, PathBuf}; +use std::thread::available_parallelism; +use xshell::Shell; + +#[derive(Debug, Default, Clone)] +pub(crate) struct SlangBuild { + source: Option, +} + +impl SlangBuild { + pub(crate) fn new>(source: Option

) -> Self { + Self { + source: source.map(|path| path.as_ref().to_path_buf()), + } + } +} + +impl SlangBuild { + pub(crate) fn assure_source(self: &Self, sh: &Shell) -> anyhow::Result { + if self.source.as_ref().is_some_and(|source| source.exists()) { + return Ok(self.source.as_ref().unwrap().clone()); + } + + let clone_dir = PathBuf::from("target/xtask/slang"); + if clone_dir.exists() { + return Ok(clone_dir); + } + + let _ = sh + .cmd("git") + .arg("clone") + .args(["--branch", "v2024.17"]) + .args(["--depth", "1"]) + .arg("--recurse-submodules") + .arg("--shallow-submodules") + .arg(format!("-j{}", available_parallelism()?)) + .arg("https://github.com/shader-slang/slang") + .arg(clone_dir.to_str().unwrap()) + .run()?; + Ok(clone_dir) + } + + pub(crate) fn assure_build(self: &Self, sh: &Shell) -> anyhow::Result { + let build_dir = sh.create_dir("target/xtask/slang-build")?; + Ok(build_dir) + } + + pub(crate) fn configure(self: &Self, sh: &Shell) -> anyhow::Result { + let source_dir = self.assure_source(&sh)?; + let build_dir = self.assure_build(&sh)?; + + sh.cmd("cmake") + .args(["-S", source_dir.to_str().unwrap()]) + .args(["-B", build_dir.to_str().unwrap()]) + .arg("-DCMAKE_BUILD_TYPE=Release") + // https://github.com/shader-slang/slang/issues/5832#issuecomment-2533324982 + .arg("-DCMAKE_SKIP_INSTALL_RULES=ON") + .run() + .context("slang-build configure")?; + + Ok(PathBuf::from(build_dir)) + } + + pub(crate) fn build_targets( + self: &Self, + sh: &Shell, + targets: &[&str], + ) -> anyhow::Result { + let build_dir = self.configure(&sh)?; + let cmd = sh + .cmd("cmake") + .args(["--build", build_dir.to_str().unwrap()]) + .arg(format!("-j{}", available_parallelism()?)); + + targets + .iter() + .fold(cmd, |cmd, target| cmd.args(["--target", target])) + .run()?; + + Ok(build_dir) + } +} diff --git a/shader-tests/enum.slang b/shader-tests/enum.slang new file mode 100644 index 0000000..fe35f43 --- /dev/null +++ b/shader-tests/enum.slang @@ -0,0 +1,67 @@ +// enum.slang +//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -shaderobj +//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj +//TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute -shaderobj + +// Confirm that basic `enum` declarations are supported. + +enum Color +{ + Red, + Green = (1 << 1), + Blue, +} + + +int test(int val) +{ + Color c = Color.Red; + + if(val > 1) + { + c = Color.Green; + } + + if(c == Color.Red) + { + if((val & 1) != 0) + { + c = Color.Blue; + } + } + + switch(c) + { + case Color.Red: + val = 1; + break; + + case Color.Green: + val = 2; + break; + + case Color.Blue: + val = 3; + break; + + default: + val = -1; + break; + } + + return (val << 4) + int(c); +} + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer outputBuffer; + +[numthreads(4, 1, 1)] +void computeMain(int3 dispatchThreadID : SV_DispatchThreadID) +{ + int tid = dispatchThreadID.x; + + int val = int(tid); + val = test(val); + + outputBuffer[tid] = val; +} \ No newline at end of file diff --git a/shader-tests/enum.slang.expected.txt b/shader-tests/enum.slang.expected.txt new file mode 100644 index 0000000..3080040 --- /dev/null +++ b/shader-tests/enum.slang.expected.txt @@ -0,0 +1,4 @@ +10 +33 +22 +22 \ No newline at end of file diff --git a/shader/hello-world.slang b/shader/hello-world.slang new file mode 100644 index 0000000..c4264f5 --- /dev/null +++ b/shader/hello-world.slang @@ -0,0 +1,11 @@ +StructuredBuffer buffer0; +StructuredBuffer buffer1; +RWStructuredBuffer result; + +[shader("compute")] +[numthreads(1,1,1)] +void computeMain(uint3 threadId : SV_DispatchThreadID) +{ + uint index = threadId.x; + result[index] = buffer0[index] + buffer1[index]; +}