8 Commits

20 changed files with 5995 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,2 @@
/.idea
target/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
[package]
name = "draw-compute"
version = "0.1.0"
edition = "2021"
[dependencies]
bytemuck = { version = "1.22", features = ["derive"] }
eframe = { version = "0.31", features = ["wgpu"] }
egui = "0.31"
egui-wgpu = "0.31"
glam = { version = "0.30", default-features = false, features = ["bytemuck", "libm"] }
wgpu = { version = "24.0", features = ["spirv"] }
shader = { path = "shader" }

View File

@ -0,0 +1,26 @@
use std::error::Error;
use std::path::PathBuf;
use std::{env, process};
pub fn main() -> Result<(), Box<dyn Error>> {
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shader.spv");
let mut cargo = process::Command::new("cargo");
let mut shader_cli = cargo
.current_dir("./shader-cli")
.arg("run")
.arg("--")
.arg("../shader")
.arg(out_path);
// Clean all `RUST*`/`CARGO*` environment variables so `rust-toolchain.toml` takes over
for env_var in env::vars() {
if env_var.0.starts_with("RUST") || env_var.0.starts_with("CARGO") {
shader_cli = shader_cli.env_remove(env_var.0);
}
}
shader_cli.status()?;
Ok(())
}

View File

@ -0,0 +1,968 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"getrandom",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "ar"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69"
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "bitflags"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "bytemuck"
version = "1.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "cc"
version = "1.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
dependencies = [
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "derive_more"
version = "0.99.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "elsa"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9abf33c656a7256451ebb7d0082c5a471820c31269e49d807c538c252352186e"
dependencies = [
"indexmap",
"stable_deref_trait",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "flate2"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "gimli"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9"
dependencies = [
"fallible-iterator",
"indexmap",
"stable_deref_trait",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashbrown"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
dependencies = [
"foldhash",
]
[[package]]
name = "indexmap"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
dependencies = [
"equivalent",
"hashbrown 0.15.2",
]
[[package]]
name = "internal-iterator"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "969ee3fc68ec2e88eb21434ce4d9b7e1600d1ce92ff974560a6c4a304f5124b9"
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jobserver"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "longest-increasing-subsequence"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86"
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata 0.1.10",
]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
dependencies = [
"adler2",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "nu-ansi-term"
version = "0.50.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"crc32fast",
"flate2",
"hashbrown 0.15.2",
"indexmap",
"memchr",
"ruzstd",
"wasmparser",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "proc-macro2"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "raw-string"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0501e134c6905fee1f10fed25b0a7e1261bf676cffac9543a7d0730dec01af2"
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.9",
"regex-syntax 0.8.5",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.5",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rspirv"
version = "0.12.0+sdk-1.3.268.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cf3a93856b6e5946537278df0d3075596371b1950ccff012f02b0f7eafec8d"
dependencies = [
"rustc-hash",
"spirv",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_codegen_spirv"
version = "0.9.0"
source = "git+https://github.com/rust-gpu/rust-gpu?rev=698f10ac14b7c952394ac5620004e4e973308902#698f10ac14b7c952394ac5620004e4e973308902"
dependencies = [
"ahash",
"ar",
"bytemuck",
"either",
"indexmap",
"itertools",
"lazy_static",
"libc",
"log",
"object",
"regex",
"rspirv",
"rustc-demangle",
"rustc_codegen_spirv-types",
"rustix",
"sanitize-filename",
"smallvec",
"spirt",
"spirv-tools",
"thorin-dwp",
"tracing",
"tracing-subscriber",
"tracing-tree",
]
[[package]]
name = "rustc_codegen_spirv-types"
version = "0.9.0"
source = "git+https://github.com/rust-gpu/rust-gpu?rev=698f10ac14b7c952394ac5620004e4e973308902#698f10ac14b7c952394ac5620004e4e973308902"
dependencies = [
"rspirv",
"serde",
"serde_json",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags",
"errno",
"itoa",
"libc",
"linux-raw-sys",
"once_cell",
"windows-sys 0.59.0",
]
[[package]]
name = "ruzstd"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f"
dependencies = [
"twox-hash",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "sanitize-filename"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c502bdb638f1396509467cb0580ef3b29aa2a45c5d43e5d84928241280296c"
dependencies = [
"lazy_static",
"regex",
]
[[package]]
name = "semver"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "shader-cli"
version = "0.1.0"
dependencies = [
"spirv-builder",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "smallvec"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
dependencies = [
"serde",
]
[[package]]
name = "spirt"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2d5968bd2a36466468aac637b355776f080edfb0c6f769b2b99b9708260c42a"
dependencies = [
"arrayvec",
"bytemuck",
"derive_more",
"elsa",
"indexmap",
"internal-iterator",
"itertools",
"lazy_static",
"longest-increasing-subsequence",
"rustc-hash",
"serde",
"serde_json",
"smallvec",
]
[[package]]
name = "spirv"
version = "0.3.0+sdk-1.3.268.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
dependencies = [
"bitflags",
]
[[package]]
name = "spirv-builder"
version = "0.9.0"
source = "git+https://github.com/rust-gpu/rust-gpu?rev=698f10ac14b7c952394ac5620004e4e973308902#698f10ac14b7c952394ac5620004e4e973308902"
dependencies = [
"memchr",
"raw-string",
"rustc_codegen_spirv",
"rustc_codegen_spirv-types",
"serde",
"serde_json",
]
[[package]]
name = "spirv-tools"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcb3b0832881834994b7ec82b709ec5491043ceb4bf8101e27da6b5234b24261"
dependencies = [
"spirv-tools-sys",
]
[[package]]
name = "spirv-tools-sys"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e68b55a97aa6856e010a6f2477425875a97873e147bb0232160e73c45bdae7"
dependencies = [
"cc",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thorin-dwp"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "813ba76597db32dc4f6992fd8bf8f394715b88d352fd97401da67dab6283b4c6"
dependencies = [
"gimli",
"hashbrown 0.14.5",
"object",
"tracing",
]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tracing"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-serde"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
dependencies = [
"serde",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
"matchers",
"nu-ansi-term 0.46.0",
"once_cell",
"regex",
"serde",
"serde_json",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
"tracing-serde",
]
[[package]]
name = "tracing-tree"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b56c62d2c80033cb36fae448730a2f2ef99410fe3ecbffc916681a32f6807dbe"
dependencies = [
"nu-ansi-term 0.50.1",
"tracing-core",
"tracing-log",
"tracing-subscriber",
]
[[package]]
name = "twox-hash"
version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
"cfg-if",
"static_assertions",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasmparser"
version = "0.222.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa210fd1788e6b37a1d1930f3389c48e1d6ebd1a013d34fa4b7f9e3e3bf03146"
dependencies = [
"bitflags",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@ -0,0 +1,13 @@
[package]
name = "shader-cli"
version = "0.1.0"
edition = "2021"
[dependencies]
spirv-builder = { git = "https://github.com/rust-gpu/rust-gpu", rev = "698f10ac14b7c952394ac5620004e4e973308902" }
# On Windows, link.exe has a maximum of 65536 symbols.
# These options are required to stay within that limit
[profile.dev]
opt-level = 3
codegen-units = 256

View File

@ -0,0 +1,4 @@
[toolchain]
channel = "nightly-2024-11-22"
components = ["rust-src", "rustc-dev", "llvm-tools"]
# commit_hash = b19329a37cedf2027517ae22c87cf201f93d776e

View File

@ -0,0 +1,12 @@
use std::env::args;
use spirv_builder::SpirvBuilder;
fn main() {
let crate_path = args().nth(1).expect("Missing crate path");
let output_path = args().nth(2).expect("Missing output path");
let builder = SpirvBuilder::new(crate_path, "spirv-unknown-vulkan1.1");
let build_result = builder.build().expect("Could not compile shader");
let compile_path = build_result.module.unwrap_single();
std::fs::copy(compile_path, output_path).expect("Unable to copy shader to destination");
}

View File

@ -0,0 +1,156 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bytemuck"
version = "1.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "glam"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945"
dependencies = [
"libm",
]
[[package]]
name = "glam"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3aa70d918d2b234126ff4f850f628f172542bf0603ded26b8ee36e5e22d5f9"
dependencies = [
"bytemuck",
"libm",
]
[[package]]
name = "libm"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
"libm",
]
[[package]]
name = "proc-macro2"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "shader"
version = "0.1.0"
dependencies = [
"bytemuck",
"glam 0.30.1",
"spirv-std",
]
[[package]]
name = "spirv-std"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68c3c0972a2df79abe2c8af2fe7f7937a9aa558b6a1f78fc5edf93f4d480d757"
dependencies = [
"bitflags",
"glam 0.24.2",
"num-traits",
"spirv-std-macros",
"spirv-std-types",
]
[[package]]
name = "spirv-std-macros"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f776bf9f2897ea7acff15d7753711fdf1693592bd7459a01c394262b1df45c"
dependencies = [
"proc-macro2",
"quote",
"spirv-std-types",
"syn 1.0.109",
]
[[package]]
name = "spirv-std-types"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a73417b7d72d95b4995c840dceb4e3b4bcbad4ff7f35df9c1655b6826c18d3a9"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"

View File

@ -0,0 +1,12 @@
[package]
name = "shader"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["rlib", "cdylib"]
[dependencies]
bytemuck = { version = "1.22", features = ["derive"] }
glam = { version = "0.30", default-features = false, features = ["bytemuck", "libm"] }
spirv-std = "0.9.0"

View File

@ -0,0 +1,133 @@
#![no_std]
use glam::Vec4Swizzles;
use spirv_std::spirv;
pub trait DrawSettings: Copy + Sized + bytemuck::Pod + bytemuck::Zeroable {}
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C)]
pub struct DrawSized {
pub image_size: glam::UVec2,
pub viewport_size: glam::UVec2,
}
impl DrawSettings for DrawSized {}
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C)]
pub struct DrawRect {
pub image_size: glam::UVec2,
pub viewport_size: glam::UVec2,
pub viewport_offset: glam::UVec2,
}
impl DrawSettings for DrawRect {}
const BLOCK_SIZE: usize = 16;
const BLACK: glam::Vec4 = glam::vec4(0.0, 0.0, 0.0, 1.0);
const WHITE: glam::Vec4 = glam::vec4(1.0, 1.0, 1.0, 1.0);
fn image_index(x: usize, y: usize, width: usize) -> usize {
y * width + x
}
fn in_bounds(image_coordinate: glam::Vec2, image_size: glam::Vec2) -> bool {
image_coordinate.cmpge(glam::Vec2::ZERO).all() && image_coordinate.cmplt(image_size).all()
}
#[spirv(compute(threads(1)))]
pub fn main_cs_bounding(
#[spirv(uniform, descriptor_set = 0, binding = 0)] viewport: &DrawSized,
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] image: &mut [glam::Vec4],
) {
let width = viewport.image_size.x as usize;
let height = viewport.image_size.y as usize;
for x in 0..width {
for y in 0..height {
image[image_index(x, y, width)] =
if x == 0 || x == width - 1 || y == 0 || y == height - 1 {
WHITE
} else {
BLACK
}
}
}
}
#[spirv(compute(threads(1)))]
pub fn main_cs_blocks(
#[spirv(uniform, descriptor_set = 0, binding = 0)] viewport: &DrawSized,
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] image: &mut [glam::Vec4],
) {
let width = viewport.image_size.x as usize;
let height = viewport.image_size.y as usize;
for x in 0..width {
let x_even = x / BLOCK_SIZE % 2 == 0;
for y in 0..height {
let y_even = y / BLOCK_SIZE % 2 == 0;
let color = if x_even == y_even { BLACK } else { WHITE };
let index = image_index(x, y, width);
image[index] = color;
}
}
}
#[spirv(vertex)]
pub fn main_vs(
#[spirv(vertex_index)] vert_id: u32,
#[spirv(position, invariant)] position: &mut glam::Vec4,
) {
let output_uv = glam::vec2(((vert_id << 1) & 2) as f32, (vert_id & 2) as f32);
*position = (output_uv * 2.0 - 1.0, 0.0, 1.0).into();
}
#[spirv(fragment)]
pub fn main_fs_size(
#[spirv(frag_coord)] frag_coord: glam::Vec4,
#[spirv(uniform, descriptor_set = 0, binding = 0)] draw_settings: &DrawSized,
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] image: &mut [glam::Vec4],
output: &mut glam::Vec4,
) {
let vp_size = draw_settings.viewport_size.as_vec2();
let img_size = draw_settings.image_size.as_vec2();
let scale = (vp_size / img_size).min_element();
let img_offset = (vp_size / scale - img_size) / 2.0;
let img_coord = frag_coord.xy() / scale - img_offset;
*output = if in_bounds(img_coord, img_size) {
image[image_index(
img_coord.x as usize,
img_coord.y as usize,
img_size.x as usize,
)]
} else {
BLACK
}
}
#[spirv(fragment)]
pub fn main_fs_rect(
#[spirv(frag_coord)] frag_coord: glam::Vec4,
#[spirv(uniform, descriptor_set = 0, binding = 0)] draw_settings: &DrawRect,
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] image: &mut [glam::Vec4],
output: &mut glam::Vec4,
) {
let vp_size = draw_settings.viewport_size.as_vec2();
let img_size = draw_settings.image_size.as_vec2();
let scale = (vp_size / img_size).min_element();
let img_offset = (vp_size / scale - img_size) / 2.0;
let img_coord =
(frag_coord.xy() - draw_settings.viewport_offset.as_vec2()) / scale - img_offset;
*output = if in_bounds(img_coord, img_size) {
image[image_index(
img_coord.x as usize,
img_coord.y as usize,
img_size.x as usize,
)]
} else {
BLACK
}
}

