summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2026-04-22 11:52:07 +0000
committers-ol <s+removethis@s-ol.nu>2026-05-14 15:00:13 +0000
commit76e48cc66816a1ec064c12a7181b7751d1c783b2 (patch)
tree7368d6ab24518d25eea7e6cf1bf8559519121cb7
parentrebind outdated textures (diff)
downloadwgsl-view-76e48cc66816a1ec064c12a7181b7751d1c783b2.tar.gz
wgsl-view-76e48cc66816a1ec064c12a7181b7751d1c783b2.zip
switch to WESL shaders
-rw-r--r--Cargo.lock444
-rw-r--r--Cargo.toml2
-rw-r--r--src/bin/wgsl_render.rs115
-rw-r--r--src/lib.rs1
-rw-r--r--src/osc.rs30
-rw-r--r--src/renderer.rs160
-rw-r--r--src/uniform.rs57
-rw-r--r--src/wesl.rs86
-rwxr-xr-xtest.sh5
-rwxr-xr-xtest_buffer.sh9
-rwxr-xr-xtest_video.sh7
11 files changed, 740 insertions, 176 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a426584..99f8318 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -81,6 +81,17 @@ dependencies = [
]
[[package]]
+name = "annotate-snippets"
+version = "0.12.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92570a3f9c98e7e84df84b71d0965ac99b1871fcd75a3773a3bd1bad13f64cf7"
+dependencies = [
+ "anstyle",
+ "memchr",
+ "unicode-width",
+]
+
+[[package]]
name = "anstyle"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -111,6 +122,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
[[package]]
+name = "ascii-canvas"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891"
+dependencies = [
+ "term",
+]
+
+[[package]]
name = "ash"
version = "0.38.0+1.3.281"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -151,7 +171,7 @@ dependencies = [
"bitflags 2.11.1",
"cexpr",
"clang-sys",
- "itertools",
+ "itertools 0.13.0",
"log",
"prettyplease",
"proc-macro2",
@@ -171,7 +191,7 @@ dependencies = [
"bitflags 2.11.1",
"cexpr",
"clang-sys",
- "itertools",
+ "itertools 0.13.0",
"proc-macro2",
"quote",
"regex",
@@ -182,15 +202,30 @@ dependencies = [
[[package]]
name = "bit-set"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
+dependencies = [
+ "bit-vec 0.8.0",
+]
+
+[[package]]
+name = "bit-set"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd"
dependencies = [
- "bit-vec",
+ "bit-vec 0.9.1",
]
[[package]]
name = "bit-vec"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
+
+[[package]]
+name = "bit-vec"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
@@ -208,6 +243,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "block2"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -310,9 +354,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.2.60"
+version = "1.2.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20"
+checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -354,7 +398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if 1.0.4",
- "cpufeatures",
+ "cpufeatures 0.3.0",
"rand_core 0.10.1",
]
@@ -459,6 +503,15 @@ dependencies = [
]
[[package]]
+name = "convert_case"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -500,6 +553,15 @@ dependencies = [
[[package]]
name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
@@ -520,6 +582,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
+name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
name = "cursor-icon"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -597,6 +669,39 @@ dependencies = [
]
[[package]]
+name = "derive_more"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 2.0.117",
+ "unicode-xid",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
name = "dispatch"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -649,6 +754,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
+name = "ena"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1"
+dependencies = [
+ "log",
+]
+
+[[package]]
name = "env_logger"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -715,6 +829,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
+name = "fixedbitset"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -778,6 +904,16 @@ dependencies = [
]
[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
name = "gethostname"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1028,6 +1164,15 @@ dependencies = [
]
[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
+[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1114,6 +1259,15 @@ dependencies = [
]
[[package]]
+name = "keccak"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
+dependencies = [
+ "cpufeatures 0.2.17",
+]
+
+[[package]]
name = "khronos-egl"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1131,16 +1285,113 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
+name = "lalrpop"
+version = "0.22.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501"
+dependencies = [
+ "ascii-canvas",
+ "bit-set 0.8.0",
+ "ena",
+ "itertools 0.14.0",
+ "lalrpop-util",
+ "petgraph",
+ "regex",
+ "regex-syntax",
+ "sha3",
+ "string_cache",
+ "term",
+ "unicode-xid",
+ "walkdir",
+]
+
+[[package]]
+name = "lalrpop-util"
+version = "0.22.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733"
+dependencies = [
+ "regex-automata",
+ "rustversion",
+]
+
+[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
+name = "lexical"
+version = "7.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bc8a009b2ff1f419ccc62706f04fe0ca6e67b37460513964a3dfdb919bb37d6"
+dependencies = [
+ "lexical-core",
+]
+
+[[package]]
+name = "lexical-core"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d8d125a277f807e55a77304455eb7b1cb52f2b18c143b60e766c120bd64a594"
+dependencies = [
+ "lexical-parse-float",
+ "lexical-parse-integer",
+ "lexical-util",
+ "lexical-write-float",
+ "lexical-write-integer",
+]
+
+[[package]]
+name = "lexical-parse-float"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52a9f232fbd6f550bc0137dcb5f99ab674071ac2d690ac69704593cb4abbea56"
+dependencies = [
+ "lexical-parse-integer",
+ "lexical-util",
+]
+
+[[package]]
+name = "lexical-parse-integer"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34"
+dependencies = [
+ "lexical-util",
+]
+
+[[package]]
+name = "lexical-util"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17"
+
+[[package]]
+name = "lexical-write-float"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50c438c87c013188d415fbabbb1dceb44249ab81664efbd31b14ae55dabb6361"
+dependencies = [
+ "lexical-util",
+ "lexical-write-integer",
+]
+
+[[package]]
+name = "lexical-write-integer"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "409851a618475d2d5796377cad353802345cba92c867d9fbcde9cf4eac4e14df"
+dependencies = [
+ "lexical-util",
+]
+
+[[package]]
name = "libc"
-version = "0.2.185"
+version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
+checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libloading"
@@ -1213,6 +1464,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
+name = "logos"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb2c55a318a87600ea870ff8c2012148b44bf18b74fad48d0f835c38c7d07c5f"
+dependencies = [
+ "logos-derive",
+]
+
+[[package]]
+name = "logos-codegen"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58b3ffaa284e1350d017a57d04ada118c4583cf260c8fb01e0fe28a2e9cf8970"
+dependencies = [
+ "fnv",
+ "proc-macro2",
+ "quote",
+ "regex-automata",
+ "regex-syntax",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "logos-derive"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52d3a9855747c17eaf4383823f135220716ab49bea5fbea7dd42cc9a92f8aa31"
+dependencies = [
+ "logos-codegen",
+]
+
+[[package]]
name = "memchr"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1258,7 +1541,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa2630921705b9b01dcdd0b6864b9562ca3c1951eecd0f0c4f5f04f61e412647"
dependencies = [
"arrayvec",
- "bit-set",
+ "bit-set 0.9.1",
"bitflags 2.11.1",
"cfg-if 1.0.4",
"cfg_aliases",
@@ -1308,6 +1591,12 @@ dependencies = [
]
[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
name = "nix"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1719,6 +2008,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
+name = "petgraph"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
+dependencies = [
+ "fixedbitset",
+ "indexmap 2.14.0",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
name = "pin-project"
version = "1.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1807,6 +2115,12 @@ dependencies = [
]
[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+
+[[package]]
name = "presser"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2176,6 +2490,16 @@ dependencies = [
]
[[package]]
+name = "sha3"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874"
+dependencies = [
+ "digest",
+ "keccak",
+]
+
+[[package]]
name = "shared_memory"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2211,6 +2535,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
+name = "siphasher"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
+
+[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2287,6 +2617,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
[[package]]
+name = "string_cache"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f"
+dependencies = [
+ "new_debug_unreachable",
+ "parking_lot",
+ "phf_shared",
+ "precomputed-hash",
+]
+
+[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2334,6 +2676,15 @@ dependencies = [
]
[[package]]
+name = "term"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2542,6 +2893,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
+name = "typenum"
+version = "1.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
+
+[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2840,6 +3197,35 @@ dependencies = [
]
[[package]]
+name = "wesl"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be113b462f90d3e303324739a4431bc63dfd0b463d7582cc76c1fd2093b5d470"
+dependencies = [
+ "annotate-snippets",
+ "derive_more",
+ "half",
+ "itertools 0.14.0",
+ "num-traits",
+ "thiserror 2.0.18",
+ "wesl-macros",
+ "wgsl-parse",
+ "wgsl-types",
+]
+
+[[package]]
+name = "wesl-macros"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d321ea9e1cc0a2e3de41c02e3a7586695a9c03beccb5460e938a7ccf00cb612"
+dependencies = [
+ "itertools 0.14.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
name = "wgpu"
version = "29.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2876,8 +3262,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e80ac6cf1895df6342f87d975162108f9d98772a0d74bc404ab7304ac29469e"
dependencies = [
"arrayvec",
- "bit-set",
- "bit-vec",
+ "bit-set 0.9.1",
+ "bit-vec 0.9.1",
"bitflags 2.11.1",
"bytemuck",
"cfg_aliases",
@@ -2938,7 +3324,7 @@ dependencies = [
"android_system_properties",
"arrayvec",
"ash",
- "bit-set",
+ "bit-set 0.9.1",
"bitflags 2.11.1",
"block2 0.6.2",
"bytemuck",
@@ -3007,6 +3393,34 @@ dependencies = [
]
[[package]]
+name = "wgsl-parse"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "946419efa46664a8070c512fb358ac4acfc6e6215c2396ae5afeb9b72dd6ca7d"
+dependencies = [
+ "annotate-snippets",
+ "derive_more",
+ "itertools 0.14.0",
+ "lalrpop",
+ "lalrpop-util",
+ "lexical",
+ "logos",
+ "thiserror 2.0.18",
+ "wgsl-types",
+]
+
+[[package]]
+name = "wgsl-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7aed068ca1dc8fb182f574c9f2ace45ec988c71ba050f6dd334883fe3b8a88e2"
+dependencies = [
+ "half",
+ "itertools 0.14.0",
+ "num-traits",
+]
+
+[[package]]
name = "wgsl-view"
version = "0.1.0"
dependencies = [
@@ -3020,6 +3434,8 @@ dependencies = [
"pollster",
"rosc",
"texture-share-vk-client",
+ "thiserror 2.0.18",
+ "wesl",
"wgpu",
"wgpu-hal",
"winit",
@@ -3363,9 +3779,9 @@ dependencies = [
[[package]]
name = "winnow"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
+checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0"
dependencies = [
"memchr",
]
diff --git a/Cargo.toml b/Cargo.toml
index 656e123..6540da3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,5 +38,7 @@ naga = { version = "29", features = ["wgsl-in"] }
pollster = "0.4"
rosc = "0.11"
bytemuck = { version = "1", features = ["derive"] }
+wesl = "0.3"
texture-share-vk-client = { git = "https://github.com/s-ol/texture-share-vk.git", branch = "3d-textures" }
texture-share-vk-base = { git = "https://github.com/s-ol/texture-share-vk.git", branch = "3d-textures" }
+thiserror = "2.0.18"
diff --git a/src/bin/wgsl_render.rs b/src/bin/wgsl_render.rs
index 2288366..bcfb393 100644
--- a/src/bin/wgsl_render.rs
+++ b/src/bin/wgsl_render.rs
@@ -1,3 +1,4 @@
+use std::error::Error;
use std::thread;
use std::time::{Duration, Instant};
@@ -6,7 +7,6 @@ use rosc::OscType;
use wgsl_view::gpu;
use wgsl_view::osc::OscServer;
use wgsl_view::renderer::Renderer;
-use wgsl_view::uniform::UniformError;
fn main() {
env_logger::init();
@@ -68,38 +68,44 @@ fn main() {
loop {
let frame_start = Instant::now();
- let handle_msg = |msg: rosc::OscMessage| -> Result<(), String> {
+ let handle_msg = |msg: rosc::OscMessage| -> Result<(), Box<dyn Error>> {
let path: Vec<_> = msg.addr.strip_prefix("/").unwrap().split('/').collect();
match (&path[..], &msg.args[..]) {
- (["shader"], [OscType::String(code)]) => renderer.load_shader(&device, code),
- (["uniform", rest @ ..], args) => {
- set_uniform(renderer.uniforms(), rest, args).map_err(|e| e.to_string())
+ // create shaders
+ (["module", module_name], [OscType::String(code)]) => {
+ renderer.load_module(module_name, code)?
}
+ (["entrypoint"], [OscType::String(module_name)]) => {
+ renderer.compile(&device, module_name)?
+ }
+ // bind values
+ (["uniform", module, item, rest @ ..], args) => {
+ renderer.set_uniform(module, item, rest, args)?
+ }
+ (["binding", module, item], [OscType::String(target)]) => {
+ renderer.set_binding(&device, module, item, target)?
+ }
+ // create/destroy texture resources
(["texture", id], [OscType::String(tsv_name)]) => {
renderer.uniforms().create_texture(&device, id, tsv_name);
- Ok(())
}
+ (["texture", id, "destroy"], []) => {
+ renderer.uniforms().destroy_texture(&device, id);
+ }
+ // create/destroy sampler resources
(["sampler", id], [OscType::String(filter), OscType::String(clamp)]) => {
let (filter_mode, address_mode) = parse_sampler_modes(filter, clamp)?;
renderer
.uniforms()
.create_sampler(&device, id, filter_mode, address_mode);
- Ok(())
- }
- (["texture", id, "destroy"], []) => {
- renderer.uniforms().destroy_texture(&device, id);
- Ok(())
}
(["sampler", id, "destroy"], []) => {
renderer.uniforms().destroy_sampler(&device, id);
- Ok(())
}
- (["binding", name], [OscType::String(target)]) => {
- bind_resource(renderer.uniforms(), &device, name, target)
- }
- _ => Err(format!("unhandled OSC message {} {:?}", msg.addr, msg.args)),
+ _ => Err(format!("unhandled OSC message {} {:?}", msg.addr, msg.args))?,
}
+ Ok(())
};
let dirty = if continuous {
@@ -136,66 +142,6 @@ fn main() {
}
/// Set a uniform value from OSC args, navigating the path.
-fn set_uniform(
- cache: &mut wgsl_view::uniform::UniformCache,
- path: &[&str],
- args: &[OscType],
-) -> Result<(), UniformError> {
- let mut parts = path.iter();
- let name = parts.next().ok_or(UniformError::NotFound)?;
-
- let mut uref = cache.get(name).ok_or(UniformError::NotFound)?;
- for component in parts {
- uref = uref.field(component)?;
- }
-
- let scalar = uref.leaf_scalar()?;
- match scalar.kind {
- naga::ScalarKind::Float => {
- let values: Vec<f32> = args
- .iter()
- .map(|a| match a {
- OscType::Float(f) => Ok(*f),
- OscType::Double(d) => Ok(*d as f32),
- OscType::Int(i) => Ok(*i as f32),
- OscType::Bool(b) => Ok(if *b { 1.0 } else { 0.0 }),
- _ => Err(UniformError::TypeMismatch),
- })
- .collect::<Result<_, _>>()?;
- uref.set_f32(&values)?;
- }
- naga::ScalarKind::Sint => {
- let values: Vec<i32> = args
- .iter()
- .map(|a| match a {
- OscType::Int(i) => Ok(*i),
- OscType::Float(f) => Ok(*f as i32),
- OscType::Double(d) => Ok(*d as i32),
- OscType::Bool(b) => Ok(if *b { 1 } else { 0 }),
- _ => Err(UniformError::TypeMismatch),
- })
- .collect::<Result<_, _>>()?;
- uref.set_i32(&values)?;
- }
- naga::ScalarKind::Uint => {
- let values: Vec<u32> = args
- .iter()
- .map(|a| match a {
- OscType::Int(i) => Ok(*i as u32),
- OscType::Float(f) => Ok(*f as u32),
- OscType::Double(d) => Ok(*d as u32),
- OscType::Bool(b) => Ok(if *b { 1 } else { 0 }),
- _ => Err(UniformError::TypeMismatch),
- })
- .collect::<Result<_, _>>()?;
- uref.set_u32(&values)?;
- }
- _ => return Err(UniformError::TypeMismatch),
- }
-
- Ok(())
-}
-
fn parse_sampler_modes(
filter: &str,
address: &str,
@@ -213,20 +159,3 @@ fn parse_sampler_modes(
};
Ok((filter_mode, address_mode))
}
-
-fn bind_resource(
- uniforms: &mut wgsl_view::uniform::UniformCache,
- device: &wgpu::Device,
- name: &str,
- target: &str,
-) -> Result<(), String> {
- if let Some(id) = target.strip_prefix("/texture/") {
- uniforms.bind_texture(device, name, id)?;
- Ok(())
- } else if let Some(id) = target.strip_prefix("/sampler/") {
- uniforms.bind_sampler(device, name, id)?;
- Ok(())
- } else {
- Err(format!("invalid binding target '{target}'"))
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 57a1042..157a9b2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,4 +3,5 @@ pub mod hap;
pub mod osc;
pub mod renderer;
pub mod uniform;
+mod wesl;
pub mod window;
diff --git a/src/osc.rs b/src/osc.rs
index fdbcb0e..6288e2f 100644
--- a/src/osc.rs
+++ b/src/osc.rs
@@ -1,4 +1,4 @@
-use std::{fmt::Display, net::UdpSocket};
+use std::{error::Error, net::UdpSocket};
use rosc::{OscMessage, OscPacket};
@@ -20,9 +20,9 @@ impl OscServer {
/// Drains all pending OSC messages, calling `on_message` for each.
/// Returns `true` if any messages were dispatched.
- pub fn poll<E: Display>(
+ pub fn poll(
&mut self,
- mut on_message: impl FnMut(OscMessage) -> Result<(), E>,
+ mut on_message: impl FnMut(OscMessage) -> Result<(), Box<dyn Error>>,
) -> bool {
let mut received = false;
loop {
@@ -30,19 +30,17 @@ impl OscServer {
Ok((size, _addr)) => {
let data = &self.buf[..size];
let res = rosc::decoder::decode_udp(data)
- .map_err(|e| format!("OSC edecode error: {}", e))
- .and_then(|(_, packet)| {
- dispatch(packet, &mut on_message).map_err(|e| e.to_string())
- });
+ .map_err(Box::from)
+ .and_then(|(_, packet)| dispatch(packet, &mut on_message));
if let Err(e) = res {
- log::warn!("{}", e);
+ log::error!("{}", e);
};
received = true;
}
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => break,
Err(e) => {
- log::warn!("OSC recv error: {}", e);
+ log::error!("OSC recv error: {}", e);
break;
}
}
@@ -51,25 +49,23 @@ impl OscServer {
}
/// Blocks until at least one OSC message arrives, then drains all pending.
- pub fn recv<E: Display>(
+ pub fn recv(
&mut self,
- mut on_message: impl FnMut(OscMessage) -> Result<(), E>,
+ mut on_message: impl FnMut(OscMessage) -> Result<(), Box<dyn Error>>,
) -> bool {
self.socket.set_nonblocking(false).expect("set blocking");
match self.socket.recv_from(&mut self.buf) {
Ok((size, _addr)) => {
let data = &self.buf[..size];
let res = rosc::decoder::decode_udp(data)
- .map_err(|e| format!("OSC edecode error: {}", e))
- .and_then(|(_, packet)| {
- dispatch(packet, &mut on_message).map_err(|e| e.to_string())
- });
+ .map_err(Box::from)
+ .and_then(|(_, packet)| dispatch(packet, &mut on_message));
if let Err(e) = res {
- log::warn!("{}", e);
+ log::error!("{}", e);
};
}
Err(e) => {
- log::warn!("OSC recv error: {}", e);
+ log::error!("OSC recv error: {}", e);
}
}
self.socket.set_nonblocking(true).expect("set nonblocking");
diff --git a/src/renderer.rs b/src/renderer.rs
index 155c318..c6f1973 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -1,4 +1,11 @@
-use crate::uniform::UniformCache;
+use std::error::Error;
+use std::str::FromStr;
+
+use rosc::OscType;
+use wesl::{Mangler, ModulePath};
+
+use crate::uniform::{UniformCache, UniformError};
+use crate::wesl::ShaderSources;
const CANVAS_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
@@ -30,6 +37,24 @@ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
}
";
+#[derive(Debug, thiserror::Error)]
+pub enum ShaderError {
+ #[error("error resolving module")]
+ InvalidModulePath,
+
+ #[error("error setting uniform '{0}': {1}")]
+ UniformError(String, crate::uniform::UniformError),
+
+ #[error("uniform '{0}' not found")]
+ UniformNotFound(String),
+
+ #[error("error binding resource to '{0}': {1}")]
+ BindError(String, crate::uniform::BindError),
+
+ #[error("error compiling shader: {0}")]
+ CompileError(#[from] wesl::Error),
+}
+
pub struct Renderer {
canvas: wgpu::Texture,
canvas_view: wgpu::TextureView,
@@ -37,6 +62,7 @@ pub struct Renderer {
vertex_bgl: wgpu::BindGroupLayout,
vertex_bg: wgpu::BindGroup,
render_pipeline: wgpu::RenderPipeline,
+ shader_sources: ShaderSources,
uniforms: UniformCache,
}
@@ -102,11 +128,19 @@ impl Renderer {
);
let mut uniforms = UniformCache::new();
+ let mut shader_sources = ShaderSources::default();
+ shader_sources.set_module(wesl::ModulePath::new_root(), DEFAULT_FRAGMENT);
+
+ let initial_fragment = shader_sources
+ .compile(&wesl::ModulePath::new_root())
+ .expect("default shader");
+ log::info!("frag source:\n {initial_fragment}");
+
let render_pipeline = build_pipeline(
device,
&vertex_module,
&vertex_bgl,
- DEFAULT_FRAGMENT,
+ &initial_fragment,
&mut uniforms,
)
.expect("default shader");
@@ -118,6 +152,7 @@ impl Renderer {
vertex_bgl,
vertex_bg,
render_pipeline,
+ shader_sources,
uniforms,
}
}
@@ -126,24 +161,115 @@ impl Renderer {
&self.canvas
}
- pub fn canvas_view(&self) -> &wgpu::TextureView {
- &self.canvas_view
- }
-
pub fn uniforms(&mut self) -> &mut UniformCache {
&mut self.uniforms
}
- pub fn load_shader(&mut self, device: &wgpu::Device, source: &str) -> Result<(), String> {
- let pipeline = build_pipeline(
+ pub fn set_uniform(
+ &mut self,
+ module: &str,
+ item: &str,
+ rest: &[&str],
+ args: &[OscType],
+ ) -> Result<(), ShaderError> {
+ let path = ModulePath::from_str(module).map_err(|_| ShaderError::InvalidModulePath)?;
+ let name = self.shader_sources.mangler().mangle(&path, item);
+
+ let mut uref = self
+ .uniforms
+ .get(&name)
+ .ok_or(ShaderError::UniformNotFound(name))?;
+
+ let lift_err = |e| ShaderError::UniformError(item.to_string(), e);
+
+ for component in rest.iter() {
+ uref = uref.field(component).map_err(lift_err)?;
+ }
+
+ let scalar = uref.leaf_scalar().map_err(lift_err)?;
+ match scalar.kind {
+ naga::ScalarKind::Float => args
+ .iter()
+ .map(|a| match a {
+ OscType::Float(f) => Ok(*f),
+ OscType::Double(d) => Ok(*d as f32),
+ OscType::Int(i) => Ok(*i as f32),
+ OscType::Bool(b) => Ok(if *b { 1.0 } else { 0.0 }),
+ _ => Err(UniformError::TypeMismatch),
+ })
+ .collect::<Result<Vec<_>, _>>()
+ .and_then(|values| uref.set_f32(&values)),
+ naga::ScalarKind::Sint => args
+ .iter()
+ .map(|a| match a {
+ OscType::Int(i) => Ok(*i),
+ OscType::Float(f) => Ok(*f as i32),
+ OscType::Double(d) => Ok(*d as i32),
+ OscType::Bool(b) => Ok(if *b { 1 } else { 0 }),
+ _ => Err(UniformError::TypeMismatch),
+ })
+ .collect::<Result<Vec<_>, _>>()
+ .and_then(|values| uref.set_i32(&values)),
+ naga::ScalarKind::Uint => args
+ .iter()
+ .map(|a| match a {
+ OscType::Int(i) => Ok(*i as u32),
+ OscType::Float(f) => Ok(*f as u32),
+ OscType::Double(d) => Ok(*d as u32),
+ OscType::Bool(b) => Ok(if *b { 1 } else { 0 }),
+ _ => Err(UniformError::TypeMismatch),
+ })
+ .collect::<Result<Vec<_>, _>>()
+ .and_then(|values| uref.set_u32(&values)),
+ _ => Err(UniformError::TypeMismatch),
+ }
+ .map_err(lift_err)
+ }
+
+ pub fn set_binding(
+ &mut self,
+ device: &wgpu::Device,
+ module: &str,
+ item: &str,
+ target: &str,
+ ) -> Result<(), ShaderError> {
+ let path = ModulePath::from_str(module).map_err(|_| ShaderError::InvalidModulePath)?;
+ let name = self.shader_sources.mangler().mangle(&path, item);
+
+ if let Some(id) = target.strip_prefix("/texture/") {
+ self.uniforms.bind_texture(device, &name, id)
+ } else if let Some(id) = target.strip_prefix("/sampler/") {
+ self.uniforms.bind_sampler(device, &name, id)
+ } else {
+ Err(crate::uniform::BindError::ResourceNotFound(
+ target.to_string(),
+ ))
+ }
+ .map_err(|e| ShaderError::BindError(name, e))
+ }
+
+ pub fn load_module(&mut self, module_name: &str, source: &str) -> Result<(), ShaderError> {
+ let path = ModulePath::from_str(module_name).map_err(|_| ShaderError::InvalidModulePath)?;
+ self.shader_sources.set_module(path, source);
+ Ok(())
+ }
+
+ pub fn compile(
+ &mut self,
+ device: &wgpu::Device,
+ module_name: &str,
+ ) -> Result<(), Box<dyn Error>> {
+ let path = ModulePath::from_str(module_name).map_err(|_| ShaderError::InvalidModulePath)?;
+ let fragment_source = self.shader_sources.compile(&path)?;
+ log::info!("frag source:\n {fragment_source}");
+
+ self.render_pipeline = build_pipeline(
device,
&self.vertex_module,
&self.vertex_bgl,
- source,
+ &fragment_source,
&mut self.uniforms,
)?;
- self.render_pipeline = pipeline;
- log::info!("shader loaded");
Ok(())
}
@@ -189,16 +315,14 @@ fn build_pipeline(
vertex_bgl: &wgpu::BindGroupLayout,
fragment_source: &str,
cache: &mut UniformCache,
-) -> Result<wgpu::RenderPipeline, String> {
- let module = naga::front::wgsl::parse_str(fragment_source)
- .map_err(|e| format!("WGSL parse error: {e}"))?;
+) -> Result<wgpu::RenderPipeline, Box<dyn Error>> {
+ let module = naga::front::wgsl::parse_str(fragment_source)?;
naga::valid::Validator::new(
naga::valid::ValidationFlags::all(),
naga::valid::Capabilities::all(),
)
- .validate(&module)
- .map_err(|e| format!("WGSL validation error: {e}"))?;
+ .validate(&module)?;
cache.refresh(module, device);
cache.rebuild_bind_group(device);
@@ -225,13 +349,13 @@ fn build_pipeline(
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: vertex_module,
- entry_point: Some("vs_main"),
+ entry_point: None,
buffers: &[],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &fragment_module,
- entry_point: Some("fs_main"),
+ entry_point: None,
targets: &[Some(wgpu::ColorTargetState {
format: CANVAS_FORMAT,
blend: Some(wgpu::BlendState::REPLACE),
diff --git a/src/uniform.rs b/src/uniform.rs
index fefd67f..db01e0d 100644
--- a/src/uniform.rs
+++ b/src/uniform.rs
@@ -11,29 +11,28 @@ pub struct UniformRef<'a> {
pub data: &'a mut [u8],
}
-#[derive(Debug)]
+#[derive(Debug, thiserror::Error)]
pub enum UniformError {
+ #[error("can't be indexed (further)")]
NotIndexable,
- IndexOutOfBounds,
- NotFound,
+ #[error("index '{0}' out of bounds")]
+ IndexOutOfBounds(usize),
+ #[error("member '{0}' not found")]
+ MemberNotFound(String),
+ #[error("mismatched value types")]
TypeMismatch,
+ #[error("invalid number of OSC values")]
SizeMismatch,
}
-impl std::fmt::Display for UniformError {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::NotIndexable => write!(f, "type is not indexable"),
- Self::IndexOutOfBounds => write!(f, "index out of bounds"),
- Self::NotFound => write!(f, "member not found"),
- Self::TypeMismatch => write!(f, "scalar type mismatch"),
- Self::SizeMismatch => write!(f, "value count mismatch"),
- }
- }
+#[derive(Debug, thiserror::Error)]
+pub enum BindError {
+ #[error("binding point '{0}' not found")]
+ NotFound(String),
+ #[error("bindable resource '{0}' not found")]
+ ResourceNotFound(String),
}
-impl std::error::Error for UniformError {}
-
impl<'a> UniformRef<'a> {
/// Navigate by path component: struct member by name, or swizzle/numeric index otherwise.
pub fn field(self, name: &str) -> Result<UniformRef<'a>, UniformError> {
@@ -51,7 +50,7 @@ impl<'a> UniformRef<'a> {
});
}
}
- return Err(UniformError::NotFound);
+ return Err(UniformError::MemberNotFound(name.to_string()));
}
let i = match name {
@@ -70,7 +69,7 @@ impl<'a> UniformRef<'a> {
match *ty_inner {
naga::TypeInner::Vector { size, scalar } => {
if i >= size as usize {
- return Err(UniformError::IndexOutOfBounds);
+ return Err(UniformError::IndexOutOfBounds(i));
}
let w = scalar.width as usize;
let scalar_ty = find_or_expect_scalar_type(self.module, scalar);
@@ -87,7 +86,7 @@ impl<'a> UniformRef<'a> {
scalar,
} => {
if i >= columns as usize {
- return Err(UniformError::IndexOutOfBounds);
+ return Err(UniformError::IndexOutOfBounds(i));
}
let col_ty = find_or_expect_vector_type(self.module, rows, scalar);
let col_layout = self.layouter[col_ty];
@@ -106,7 +105,7 @@ impl<'a> UniformRef<'a> {
_ => return Err(UniformError::NotIndexable),
};
if i >= len {
- return Err(UniformError::IndexOutOfBounds);
+ return Err(UniformError::IndexOutOfBounds(i));
}
let elem_size = self.layouter[base].size as usize;
Ok(UniformRef {
@@ -733,6 +732,10 @@ impl UniformCache {
None => continue,
};
+ if let Some(ref name) = var.name {
+ log::info!("found uniform '{name}'");
+ }
+
match var.space {
naga::AddressSpace::Uniform => {
let layout = new_layouter[var.ty];
@@ -855,6 +858,7 @@ impl UniformCache {
// --- Resource pool operations ---
pub fn create_texture(&mut self, device: &wgpu::Device, id: &str, tsv_name: &str) {
+ log::info!("creating texture {id} (tsv {tsv_name})");
self.texture_pool.insert(
id.to_string(),
TextureResource::new(device, id.to_string(), tsv_name.to_string()),
@@ -863,6 +867,7 @@ impl UniformCache {
}
pub fn destroy_texture(&mut self, device: &wgpu::Device, id: &str) {
+ log::info!("destroying texture {id}");
let removed = self.texture_pool.remove(id).is_some();
let unbound = self.unbind_texture_resource(id);
if removed || unbound {
@@ -881,6 +886,7 @@ impl UniformCache {
filter: wgpu::FilterMode,
address_mode: wgpu::AddressMode,
) {
+ log::info!("creating sampler {id} ({filter:?} {address_mode:?})");
self.sampler_pool.insert(
id.to_string(),
SamplerResource::new(device, id.to_string(), filter, address_mode),
@@ -889,6 +895,7 @@ impl UniformCache {
}
pub fn destroy_sampler(&mut self, device: &wgpu::Device, id: &str) {
+ log::info!("destroying sampler {id}");
let removed = self.sampler_pool.remove(id).is_some();
let unbound = self.unbind_sampler_resource(id);
if removed || unbound {
@@ -901,15 +908,15 @@ impl UniformCache {
device: &wgpu::Device,
slot_name: &str,
resource_id: &str,
- ) -> Result<(), String> {
+ ) -> Result<(), BindError> {
if !self.texture_pool.contains_key(resource_id) {
- return Err(format!("texture '{resource_id}' not found"));
+ return Err(BindError::ResourceNotFound(resource_id.to_string()));
}
let slot = self
.texture_slots
.iter_mut()
.find(|t| t.name == slot_name)
- .ok_or_else(|| format!("texture binding '{slot_name}' not found in shader"))?;
+ .ok_or(BindError::NotFound(slot_name.to_string()))?;
slot.resource_id = Some(resource_id.to_string());
self.build_bind_group(device);
Ok(())
@@ -920,15 +927,15 @@ impl UniformCache {
device: &wgpu::Device,
slot_name: &str,
resource_id: &str,
- ) -> Result<(), String> {
+ ) -> Result<(), BindError> {
if !self.sampler_pool.contains_key(resource_id) {
- return Err(format!("sampler '{resource_id}' not found"));
+ return Err(BindError::ResourceNotFound(resource_id.to_string()));
}
let slot = self
.sampler_slots
.iter_mut()
.find(|s| s.name == slot_name)
- .ok_or_else(|| format!("sampler binding '{slot_name}' not found in shader"))?;
+ .ok_or(BindError::NotFound(slot_name.to_string()))?;
slot.resource_id = Some(resource_id.to_string());
self.build_bind_group(device);
Ok(())
diff --git a/src/wesl.rs b/src/wesl.rs
new file mode 100644
index 0000000..446c915
--- /dev/null
+++ b/src/wesl.rs
@@ -0,0 +1,86 @@
+use std::borrow::Cow;
+use std::iter;
+use std::path::PathBuf;
+
+use wesl::{
+ syntax::PathOrigin, EscapeMangler, FileResolver, Mangler, ModulePath, ResolveError, Resolver,
+ VirtualResolver, Wesl,
+};
+
+pub struct ShaderSources {
+ virtual_resolver: VirtualResolver<'static>,
+ file_resolver: Option<FileResolver>,
+ mangler: EscapeMangler,
+}
+
+impl Default for ShaderSources {
+ fn default() -> Self {
+ Self::new(Some("wesl-lib".into()))
+ }
+}
+
+impl ShaderSources {
+ pub fn new(lib_dir: Option<PathBuf>) -> Self {
+ Self {
+ virtual_resolver: VirtualResolver::new(),
+ file_resolver: lib_dir.map(FileResolver::new),
+ mangler: Default::default(),
+ }
+ }
+
+ pub fn set_module(&mut self, module: ModulePath, source: impl Into<String>) {
+ self.virtual_resolver
+ .add_module(module, Cow::Owned(source.into()));
+ }
+
+ pub fn compile(&mut self, root_module: &ModulePath) -> Result<String, wesl::Error> {
+ log::info!("--- all modules ---");
+ for (path, text) in self.virtual_resolver.modules() {
+ log::info!("==== {path}:\n{text}");
+ }
+ log::info!("--- end modules ---");
+
+ Wesl::new_barebones()
+ .set_custom_resolver(&*self)
+ .set_custom_mangler(self.mangler.clone())
+ .set_options(wesl::CompileOptions {
+ mangle_root: true,
+ ..Default::default()
+ })
+ .compile(root_module)
+ .map(|compiled| compiled.to_string())
+ }
+
+ pub fn mangler(&self) -> &impl Mangler {
+ &self.mangler
+ }
+}
+
+impl Resolver for ShaderSources {
+ fn resolve_source<'a>(&'a self, path: &ModulePath) -> Result<Cow<'a, str>, ResolveError> {
+ match path.origin {
+ PathOrigin::Absolute => self.virtual_resolver.resolve_source(path),
+ PathOrigin::Package(ref pname) => {
+ if let Some(ref files) = self.file_resolver {
+ let fpath = ModulePath {
+ origin: PathOrigin::Absolute,
+ components: iter::once(pname)
+ .chain(path.components.iter())
+ .cloned()
+ .collect(),
+ };
+ files.resolve_source(&fpath)
+ } else {
+ Err(ResolveError::ModuleNotFound(
+ path.clone(),
+ "no library director found".into(),
+ ))
+ }
+ }
+ _ => Err(ResolveError::ModuleNotFound(
+ path.clone(),
+ "can't resolve relative module".into(),
+ )),
+ }
+ }
+}
diff --git a/test.sh b/test.sh
index 6ed469e..15bcc7f 100755
--- a/test.sh
+++ b/test.sh
@@ -37,7 +37,8 @@ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
return vec4f(color, 1);
}
'
-oscsend "$HOST" "$PORT" /shader s "$SHADER"
-oscsend "$HOST" "$PORT" /uniform/color fff 1 0 0
+oscsend "$HOST" "$PORT" /module/package::main s "$SHADER"
+oscsend "$HOST" "$PORT" /entrypoint s package::main
+oscsend "$HOST" "$PORT" /uniform/package::main/color fff 1 0 0
cargo run --bin tsv-view
diff --git a/test_buffer.sh b/test_buffer.sh
index e3e6520..20d32e6 100755
--- a/test_buffer.sh
+++ b/test_buffer.sh
@@ -45,16 +45,17 @@ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
'
oscsend "$HOST" "$PORT" /texture/tex1 s "$TSV_SRC"
oscsend "$HOST" "$PORT" /sampler/samp1 ss nearest repeat
-oscsend "$HOST" "$PORT" /shader s "$SHADER"
-oscsend "$HOST" "$PORT" /binding/video s /texture/tex1
-oscsend "$HOST" "$PORT" /binding/s s /sampler/samp1
+oscsend "$HOST" "$PORT" /module/package::main s "$SHADER"
+oscsend "$HOST" "$PORT" /entrypoint s package::main
+oscsend "$HOST" "$PORT" /binding/package::main/video s /texture/tex1
+oscsend "$HOST" "$PORT" /binding/package::main/s s /sampler/samp1
spawn tsv-view
echo "Playing video buffer (Ctrl+C to stop)..."
T=0
while true; do
- oscsend "$HOST" "$PORT" /uniform/time f "$T"
+ oscsend "$HOST" "$PORT" /uniform/package::main/time f "$T"
T=$((T+1 % 60))
sleep 0.016
done
diff --git a/test_video.sh b/test_video.sh
index a3432d8..f941878 100755
--- a/test_video.sh
+++ b/test_video.sh
@@ -43,8 +43,9 @@ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
'
oscsend "$HOST" "$PORT" /texture/tex1 s "$TSV_SRC"
oscsend "$HOST" "$PORT" /sampler/samp1 ss nearest clamp
-oscsend "$HOST" "$PORT" /shader s "$SHADER"
-oscsend "$HOST" "$PORT" /binding/video s /texture/tex1
-oscsend "$HOST" "$PORT" /binding/s s /sampler/samp1
+oscsend "$HOST" "$PORT" /module/package::main s "$SHADER"
+oscsend "$HOST" "$PORT" /entrypoint s package::main
+oscsend "$HOST" "$PORT" /binding/package::main/video s /texture/tex1
+oscsend "$HOST" "$PORT" /binding/package::main/s s /sampler/samp1
cargo run --bin tsv-view