View File

@ -0,0 +1,16 @@
use draw_compute::draw_shaders::ShaderBounding;
use draw_compute::ComputeDraw;
fn main() {
let native_options = eframe::NativeOptions {
renderer: eframe::Renderer::Wgpu,
..Default::default()
};
eframe::run_native(
"Compute Draw",
native_options,
Box::new(|_cc| Ok(Box::new(ComputeDraw::<ShaderBounding>::new()))),
)
.unwrap()
}

View File

@ -0,0 +1,15 @@
use draw_compute::draw_shaders::ShaderOffset;
use draw_compute::ComputeDraw;
fn main() {
let native_options = eframe::NativeOptions {
renderer: eframe::Renderer::Wgpu,
..Default::default()
};
eframe::run_native(
"Compute Draw",
native_options,
Box::new(|_cc| Ok(Box::new(ComputeDraw::<ShaderOffset>::new()))),
).unwrap()
}

View File

@ -0,0 +1,15 @@
use draw_compute::draw_shaders::ShaderOffsetBlocks;
use draw_compute::ComputeDraw;
fn main() {
let native_options = eframe::NativeOptions {
renderer: eframe::Renderer::Wgpu,
..Default::default()
};
eframe::run_native(
"Compute Draw",
native_options,
Box::new(|_cc| Ok(Box::new(ComputeDraw::<ShaderOffsetBlocks>::new()))),
).unwrap()
}

View File

@ -0,0 +1,196 @@
use shader::DrawSettings;
use std::marker::PhantomData;
pub trait ShaderSettings: Send + Sync {
type DrawSettings: DrawSettings;
fn compute_shader() -> &'static str;
fn fragment_shader() -> &'static str;
fn new(interact_rect: egui::Rect) -> Self;
fn write_buffer(&self, queue: &wgpu::Queue, buffer: &wgpu::Buffer, image_size: glam::UVec2);
}
pub struct DrawResources<S: ShaderSettings> {
bind_group_layout: wgpu::BindGroupLayout,
pub bind_group: wgpu::BindGroup,
pub viewport_buffer: wgpu::Buffer,
image_buffer: wgpu::Buffer,
pub image_size: glam::UVec2,
pub compute_pipeline: wgpu::ComputePipeline,
pub render_pipeline: wgpu::RenderPipeline,
settings: PhantomData<S>,
}
impl<S: ShaderSettings> DrawResources<S> {
fn bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("compute_draw"),
entries: &[
// draw_settings
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
// image
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: false },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
})
}
fn bind_group(
device: &wgpu::Device,
bind_group_layout: &wgpu::BindGroupLayout,
viewport_buffer: &wgpu::Buffer,
image_buffer: &wgpu::Buffer,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("compute_draw"),
layout: bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: viewport_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: image_buffer.as_entire_binding(),
},
],
})
}
fn viewport_buffer(device: &wgpu::Device) -> wgpu::Buffer {
device.create_buffer(&wgpu::BufferDescriptor {
label: Some("viewport"),
size: size_of::<S::DrawSettings>() as u64,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,
mapped_at_creation: false,
})
}
fn image_buffer(device: &wgpu::Device, width: u64, height: u64) -> wgpu::Buffer {
device.create_buffer(&wgpu::BufferDescriptor {
label: Some("image"),
size: width * height * 4 * size_of::<f32>() as u64,
usage: wgpu::BufferUsages::STORAGE,
mapped_at_creation: false,
})
}
fn module(device: &wgpu::Device) -> wgpu::ShaderModule {
let module_descriptor = wgpu::include_spirv!(concat!(env!("OUT_DIR"), "/shader.spv"));
device.create_shader_module(module_descriptor)
}
fn compute_pipeline(
device: &wgpu::Device,
module: &wgpu::ShaderModule,
bind_group_layout: &wgpu::BindGroupLayout,
) -> wgpu::ComputePipeline {
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("compute"),
bind_group_layouts: &[bind_group_layout],
push_constant_ranges: &[],
});
device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("compute"),
layout: Some(&pipeline_layout),
module: &module,
entry_point: Some(S::compute_shader()),
compilation_options: Default::default(),
cache: None,
})
}
fn render_pipeline(
device: &wgpu::Device,
module: &wgpu::ShaderModule,
bind_group_layout: &wgpu::BindGroupLayout,
format: &wgpu::TextureFormat,
) -> wgpu::RenderPipeline {
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("draw"),
bind_group_layouts: &[bind_group_layout],
push_constant_ranges: &[],
});
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("draw"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module,
entry_point: Some("main_vs"),
compilation_options: Default::default(),
buffers: &[],
},
primitive: Default::default(),
depth_stencil: None,
multisample: Default::default(),
fragment: Some(wgpu::FragmentState {
module,
entry_point: Some(S::fragment_shader()),
compilation_options: Default::default(),
targets: &[Some((*format).into())],
}),
multiview: None,
cache: None,
})
}
pub fn new(
device: &wgpu::Device,
format: &wgpu::TextureFormat,
width: u64,
height: u64,
) -> Self {
let bind_group_layout = Self::bind_group_layout(device);
let viewport_buffer = Self::viewport_buffer(device);
let image_buffer = Self::image_buffer(device, width, height);
let image_size = glam::uvec2(width as u32, height as u32);
let bind_group =
Self::bind_group(device, &bind_group_layout, &viewport_buffer, &image_buffer);
let module = Self::module(device);
let compute_pipeline = Self::compute_pipeline(device, &module, &bind_group_layout);
let render_pipeline = Self::render_pipeline(device, &module, &bind_group_layout, format);
Self {
bind_group_layout,
bind_group,
viewport_buffer,
image_buffer,
image_size,
compute_pipeline,
render_pipeline,
settings: PhantomData,
}
}
pub fn resize(&mut self, device: &wgpu::Device, width: u64, height: u64) {
self.image_buffer = Self::image_buffer(device, width, height);
self.image_size = glam::uvec2(width as u32, height as u32);
self.bind_group = Self::bind_group(
device,
&self.bind_group_layout,
&self.viewport_buffer,
&self.image_buffer,
);
}
}

View File

@ -0,0 +1,96 @@
use crate::draw_resources::ShaderSettings;
use glam::UVec2;
use shader::{DrawRect, DrawSized};
use wgpu::{Buffer, Queue};
pub struct ShaderBounding {
draw_size: egui::Vec2,
}
impl ShaderSettings for ShaderBounding {
type DrawSettings = DrawSized;
fn compute_shader() -> &'static str {
"main_cs_bounding"
}
fn fragment_shader() -> &'static str {
"main_fs_size"
}
fn new(interact_rect: egui::Rect) -> Self {
Self {
draw_size: interact_rect.size()
}
}
fn write_buffer(&self, queue: &Queue, buffer: &Buffer, image_size: glam::UVec2) {
let draw_settings = DrawSized {
image_size,
viewport_size: glam::uvec2(self.draw_size.x as u32, self.draw_size.y as u32),
};
queue.write_buffer(&buffer, 0, bytemuck::cast_slice(&[draw_settings]));
}
}
pub struct ShaderOffset {
draw_rect: egui::Rect,
}
impl ShaderSettings for ShaderOffset {
type DrawSettings = DrawRect;
fn compute_shader() -> &'static str {
"main_cs_bounding"
}
fn fragment_shader() -> &'static str {
"main_fs_rect"
}
fn new(interact_rect: egui::Rect) -> Self {
Self { draw_rect: interact_rect }
}
fn write_buffer(&self, queue: &Queue, buffer: &Buffer, image_size: UVec2) {
let viewport_size = glam::uvec2(self.draw_rect.size().x as u32, self.draw_rect.size().y as u32);
let viewport_offset = glam::uvec2(self.draw_rect.min.x as u32, self.draw_rect.min.y as u32);
let draw_settings = DrawRect {
image_size,
viewport_size,
viewport_offset
};
queue.write_buffer(&buffer, 0, bytemuck::cast_slice(&[draw_settings]));
}
}
pub struct ShaderOffsetBlocks {
draw_rect: egui::Rect,
}
impl ShaderSettings for ShaderOffsetBlocks {
type DrawSettings = DrawRect;
fn compute_shader() -> &'static str {
"main_cs_blocks"
}
fn fragment_shader() -> &'static str {
"main_fs_rect"
}
fn new(interact_rect: egui::Rect) -> Self {
Self { draw_rect: interact_rect }
}
fn write_buffer(&self, queue: &Queue, buffer: &Buffer, image_size: UVec2) {
let viewport_size = glam::uvec2(self.draw_rect.size().x as u32, self.draw_rect.size().y as u32);
let viewport_offset = glam::uvec2(self.draw_rect.min.x as u32, self.draw_rect.min.y as u32);
let draw_settings = DrawRect {
image_size,
viewport_size,
viewport_offset
};
queue.write_buffer(&buffer, 0, bytemuck::cast_slice(&[draw_settings]));
}
}

View File

@ -0,0 +1,158 @@
mod draw_resources;
pub mod draw_shaders;
use std::marker::PhantomData;
use eframe::Frame;
use egui::{Context, Sense};
use crate::draw_resources::{DrawResources, ShaderSettings};
use eframe::epaint::PaintCallbackInfo;
use egui_wgpu::{CallbackResources, CallbackTrait, ScreenDescriptor};
use wgpu::{CommandBuffer, CommandEncoder, Device, Queue, RenderPass};
struct DrawCallback<S> {
interact_rect: egui::Rect,
interact_resize: bool,
settings: S,
}
impl<S: ShaderSettings> DrawCallback<S> {
fn new(interact_rect: egui::Rect, interact_resize: bool) -> Self {
Self {
interact_rect,
interact_resize,
settings: S::new(interact_rect),
}
}
}
impl<S: ShaderSettings + Send + Sync + 'static> CallbackTrait for DrawCallback<S> {
fn prepare(
&self,
device: &Device,
queue: &Queue,
_screen_descriptor: &ScreenDescriptor,
egui_encoder: &mut CommandEncoder,
callback_resources: &mut CallbackResources,
) -> Vec<CommandBuffer> {
let resources = callback_resources
.get_mut::<DrawResources<S>>()
.expect("missing draw resources");
if self.interact_resize {
resources.resize(
device,
self.interact_rect.width() as u64,
self.interact_rect.height() as u64,
);
}
self.settings.write_buffer(queue, &resources.viewport_buffer, resources.image_size);
if self.interact_resize {
let mut compute_pass = egui_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: Some("compute"),
timestamp_writes: None,
});
compute_pass.set_pipeline(&resources.compute_pipeline);
compute_pass.set_bind_group(0, &resources.bind_group, &[]);
compute_pass.dispatch_workgroups(1, 1, 1);
}
vec![]
}
fn paint(
&self,
_info: PaintCallbackInfo,
render_pass: &mut RenderPass<'static>,
callback_resources: &CallbackResources,
) {
let resources = callback_resources.get::<DrawResources<S>>().unwrap();
render_pass.set_pipeline(&resources.render_pipeline);
render_pass.set_bind_group(0, &resources.bind_group, &[]);
render_pass.draw(0..3, 0..1);
}
}
#[derive(Copy, Clone)]
pub struct ComputeDraw<S> {
initial_draw: bool,
settings: PhantomData<S>,
}
impl <S: ShaderSettings> ComputeDraw<S> {
pub fn new() -> Self {
ComputeDraw {
initial_draw: true,
settings: PhantomData,
}
}
}
impl<S: ShaderSettings + 'static> eframe::App for ComputeDraw<S> {
fn update(&mut self, ctx: &Context, frame: &mut Frame) {
let initial_draw = self.initial_draw;
self.initial_draw = false;
if initial_draw {
let wgpu_render_state = frame.wgpu_render_state().expect("missing WGPU state");
let device = wgpu_render_state.device.clone();
let format = wgpu_render_state.target_format.clone();
let callback_resources = &mut wgpu_render_state
.renderer
.as_ref()
.write()
.callback_resources;
// Guess an initial size for initializing GPU resources, it will be adjusted later
callback_resources.insert(DrawResources::<S>::new(&device, &format, 800, 600));
}
/*
egui::TopBottomPanel::bottom("bottom").show(ctx, |ui| {
let wgpu_render_state = frame.wgpu_render_state().expect("missing WGPU state");
let image_size = wgpu_render_state
.renderer
.as_ref()
.read()
.callback_resources
.get::<DrawResources<DrawSimple>>()
.unwrap()
.image_size;
ui.label(format!("Viewport: image={image_size}"))
});
*/
egui::CentralPanel::default().show(ctx, |ui| {
egui::Frame::canvas(ui.style()).show(ui, |ui| {
let interact_rect = ui.available_rect_before_wrap();
let (response, painter) = ui.allocate_painter(interact_rect.size(), Sense::click());
painter.add(egui_wgpu::Callback::new_paint_callback(
interact_rect,
DrawCallback::<S>::new(interact_rect, initial_draw || response.clicked()),
))
});
});
}
}
/*
fn main() {
let native_options = eframe::NativeOptions {
renderer: eframe::Renderer::Wgpu,
..Default::default()
};
eframe::run_native(
"Compute Draw",
native_options,
Box::new(|_cc| Ok(Box::new(ComputeDraw { initial_draw: true }))),
)
.unwrap()
}
*/

View File

@ -0,0 +1,107 @@
---
slug: 2025/04/drawing-compute-shader
title: "Drawing with a compute shader"
date: 2025-04-27 12:00:00
authors: [bspeice]
tags: []
---
My goal studying the [fractal flame algorithm](../2024-11-15-playing-with-fire/1-introduction/index.mdx) was not just
to satisfy an inner curiosity. The algorithm has been ported to GPUs, but those implementations require either CUDA
(for [flam4](https://sourceforge.net/projects/flam4/)) or OpenCL (for [Fractorium](http://fractorium.com/)).
I'd like to try implementing a fractal flame editor using standard GPU shaders.
The first step is showing an application window and drawing to it using a GPU. This post covers:
- Setting up an application window using [`egui`](https://github.com/emilk/egui)
- Writing and compiling shaders written in Rust with [Rust GPU](https://rust-gpu.github.io/)
- Rendering an image using a compute shader, and displaying the image using a fragment shader
Not that interesting if you're already familiar with shaders and GPU programming, but I found myself wishing
there were more detailed resources on how to get started.
TODO: Image of the end goal here?
<!-- truncate -->
## GUIs in Rust with `egui`
:::note
This section focuses on creating an application that displays the GPU results. If that's not your style,
you can [skip ahead to the shaders](#shaders-in-rust).
:::
## Shaders in Rust
All the code in this post is written in Rust - including the parts that run on a GPU. Rust GPU
compiles Rust code into a shader that runs on a graphics card. There are three primary shader types used:
- [Compute shader](https://www.khronos.org/opengl/wiki/Compute_Shader); can run arbitrary computation, but can't
display to a screen
- [Vertex shader](https://www.khronos.org/opengl/wiki/Vertex_Shader); used to define the output image area
- [Fragment shader](https://www.khronos.org/opengl/wiki/Fragment_Shader); draws an image by returning
a color for each pixel
The application will output pixel information into an image buffer using a compute shader, then display that image
using a combination of vertex and fragment shaders.
### Compute shader
<details>
<summary>Why the focus on compute shaders if they can't display an image on screen? Why not use a fragment shader?</summary>
The answer is related to how fragment shaders run on a graphics card. Fragment shaders are functions that receive
an input coordinate and return the color of that coordinate in the output. By running the fragment shader once per
pixel, we build up an image to display.
This "run once per pixel" mode maps poorly to the fractal flame algorithm. Specifically, the "[chaos game](https://en.wikipedia.org/wiki/Chaos_game)"
jumps around to random locations; only by iterating many times can we figure out what the color of a pixel
should be. However, once the fragment shader ends, we'd lose all the information gathered about the other pixels.
Using a compute shader avoids this "discarded information" problem. Because it can run arbitrary computations,
we can record information about the whole image and make it available later. Compute shaders are overkill
for the examples in this blog post, but are important to a GPU implementation of the fractal flame algorithm.
</details>
The first compute shader example is nothing special; it draws a white rectangle at the edges of an image:
```rust
const BLACK: glam::Vec4 = glam::vec4(0.0, 0.0, 0.0, 1.0);
const WHITE: glam::Vec4 = glam::vec4(1.0, 1.0, 1.0, 1.0);
pub struct DrawSized {
pub image_size: glam::UVec2,
pub viewport_size: glam::UVec2,
}
// The `#[spirv]` annotations describe some details of how this code should run
// on the GPU; they can be ignored for now
#[spirv(compute(threads(1)))]
pub fn main_cs_bounding(
#[spirv(uniform, descriptor_set = 0, binding = 0)] viewport: &DrawSized,
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] image: &mut [glam::Vec4],
) {
let width = viewport.image_size.x as usize;
let height = viewport.image_size.y as usize;
for x in 0..width {
for y in 0..height {
image[image_index(x, y, width)] =
if x == 0 || x == width - 1 || y == 0 || y == height - 1 {
WHITE
} else {
BLACK
}
}
}
}
```
After running this code, the contents of the `image` buffer should look something like this:
![White square with black interior](./bounding%20box.png)
## Vertex/Fragment shaders

View File

@ -37,6 +37,10 @@ const config: Config = {
docs: false,
blog: {
routeBasePath: '/',
exclude: [
"**/_*.md",
"**/target/**"
],
blogSidebarTitle: 'All posts',
blogSidebarCount: 'ALL',
showReadingTime: true,