diff --git a/.DS_Store b/.DS_Store index 39bf198..2264ea6 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index ad67955..8be5543 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ target # MSVC Windows builds of rustc generate these, which store debugging information *.pdb +media + # Generated by cargo mutants # Contains mutation testing data **/mutants.out*/ diff --git a/Cargo.lock b/Cargo.lock index 7fc0581..3e5fe8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,19 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if 1.0.4", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -35,14 +48,14 @@ dependencies = [ [[package]] name = "alsa" -version = "0.7.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2562ad8dcf0f789f65c6fdaad8a8a9708ed6b488e649da28c01656ad66b8b47" +checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" dependencies = [ "alsa-sys", - "bitflags 1.3.2", + "bitflags 2.11.0", + "cfg-if 1.0.4", "libc", - "nix 0.24.3", ] [[package]] @@ -57,20 +70,23 @@ dependencies = [ [[package]] name = "android-activity" -version = "0.4.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64529721f27c2314ced0890ce45e469574a73e5e6fdd6e9da1860eb29285f5e0" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 1.3.2", + "bitflags 2.11.0", "cc", + "cesu8", + "jni", "jni-sys", "libc", "log", "ndk", "ndk-context", - "ndk-sys", - "num_enum 0.6.1", + "ndk-sys 0.6.0+11769913", + "num_enum", + "thiserror 1.0.69", ] [[package]] @@ -80,10 +96,63 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" [[package]] -name = "anyhow" -version = "1.0.102" +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] [[package]] name = "arrayref" @@ -97,6 +166,27 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.0" @@ -120,12 +210,47 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", - "syn 2.0.111", + "syn", "which", ] +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.1", + "shlex", + "syn", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -134,9 +259,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "block" @@ -144,32 +269,13 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[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 = "block-sys" -version = "0.1.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" -dependencies = [ - "objc-sys", -] - [[package]] name = "block2" -version = "0.2.0-alpha.6" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "block-sys", - "objc2-encode", + "objc2", ] [[package]] @@ -183,6 +289,20 @@ name = "bytemuck" version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "byteorder" @@ -204,16 +324,28 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "calloop" -version = "0.10.6" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.11.0", "log", - "nix 0.25.1", - "slotmap", + "polling", + "rustix 0.38.44", + "slab", "thiserror 1.0.69", - "vec_map", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix 0.38.44", + "wayland-backend", + "wayland-client", ] [[package]] @@ -228,6 +360,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -256,13 +394,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] -name = "cgl" -version = "0.3.2" +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" -dependencies = [ - "libc", -] +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clang-sys" @@ -272,7 +407,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading 0.8.9", + "libloading", ] [[package]] @@ -285,7 +420,7 @@ dependencies = [ "block", "core-foundation 0.7.0", "core-graphics 0.19.2", - "foreign-types", + "foreign-types 0.3.2", "libc", "objc", ] @@ -296,13 +431,48 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "block", "core-foundation 0.10.1", "core-graphics-types 0.2.0", "objc", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "core-foundation" version = "0.7.0" @@ -353,20 +523,20 @@ checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" dependencies = [ "bitflags 1.3.2", "core-foundation 0.7.0", - "foreign-types", + "foreign-types 0.3.2", "libc", ] [[package]] name = "core-graphics" -version = "0.22.3" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", "core-graphics-types 0.1.3", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -387,7 +557,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "core-foundation 0.10.1", "libc", ] @@ -413,16 +583,17 @@ dependencies = [ "core-foundation-sys 0.7.0", "core-graphics 0.19.2", "libc", - "metal", + "metal 0.18.0", "objc", ] [[package]] name = "coremidi" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7847ca018a67204508b77cb9e6de670125075f7464fff5f673023378fa34f5" +checksum = "964eb3e10ea8b0d29c797086aab3ca730f75e06dced0cb980642fd274a5cca30" dependencies = [ + "block", "core-foundation 0.9.4", "core-foundation-sys 0.8.7", "coremidi-sys", @@ -437,15 +608,6 @@ dependencies = [ "core-foundation-sys 0.8.7", ] -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -456,30 +618,25 @@ dependencies = [ ] [[package]] -name = "crypto-common" -version = "0.1.7" +name = "crossbeam-channel" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ - "generic-array", - "typenum", + "crossbeam-utils", ] [[package]] -name = "data-encoding" -version = "2.10.0" +name = "crossbeam-utils" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] -name = "digest" -version = "0.10.7" +name = "cursor-icon" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "dispatch" @@ -488,23 +645,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] -name = "displaydoc" -version = "0.2.5" +name = "dlib" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", + "libloading", ] [[package]] -name = "dlib" -version = "0.5.2" +name = "document-features" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ - "libloading 0.8.9", + "litrs", ] [[package]] @@ -513,12 +668,47 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "env_filter" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -544,6 +734,42 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "ffmpeg-next" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c4bd5ab1ac61f29c634df1175d350ded29cf74c3c6d4f7030431a5ae3c7d5d" +dependencies = [ + "bitflags 2.11.0", + "ffmpeg-sys-next", + "libc", +] + +[[package]] +name = "ffmpeg-sys-next" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a314bc0e022a33a99567ed4bd2576bd58ffd8fcff7891c29194cfecc26a62547" +dependencies = [ + "bindgen 0.72.1", + "cc", + "libc", + "num_cpus", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if 1.0.4", + "libc", + "libredox", +] + [[package]] name = "find-msvc-tools" version = "0.1.6" @@ -557,6 +783,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", + "libz-rs-sys", "miniz_oxide", ] @@ -572,81 +799,83 @@ dependencies = [ "spin", ] +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", ] [[package]] -name = "foreign-types-shared" -version = "0.1.1" +name = "foreign-types" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] [[package]] -name = "form_urlencoded" -version = "1.2.2" +name = "foreign-types-macros" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ - "percent-encoding", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "futures-core" -version = "0.3.32" +name = "foreign-types-shared" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] -name = "futures-macro" -version = "0.3.32" +name = "foreign-types-shared" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] -name = "futures-sink" -version = "0.3.32" +name = "fsevent-sys" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] [[package]] -name = "futures-task" +name = "futures-core" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] -name = "futures-util" +name = "futures-sink" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-core", - "futures-macro", - "futures-sink", - "futures-task", - "pin-project-lite", - "slab", -] +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] -name = "generic-array" -version = "0.14.7" +name = "gethostname" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "typenum", - "version_check", + "rustix 1.1.4", + "windows-link", ] [[package]] @@ -693,9 +922,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "glow" -version = "0.13.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483" dependencies = [ "js-sys", "slotmap", @@ -704,241 +933,217 @@ dependencies = [ ] [[package]] -name = "glutin" -version = "0.30.10" +name = "glutin_wgl_sys" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc93b03242719b8ad39fb26ed2b01737144ce7bd4bfc7adadcef806596760fe" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" dependencies = [ - "bitflags 1.3.2", - "cfg_aliases", - "cgl", - "core-foundation 0.9.4", - "dispatch", - "glutin_egl_sys", - "glutin_glx_sys", - "glutin_wgl_sys", - "libloading 0.7.4", - "objc2", - "once_cell", - "raw-window-handle", - "wayland-sys 0.30.1", - "windows-sys 0.45.0", - "x11-dl", + "gl_generator", ] [[package]] -name = "glutin-winit" -version = "0.3.0" +name = "gpu-alloc" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629a873fc04062830bfe8f97c03773bcd7b371e23bcc465d0a61448cd1588fa4" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "cfg_aliases", - "glutin", - "raw-window-handle", - "winit", + "bitflags 2.11.0", + "gpu-alloc-types", ] [[package]] -name = "glutin_egl_sys" -version = "0.5.1" +name = "gpu-alloc-types" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af784eb26c5a68ec85391268e074f0aa618c096eadb5d6330b0911cf34fe57c5" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "gl_generator", - "windows-sys 0.45.0", + "bitflags 2.11.0", ] [[package]] -name = "glutin_glx_sys" -version = "0.4.0" +name = "gpu-allocator" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b53cb5fe568964aa066a3ba91eac5ecbac869fb0842cd0dc9e412434f1a1494" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" dependencies = [ - "gl_generator", - "x11-dl", + "log", + "presser", + "thiserror 1.0.69", + "windows 0.58.0", ] [[package]] -name = "glutin_wgl_sys" -version = "0.4.0" +name = "gpu-descriptor" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef89398e90033fc6bc65e9bd42fd29bbbfd483bda5b56dc5562f455550618165" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "gl_generator", + "bitflags 2.11.0", + "gpu-descriptor-types", + "hashbrown 0.15.5", ] [[package]] -name = "hashbrown" -version = "0.16.1" +name = "gpu-descriptor-types" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.11.0", +] [[package]] -name = "home" -version = "0.5.12" +name = "grafton-ndi" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +checksum = "83fc4d5740ce73851ed15845aba8e26088bfc70f51c62ad00b883c20c09cc773" dependencies = [ - "windows-sys 0.61.2", + "bindgen 0.72.1", + "lodepng", + "num_enum", + "once_cell", + "png", + "thiserror 2.0.17", ] [[package]] -name = "http" -version = "1.4.0" +name = "hashbrown" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "bytes", - "itoa", + "foldhash", ] [[package]] -name = "httparse" -version = "1.10.1" +name = "hashbrown" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] -name = "icu_collections" -version = "2.1.1" +name = "hermit-abi" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] -name = "icu_locale_core" -version = "2.1.1" +name = "hexf-parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] -name = "icu_normalizer" -version = "2.1.1" +name = "home" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", + "windows-sys 0.61.2", ] [[package]] -name = "icu_normalizer_data" -version = "2.1.1" +name = "image" +version = "0.25.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", +] [[package]] -name = "icu_properties" -version = "2.1.2" +name = "indexmap" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", + "equivalent", + "hashbrown 0.16.1", ] [[package]] -name = "icu_properties_data" -version = "2.1.2" +name = "inotify" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] [[package]] -name = "icu_provider" -version = "2.1.1" +name = "inotify-sys" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", + "libc", ] [[package]] -name = "idna" -version = "1.1.0" +name = "is_terminal_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] -name = "idna_adapter" -version = "1.2.1" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ - "icu_normalizer", - "icu_properties", + "either", ] [[package]] -name = "image" -version = "0.25.9" +name = "itoa" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" -dependencies = [ - "bytemuck", - "byteorder-lite", - "moxcms", - "num-traits", -] +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] -name = "indexmap" -version = "2.12.1" +name = "jiff" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" dependencies = [ - "equivalent", - "hashbrown", + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", ] [[package]] -name = "instant" -version = "0.1.13" +name = "jiff-static" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" dependencies = [ - "cfg-if 1.0.4", - "js-sys", - "wasm-bindgen", - "web-sys", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "itoa" -version = "1.0.17" +name = "jni" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if 1.0.4", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] [[package]] name = "jni-sys" @@ -966,12 +1171,43 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + [[package]] name = "khronos_api" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -986,19 +1222,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.178" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" - -[[package]] -name = "libloading" -version = "0.7.4" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if 1.0.4", - "winapi", -] +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libloading" @@ -1016,11 +1242,20 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "libc", "redox_syscall 0.7.0", ] +[[package]] +name = "libz-rs-sys" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c10501e7805cee23da17c7790e59df2870c0d4043ec6d03f67d31e2b53e77415" +dependencies = [ + "zlib-rs", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -1028,10 +1263,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] -name = "litemap" -version = "0.8.1" +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litrs" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" @@ -1042,6 +1283,18 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lodepng" +version = "3.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe7982db11054edc023a1b424dddcc65be18f71fa46ec6bde2efcfc1fb6b22da" +dependencies = [ + "crc32fast", + "flate2", + "libc", + "rgb", +] + [[package]] name = "log" version = "0.4.29" @@ -1057,15 +1310,6 @@ dependencies = [ "libc", ] -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - [[package]] name = "memchr" version = "2.7.6" @@ -1074,22 +1318,13 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" -version = "0.5.10" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "metal" version = "0.18.0" @@ -1100,25 +1335,41 @@ dependencies = [ "block", "cocoa", "core-graphics 0.19.2", - "foreign-types", + "foreign-types 0.3.2", + "log", + "objc", +] + +[[package]] +name = "metal" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +dependencies = [ + "bitflags 2.11.0", + "block", + "core-graphics-types 0.1.3", + "foreign-types 0.5.0", "log", "objc", + "paste", ] [[package]] name = "midir" -version = "0.9.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a456444d83e7ead06ae6a5c0a215ed70282947ff3897fb45fcb052b757284731" +checksum = "b73f8737248ad37b88291a2108d9df5f991dc8555103597d586b5a29d4d703c0" dependencies = [ "alsa", "bitflags 1.3.2", "coremidi", "js-sys", "libc", + "parking_lot", "wasm-bindgen", "web-sys", - "windows 0.43.0", + "windows 0.56.0", ] [[package]] @@ -1150,24 +1401,60 @@ dependencies = [ ] [[package]] -name = "mio" -version = "1.1.1" +name = "moxcms" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "mozjpeg" +version = "0.10.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "b7891b80aaa86097d38d276eb98b3805d6280708c4e0a1e6f6aed9380c51fec9" dependencies = [ + "arrayvec", + "bytemuck", "libc", - "wasi", - "windows-sys 0.61.2", + "mozjpeg-sys", + "rgb", ] [[package]] -name = "moxcms" -version = "0.7.11" +name = "mozjpeg-sys" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +checksum = "7f0dc668bf9bf888c88e2fb1ab16a406d2c380f1d082b20d51dd540ab2aa70c1" dependencies = [ - "num-traits", - "pxfm", + "cc", + "dunce", + "libc", + "nasm-rs", +] + +[[package]] +name = "naga" +version = "23.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags 2.11.0", + "cfg_aliases 0.1.1", + "codespan-reporting", + "hexf-parse", + "indexmap", + "log", + "pp-rs", + "rustc-hash 1.1.0", + "spirv", + "termcolor", + "thiserror 1.0.69", + "unicode-xid", ] [[package]] @@ -1179,16 +1466,27 @@ dependencies = [ "getrandom 0.2.17", ] +[[package]] +name = "nasm-rs" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706bf8a5e8c8ddb99128c3291d31bd21f4bcde17f0f4c20ec678d85c74faa149" +dependencies = [ + "jobserver", + "log", +] + [[package]] name = "ndk" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.11.0", "jni-sys", - "ndk-sys", - "num_enum 0.5.11", + "log", + "ndk-sys 0.6.0+11769913", + "num_enum", "raw-window-handle", "thiserror 1.0.69", ] @@ -1201,36 +1499,20 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.4.1+23.1.7779620" +version = "0.5.0+25.2.9519653" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" dependencies = [ "jni-sys", ] [[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags 1.3.2", - "cfg-if 1.0.4", - "libc", - "memoffset", -] - -[[package]] -name = "nix" -version = "0.25.1" +name = "ndk-sys" +version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if 1.0.4", - "libc", - "memoffset", + "jni-sys", ] [[package]] @@ -1245,6 +1527,7 @@ dependencies = [ "nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-core", + "parking_lot", "paste", "thiserror 2.0.17", ] @@ -1295,6 +1578,7 @@ checksum = "109975552bbd690894f613bce3d408222911e317197c72b2e8b9a1912dc261ae" dependencies = [ "bytes", "image", + "mozjpeg", "thiserror 2.0.17", ] @@ -1309,12 +1593,22 @@ dependencies = [ ] [[package]] -name = "nu-ansi-term" -version = "0.50.3" +name = "notify" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "windows-sys 0.61.2", + "bitflags 2.11.0", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "walkdir", + "windows-sys 0.48.0", ] [[package]] @@ -1327,45 +1621,35 @@ dependencies = [ ] [[package]] -name = "num_enum" -version = "0.5.11" +name = "num_cpus" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "num_enum_derive 0.5.11", + "hermit-abi", + "libc", ] [[package]] name = "num_enum" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" -dependencies = [ - "num_enum_derive 0.6.1", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", + "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.6.1" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -1380,28 +1664,205 @@ dependencies = [ [[package]] name = "objc-sys" -version = "0.2.0-beta.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" [[package]] name = "objc2" -version = "0.3.0-beta.3.patch-leaks.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ - "block2", "objc-sys", "objc2-encode", ] +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.11.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + [[package]] name = "objc2-encode" -version = "2.0.0-pre.2" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "objc-sys", + "bitflags 2.11.0", + "block2", + "dispatch", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", ] [[package]] @@ -1419,6 +1880,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "orbclient" version = "0.3.49" @@ -1478,6 +1945,26 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pin-project" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1492,11 +1979,11 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "png" -version = "0.17.16" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.11.0", "crc32fast", "fdeflate", "flate2", @@ -1504,23 +1991,55 @@ dependencies = [ ] [[package]] -name = "potential_utf" -version = "0.1.4" +name = "polling" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ - "zerovec", + "cfg-if 1.0.4", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", ] [[package]] -name = "ppv-lite86" -version = "0.2.21" +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "portable-atomic" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" dependencies = [ - "zerocopy", + "portable-atomic", ] +[[package]] +name = "pp-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb458bb7f6e250e6eb79d5026badc10a3ebb8f9a15d1fff0f13d17c71f4d6dee" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + [[package]] name = "prettyplease" version = "0.2.37" @@ -1528,16 +2047,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.111", + "syn", ] [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" dependencies = [ - "once_cell", "toml_edit", ] @@ -1550,6 +2068,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" + [[package]] name = "pxfm" version = "0.1.27" @@ -1559,6 +2083,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "quick-xml" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.42" @@ -1575,46 +2108,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "rand" -version = "0.8.5" +name = "range-alloc" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" [[package]] name = "raw-window-handle" -version = "0.5.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] @@ -1625,7 +2134,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -1634,7 +2143,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -1666,6 +2175,21 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] + [[package]] name = "rosc" version = "0.10.1" @@ -1682,19 +2206,38 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustix" version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.15", "windows-sys 0.59.0", ] +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -1702,36 +2245,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] -name = "scheng-bridge" -version = "0.1.0" -dependencies = [ - "anyhow", - "futures-util", - "glow", - "glutin", - "glutin-winit", - "midir", - "raw-window-handle", - "rosc", - "scheng-core", - "scheng-graph", - "scheng-runtime", - "scheng-runtime-glow", - "serde", - "serde_json", - "tokio", - "tokio-tungstenite", - "tracing", - "tracing-subscriber", - "winit", -] - -[[package]] -name = "scheng-buffers" -version = "0.1.0" +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ - "glow", - "scheng-runtime-glow", + "winapi-util", ] [[package]] @@ -1751,404 +2270,200 @@ dependencies = [ ] [[package]] -name = "scheng-core" -version = "0.1.0" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "scheng-example-feedback-orb" -version = "0.1.0" -dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-control-osc", - "scheng-passes", - "scheng-runtime-glow", - "winit", -] - -[[package]] -name = "scheng-example-feedback-pingpong" -version = "0.1.0" -dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-control-osc", - "scheng-passes", - "scheng-runtime-glow", - "winit", -] - -[[package]] -name = "scheng-example-graph-chain2" +name = "scheng-control-osc-wgpu" version = "0.1.0" dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-core", - "scheng-graph", - "scheng-runtime-glow", - "winit", + "log", + "rosc", + "scheng-param-store", + "thiserror 1.0.69", ] [[package]] -name = "scheng-example-graph-matrix-mix4" +name = "scheng-core" version = "0.1.0" dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-graph", - "scheng-runtime", - "scheng-runtime-glow", - "winit", + "serde", + "serde_json", ] [[package]] -name = "scheng-example-graph-matrix-mix4-webcam" +name = "scheng-example-instrument" version = "0.1.0" dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-control-osc", - "scheng-core", + "env_logger", + "foreign-types-shared 0.3.1", + "log", + "pollster", + "scheng-control-osc-wgpu", "scheng-graph", + "scheng-hotreload", + "scheng-input-midi", + "scheng-input-rtmp", + "scheng-input-spout", + "scheng-input-syphon", + "scheng-input-video", "scheng-input-webcam", - "scheng-runtime", - "scheng-runtime-glow", - "winit", -] - -[[package]] -name = "scheng-example-graph-minimal" -version = "0.1.0" -dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-graph", - "scheng-runtime-glow", + "scheng-output-ffmpeg", + "scheng-output-ndi", + "scheng-output-spout", + "scheng-output-syphon", + "scheng-param-store", + "scheng-runtime-wgpu", + "wgpu", "winit", ] [[package]] -name = "scheng-example-graph-mixer-builtin" -version = "0.1.0" -dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-graph", - "scheng-runtime", - "scheng-runtime-glow", - "winit", -] - -[[package]] -name = "scheng-example-graph-mixer2" -version = "0.1.0" -dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-core", - "scheng-graph", - "scheng-runtime-glow", - "winit", -] - -[[package]] -name = "scheng-example-graph-webcam-luma-key" +name = "scheng-graph" version = "0.1.0" dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-control-osc", "scheng-core", - "scheng-graph", - "scheng-input-webcam", - "scheng-runtime", - "scheng-runtime-glow", - "winit", -] - -[[package]] -name = "scheng-example-minimal" -version = "0.1.0" -dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-runtime-glow", - "winit", -] - -[[package]] -name = "scheng-example-osc-minimal" -version = "0.1.0" -dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-control-osc", - "scheng-runtime-glow", - "winit", -] - -[[package]] -name = "scheng-example-pure-single-pass" -version = "0.1.0" -dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-runtime-glow", - "winit", ] [[package]] -name = "scheng-example-readback-minimal" +name = "scheng-hotreload" version = "0.1.0" dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", + "log", + "notify", "scheng-graph", - "scheng-runtime-glow", - "winit", -] - -[[package]] -name = "scheng-example-render-target-only" -version = "0.1.0" -dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-runtime-glow", - "winit", + "scheng-param-store", + "scheng-runtime-wgpu", + "thiserror 1.0.69", ] [[package]] -name = "scheng-example-static-source-minimal" +name = "scheng-input-midi" version = "0.1.0" dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-core", - "scheng-graph", - "scheng-runtime", - "scheng-runtime-glow", - "winit", + "log", + "midir", + "scheng-param-store", + "thiserror 1.0.69", ] [[package]] -name = "scheng-example-syphon-builtin" +name = "scheng-input-ndi" version = "0.1.0" dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-graph", - "scheng-runtime", - "scheng-runtime-glow", - "winit", + "grafton-ndi", + "log", + "thiserror 1.0.69", + "wgpu", ] [[package]] -name = "scheng-example-syphon-minimal" +name = "scheng-input-rtmp" version = "0.1.0" dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-graph", - "scheng-runtime-glow", - "winit", + "crossbeam-channel", + "log", + "thiserror 1.0.69", + "wgpu", ] [[package]] -name = "scheng-example-syphon-patchbay" +name = "scheng-input-spout" version = "0.1.0" dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-graph", - "scheng-runtime-glow", - "winit", + "cc", + "log", + "thiserror 1.0.69", + "wgpu", ] [[package]] -name = "scheng-example-texture-input-minimal" +name = "scheng-input-syphon" version = "0.1.0" dependencies = [ - "bytemuck", - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-core", - "scheng-graph", - "scheng-runtime", - "scheng-runtime-glow", - "winit", + "cc", + "log", + "thiserror 1.0.69", + "wgpu", ] [[package]] -name = "scheng-example-video-decode-source-minimal" +name = "scheng-input-video" version = "0.1.0" dependencies = [ - "bytemuck", - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-core", + "ffmpeg-next", + "log", "scheng-graph", - "scheng-runtime", - "scheng-runtime-glow", - "serde", - "serde_json", - "winit", + "thiserror 1.0.69", + "wgpu", ] [[package]] -name = "scheng-example-video-device-capture-macos" +name = "scheng-input-webcam" version = "0.1.0" dependencies = [ - "bytemuck", - "glow", - "glutin", - "glutin-winit", + "log", "nokhwa", - "raw-window-handle", - "scheng-core", - "scheng-graph", - "scheng-input-video", - "scheng-input-webcam", - "scheng-runtime", - "scheng-runtime-glow", - "scrubbable_controls", - "winit", -] - -[[package]] -name = "scheng-example-video-scrub-keyboard-transport" -version = "0.1.0" -dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-core", - "scheng-graph", - "scheng-input-video", - "scheng-runtime", - "scheng-runtime-glow", - "serde", - "serde_json", - "winit", + "thiserror 1.0.69", + "wgpu", ] [[package]] -name = "scheng-example-video-source-minimal" +name = "scheng-output-ffmpeg" version = "0.1.0" dependencies = [ - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-control-osc", + "crossbeam-channel", + "log", "scheng-graph", - "scheng-input-video", - "scheng-runtime-glow", + "scheng-runtime-wgpu", "serde", - "serde_json", - "winit", -] - -[[package]] -name = "scheng-example-webcam-source-minimal" -version = "0.1.0" -dependencies = [ - "bytemuck", - "glow", - "glutin", - "glutin-winit", - "raw-window-handle", - "scheng-core", - "scheng-graph", - "scheng-input-webcam", - "scheng-runtime", - "scheng-runtime-glow", - "winit", -] - -[[package]] -name = "scheng-graph" -version = "0.1.0" -dependencies = [ - "scheng-core", + "serde_json", + "thiserror 1.0.69", + "wgpu", ] [[package]] -name = "scheng-host-winit" +name = "scheng-output-ndi" version = "0.1.0" dependencies = [ - "scheng-runtime-glow", + "grafton-ndi", + "log", + "scheng-graph", + "scheng-runtime-wgpu", + "thiserror 1.0.69", + "wgpu", ] [[package]] -name = "scheng-input-video" +name = "scheng-output-spout" version = "0.1.0" dependencies = [ - "serde", - "serde_json", + "cc", + "log", + "scheng-graph", + "scheng-runtime-wgpu", "thiserror 1.0.69", + "wgpu", ] [[package]] -name = "scheng-input-webcam" +name = "scheng-output-syphon" version = "0.1.0" dependencies = [ - "nokhwa", + "cc", + "log", + "scheng-graph", + "scheng-runtime-wgpu", "thiserror 1.0.69", + "wgpu", ] [[package]] -name = "scheng-passes" +name = "scheng-param-store" version = "0.1.0" dependencies = [ - "glow", - "scheng-runtime-glow", + "log", + "scheng-graph", + "serde", + "serde_json", + "thiserror 1.0.69", + "wgpu", ] [[package]] @@ -2162,16 +2477,22 @@ dependencies = [ ] [[package]] -name = "scheng-runtime-glow" +name = "scheng-runtime-wgpu" version = "0.1.0" dependencies = [ "bytemuck", - "cc", - "glow", + "env_logger", + "log", + "naga", + "once_cell", + "pollster", + "regex", "scheng-core", "scheng-graph", - "scheng-input-video", + "scheng-param-store", "scheng-runtime", + "thiserror 1.0.69", + "wgpu", ] [[package]] @@ -2197,9 +2518,9 @@ dependencies = [ [[package]] name = "sctk-adwaita" -version = "0.5.4" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" dependencies = [ "ab_glyph", "log", @@ -2244,7 +2565,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -2260,42 +2581,12 @@ dependencies = [ "zmij", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if 1.0.4", - "cpufeatures", - "digest", -] - -[[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 = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - [[package]] name = "simd-adler32" version = "0.3.8" @@ -2325,31 +2616,36 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay-client-toolkit" -version = "0.16.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.11.0", "calloop", - "dlib", - "lazy_static", + "calloop-wayland-source", + "cursor-icon", + "libc", "log", "memmap2", - "nix 0.24.3", - "pkg-config", + "rustix 0.38.44", + "thiserror 1.0.69", + "wayland-backend", "wayland-client", + "wayland-csd-frame", "wayland-cursor", "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", ] [[package]] -name = "socket2" -version = "0.6.2" +name = "smol_str" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" dependencies = [ - "libc", - "windows-sys 0.60.2", + "serde", ] [[package]] @@ -2362,10 +2658,19 @@ dependencies = [ ] [[package]] -name = "stable_deref_trait" -version = "1.2.1" +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 2.11.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strict-num" @@ -2373,17 +2678,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -[[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.111" @@ -2396,14 +2690,12 @@ dependencies = [ ] [[package]] -name = "synstructure" -version = "0.13.2" +name = "termcolor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", + "winapi-util", ] [[package]] @@ -2432,7 +2724,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -2443,93 +2735,34 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if 1.0.4", + "syn", ] [[package]] name = "tiny-skia" -version = "0.8.4" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" dependencies = [ "arrayref", "arrayvec", "bytemuck", "cfg-if 1.0.4", - "png", + "log", "tiny-skia-path", ] [[package]] name = "tiny-skia-path" -version = "0.8.4" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" dependencies = [ "arrayref", "bytemuck", "strict-num", ] -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tokio" -version = "1.49.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" -dependencies = [ - "bytes", - "libc", - "mio 1.1.1", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - [[package]] name = "toml_datetime" version = "0.6.11" @@ -2538,9 +2771,9 @@ checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ "indexmap", "toml_datetime", @@ -2554,59 +2787,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "tracing-core" version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -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-subscriber" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] [[package]] name = "ttf-parser" @@ -2614,31 +2802,6 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -[[package]] -name = "tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "sha1", - "thiserror 1.0.69", - "url", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - [[package]] name = "unicode-ident" version = "1.0.22" @@ -2646,28 +2809,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] -name = "url" -version = "2.5.8" +name = "unicode-segmentation" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] -name = "utf-8" -version = "0.7.6" +name = "unicode-width" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] -name = "utf8_iter" -version = "1.0.4" +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "v4l" @@ -2686,20 +2849,14 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6779878362b9bacadc7893eac76abe69612e8837ef746573c4a5239daf11990b" dependencies = [ - "bindgen", + "bindgen 0.65.1", ] [[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "vec_map" -version = "0.8.2" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" @@ -2707,6 +2864,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -2735,6 +2902,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +dependencies = [ + "cfg-if 1.0.4", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.106" @@ -2754,7 +2934,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.111", + "syn", "wasm-bindgen-shared", ] @@ -2768,98 +2948,238 @@ dependencies = [ ] [[package]] -name = "wayland-client" -version = "0.29.5" +name = "wayland-backend" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9" dependencies = [ - "bitflags 1.3.2", + "cc", "downcast-rs", - "libc", - "nix 0.24.3", + "rustix 1.1.4", "scoped-tls", - "wayland-commons", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec" +dependencies = [ + "bitflags 2.11.0", + "rustix 1.1.4", + "wayland-backend", "wayland-scanner", - "wayland-sys 0.29.5", ] [[package]] -name = "wayland-commons" -version = "0.29.5" +name = "wayland-csd-frame" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "nix 0.24.3", - "once_cell", - "smallvec", - "wayland-sys 0.29.5", + "bitflags 2.11.0", + "cursor-icon", + "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.29.5" +version = "0.31.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +checksum = "5864c4b5b6064b06b1e8b74ead4a98a6c45a285fe7a0e784d24735f011fdb078" dependencies = [ - "nix 0.24.3", + "rustix 1.1.4", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.29.5" +version = "0.32.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa98634619300a535a9a97f338aed9a5ff1e01a461943e8346ff4ae26007306b" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", "wayland-client", - "wayland-commons", + "wayland-protocols", "wayland-scanner", ] [[package]] -name = "wayland-scanner" -version = "0.29.5" +name = "wayland-scanner" +version = "0.31.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86287151a309799b821ca709b7345a048a2956af05957c89cb824ab919fa4e3" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wgpu" +version = "23.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +checksum = "80f70000db37c469ea9d67defdc13024ddf9a5f1b89cb2941b812ad7cde1735a" dependencies = [ - "proc-macro2", - "quote", - "xml-rs", + "arrayvec", + "cfg_aliases 0.1.1", + "document-features", + "js-sys", + "log", + "naga", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", ] [[package]] -name = "wayland-sys" -version = "0.29.5" +name = "wgpu-core" +version = "23.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a" dependencies = [ - "dlib", - "lazy_static", - "pkg-config", + "arrayvec", + "bit-vec", + "bitflags 2.11.0", + "cfg_aliases 0.1.1", + "document-features", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "wgpu-hal", + "wgpu-types", ] [[package]] -name = "wayland-sys" -version = "0.30.1" +name = "wgpu-hal" +version = "23.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" +checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821" dependencies = [ - "dlib", - "lazy_static", + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.11.0", + "block", + "bytemuck", + "cfg_aliases 0.1.1", + "core-graphics-types 0.1.3", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "js-sys", + "khronos-egl", + "libc", + "libloading", "log", - "pkg-config", + "metal 0.29.0", + "naga", + "ndk-sys 0.5.0+25.2.9519653", + "objc", + "once_cell", + "parking_lot", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] -name = "web-sys" -version = "0.3.83" +name = "wgpu-types" +version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068" dependencies = [ + "bitflags 2.11.0", "js-sys", - "wasm-bindgen", + "web-sys", ] [[package]] @@ -2871,44 +3191,36 @@ dependencies = [ "either", "home", "once_cell", - "rustix", + "rustix 0.38.44", ] [[package]] -name = "winapi" -version = "0.3.9" +name = "winapi-util" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-sys 0.61.2", ] [[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" +name = "windows" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" +dependencies = [ + "windows-core 0.56.0", + "windows-targets 0.52.6", +] [[package]] name = "windows" -version = "0.43.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-core 0.58.0", + "windows-targets 0.52.6", ] [[package]] @@ -2918,7 +3230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ "windows-collections", - "windows-core", + "windows-core 0.62.2", "windows-future", "windows-numerics", ] @@ -2929,7 +3241,32 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core", + "windows-core 0.62.2", +] + +[[package]] +name = "windows-core" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" +dependencies = [ + "windows-implement 0.56.0", + "windows-interface 0.56.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", ] [[package]] @@ -2938,11 +3275,11 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.60.2", + "windows-interface 0.59.3", "windows-link", - "windows-result", - "windows-strings", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -2951,11 +3288,33 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core", + "windows-core 0.62.2", "windows-link", "windows-threading", ] +[[package]] +name = "windows-implement" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-implement" version = "0.60.2" @@ -2964,7 +3323,29 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2975,7 +3356,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -2990,10 +3371,28 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core", + "windows-core 0.62.2", "windows-link", ] +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.4.1" @@ -3003,6 +3402,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-strings" version = "0.5.1" @@ -3032,20 +3441,20 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.60.2" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.53.5", + "windows-targets 0.52.6", ] [[package]] @@ -3096,30 +3505,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows-threading" version = "0.2.1" @@ -3147,12 +3539,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -3171,12 +3557,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -3195,24 +3575,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[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_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -3231,12 +3599,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -3255,12 +3617,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -3279,12 +3635,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -3303,45 +3653,56 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winit" -version = "0.28.7" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9596d90b45384f5281384ab204224876e8e8bf7d58366d9b795ad99aa9894b94" +checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d" dependencies = [ + "ahash", "android-activity", - "bitflags 1.3.2", - "cfg_aliases", + "atomic-waker", + "bitflags 2.11.0", + "block2", + "bytemuck", + "calloop", + "cfg_aliases 0.2.1", + "concurrent-queue", "core-foundation 0.9.4", - "core-graphics 0.22.3", - "dispatch", - "instant", + "core-graphics 0.23.2", + "cursor-icon", + "dpi", + "js-sys", "libc", - "log", - "mio 0.8.11", + "memmap2", "ndk", "objc2", - "once_cell", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", "orbclient", "percent-encoding", + "pin-project", "raw-window-handle", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", + "rustix 0.38.44", "sctk-adwaita", "smithay-client-toolkit", + "smol_str", + "tracing", + "unicode-segmentation", "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", "wayland-client", - "wayland-commons", "wayland-protocols", - "wayland-scanner", + "wayland-protocols-plasma", "web-sys", - "windows-sys 0.45.0", + "web-time", + "windows-sys 0.52.0", "x11-dl", + "x11rb", + "xkbcommon-dl", ] [[package]] @@ -3359,12 +3720,6 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - [[package]] name = "x11-dl" version = "2.21.0" @@ -3376,6 +3731,27 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix 1.1.4", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + [[package]] name = "xcursor" version = "0.3.10" @@ -3383,33 +3759,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" [[package]] -name = "xml-rs" -version = "0.8.28" +name = "xkbcommon-dl" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.11.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] [[package]] -name = "yoke" -version = "0.8.1" +name = "xkeysym" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] -name = "yoke-derive" -version = "0.8.1" +name = "xml-rs" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", - "synstructure", -] +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "zerocopy" @@ -3428,62 +3800,14 @@ checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", - "synstructure", -] - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", + "syn", ] [[package]] -name = "zerovec-derive" -version = "0.11.2" +name = "zlib-rs" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] +checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" [[package]] name = "zmij" diff --git a/Cargo.toml b/Cargo.toml index e9494c3..7a9354d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,20 +2,50 @@ resolver = "2" members = [ + # ── Core SDK ────────────────────────────────────────────────────────────── "crates/sdk-compat", - "crates/scheng-buffers", - "crates/scheng-control-osc", "crates/scheng-core", "crates/scheng-graph", - "crates/scheng-host-winit", - "crates/scheng-passes", "crates/scheng-runtime", - "crates/scheng-runtime-glow", - "crates/scheng-contract-tests", + "crates/scheng-runtime-wgpu", + "crates/scheng-param-store", + "crates/scrubbable_controls", + + # ── Outputs ─────────────────────────────────────────────────────────────── + "crates/scheng-output-ffmpeg", + "crates/scheng-output-syphon", + "crates/scheng-output-spout", + "crates/scheng-output-ndi", + + # ── Inputs ──────────────────────────────────────────────────────────────── + "crates/scheng-input-midi", "crates/scheng-input-webcam", "crates/scheng-input-video", - "crates/scrubbable_controls", + "crates/scheng-input-ndi", + + # ── Control ─────────────────────────────────────────────────────────────── + "crates/scheng-control-osc", + "crates/scheng-hotreload", + + # ── Tests ───────────────────────────────────────────────────────────────── + "crates/scheng-contract-tests", + + # ── Example instrument ──────────────────────────────────────────────────── + "examples/scheng-instrument", +] + +exclude = [ + # Legacy OpenGL (glow) runtime — not part of wgpu SDK + "crates/scheng-runtime-glow", + "crates/scheng-buffers", + "crates/scheng-host-winit", + "crates/scheng-passes", "crates/scheng-bridge", + + # Legacy glow-based examples + "examples/syphon_builtin_legacy", + "examples/syphon_minimal", + "examples/syphon_patchbay", "examples/minimal", "examples/pure_single_pass", "examples/render_target_only", @@ -28,9 +58,6 @@ members = [ "examples/graph_matrix_mix4", "examples/graph_matrix_mix4_webcam", "examples/graph_webcam_luma_key", - "examples/syphon_builtin_legacy", - "examples/syphon_minimal", - "examples/syphon_patchbay", "examples/readback_minimal", "examples/texture_input_minimal", "examples/static_source_minimal", @@ -40,20 +67,19 @@ members = [ "examples/video_device_capture_macos", "examples/osc_minimal", "examples/video_scrub_keyboard_transport", + "examples/temporal_slitscan", ] -exclude = ["examples/syphon_builtin_legacy"] - [workspace.package] edition = "2021" [workspace.dependencies] -serde = { version = "1", features = ["derive"] } +serde = { version = "1", features = ["derive"] } serde_json = "1" [profile.dev] opt-level = 1 [profile.release] -lto = true +lto = true codegen-units = 1 diff --git a/SCHENG_DEV (3).md b/SCHENG_DEV (3).md new file mode 100644 index 0000000..ffdced1 --- /dev/null +++ b/SCHENG_DEV (3).md @@ -0,0 +1,269 @@ +# scheng — Development Reference +> Last updated: 2026-03-15 — Phases 1–4 complete + +--- + +## Project North Star + +scheng is a Rust SDK for building GPU-accelerated video synthesis instruments. +Node-graph execution model, explicit shader contracts, modular I/O (Syphon, Spout, +NDI, FFmpeg). Instruments ship cross-platform via Tauri. Shader layer is intentionally +low-level — enabling faithful reproductions of historical analog video synthesis hardware. + +shadecore = proven single-binary reference. Features that work there are the target spec. + +--- + +## Workspace Layout (current state) + +``` +crates/ +├── scheng-core Foundation: config, assets, error, events +├── scheng-graph Node/port/edge/plan model ✓ +├── scheng-runtime Backend-agnostic ops, params, banks ✓ +├── scheng-runtime-glow OpenGL backend (proven in shadecore) ✓ +├── scheng-runtime-wgpu Metal/DX12/Vulkan backend ✅ PHASE 1 +├── scheng-bridge WebSocket bridge + browser editor ✓ +├── scheng-passes Ping-pong, temporal ring buffers +├── scheng-buffers GPU ring buffer primitives +├── scheng-host-winit winit + glutin window/context +├── scheng-input-video Video file → GL texture +├── scheng-input-webcam Webcam → RGBA (optional) +├── scheng-control-osc UDP OSC receiver (existing, rosc-based) +├── scheng-contract-tests Golden fixture + behavioral contract tests ✓ +├── scrubbable_controls Keyboard + OSC control layer (existing) +├── sdk-compat Compile-only API regression witness ✓ +│ +│ ── Phase 3: Output crates ── +├── scheng-output-ffmpeg FFmpeg: RTSP/RTMP/file recording ✅ PHASE 3 +├── scheng-output-syphon Syphon Metal (macOS) ✅ PHASE 3 +├── scheng-output-spout Spout2 (Windows) — bridge port needed +├── scheng-output-ndi NDI — SDK stub, interface defined +│ +│ ── Phase 4: Control crates ── +├── scheng-param-store JSON schema → live values → NodeConfig ✅ PHASE 4 +├── scheng-input-midi MIDI CC → ParamStore (midir) ✅ PHASE 4 +├── scheng-control-osc-wgpu OSC UDP → ParamStore adapter ✅ PHASE 4 +└── scheng-hotreload File watcher → shader/params reload ✅ PHASE 4 +``` + +--- + +## Master Punchlist + +Status: `[x]` done · `[~]` in progress · `[ ]` todo · `[!]` blocked + +### Phase 0 — Baseline +- [x] Read and understand scheng + shadecore codebases +- [x] Map shadecore features → scheng SDK gaps +- [x] wgpu backend decision +- [ ] Verify `cargo build --workspace` passes clean on current main +- [ ] Verify `cargo test -p scheng-contract-tests` passes + +### Phase 1 — wgpu Backend ✅ COMPLETE (11/11 tests on Apple M1 Max) +- [x] scheng-runtime-wgpu: WgpuContext (Metal/DX12/Vulkan headless init) +- [x] compat.rs: GLSL 330→450 preprocessor (bindings 0-5, split iChannelN, #define aliases) +- [x] shader.rs: GLSL→naga→wgpu ShaderModule cache +- [x] render_target.rs: offscreen RGBA8 + CPU readback +- [x] uniforms.rs: FrameBlock uniform buffer (uTime/uResolution/uFrame, 16 bytes) +- [x] pipeline.rs: RenderPipeline cache + bind group layout +- [x] executor.rs: execute_frame(), OutputSink trait, PixelReadbackSink +- [x] 11/11 tests passing on Apple M1 Max (Metal backend) +- [ ] Custom u_* uniform injection — Phase 1.2 +- [ ] PingPong buffers for Feedback/PreviousFrame — Phase 1.3 + +### Phase 2 — Input Layer +- [ ] scheng-input-video: wgpu texture upload path +- [ ] scheng-input-webcam: wgpu upload path +- [ ] scheng-input-ndi (receive side) +- [ ] scheng-input-syphon (macOS receive) +- [ ] scheng-input-spout (Windows receive) + +### Phase 3 — Output Layer ✅ COMPLETE +- [x] scheng-output-ffmpeg: FfmpegSink, FfmpegWorker, FfmpegConfig (RTSP/RTMP/File) +- [x] scheng-output-syphon: SyphonSink, Metal ObjC bridge, build.rs +- [~] scheng-output-spout: interface + FFI ready — copy native bridge from scheng-runtime-glow +- [~] scheng-output-ndi: NdiSink + NdiConfig ready — wire NDI SDK Rust bindings +- [x] Headless OutputSink: PixelReadbackSink in scheng-runtime-wgpu + +### Phase 4 — Control Layer ✅ COMPLETE +- [x] scheng-param-store: ParamSchema (params.json), ParamStore (targets+smoothing), NodeConfigBuilder +- [x] scheng-input-midi: MidiInput (midir, CoreMIDI/WinMM/ALSA), CC→ParamStore +- [x] scheng-control-osc-wgpu: OscReceiver (non-blocking UDP poll), OSC→ParamStore +- [x] scheng-hotreload: AssetWatcher (notify FSEvents/inotify), HotReloader (shader + params.json) +- [x] CONTROL_LAYER_README.md: complete instrument wiring example + +### Phase 5 — Tauri Shell ✅ COMPLETE +- [x] Create crates/scheng-tauri +- [x] Embed scheng-runtime-wgpu in Tauri Rust backend on dedicated thread +- [x] IPC surface: set_param, load_graph, set_output_mode, start/stop_recording, get_engine_status +- [x] Preview frame IPC: readback → JPEG (scaled 320×180) → Tauri event ~15fps +- [x] Instrument template: ui/index.html (param sliders, preview, output mode, recording) +- [x] macOS: cargo tauri dev confirmed running on Apple M1 Max + + +### First-run: create assets/ + +The render thread looks for `assets/` relative to CWD. +Create these two files to get live preview working: + +```bash +mkdir -p assets/shaders + +# assets/params.json +cat > assets/params.json << 'JSON' +{"version":1,"params":[{"name":"u_speed","min":0,"max":5,"default":1,"smooth":0.05,"midi_cc":1}]} +JSON + +# assets/shaders/default.frag +cat > assets/shaders/default.frag << 'GLSL' +#version 330 core +in vec2 v_uv; +out vec4 fragColor; +uniform float uTime; +void main() { + float r = v_uv.x + 0.5 * sin(uTime); + float g = v_uv.y + 0.5 * cos(uTime * 0.7); + fragColor = vec4(r, g, 0.2, 1.0); +} +GLSL +``` + +The render thread hot-reloads both files — edit and save while running. + +### Phase 6 — Shader Library (LZX-inspired) +- [ ] Colorizer (hue rotation, RGB↔component) +- [ ] Ramp generator (H, V, radial, angular) +- [ ] Luma keyer / chroma keyer +- [ ] Hard keyer / soft keyer +- [ ] Proc amp (brightness, contrast, saturation, hue) +- [ ] Crossfader with T-bar uniform +- [ ] Matrix mixer 4→1 +- [ ] Video feedback (ping-pong convergence) +- [ ] Pattern generator (color bars, test card, grid) +- [ ] Waveform monitor / vectorscope + +### Phase 7 — GUI / Editor (design before building) +- [ ] Node graph editor research +- [ ] Prototyping → export workflow definition +- [ ] JSON graph save/load format +- [ ] Parameter UI contract + +--- + +## CLI Test Reference + +```bash +# Phase 1 — wgpu backend (all 11 tests) +cargo test -p scheng-runtime-wgpu -- --nocapture + +# Phase 1 — CPU-only subset (no GPU needed) +cargo test -p scheng-runtime-wgpu compat -- --nocapture +cargo test -p scheng-runtime-wgpu test_naga -- --nocapture +cargo test -p scheng-runtime-wgpu size_is_16 -- --nocapture +cargo test -p scheng-runtime-wgpu align_to_256 -- --nocapture + +# Phase 3 — output crate checks +cargo check -p scheng-output-ffmpeg +cargo check -p scheng-output-syphon +cargo check -p scheng-output-spout +cargo check -p scheng-output-ndi + +# Phase 3 — FFmpeg config + arg builder tests (no ffmpeg needed) +cargo test -p scheng-output-ffmpeg -- --nocapture + +# Phase 4 — control layer +cargo test -p scheng-param-store -- --nocapture +cargo test -p scheng-input-midi -- --nocapture +cargo test -p scheng-control-osc-wgpu -- --nocapture + +# Contract tests (scheng-graph public API regression) +cargo test -p scheng-contract-tests -- --nocapture + +# SDK compat witness (compile-only) +cargo build -p sdk-compat + +# Full workspace +cargo build --workspace +cargo test --workspace +``` + +--- + +## GLSL Shader Contract (verified working on Metal) + +```glsl +#version 330 core // stripped by compat.rs +in vec2 v_uv; // stripped, provided by compat header +out vec4 fragColor; // stripped, provided by compat header +uniform sampler2D iChannel0; // up to iChannel3 — stripped, split texture+sampler +uniform float uTime; // stripped, in FrameBlock +uniform vec2 uResolution; // stripped, in FrameBlock + +void main() { + vec2 uv = v_uv; + fragColor = texture(iChannel0, uv) + vec4(sin(uTime) * 0.1); +} +``` + +## Bind Group Layout (bindings 0–5) + +| binding | resource | GLSL alias | +|--------:|---------|------------| +| 0 | texture2D | iChannel0 | +| 1 | texture2D | iChannel1 | +| 2 | texture2D | iChannel2 | +| 3 | texture2D | iChannel3 | +| 4 | sampler(filter) | iSampler | +| 5 | UniformBuffer | FrameBlock (uResolution, uTime, uFrame) | + +--- + +## Architecture Rules (never violate) + +1. Engine does not own time — FrameCtx always supplied by host +2. Topology static after compile() — NodeConfig dynamic +3. No layer reaches backward — Graph → Runtime → Backend +4. Control writes NodeConfig/ParamStore only — never touches graph or render loop +5. Runtime is single-threaded — GPU context on calling thread only +6. Errors returned immediately — no silent recovery +7. Optional features truly optional — Syphon/Spout/webcam never in core crates +8. submit() before present() — sink.present() always after queue.submit() + +--- + +## Known Phase 1 Gaps (Phase 1.2/1.3) + +| Gap | Phase | Location | +|-----|-------|----------| +| Custom u_* uniform injection | 1.2 | executor.rs + NodeConfig + compat.rs | +| PingPong buffers (Feedback/PreviousFrame) | 1.3 | new: scheng-passes-wgpu | +| Y-axis flip vs OpenGL (top-left vs bottom-left) | 1.5 | presenter layer | +| scheng-runtime NodeProps integration | after API verified | executor.rs NodeConfig | + +--- + +## Dependency Map (as-built) + +``` +scheng-editor.html ──WebSocket──▶ scheng-bridge + │ + scheng-runtime-glow (OpenGL, proven) + scheng-runtime-wgpu (Metal/DX12/Vulkan) ✅ + │ │ + scheng-runtime (abstract) + │ + scheng-graph + │ + scheng-core + +scheng-output-ffmpeg ──▶ scheng-runtime-wgpu OutputSink ✅ +scheng-output-syphon ──▶ scheng-runtime-wgpu OutputSink ✅ +scheng-output-spout ──▶ scheng-runtime-wgpu OutputSink (~) +scheng-output-ndi ──▶ scheng-runtime-wgpu OutputSink (~) + +scheng-param-store ──▶ scheng-graph, scheng-runtime-wgpu ✅ +scheng-input-midi ──▶ scheng-param-store (Arc>) ✅ +scheng-control-osc-wgpu ──▶ scheng-param-store ✅ +scheng-hotreload ──▶ scheng-param-store, scheng-runtime-wgpu ✅ +``` diff --git a/SCHENG_DEV-PHASE1.md b/SCHENG_DEV-PHASE1.md new file mode 100644 index 0000000..39a99c9 --- /dev/null +++ b/SCHENG_DEV-PHASE1.md @@ -0,0 +1,223 @@ +# scheng — Development Reference +> Last updated: 2026-03-15 — Phase 1 complete + +--- + +## Project North Star + +scheng is a Rust SDK for building GPU-accelerated video synthesis instruments. +Node-graph execution model, explicit shader contracts, modular I/O (Syphon, Spout, +NDI, FFmpeg). Instruments ship cross-platform via Tauri. Shader layer is intentionally +low-level — enabling faithful reproductions of historical analog video synthesis hardware. + +shadecore = proven single-binary reference. Features that work there are the target spec. + +--- + +## Phase 1 — wgpu Backend ✅ COMPLETE + +**11/11 tests passing on Apple M1 Max (Metal backend)** + +``` +Running unittests src/lib.rs + test uniforms::tests::from_ctx_correct ... ok + test uniforms::tests::size_is_16_bytes ... ok + test render_target::tests::align_to_256 ... ok + test compat::tests::custom_uniforms_reported ... ok + test compat::tests::strips_version_and_declarations ... ok + +Running tests/headless.rs + test test_naga_compile_cpu_only ... ok [CPU only] + test test_context_init ... ok Apple M1 Max + test test_single_node_renders ... ok first px [109, 255, 51, 255] + test test_two_node_pipeline ... ok + test test_time_varies_between_frames ... ok + test test_custom_shader_solid_red ... ok pixel-perfect red confirmed +``` + +### What was built + +``` +crates/scheng-runtime-wgpu/ +├── Cargo.toml wgpu=22, naga=22 (glsl-in only), bytemuck, pollster, once_cell, regex +├── src/ +│ ├── lib.rs Public API, WgpuError enum +│ ├── frame_ctx.rs FrameCtx defined locally (not from scheng-core) +│ ├── context.rs WgpuContext::new() — headless Metal/DX12/Vulkan Device+Queue +│ ├── compat.rs GLSL 330→450 preprocessor (strip decls, split iChannelN, inject header) +│ ├── shader.rs ShaderCache: GLSL→naga→wgpu::ShaderModule, WGSL vertex shader +│ ├── render_target.rs RenderTarget: offscreen RGBA8 texture + CPU readback +│ ├── uniforms.rs UniformManager: FrameBlock buffer (uTime/uResolution/uFrame, 16 bytes) +│ ├── pipeline.rs PipelineCache: RenderPipeline per shader hash + bind group layout +│ └── executor.rs WgpuRuntime::execute_frame(), OutputSink trait, PixelReadbackSink +└── tests/ + └── headless.rs 6 GPU integration tests (skip gracefully if no adapter) +``` + +### Key lessons learned (bugs fixed) + +| Bug | Root cause | Fix | +|-----|-----------|-----| +| `naga validate` feature missing | Feature doesn't exist in naga 22 | Removed from Cargo.toml | +| `scheng_core::FrameCtx` not found | FrameCtx lives in scheng-runtime-glow | Define locally in frame_ctx.rs | +| `plan.node_ids()` not found | Plan has `pub nodes: Vec` field | Use `plan.nodes.iter()` | +| `NodeKind` not Copy | Clone needed | Take `&NodeKind` throughout | +| Borrow conflict (mut+immut on render_targets) | entry().or_insert_with() held mut borrow | Split into Phase A (resize) + Phase B (encode) | +| Borrow conflict (pipeline + resolve_inputs) | get_or_create() held mut borrow on self | Free functions taking individual fields | +| `errors.iter()` | naga ParseErrors has `.errors` field | `errors.errors.iter()` | +| `@interpolate` mismatch | WGSL default is `perspective,center`; naga emits `perspective` | Add `@interpolate(perspective)` to vertex out | +| All pixels zero | sink.present() called before queue.submit() | Collect outputs, submit first, then present | + +### GLSL shader contract (verified working) + +```glsl +#version 330 core // stripped by compat.rs, replaced with 450 +in vec2 v_uv; // stripped, provided by compat header +out vec4 fragColor; // stripped, provided by compat header +uniform sampler2D iChannel0; // stripped, replaced with split texture+sampler +uniform float uTime; // stripped, in FrameBlock +uniform vec2 uResolution; // stripped, in FrameBlock + +void main() { + fragColor = texture(iChannel0, v_uv) + vec4(uTime * 0.1, 0.0, 0.0, 0.0); +} +``` + +### Bind group layout (fixed, matches compat header) + +| binding | resource | GLSL alias | +|--------:|---------|------------| +| 0 | texture2D | iChannel0_tex → iChannel0 macro | +| 1 | texture2D | iChannel1_tex → iChannel1 macro | +| 2 | texture2D | iChannel2_tex → iChannel2 macro | +| 3 | texture2D | iChannel3_tex → iChannel3 macro | +| 4 | sampler(filter) | iSampler | +| 5 | UniformBuffer | FrameBlock (uResolution, uTime, uFrame) | + +--- + +## Master Punchlist + +Status: `[x]` done · `[~]` in progress · `[ ]` todo · `[!]` blocked + +### Phase 0 — Baseline +- [x] Read and understand scheng + shadecore codebases +- [x] Map shadecore features → scheng SDK gaps +- [x] Make wgpu backend decision +- [ ] Verify `cargo build --workspace` passes clean +- [ ] Verify `cargo test -p scheng-contract-tests` passes + +### Phase 1 — wgpu Backend ✅ +- [x] Create crates/scheng-runtime-wgpu with Cargo.toml +- [x] Add to workspace Cargo.toml members +- [x] WgpuContext: headless Device+Queue init (Metal/DX12/Vulkan) +- [x] compat.rs: GLSL 330→450 preprocessor +- [x] shader.rs: GLSL→naga→wgpu ShaderModule cache +- [x] render_target.rs: offscreen RGBA8 + CPU readback +- [x] uniforms.rs: FrameBlock uniform buffer +- [x] pipeline.rs: RenderPipeline cache + bind group layout +- [x] executor.rs: execute_frame(), OutputSink trait, PixelReadbackSink +- [x] 11/11 tests passing on Apple M1 Max +- [ ] Custom u_* uniform injection (Phase 1.2) +- [ ] PingPong buffers for Feedback/PreviousFrame nodes (Phase 1.3) + +### Phase 2 — Input Layer +- [ ] scheng-input-video: add wgpu texture upload path +- [ ] scheng-input-webcam: wgpu upload path +- [ ] scheng-input-midi: new crate — port from shadecore's midir integration + - [ ] CoreMIDI / midir cross-platform + - [ ] JSON CC → NodeId + param name mapping +- [ ] scheng-input-ndi: NDI frames → texture +- [ ] scheng-input-syphon (receive side) +- [ ] scheng-input-spout (receive side) + +### Phase 3 — Output Layer +- [ ] scheng-output-syphon: port syphon_bridge.m, wire OutputSink +- [ ] scheng-output-spout: port C++ Spout2 bridge, wire OutputSink +- [ ] scheng-output-ffmpeg: bounded queue worker, RTSP/RTMP + local recording +- [ ] scheng-output-ndi: formalize NDI sender as OutputSink +- [ ] Headless / offscreen OutputSink (pixels only, no window) + +### Phase 4 — Control Layer +- [ ] Verify scheng-control-osc end-to-end +- [ ] Port MIDI from shadecore into scheng-input-midi +- [ ] Hot-reload: file watcher → shader recompile without restart +- [ ] State ownership: match shadecore authority table + (render.json, params.json, output.json, recording.json) + +### Phase 5 — Tauri Shell +- [ ] Create crates/scheng-tauri +- [ ] Embed scheng-runtime-wgpu in Tauri Rust backend +- [ ] IPC surface: set_param, load_graph, set_output_mode, start/stop_recording +- [ ] Preview frame IPC: readback → JPEG → Tauri event at ~15fps +- [ ] Instrument template (fork-ready) +- [ ] macOS .app build +- [ ] Windows .exe build +- [ ] Linux build + +### Phase 6 — Shader Library (LZX-inspired) +- [ ] Colorizer (hue rotation, RGB↔component) +- [ ] Ramp generator (H, V, radial, angular) +- [ ] Luma keyer / chroma keyer +- [ ] Hard keyer / soft keyer +- [ ] Proc amp (brightness, contrast, saturation, hue) +- [ ] Crossfader with T-bar param +- [ ] Matrix mixer 4→1 +- [ ] Video feedback (ping-pong convergence) +- [ ] Pattern generator (color bars, test card, grid) +- [ ] Waveform monitor / vectorscope + +### Phase 7 — GUI / Editor (design before building) +- [ ] Research: node graph editor options +- [ ] Define prototyping → export workflow +- [ ] JSON graph save/load format +- [ ] Parameter UI contract (sliders, toggles, XY pads) + +--- + +## CLI Test Reference + +```bash +# All tests +cargo test -p scheng-runtime-wgpu -- --nocapture + +# CPU-only (no GPU needed) +cargo test -p scheng-runtime-wgpu compat -- --nocapture +cargo test -p scheng-runtime-wgpu test_naga -- --nocapture + +# Single GPU test +cargo test -p scheng-runtime-wgpu test_single_node_renders -- --nocapture + +# Software fallback (CI / no discrete GPU) +WGPU_BACKEND=gl cargo test -p scheng-runtime-wgpu -- --nocapture + +# Contract tests (scheng-graph public API regression) +cargo test -p scheng-contract-tests -- --nocapture + +# SDK compat witness (compile-only) +cargo build -p sdk-compat + +# Full workspace +cargo build --workspace +cargo test --workspace +``` + +--- + +## Architecture Rules (never violate) + +1. Engine does not own time — FrameCtx always supplied by host +2. Topology static after compile() — NodeProps/NodeConfig dynamic +3. No layer reaches backward — Graph → Runtime → Backend +4. Control writes NodeProps only — never touches graph or render loop +5. Runtime is single-threaded — GPU context on calling thread only +6. Errors returned immediately — no silent recovery +7. Optional features truly optional — Syphon/Spout/webcam never in core + +--- + +## Open for Phase 1.2 + +- Custom u_* uniform injection into FrameBlock or a CustomBlock (binding 6) +- Port field names: verify `Port { id, name }` match — executor uses p.id and p.name +- Y-axis flip between OpenGL (bottom-left origin) and wgpu (top-left) — handle in presenter diff --git a/SCHENG_DEV-PHASE3.md b/SCHENG_DEV-PHASE3.md new file mode 100644 index 0000000..01a7bbc --- /dev/null +++ b/SCHENG_DEV-PHASE3.md @@ -0,0 +1,223 @@ +# scheng — Development Reference +> Last updated: 2026-03-15 — Phase 1 complete + +--- + +## Project North Star + +scheng is a Rust SDK for building GPU-accelerated video synthesis instruments. +Node-graph execution model, explicit shader contracts, modular I/O (Syphon, Spout, +NDI, FFmpeg). Instruments ship cross-platform via Tauri. Shader layer is intentionally +low-level — enabling faithful reproductions of historical analog video synthesis hardware. + +shadecore = proven single-binary reference. Features that work there are the target spec. + +--- + +## Phase 1 — wgpu Backend ✅ COMPLETE + +**11/11 tests passing on Apple M1 Max (Metal backend)** + +``` +Running unittests src/lib.rs + test uniforms::tests::from_ctx_correct ... ok + test uniforms::tests::size_is_16_bytes ... ok + test render_target::tests::align_to_256 ... ok + test compat::tests::custom_uniforms_reported ... ok + test compat::tests::strips_version_and_declarations ... ok + +Running tests/headless.rs + test test_naga_compile_cpu_only ... ok [CPU only] + test test_context_init ... ok Apple M1 Max + test test_single_node_renders ... ok first px [109, 255, 51, 255] + test test_two_node_pipeline ... ok + test test_time_varies_between_frames ... ok + test test_custom_shader_solid_red ... ok pixel-perfect red confirmed +``` + +### What was built + +``` +crates/scheng-runtime-wgpu/ +├── Cargo.toml wgpu=22, naga=22 (glsl-in only), bytemuck, pollster, once_cell, regex +├── src/ +│ ├── lib.rs Public API, WgpuError enum +│ ├── frame_ctx.rs FrameCtx defined locally (not from scheng-core) +│ ├── context.rs WgpuContext::new() — headless Metal/DX12/Vulkan Device+Queue +│ ├── compat.rs GLSL 330→450 preprocessor (strip decls, split iChannelN, inject header) +│ ├── shader.rs ShaderCache: GLSL→naga→wgpu::ShaderModule, WGSL vertex shader +│ ├── render_target.rs RenderTarget: offscreen RGBA8 texture + CPU readback +│ ├── uniforms.rs UniformManager: FrameBlock buffer (uTime/uResolution/uFrame, 16 bytes) +│ ├── pipeline.rs PipelineCache: RenderPipeline per shader hash + bind group layout +│ └── executor.rs WgpuRuntime::execute_frame(), OutputSink trait, PixelReadbackSink +└── tests/ + └── headless.rs 6 GPU integration tests (skip gracefully if no adapter) +``` + +### Key lessons learned (bugs fixed) + +| Bug | Root cause | Fix | +|-----|-----------|-----| +| `naga validate` feature missing | Feature doesn't exist in naga 22 | Removed from Cargo.toml | +| `scheng_core::FrameCtx` not found | FrameCtx lives in scheng-runtime-glow | Define locally in frame_ctx.rs | +| `plan.node_ids()` not found | Plan has `pub nodes: Vec` field | Use `plan.nodes.iter()` | +| `NodeKind` not Copy | Clone needed | Take `&NodeKind` throughout | +| Borrow conflict (mut+immut on render_targets) | entry().or_insert_with() held mut borrow | Split into Phase A (resize) + Phase B (encode) | +| Borrow conflict (pipeline + resolve_inputs) | get_or_create() held mut borrow on self | Free functions taking individual fields | +| `errors.iter()` | naga ParseErrors has `.errors` field | `errors.errors.iter()` | +| `@interpolate` mismatch | WGSL default is `perspective,center`; naga emits `perspective` | Add `@interpolate(perspective)` to vertex out | +| All pixels zero | sink.present() called before queue.submit() | Collect outputs, submit first, then present | + +### GLSL shader contract (verified working) + +```glsl +#version 330 core // stripped by compat.rs, replaced with 450 +in vec2 v_uv; // stripped, provided by compat header +out vec4 fragColor; // stripped, provided by compat header +uniform sampler2D iChannel0; // stripped, replaced with split texture+sampler +uniform float uTime; // stripped, in FrameBlock +uniform vec2 uResolution; // stripped, in FrameBlock + +void main() { + fragColor = texture(iChannel0, v_uv) + vec4(uTime * 0.1, 0.0, 0.0, 0.0); +} +``` + +### Bind group layout (fixed, matches compat header) + +| binding | resource | GLSL alias | +|--------:|---------|------------| +| 0 | texture2D | iChannel0_tex → iChannel0 macro | +| 1 | texture2D | iChannel1_tex → iChannel1 macro | +| 2 | texture2D | iChannel2_tex → iChannel2 macro | +| 3 | texture2D | iChannel3_tex → iChannel3 macro | +| 4 | sampler(filter) | iSampler | +| 5 | UniformBuffer | FrameBlock (uResolution, uTime, uFrame) | + +--- + +## Master Punchlist + +Status: `[x]` done · `[~]` in progress · `[ ]` todo · `[!]` blocked + +### Phase 0 — Baseline +- [x] Read and understand scheng + shadecore codebases +- [x] Map shadecore features → scheng SDK gaps +- [x] Make wgpu backend decision +- [ ] Verify `cargo build --workspace` passes clean +- [ ] Verify `cargo test -p scheng-contract-tests` passes + +### Phase 1 — wgpu Backend ✅ +- [x] Create crates/scheng-runtime-wgpu with Cargo.toml +- [x] Add to workspace Cargo.toml members +- [x] WgpuContext: headless Device+Queue init (Metal/DX12/Vulkan) +- [x] compat.rs: GLSL 330→450 preprocessor +- [x] shader.rs: GLSL→naga→wgpu ShaderModule cache +- [x] render_target.rs: offscreen RGBA8 + CPU readback +- [x] uniforms.rs: FrameBlock uniform buffer +- [x] pipeline.rs: RenderPipeline cache + bind group layout +- [x] executor.rs: execute_frame(), OutputSink trait, PixelReadbackSink +- [x] 11/11 tests passing on Apple M1 Max +- [ ] Custom u_* uniform injection (Phase 1.2) +- [ ] PingPong buffers for Feedback/PreviousFrame nodes (Phase 1.3) + +### Phase 2 — Input Layer +- [ ] scheng-input-video: add wgpu texture upload path +- [ ] scheng-input-webcam: wgpu upload path +- [ ] scheng-input-midi: new crate — port from shadecore's midir integration + - [ ] CoreMIDI / midir cross-platform + - [ ] JSON CC → NodeId + param name mapping +- [ ] scheng-input-ndi: NDI frames → texture +- [ ] scheng-input-syphon (receive side) +- [ ] scheng-input-spout (receive side) + +### Phase 3 — Output Layer +- [x] scheng-output-syphon: SyphonSink, Metal bridge (ObjC), build.rs, FFI +- [~] scheng-output-spout: SpoutSink + ffi.rs ready — needs native bridge ported from runtime-glow +- [x] scheng-output-ffmpeg: FfmpegSink, FfmpegWorker, FfmpegConfig, bounded queue, RTSP/RTMP/File +- [~] scheng-output-ndi: NdiSink + NdiConfig ready — needs NDI SDK Rust bindings wired in +- [x] Headless/offscreen OutputSink: PixelReadbackSink in scheng-runtime-wgpu (Phase 1) + +### Phase 4 — Control Layer +- [ ] Verify scheng-control-osc end-to-end +- [ ] Port MIDI from shadecore into scheng-input-midi +- [ ] Hot-reload: file watcher → shader recompile without restart +- [ ] State ownership: match shadecore authority table + (render.json, params.json, output.json, recording.json) + +### Phase 5 — Tauri Shell +- [ ] Create crates/scheng-tauri +- [ ] Embed scheng-runtime-wgpu in Tauri Rust backend +- [ ] IPC surface: set_param, load_graph, set_output_mode, start/stop_recording +- [ ] Preview frame IPC: readback → JPEG → Tauri event at ~15fps +- [ ] Instrument template (fork-ready) +- [ ] macOS .app build +- [ ] Windows .exe build +- [ ] Linux build + +### Phase 6 — Shader Library (LZX-inspired) +- [ ] Colorizer (hue rotation, RGB↔component) +- [ ] Ramp generator (H, V, radial, angular) +- [ ] Luma keyer / chroma keyer +- [ ] Hard keyer / soft keyer +- [ ] Proc amp (brightness, contrast, saturation, hue) +- [ ] Crossfader with T-bar param +- [ ] Matrix mixer 4→1 +- [ ] Video feedback (ping-pong convergence) +- [ ] Pattern generator (color bars, test card, grid) +- [ ] Waveform monitor / vectorscope + +### Phase 7 — GUI / Editor (design before building) +- [ ] Research: node graph editor options +- [ ] Define prototyping → export workflow +- [ ] JSON graph save/load format +- [ ] Parameter UI contract (sliders, toggles, XY pads) + +--- + +## CLI Test Reference + +```bash +# All tests +cargo test -p scheng-runtime-wgpu -- --nocapture + +# CPU-only (no GPU needed) +cargo test -p scheng-runtime-wgpu compat -- --nocapture +cargo test -p scheng-runtime-wgpu test_naga -- --nocapture + +# Single GPU test +cargo test -p scheng-runtime-wgpu test_single_node_renders -- --nocapture + +# Software fallback (CI / no discrete GPU) +WGPU_BACKEND=gl cargo test -p scheng-runtime-wgpu -- --nocapture + +# Contract tests (scheng-graph public API regression) +cargo test -p scheng-contract-tests -- --nocapture + +# SDK compat witness (compile-only) +cargo build -p sdk-compat + +# Full workspace +cargo build --workspace +cargo test --workspace +``` + +--- + +## Architecture Rules (never violate) + +1. Engine does not own time — FrameCtx always supplied by host +2. Topology static after compile() — NodeProps/NodeConfig dynamic +3. No layer reaches backward — Graph → Runtime → Backend +4. Control writes NodeProps only — never touches graph or render loop +5. Runtime is single-threaded — GPU context on calling thread only +6. Errors returned immediately — no silent recovery +7. Optional features truly optional — Syphon/Spout/webcam never in core + +--- + +## Open for Phase 1.2 + +- Custom u_* uniform injection into FrameBlock or a CustomBlock (binding 6) +- Port field names: verify `Port { id, name }` match — executor uses p.id and p.name +- Y-axis flip between OpenGL (bottom-left origin) and wgpu (top-left) — handle in presenter diff --git a/SCHENG_DEV-PHASE4.md b/SCHENG_DEV-PHASE4.md new file mode 100644 index 0000000..e54f944 --- /dev/null +++ b/SCHENG_DEV-PHASE4.md @@ -0,0 +1,263 @@ +# scheng Control Layer — Phase 4 + +Four crates completing the control plane. + +--- + +## Overview + +| Crate | Role | +|-------|------| +| `scheng-param-store` | Central state: JSON schema → live values → NodeConfig | +| `scheng-input-midi` | MIDI CC → ParamStore (background thread, midir) | +| `scheng-control-osc-wgpu` | OSC UDP → ParamStore (non-blocking, poll each frame) | +| `scheng-hotreload` | File watcher → shader/params live reload | + +--- + +## Add to workspace + +```toml +[workspace] +members = [ + # ... + "crates/scheng-param-store", + "crates/scheng-input-midi", + "crates/scheng-control-osc-wgpu", + "crates/scheng-hotreload", +] +``` + +--- + +## params.json schema + +All four crates share this JSON format (matches shadecore exactly): + +```json +{ + "version": 1, + "params": [ + { + "name": "u_brightness", + "ty": "float", + "min": 0.0, + "max": 2.0, + "default": 1.0, + "smooth": 0.05, + "midi_cc": 14, + "midi_channel": 1, + "osc_addr": "/scheng/node/proc/uniform/u_brightness", + "node_label": "proc", + "description": "Overall brightness multiplier" + }, + { + "name": "u_tbar", + "ty": "float", + "min": 0.0, + "max": 1.0, + "default": 0.5, + "smooth": 0.02, + "midi_cc": 7, + "osc_addr": "/scheng/node/xfad/uniform/u_tbar", + "node_label": "xfad" + } + ] +} +``` + +--- + +## Complete instrument wiring + +```rust +use std::sync::{Arc, Mutex}; +use std::collections::HashMap; + +use scheng_graph::{Graph, NodeKind}; +use scheng_runtime_wgpu::{WgpuRuntime, FrameCtx}; +use scheng_param_store::{ParamStore, NodeConfigBuilder}; +use scheng_input_midi::MidiInput; +use scheng_control_osc_wgpu::OscReceiver; +use scheng_hotreload::HotReloader; + +fn main() -> anyhow::Result<()> { + // ── 1. Build graph ──────────────────────────────────────────────────── + let mut graph = Graph::new(); + let src = graph.add_node(NodeKind::ShaderSource); + let proc = graph.add_node(NodeKind::ShaderPass); + let xfad = graph.add_node(NodeKind::Crossfade); + let out = graph.add_node(NodeKind::PixelsOut); + graph.connect_named(src, "out", proc, "in").unwrap(); + graph.connect_named(proc, "out", xfad, "a").unwrap(); + graph.connect_named(src, "out", xfad, "b").unwrap(); + graph.connect_named(xfad, "out", out, "in").unwrap(); + let plan = graph.compile().unwrap(); + + // ── 2. GPU runtime ──────────────────────────────────────────────────── + let mut runtime = WgpuRuntime::new(1280, 720)?; + + // ── 3. Parameter store ──────────────────────────────────────────────── + let store = Arc::new(Mutex::new( + ParamStore::from_json_file("assets/params.json")? + )); + + // ── 4. Node config builder ──────────────────────────────────────────── + let mut builder = NodeConfigBuilder::new(); + builder.register("src", src); + builder.register("proc", proc); + builder.register("xfad", xfad); + builder.register("out", out); + + // Load initial shaders + builder.set_shader(src, std::fs::read_to_string("assets/shaders/src.frag")?); + builder.set_shader(proc, std::fs::read_to_string("assets/shaders/proc.frag")?); + builder.set_shader(xfad, std::fs::read_to_string("assets/shaders/xfad.frag")?); + + // ── 5. MIDI (background thread) ─────────────────────────────────────── + let midi = MidiInput::connect_first(Arc::clone(&store)) + .map_err(|e| { log::warn!("MIDI not available: {e}"); e }) + .ok(); // Don't fail if no MIDI device + + if let Some(ref m) = midi { + log::info!("MIDI connected: {}", m.port_name()); + } + + // ── 6. OSC (non-blocking, polled each frame) ────────────────────────── + let mut osc = OscReceiver::bind("127.0.0.1:9000") + .map_err(|e| { log::warn!("OSC not available: {e}"); e }) + .ok(); + + // ── 7. Hot-reload watcher ───────────────────────────────────────────── + let mut reloader = HotReloader::new("assets/").ok(); + if let Some(ref mut r) = reloader { + r.register_shader("assets/shaders/src.frag", src); + r.register_shader("assets/shaders/proc.frag", proc); + r.register_shader("assets/shaders/xfad.frag", xfad); + } + + // ── 8. Output sink ──────────────────────────────────────────────────── + use scheng_output_ffmpeg::{FfmpegSink, FfmpegConfig, config::OutputTarget}; + let mut sink = FfmpegSink::new(FfmpegConfig { + width: 1280, height: 720, framerate: 30, + target: OutputTarget::Rtsp { url: "rtsp://localhost:8554/live".into() }, + ..Default::default() + })?; + + // ── 9. Render loop ──────────────────────────────────────────────────── + let start = std::time::Instant::now(); + let mut frame: u64 = 0; + + loop { + // OSC: drain all pending messages + if let Some(ref mut osc_recv) = osc { + let mut s = store.lock().unwrap(); + osc_recv.poll(&mut s); + } + + // Hot-reload: apply any file changes + if let Some(ref mut r) = reloader { + let mut s = store.lock().unwrap(); + r.check(&mut builder, &mut s); + } + + // Advance smoothed parameter values + store.lock().unwrap().step_frame(); + + // Build NodeConfigs from current param values + let configs = { + let s = store.lock().unwrap(); + builder.build(&s) + }; + + // Execute one frame + let ctx = FrameCtx { + width: 1280, + height: 720, + time: start.elapsed().as_secs_f32(), + frame, + }; + + runtime.execute_frame(&graph, &plan, &configs, &ctx, &mut sink)?; + frame += 1; + } +} +``` + +--- + +## OSC addressing + +Every param is addressable over OSC. Use the `osc_addr` field in params.json +as the target address. The scheng editor shows the OSC address as a tooltip +on every slider. + +``` +/scheng/node/proc/uniform/u_brightness 0.75 +/scheng/node/xfad/uniform/u_tbar 0.5 +``` + +Short forms also work: +``` +/param/u_brightness 0.75 +/u_tbar 0.5 +``` + +Send from any OSC client (TouchOSC, Max/MSP, Python python-osc, etc.): +```python +from pythonosc.udp_client import SimpleUDPClient +c = SimpleUDPClient("127.0.0.1", 9000) +c.send_message("/scheng/node/proc/uniform/u_brightness", 0.75) +``` + +--- + +## MIDI CC mapping + +Map CC numbers in params.json: + +```json +{ "name": "u_tbar", "midi_cc": 7 } +``` + +List available MIDI ports: + +```bash +# Quick port listing tool +cargo run --example list_midi_ports -p scheng-input-midi +``` + +Or in code: +```rust +for port in MidiInput::list_ports().unwrap() { + println!(" {}", port); +} +``` + +--- + +## Hot-reload + +Edit a `.frag` file in `assets/shaders/` while the instrument is running. +The watcher detects the change within ~100ms and the new shader compiles +on the next frame. If the shader has a compile error, the old shader +continues running — the error is logged but the instrument doesn't crash. + +Edit `assets/params.json` while running to add/remove/adjust params. +Existing values are preserved; new params start at their defaults. + +--- + +## Tests + +```bash +# All control layer tests +cargo test -p scheng-param-store -- --nocapture +cargo test -p scheng-input-midi -- --nocapture +cargo test -p scheng-control-osc-wgpu -- --nocapture + +# Specific test suites +cargo test -p scheng-param-store schema -- --nocapture +cargo test -p scheng-param-store store -- --nocapture +cargo test -p scheng-input-midi cc_message -- --nocapture +``` diff --git a/SCHENG_DEV.md b/SCHENG_DEV.md new file mode 100644 index 0000000..881dab6 --- /dev/null +++ b/SCHENG_DEV.md @@ -0,0 +1,574 @@ +# scheng — Development Reference +> Living document. Update as work progresses. +> Last updated: 2026-03-15 + +--- + +## Project North Star + +**scheng** is a Rust SDK for building GPU-accelerated video synthesis instruments. +It provides a node-graph execution model, explicit shader contracts, and a modular I/O surface. +Instruments built on the SDK ship as cross-platform native applications via Tauri. +The shader layer is intentionally low-level — enabling developers to implement any signal +processing topology, including faithful reproductions of historical analog video synthesis hardware. + +**shadecore** = the proven single-binary reference implementation. Features that work there are the target spec for scheng's SDK crates. + +--- + +## The wgpu Backend Decision + +### Decision: Dual Backend — Keep glow, Add wgpu + +**Don't delete `scheng-runtime-glow`. Add `scheng-runtime-wgpu` alongside it.** + +This is not a migration — it's an extension. The architecture already supports it because +`scheng-runtime` is backend-agnostic. The `OutputSink` trait, `Plan`, `FrameCtx`, and +`NodeProps` don't change at all. + +--- + +### Why Keep glow + +- **shadecore validated it.** Syphon, Spout, NDI, FFmpeg, MIDI — everything works end-to-end in glow. +- **GLSL shaders run as-is.** Your entire shader library is native GLSL. No translation needed. +- **Linux/Windows still work fine with OpenGL 3.3+.** No deprecation concern there. +- **It's the regression baseline.** Contract tests run against it. Keep it green. + +--- + +### Why Add wgpu + +| Problem | glow | wgpu | +|---|---|---| +| macOS long-term | Deprecated (10.14+), translation layer | Metal native | +| Tauri embedding | Separate GL window, hostile | First-class via `wgpu-core` | +| Cross-platform one backend | No (GL quirks per platform) | Yes (Metal/DX12/Vulkan/WebGPU) | +| WebGPU/browser future | No | Yes (same API) | +| Shader language | GLSL only | WGSL + **GLSL via naga** | + +The GLSL → wgpu path works via **naga**, wgpu's shader translation library. Naga compiles +GLSL fragment shaders to WGSL/SPIR-V/Metal at load time. This means your existing shader +library stays GLSL — naga handles the translation transparently. + +``` +your .frag file (GLSL 330 core) + │ + ▼ naga::front::glsl + naga IR (intermediate) + │ + ├──▶ WGSL (wgpu native / WebGPU) + ├──▶ MSL (Metal / macOS / iOS) + ├──▶ HLSL (DX12 / Windows) + └──▶ SPIR-V (Vulkan / Linux) +``` + +**Known naga GLSL limitation:** naga's GLSL frontend targets GLSL 450 / Vulkan-style GLSL, +not the exact `#version 330 core` profile used in shadecore. The delta is small: +- `gl_FragCoord` works fine +- `texture()` calls work fine +- Extensions and legacy builtins may need minor adjustment +- The `v_uv` vertex output convention is compatible + +Mitigation: write a small compatibility header injected at the top of every shader before +naga compilation. This normalizes any 330→450 gaps transparently. + +--- + +### How wgpu Slots Into the Crate Structure + +No existing crate changes. One new crate added: + +``` +crates/ +├── scheng-core (unchanged) +├── scheng-graph (unchanged) +├── scheng-runtime (unchanged — abstract contracts) +├── scheng-runtime-glow (unchanged — proven baseline) +├── scheng-runtime-wgpu (NEW — Metal/DX12/Vulkan backend) +│ ├── Depends on: scheng-runtime, scheng-graph, wgpu +│ ├── Implements: execute_plan_to_sink() via wgpu render passes +│ ├── Manages: wgpu Device, Queue, bind groups, render pipelines +│ └── GLSL shaders compiled via naga at load time +├── scheng-bridge (unchanged) +... +``` + +**Cargo feature flags on the host binary:** + +```toml +[features] +default = ["backend-wgpu"] +gl = ["scheng-runtime-glow"] +wgpu = ["scheng-runtime-wgpu"] # default going forward +``` + +--- + +### Tauri Integration Model + +``` +Tauri Binary +├── Rust backend +│ ├── scheng-runtime-wgpu running on dedicated render thread +│ ├── wgpu renders into offscreen texture (not visible yet) +│ ├── OutputSink implementations: Syphon, Spout, NDI, FFmpeg +│ └── Tauri commands: set_param, load_graph, set_output_mode +│ +└── WebView (WKWebView on macOS, WebView2 on Windows) + ├── Instrument UI: parameter sliders, graph editor (future) + ├── Calls Tauri commands via invoke() + └── Receives preview frames via IPC (optional, downscaled JPEG/PNG) +``` + +The render loop runs entirely in native Rust. The WebView is a control surface only. +Preview frames can be sent via Tauri's event system as base64-encoded images at 15-30fps — +enough for monitoring without impacting GPU performance. + +--- + +### wgpu Backend Implementation Checklist + +- [ ] Add `scheng-runtime-wgpu` crate to workspace +- [ ] Add wgpu, naga as dependencies +- [ ] Implement `RenderTarget` (wgpu Texture + TextureView + Framebuffer equivalent) +- [ ] Implement shader compilation: GLSL → naga → wgpu `ShaderModule` +- [ ] Implement GLSL compat header (330→450 normalization) +- [ ] Implement uniform binding: uTime, uResolution, uFrame, custom u_ params +- [ ] Implement texture binding: iChannel0..iChannel3 via bind groups +- [ ] Implement `execute_plan_to_sink()` — iterate Plan, dispatch render passes +- [ ] Implement ping-pong buffers for PreviousFrame / Feedback nodes +- [ ] Port `OutputSink` implementations: preview window, Syphon, Spout +- [ ] Add contract tests running against wgpu backend (same golden fixtures) +- [ ] Feature-flag the backend selection in the host binary + +--- + +## Master Punchlist + +Status key: `[ ]` todo · `[~]` in progress · `[x]` done · `[!]` blocked + +--- + +### Phase 0 — Orientation & Baseline ✓ + +- [x] Read and understand scheng codebase + docs +- [x] Read and understand shadecore features +- [x] Map shadecore features → scheng SDK gaps +- [x] Make wgpu backend decision +- [ ] Verify `cargo build --workspace` passes clean on current main +- [ ] Verify `cargo test -p scheng-contract-tests` passes +- [ ] Verify shadecore `cargo run` produces a render window with default shader + +--- + +### Phase 1 — wgpu Backend (current focus) + +- [x] Create `crates/scheng-runtime-wgpu/` with Cargo.toml +- [~] Add to workspace Cargo.toml members list ← YOU ARE HERE: add "crates/scheng-runtime-wgpu" to members +- [x] Scaffold: lib.rs, context.rs, compat.rs, shader.rs, render_target.rs, uniforms.rs, pipeline.rs, executor.rs +- [x] Wire up wgpu Device + Queue initialization (headless) — context.rs +- [x] Implement GLSL compat header for naga — compat.rs (bindings 0-5, split texture/sampler, #define aliases) +- [~] Compile a single hardcoded frag shader through naga → wgpu pipeline ← verify with: cargo test test_naga_glsl_compile +- [~] Render one frame to offscreen texture — verify pixel readback ← cargo test test_single_node_renders +- [x] Implement execute_frame() with full Plan iteration — executor.rs (ShaderSource → PixelsOut) +- [x] Implement iChannel0..3 via bind groups (split texture+sampler, blank fallback) +- [x] Implement FrameBlock uniform buffer (uTime, uResolution, uFrame) — uniforms.rs +- [ ] Implement custom u_ uniform injection — Phase 1.2 +- [ ] Port PingPongTarget for Feedback/PreviousFrame nodes — Phase 1.3 +- [~] Run integration tests against wgpu backend ← cargo test -p scheng-runtime-wgpu -- --nocapture +- [~] CLI test: single shader, pixel readback ← cargo test test_custom_frag_shader + +--- + +### Phase 2 — Input Layer (port from shadecore) + +- [ ] `scheng-input-video`: audit current state, verify gapless decode → GL texture +- [ ] `scheng-input-video`: add wgpu texture upload path +- [ ] `scheng-input-webcam`: verify native feature builds on macOS + Windows +- [ ] `scheng-input-webcam`: wgpu upload path +- [ ] `scheng-input-midi`: new crate — port from shadecore's midir integration + - [ ] CoreMIDI on macOS + - [ ] cross-platform via midir + - [ ] JSON mapping: CC → NodeId + param name (matches shadecore params.json model) +- [ ] `scheng-input-ndi` (receive side): NDI frames → texture +- [ ] `scheng-input-syphon` (macOS receive): Syphon client → texture +- [ ] `scheng-input-spout` (Windows receive): Spout receiver → texture + +--- + +### Phase 3 — Output Layer (port from shadecore) + +- [ ] `scheng-output-syphon`: port syphon_bridge.m from shadecore, wire OutputSink +- [ ] `scheng-output-spout`: port C++ Spout2 bridge from shadecore, wire OutputSink +- [ ] `scheng-output-ffmpeg`: port recording.rs FFmpeg worker from shadecore + - [ ] Bounded queue model (drop frames rather than stall render) + - [ ] RTSP / RTMP streaming mode + - [ ] Local file recording mode + - [ ] JSON config: codec, bitrate, preset, framerate, resolution +- [ ] `scheng-output-ndi`: formalize NDI sender as OutputSink +- [ ] Headless / offscreen OutputSink (pixel readback only — for testing + CLI tools) + +--- + +### Phase 4 — Control Layer + +- [ ] Consolidate `scheng-control-osc` (exists, verify it works) +- [ ] Port MIDI from shadecore into `scheng-input-midi` +- [ ] `scrubbable_controls`: verify keymap.json / osc_map.json hot-reload +- [ ] State ownership model: match shadecore's authority table + - render.json → active shader + - params.json → parameter defaults + MIDI map + - output.json → output mode + hotkeys + - recording.json → recording profiles +- [ ] Hot-reload: file watcher → reload shader source without restart (port hotreload.rs) + +--- + +### Phase 5 — Tauri Shell + +- [ ] Create `crates/scheng-tauri` — the Tauri integration crate +- [ ] Embed scheng-runtime-wgpu in Tauri backend +- [ ] Define IPC command surface: + - `set_param(node_id, param_name, value: f32)` + - `load_graph(graph_json: String)` + - `set_output_mode(mode: String)` + - `get_params() -> NodePropsSnapshot` + - `start_recording() / stop_recording()` +- [ ] Preview frame IPC: readback → JPEG → Tauri event at 15fps +- [ ] Create minimal instrument template (fork-ready) +- [ ] Test: macOS `.app` build via `tauri build` +- [ ] Test: Windows `.exe` build via `tauri build` +- [ ] Test: Linux build via `tauri build` + +--- + +### Phase 6 — Shader Library (LZX-inspired) + +> Goal: reverse-engineer historical analog video synthesis hardware as GLSL shaders. +> Each shader = a documented module with its own params.json profile. + +- [ ] Colorizer (hue rotation, RGB→component, component→RGB) +- [ ] Ramp generator (horizontal, vertical, radial, angular) +- [ ] Key generator (luma key, chroma key, threshold) +- [ ] Hard keyer +- [ ] Soft keyer +- [ ] Fade to black / white +- [ ] Proc amp (brightness, contrast, saturation, hue) +- [ ] Sync processor (H-sync, V-sync simulation) +- [ ] Video feedback (ping-pong, convergence modes) +- [ ] Matrix mixer 4→1 (weighted sum) +- [ ] Crossfader (T-bar, soft cuts) +- [ ] Waveform monitor (parade, overlay) +- [ ] Vectorscope +- [ ] Pattern generator (color bars, test card, grid) + +--- + +### Phase 7 — GUI / Editor (deferred — design carefully) + +> Don't build until the SDK surface is stable and fully tested CLI-first. + +- [ ] Research: node graph editor options (egui, iced, or Tauri-based) +- [ ] Define the prototyping → export workflow +- [ ] Define the JSON graph format for save/load +- [ ] Design the parameter UI contract (sliders, toggles, XY pads) +- [ ] Design the preview display model in GUI context + +--- + +## CLI Testing Guide + +### Prerequisites + +```bash +# Install Rust stable +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# macOS: Xcode CLI tools +xcode-select --install + +# macOS + Syphon output: place Syphon.framework at vendor/Syphon.framework +# Windows + Spout: C++ build tools required +``` + +### 1. Build everything + +```bash +cd scheng +cargo build --workspace +``` + +Expected: all crates compile, no errors. + +### 2. Run contract tests + +```bash +cargo test -p scheng-contract-tests +``` + +Expected: all golden fixture tests pass. These pin the public SDK surface. +If these fail after any change, the SDK has a regression. + +### 3. Run all tests + +```bash +cargo test --workspace +``` + +### 4. Run the bridge (engine + browser editor) + +```bash +cargo run -p scheng-bridge + +# With debug logging: +RUST_LOG=scheng_bridge=debug cargo run -p scheng-bridge + +# Custom WebSocket address: +SCHENG_BRIDGE_ADDR=0.0.0.0:7777 cargo run -p scheng-bridge +``` + +Then open `crates/scheng-bridge/scheng-editor.html` in Chrome/Firefox → Connect → load a template → Compile. + +### 5. Test OSC control + +```bash +# Start bridge in one terminal +cargo run -p scheng-bridge + +# Send OSC from another terminal (requires oscsend or similar) +oscsend 127.0.0.1 9000 /scheng/node/xfad/uniform/u_tbar f 0.75 +oscsend 127.0.0.1 9000 /scheng/node/key/uniform/u_thresh f 0.35 +``` + +### 6. Test contract tests with verbose output + +```bash +cargo test -p scheng-contract-tests -- --nocapture +``` + +### 7. Validate sdk-compat (API regression check) + +```bash +cargo build -p sdk-compat +``` + +This is a compile-only witness. If it doesn't compile, the public API has broken. + +### 8. Build with Syphon (macOS only) + +```bash +cargo build -p scheng-runtime-glow --features syphon +``` + +### 9. Build with webcam input + +```bash +cargo build -p scheng-input-webcam --features native +``` + +--- + +## shadecore Reference Commands + +> shadecore is the proven single-binary reference. Use these to verify features work +> before porting to scheng. + +```bash +cd shadecore + +# Standard run +cargo run + +# With NDI output +cargo run --features ndi + +# Observe render hotkeys at runtime: +# 1 = preview only +# 2 = Syphon (macOS) +# 3 = Spout (Windows) +# 4 = FFmpeg stream +# 6 = NDI +``` + +--- + +## Key Architecture Rules (Never Violate) + +1. **The engine does not own time.** FrameCtx is always supplied by the host. +2. **Topology is static after compile().** NodeProps is dynamic. Structural changes require recompile. +3. **No layer reaches backward.** Graph → Runtime → Backend. Never reverse. +4. **Control updates NodeProps only.** OSC/MIDI never touches the graph or render loop directly. +5. **The runtime is single-threaded.** GL/wgpu context must be on the calling thread. +6. **Errors are returned immediately.** No silent recovery anywhere in the engine. +7. **Optional features are truly optional.** Syphon, Spout, webcam never bleed into core crates. + +--- + +## GLSL Shader Contract (v_uv convention) + +All fragment shaders must follow this interface: + +```glsl +#version 330 core +in vec2 v_uv; // lowercase — matches vertex shader out +out vec4 fragColor; + +uniform sampler2D iChannel0; // input textures, up to iChannel3 +uniform float uTime; // seconds since start +uniform vec2 uResolution; // output dimensions +uniform int uFrame; // monotonic frame counter + +// Custom params — u_ prefix by convention +uniform float u_myParam; +``` + +Port mappings → texture units: +| Port name | iChannel | +|---|---| +| `in`, `in0`, `a`, `src` | iChannel0 | +| `in1`, `b`, `src1` | iChannel1 | +| `in2`, `c`, `src2` | iChannel2 | +| `in3`, `d`, `src3` | iChannel3 | + +--- + +## Dependency Graph (current) + +``` +scheng-editor.html + │ WebSocket JSON + ▼ +scheng-bridge (tokio + tungstenite) + graph_manager.rs + │ + ├── scheng-runtime-glow (OpenGL / glow) ← proven baseline + │ ├── scheng-runtime (abstract ops, params, banks) + │ │ └── scheng-graph (node/port/edge/plan) + │ │ └── scheng-core (error, config, events) + │ └── scheng-input-video + │ + └── [scheng-runtime-wgpu] ← to be added (Metal/DX12/Vulkan) + +scheng-passes (ping-pong, temporal ring) +scheng-buffers (GPU ring buffer) +scheng-host-winit (window + GL context) +scheng-input-webcam (camera, optional) +scheng-control-osc (UDP OSC) +scrubbable_controls (keyboard + OSC layer) +scheng-contract-tests +sdk-compat +``` + +--- + +## Open Questions (resolve before building) + +- [ ] **naga GLSL compatibility**: Run a shadecore shader through naga today. Document any + adjustments needed for the compat header. +- [ ] **wgpu + winit on macOS**: Verify wgpu + winit event loop works without the OpenGL + path. The winit version in scheng-host-winit may need updating. +- [ ] **Tauri + wgpu surface sharing**: Can wgpu render into a surface that Tauri's WebView + can composite over, or does the preview need to go through IPC frames? + Research: `wgpu` + `raw-window-handle` + Tauri's `setup` hook. +- [ ] **shadecore Vosk resources**: Speech recognition library present in repo. + Intentional? If yes, add `scheng-input-voice` to Phase 2. +- [ ] **Max/MSP integration**: `max/` directory in scheng repo. Define the IPC boundary. + +--- + +## Phase 1 Integration Notes + +### Files Created in This Session + +``` +crates/scheng-runtime-wgpu/ +├── Cargo.toml ← dependencies: wgpu=22, naga=22, bytemuck, pollster, regex +├── src/ +│ ├── lib.rs ← public API, re-exports, WgpuError enum +│ ├── context.rs ← WgpuContext::new() — headless Device+Queue init +│ ├── compat.rs ← GLSL 330→450 preprocessor (strip, rewrite, inject header) +│ ├── shader.rs ← ShaderCache: GLSL→naga→wgpu::ShaderModule +│ ├── render_target.rs ← RenderTarget: offscreen RGBA8 texture + readback +│ ├── uniforms.rs ← UniformManager: FrameBlock buffer (uTime/uResolution/uFrame) +│ ├── pipeline.rs ← PipelineCache: RenderPipeline per shader hash +│ └── executor.rs ← WgpuRuntime::execute_frame(), OutputSink trait, NodeConfig +└── tests/ + └── headless.rs ← 8 integration tests (compat, naga, GPU rendering, readback) +``` + +### Step 1: Add to Workspace + +In the root `Cargo.toml`, add to the `[workspace]` members list: +```toml +[workspace] +members = [ + "crates/scheng-core", + "crates/scheng-graph", + "crates/scheng-runtime", + "crates/scheng-runtime-glow", + "crates/scheng-runtime-wgpu", # ← ADD THIS + # ... other crates +] +``` + +### Step 2: Verify Plan API (CRITICAL — do this before running tests) + +The executor uses `plan.node_ids()`, `plan.node_kind(id)`, and `plan.inputs_for(id)`. +These method names are assumed from the README. Check the actual scheng-graph source: + +```bash +grep -n "pub fn" crates/scheng-graph/src/lib.rs +``` + +Adjust `executor.rs` to match. The three key calls: +- Iterating plan nodes in order +- Getting a node's `NodeKind` +- Getting the upstream `(NodeId, channel_index)` pairs for a node's inputs + +### Step 3: Run the CPU-only tests first (no GPU needed) + +```bash +cargo test -p scheng-runtime-wgpu test_compat_preprocessing -- --nocapture +cargo test -p scheng-runtime-wgpu test_naga_glsl_compile -- --nocapture +``` + +These must pass before any GPU tests. If `test_naga_glsl_compile` fails: +- The naga GLSL 450 source in compat.rs has a syntax error +- Check the processed source printed by `--nocapture` for the exact error line + +### Step 4: Run GPU tests + +```bash +cargo test -p scheng-runtime-wgpu -- --nocapture +``` + +If no adapter is found in CI: `WGPU_BACKEND=gl cargo test -p scheng-runtime-wgpu` + +### Step 5: Fix `once_cell` dependency + +`compat.rs` uses `once_cell::sync::Lazy` for compiled regexes. +Add to Cargo.toml if not already present in workspace: +```toml +once_cell = "1" +``` +Or replace with `std::sync::OnceLock` (Rust 1.70+): +```rust +use std::sync::OnceLock; +static RE_VERSION: OnceLock = OnceLock::new(); +// in the function: +RE_VERSION.get_or_init(|| Regex::new(...).unwrap()) +``` + +### Known Phase 1 Gaps (to fix in 1.2/1.3) + +| Gap | Location | Phase | +|---|---|---| +| Custom u_ uniforms stripped with warning | compat.rs, executor.rs | 1.2 | +| plan.inputs_for() API needs verification | executor.rs line ~140 | NOW | +| plan.node_kind() API needs verification | executor.rs line ~105 | NOW | +| Y-axis flip vs OpenGL convention | render_target.rs, shader.rs | 1.5 | +| Vertex module created per-pipeline (wasteful) | pipeline.rs | 1.2 | +| PingPong / Feedback node support | executor.rs | 1.3 | +| Hot-reload: recompile on source change | executor.rs | Phase 4 | +| scheng-runtime NodeProps integration | executor.rs NodeConfig | After API verified | diff --git a/architecture.html b/architecture.html new file mode 100644 index 0000000..8485e6e --- /dev/null +++ b/architecture.html @@ -0,0 +1,658 @@ + + + + + +Architecture — scheng SDK + + + + + + + + + + + +
+
// Diagram 01
+

Full System Architecture

+

Every component in a running scheng instrument and how they relate to each other.

+
+ + + + + + + + + + + + + + + + + + INPUTS + + Syphon In + + NDI Receive + + Webcam + + Video File + + RTMP / RTSP + + + + + + + + + + CONTROL + + MIDI CC + + OSC + + + + ParamStore + smooth() targets + → values + step_frame() + each tick + + + + + + + + + + GPU CORE — WgpuRuntime + + + + Hot-Reload + AssetWatcher + naga GLSL compile + pipeline cache + + + + Shader Graph + NodeConfig + uniforms u_* + input_textures[4] + iChannel0–3 + + + + Render Pipeline + Rgba16Float targets + MSAA 1x / 4x / 8x + bind groups + FrameBlock UBO + CustomBlock UBO + iChannel0–3 + RenderPass/node + queue.submit() + one batch/frame + + + + OutputSinks + PreviewSink + SyphonSink + NdiSink + FfmpegSink + SpoutSink + present() called + after submit() + non-blocking + drop-safe + + + + + + + + about_to_wait() → tick() → execute_frame() → present() + + + OUTPUTS + + Syphon Out + + NDI Out + + RTMP Stream + + RTSP Stream + + Record MP4 + + Preview Window + + + + + + + + + + + + Video / texture inputs + + Control signals + + GPU processing + + Output / sink + +
+
+ + +
+
// Diagram 02
+

Frame Render Loop

+

What happens every single frame, in order. The render loop never blocks — inputs that fall behind drop frames gracefully.

+
+ + + + + + + + + + + 01 POLL INPUTS + Webcam.poll() + VideoDecoder + .upload_frame() + Syphon.poll() + + + + + + + 02 PARAMS + store.step_frame() + MIDI targets → + smoothed values + + + + + + 03 BUILD CFG + NodeConfig per node + frag_shader source + uniforms HashMap + input_textures[4] + + + + + + 04 GPU RENDER + graph.compile() + RenderPass per node + Rgba16Float targets + MSAA if active + + + + + + 05 SUBMIT + queue.submit() + All GPU commands + batched as one + CommandEncoder + + + + + + 06 PRESENT + sink.present() + Preview blit + Syphon / NDI + FFmpeg pipe + + + — non-blocking — inputs and outputs run on dedicated threads — frame drops preferred over stalling — + +
+
+ + +
+
// Diagram 03
+

Graph Node Topology

+

How nodes connect. Any output can feed any input. The graph compiles to a topologically sorted execution plan each frame.

+
+ + + + + + + + + + + + + + SINGLE NODE + + + ShaderSource + generates content + + + PixelsOut + triggers sinks + + + EFFECT CHAIN + + + ShaderSource + webcam texture + + + ShaderPass + solarize effect + + + ShaderPass + color grade + + + PixelsOut + → preview + + + + + + + + FEEDBACK / TEMPORAL + + + + ShaderSource + current frame + + + ShaderPass + mixes prev frame + + + PixelsOut + → output + + + + PreviousFrame + last frame texture + + + + + + + A/B MIXER + + + + ShaderSource + Source A + + + + ShaderSource + Source B + + + + Crossfade + u_tbar = MIDI CC1 + mix(A, B, tbar) + + + + + + + + + PixelsOut + → Syphon / NDI + + + ↑ MIDI CC1 controls mix + + + graph.compile() topologically sorts nodes — execution order is always dependency-correct + +
+
+ + +
+
// Diagram 04
+

MIDI → Shader Routing

+

How a MIDI CC message becomes a shader uniform value — with optional smoothing to remove steppy artifacts.

+
+ + + + + + + + + + MIDI Controller + CC1 = 64 + (any device) + + + + + midir callback + msg[0]=0xB0 ch=1 + cc=1 val=64 + + + + + ParamStore + set_by_midi_cc(1, 64) + targets["u_tbar"]=0.5 + step_frame() each tick + smooth → values[0.5] + + + + + NodeConfig + uniforms["u_tbar"] + = store.get("u_tbar") + + + + + CustomBlock GPU + binding 6 UBO + float u_tbar = 0.5 + + + + + GLSL Shader + uniform float + u_tbar; + mix(a, b, u_tbar) + + + MIDI THREAD + RENDER THREAD (each tick) + + + + smooth=0.05 → reaches target in ~20 frames — eliminates steppy CC artifacts + +
+
+ + +
+
// Diagram 05
+

Use Case Signal Flows

+

Three complete real-world signal chains from input to output.

+
+ + + + + + + + + LIVE VIDEO MIXER + + Resolume + Syphon Out + + + scheng + crossfade + + + Resolume + Syphon In + ↑ MIDI T-bar controls mix + + + BROADCAST STREAM + + Webcam + FaceTime HD + + + scheng + color grade + + + YouTube + RTMP ingest + ↑ MIDI CC: brightness / contrast / hue + + + VIDEO INSTALLATION + + Video File + loops forever + + + scheng + generative + + + NDI Out + → projectors + + + + + + COLORSPACE CHAIN + + + Input + Rgba8Unorm + 8-bit + + + + + GPU Render + Rgba16Float + 16-bit half-float + + + + + FFmpeg Encode + YUV420P bt.709 + broadcast color range + + + + + VLC / OBS + Rec.709 tagged + correct colors + + + + Without bt.709 + defaults to bt.601 (SD) + washed / shifted colors + + scheng sets: -colorspace bt709 -color_primaries bt709 -color_trc bt709 automatically + + + MSAA ANTI-ALIASING + + + sample_count = 1 + No MSAA — aliased edges + + + sample_count = 4 + 4x MSAA — smooth edges + + + sample_count = 8 + 8x MSAA — highest quality + + Pipeline keyed on (shader_hash, sample_count) — change anytime + +
+
+ +
+ ← Back to scheng home +
+ + + diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 0000000..51cc697 Binary files /dev/null and b/assets/.DS_Store differ diff --git a/assets/SHADER_LIBRARY_README.md b/assets/SHADER_LIBRARY_README.md new file mode 100644 index 0000000..0766572 --- /dev/null +++ b/assets/SHADER_LIBRARY_README.md @@ -0,0 +1,260 @@ +# scheng Shader Library — Phase 6 + +LZX-inspired analog video synthesis modules implemented as GLSL 330 shaders. + +Each shader is a self-contained `.frag` file that works with the scheng compat +header. All use the `iChannel0–3` / `uTime` / `uResolution` convention. + +--- + +## Module Reference + +### proc-amp.frag — Processing Amplifier +**Type:** Processor (1 input) + +Broadcast-standard video processing in YIQ colour space (NTSC). +Adjusts brightness (luma offset), contrast (luma gain around mid-grey), +saturation (chroma amplitude), and hue (IQ vector rotation). + +Hardware reference: Extron DA2 proc amp, Kramer VP-41 series. + +``` +iChannel0 → [brightness → contrast → saturation → hue rotation] → out +``` + +| Uniform | Range | Default | Notes | +|---------|-------|---------|-------| +| u_brightness | -1 to 1 | 0.0 | IRE shift | +| u_contrast | 0 to 3 | 1.0 | Gain around 0.5 | +| u_saturation | 0 to 3 | 1.0 | Chroma scale | +| u_hue | -180 to 180 | 0.0 | Degrees | + +--- + +### colorizer.frag — Colour Encoder +**Type:** Processor (1 input) + +Maps input luminance to a configurable hue sweep. A luma=0 pixel becomes +`u_hue_start`; luma=1 becomes `u_hue_start + u_hue_range`. +Creates false colour effects, colour encoding of greyscale signals, +and thermal-style visualisations. + +Hardware reference: LZX Visual Cortex colouriser, Fairlight CVI colour encoder. + +| Uniform | Range | Default | Notes | +|---------|-------|---------|-------| +| u_hue_start | 0–360 | 0.0 | Hue at luma=0 | +| u_hue_range | -360 to 360 | 360.0 | Full sweep | +| u_saturation | 0–1 | 1.0 | | +| u_luminance | 0–1 | 0.5 | Output L in HSL | +| u_invert | 0–1 | 0.0 | Flip luma | + +--- + +### ramp-generator.frag — Ramp Generator +**Type:** Source (no input) + +Produces geometric voltage ramps — the fundamental waveform of video synthesis. +Horizontal, vertical, radial, and angular modes. Used as CV inputs to keyers, +oscillators, and mixers to create geometric shapes. + +Hardware reference: LZX Cadet I (H ramp), Cadet II (V ramp), Cadet III (radial). + +| Uniform | Range | Default | Notes | +|---------|-------|---------|-------| +| u_mode | 0–3 | 0 | 0=H 1=V 2=radial 3=angular | +| u_freq | 0.1–16 | 1.0 | Tile frequency | +| u_phase | 0–1 | 0.0 | Phase offset | +| u_invert | 0–1 | 0.0 | Polarity flip | +| u_center_x/y | 0–1 | 0.5/0.5 | For radial/angular | + +--- + +### luma-keyer.frag — Luma Keyer +**Type:** Mixer (3 inputs) + +Extracts a matte from iChannel0 luminance and composites iChannel1 (fg) +over iChannel2 (bg). Soft key uses smoothstep for feathered edges. + +Hardware reference: LZX Cadet IV key generator, Panasonic MX-50 keyer. + +``` +iChannel0 (key source) → [threshold + softness] → matte +matte → mix(iChannel2, iChannel1, matte) → out +``` + +| Uniform | Range | Default | Notes | +|---------|-------|---------|-------| +| u_thresh | 0–1 | 0.5 | Clip point | +| u_softness | 0–0.5 | 0.05 | Edge feather | +| u_gain | 0–4 | 1.0 | Pre-key amplifier | +| u_invert | 0–1 | 0.0 | Flip matte | + +--- + +### chroma-keyer.frag — Chroma Keyer +**Type:** Mixer (2 inputs) + +Keys on a target hue (green screen, blue screen, or custom colour). +Includes spill suppression to remove colour fringing on edges. + +| Uniform | Range | Default | Notes | +|---------|-------|---------|-------| +| u_key_hue | 0–360 | 120.0 | 120=green, 240=blue | +| u_hue_range | 0–180 | 30.0 | Acceptance range ± | +| u_saturation | 0–1 | 0.2 | Min sat to key | +| u_softness | 0–1 | 0.1 | Edge feather | +| u_spill_reduce | 0–1 | 0.5 | Spill suppression | + +--- + +### crossfader.frag — T-Bar Crossfader +**Type:** Mixer (2 inputs) + +Five transition modes: dissolve, additive, multiply, hard wipe, soft wipe. +`u_tbar` is the primary performance control (map to MIDI CC 7 / mod wheel). + +Hardware reference: LZX Cadet VIII, broadcast vision mixer T-bar. + +| Uniform | Range | Default | Notes | +|---------|-------|---------|-------| +| u_tbar | 0–1 | 0.5 | 0=full A, 1=full B | +| u_mode | 0–4 | 0 | dissolve/add/mult/hard-wipe/soft-wipe | +| u_softness | 0–0.5 | 0.05 | Wipe edge | + +--- + +### matrix-mixer.frag — Matrix Mixer 4→1 +**Type:** Mixer (4 inputs) + +Weighted sum of up to 4 channels. Negative gains invert (phase reversal). +DC offset adds a constant bias. The core routing primitive of analog synthesis. + +Hardware reference: LZX Matrix Mixer, Buchla 256 spatial sound director. + +| Uniform | Range | Default | Notes | +|---------|-------|---------|-------| +| u_gain0–3 | -2 to 2 | 1/0/0/0 | Per-channel gain | +| u_offset | 0–1 | 0.0 | DC bias | +| u_clip | 0–1 | 1.0 | Enable clipping | + +--- + +### feedback.frag — Video Feedback +**Type:** Processor (2 inputs) + +Mix live source with a transformed version of the previous frame. +Zoom + rotation + drift create the characteristic feedback spiral. +**Connect iChannel1 to a PreviousFrame node** in the graph. + +Hardware reference: LZX Cadet VI lag processor, physical camera-monitor loops. + +| Uniform | Range | Default | Notes | +|---------|-------|---------|-------| +| u_decay | 0–0.99 | 0.85 | Feedback tail length | +| u_zoom | 0.9–1.1 | 1.0 | Scale per frame | +| u_rotation | -5 to 5 | 0.0 | Degrees per frame | +| u_offset_x/y | -0.1 to 0.1 | 0 | Drift per frame | +| u_blend_mode | 0–1 | 0 | 0=additive 1=mix | + +--- + +### pattern-generator.frag — Pattern Generator +**Type:** Source (no input) + +SMPTE colour bars (75% and 100%), grid, crosshatch, test card, circle, checkerboard. +Essential for calibration and as CV source signals. + +Hardware reference: Tektronix 1910 signal generator, EIA RS-189 colour bars. + +| u_mode | Pattern | +|--------|---------| +| 0 | SMPTE 75% colour bars | +| 1 | 100% colour bars | +| 2 | Grid | +| 3 | Crosshatch (±45°) | +| 4 | Luma + hue ramp test card | +| 5 | Filled circle | +| 6 | Checkerboard | + +--- + +### waveform-monitor.frag — Waveform Monitor +**Type:** Processor (1 input) + +Plots signal levels as a broadcast waveform trace. Three modes: luma parade, +RGB parade (R/G/B side by side), and overlay on the source image. + +Hardware reference: Tektronix 1740, Leader LV-5800. + +| Uniform | Range | Default | Notes | +|---------|-------|---------|-------| +| u_mode | 0–2 | 0 | 0=luma 1=RGB 2=overlay | +| u_intensity | 0–1 | 0.8 | Trace brightness | +| u_persistence | 0–32 | 1.5 | Trace thickness | + +--- + +### vectorscope.frag — Vectorscope +**Type:** Processor (1 input) + +Plots each pixel as a point on the Cb/Cr (chroma) plane. +Angle = hue, radius = saturation. Shows SMPTE colour bar targets, +axis graticule, and the broadcast skin-tone line at ~123°. + +Hardware reference: Tektronix 1760, Leader VC-5800. + +| Uniform | Range | Default | Notes | +|---------|-------|---------|-------| +| u_gain | 0.1–4 | 1.0 | Plot brightness | +| u_graticule | 0–1 | 1.0 | Show targets + axes | +| u_skin_line | 0–1 | 1.0 | Skin-tone indicator | + +--- + +## Signal Chain Examples + +### Classic proc amp → colorizer +``` +PatternGenerator → ProcAmp → Colorizer → PixelsOut +``` +Generate greyscale bars, adjust luma, recolour by luminance mapping. + +### Luma-keyed composite +``` +ShaderSource (fg) ──────────────────────────────▶ LumaKeyer (iCh1) +RampGenerator (key) ─────────────────────────────▶ LumaKeyer (iCh0) +ShaderSource (bg) ────────────────────────────────▶ LumaKeyer (iCh2) +LumaKeyer ──────────────────────────────────────▶ ProcAmp → PixelsOut +``` + +### Video feedback spiral +``` +ShaderSource ──────────────────────────────────▶ Feedback (iCh0) +PreviousFrame ─────────────────────────────────▶ Feedback (iCh1) +Feedback → Colorizer → PixelsOut +``` + +### Full chain with monitoring +``` +PatternGenerator ──▶ ProcAmp ──▶ Crossfader (A) +ShaderSource ──────────────────▶ Crossfader (B) +Crossfader ──▶ MatrixMixer ──▶ PixelsOut (main) +MatrixMixer ──▶ WaveformMonitor ──▶ PixelsOut (monitor) +MatrixMixer ──▶ Vectorscope ──▶ PixelsOut (vectorscope) +``` + +--- + +## Adding to your instrument + +Copy the relevant shader to `assets/shaders/` and add the params block +from `params-library.json` to your `assets/params.json`. + +```bash +cp proc-amp.frag assets/shaders/ +cp crossfader.frag assets/shaders/ +cp pattern-generator.frag assets/shaders/ +``` + +The scheng hot-reload watcher picks up changes instantly — no restart needed. diff --git a/assets/params-library.json b/assets/params-library.json new file mode 100644 index 0000000..2a4d96b --- /dev/null +++ b/assets/params-library.json @@ -0,0 +1,133 @@ +{ + "version": 1, + "_comment": "scheng Phase 6 shader library — complete parameter profiles. Copy the relevant params block into your instrument's assets/params.json", + + "shaders": { + + "proc-amp": { + "node_label": "proc", + "description": "Broadcast processing amplifier: brightness, contrast, saturation, hue", + "params": [ + { "name": "u_brightness", "min": -1.0, "max": 1.0, "default": 0.0, "smooth": 0.05, "midi_cc": 14, "osc_addr": "/scheng/node/proc/uniform/u_brightness", "description": "Luma offset (IRE shift)" }, + { "name": "u_contrast", "min": 0.0, "max": 3.0, "default": 1.0, "smooth": 0.05, "midi_cc": 15, "osc_addr": "/scheng/node/proc/uniform/u_contrast", "description": "Luma gain around mid-grey" }, + { "name": "u_saturation", "min": 0.0, "max": 3.0, "default": 1.0, "smooth": 0.05, "midi_cc": 16, "osc_addr": "/scheng/node/proc/uniform/u_saturation", "description": "Chroma amplitude scale" }, + { "name": "u_hue", "min": -180, "max": 180, "default": 0.0, "smooth": 0.05, "midi_cc": 17, "osc_addr": "/scheng/node/proc/uniform/u_hue", "description": "Hue rotation in degrees" } + ] + }, + + "colorizer": { + "node_label": "color", + "description": "Maps luminance to a hue sweep — LZX colour encoder style", + "params": [ + { "name": "u_hue_start", "min": 0.0, "max": 360.0, "default": 0.0, "smooth": 0.05, "midi_cc": 20, "osc_addr": "/scheng/node/color/uniform/u_hue_start", "description": "Hue at luma=0" }, + { "name": "u_hue_range", "min": -360, "max": 360.0, "default": 360.0, "smooth": 0.05, "midi_cc": 21, "osc_addr": "/scheng/node/color/uniform/u_hue_range", "description": "Hue sweep across full luma range" }, + { "name": "u_saturation", "min": 0.0, "max": 1.0, "default": 1.0, "smooth": 0.05, "midi_cc": 22, "osc_addr": "/scheng/node/color/uniform/u_saturation", "description": "Output saturation" }, + { "name": "u_luminance", "min": 0.0, "max": 1.0, "default": 0.5, "smooth": 0.05, "midi_cc": 23, "osc_addr": "/scheng/node/color/uniform/u_luminance", "description": "Output luminance" }, + { "name": "u_invert", "min": 0.0, "max": 1.0, "default": 0.0, "smooth": 0.0, "midi_cc": 24, "osc_addr": "/scheng/node/color/uniform/u_invert", "description": "Invert luma before mapping" } + ] + }, + + "ramp-generator": { + "node_label": "ramp", + "description": "Geometric voltage ramps — LZX Cadet I/II style signal source", + "params": [ + { "name": "u_mode", "min": 0.0, "max": 3.0, "default": 0.0, "smooth": 0.0, "midi_cc": 30, "osc_addr": "/scheng/node/ramp/uniform/u_mode", "description": "0=H, 1=V, 2=radial, 3=angular" }, + { "name": "u_freq", "min": 0.1, "max": 16, "default": 1.0, "smooth": 0.05, "midi_cc": 31, "osc_addr": "/scheng/node/ramp/uniform/u_freq", "description": "Tile frequency" }, + { "name": "u_phase", "min": 0.0, "max": 1.0, "default": 0.0, "smooth": 0.05, "midi_cc": 32, "osc_addr": "/scheng/node/ramp/uniform/u_phase", "description": "Phase offset" }, + { "name": "u_invert", "min": 0.0, "max": 1.0, "default": 0.0, "smooth": 0.0, "midi_cc": 33, "osc_addr": "/scheng/node/ramp/uniform/u_invert", "description": "Invert ramp polarity" }, + { "name": "u_center_x", "min": 0.0, "max": 1.0, "default": 0.5, "smooth": 0.05, "midi_cc": 34, "osc_addr": "/scheng/node/ramp/uniform/u_center_x", "description": "Centre X (radial/angular)" }, + { "name": "u_center_y", "min": 0.0, "max": 1.0, "default": 0.5, "smooth": 0.05, "midi_cc": 35, "osc_addr": "/scheng/node/ramp/uniform/u_center_y", "description": "Centre Y (radial/angular)" } + ] + }, + + "luma-keyer": { + "node_label": "lkey", + "description": "Luma key generator — LZX Cadet IV style threshold key", + "params": [ + { "name": "u_thresh", "min": 0.0, "max": 1.0, "default": 0.5, "smooth": 0.03, "midi_cc": 40, "osc_addr": "/scheng/node/lkey/uniform/u_thresh", "description": "Key clip threshold" }, + { "name": "u_softness", "min": 0.0, "max": 0.5, "default": 0.05, "smooth": 0.03, "midi_cc": 41, "osc_addr": "/scheng/node/lkey/uniform/u_softness", "description": "Edge feather width" }, + { "name": "u_gain", "min": 0.0, "max": 4.0, "default": 1.0, "smooth": 0.05, "midi_cc": 42, "osc_addr": "/scheng/node/lkey/uniform/u_gain", "description": "Pre-key luma amplifier" }, + { "name": "u_invert", "min": 0.0, "max": 1.0, "default": 0.0, "smooth": 0.0, "midi_cc": 43, "osc_addr": "/scheng/node/lkey/uniform/u_invert", "description": "Invert key matte" } + ] + }, + + "chroma-keyer": { + "node_label": "ckey", + "description": "Chroma key (green/blue screen) with spill suppression", + "params": [ + { "name": "u_key_hue", "min": 0, "max": 360, "default": 120.0, "smooth": 0.03, "midi_cc": 50, "osc_addr": "/scheng/node/ckey/uniform/u_key_hue", "description": "Target hue to key out (120=green)" }, + { "name": "u_hue_range", "min": 0, "max": 180, "default": 30.0, "smooth": 0.03, "midi_cc": 51, "osc_addr": "/scheng/node/ckey/uniform/u_hue_range", "description": "Hue acceptance range ±degrees" }, + { "name": "u_saturation", "min": 0.0, "max": 1.0, "default": 0.2, "smooth": 0.03, "midi_cc": 52, "osc_addr": "/scheng/node/ckey/uniform/u_saturation", "description": "Min saturation to trigger key" }, + { "name": "u_softness", "min": 0.0, "max": 1.0, "default": 0.1, "smooth": 0.03, "midi_cc": 53, "osc_addr": "/scheng/node/ckey/uniform/u_softness", "description": "Edge feather" }, + { "name": "u_spill_reduce", "min": 0.0, "max": 1.0, "default": 0.5, "smooth": 0.05, "midi_cc": 54, "osc_addr": "/scheng/node/ckey/uniform/u_spill_reduce", "description": "Green/blue spill suppression" } + ] + }, + + "crossfader": { + "node_label": "xfad", + "description": "T-bar crossfader with dissolve, additive, multiply, and wipe modes", + "params": [ + { "name": "u_tbar", "min": 0.0, "max": 1.0, "default": 0.5, "smooth": 0.02, "midi_cc": 7, "osc_addr": "/scheng/node/xfad/uniform/u_tbar", "description": "Crossfade position (0=A, 1=B)" }, + { "name": "u_mode", "min": 0.0, "max": 4.0, "default": 0.0, "smooth": 0.0, "midi_cc": 60, "osc_addr": "/scheng/node/xfad/uniform/u_mode", "description": "0=dissolve 1=add 2=mult 3=hard-wipe 4=soft-wipe" }, + { "name": "u_softness", "min": 0.0, "max": 0.5, "default": 0.05, "smooth": 0.03, "midi_cc": 61, "osc_addr": "/scheng/node/xfad/uniform/u_softness", "description": "Wipe edge softness" } + ] + }, + + "matrix-mixer": { + "node_label": "mtx", + "description": "4→1 weighted matrix mixer with optional DC offset and clip", + "params": [ + { "name": "u_gain0", "min": -2.0, "max": 2.0, "default": 1.0, "smooth": 0.05, "midi_cc": 70, "osc_addr": "/scheng/node/mtx/uniform/u_gain0", "description": "iChannel0 gain" }, + { "name": "u_gain1", "min": -2.0, "max": 2.0, "default": 0.0, "smooth": 0.05, "midi_cc": 71, "osc_addr": "/scheng/node/mtx/uniform/u_gain1", "description": "iChannel1 gain" }, + { "name": "u_gain2", "min": -2.0, "max": 2.0, "default": 0.0, "smooth": 0.05, "midi_cc": 72, "osc_addr": "/scheng/node/mtx/uniform/u_gain2", "description": "iChannel2 gain" }, + { "name": "u_gain3", "min": -2.0, "max": 2.0, "default": 0.0, "smooth": 0.05, "midi_cc": 73, "osc_addr": "/scheng/node/mtx/uniform/u_gain3", "description": "iChannel3 gain" }, + { "name": "u_offset", "min": 0.0, "max": 1.0, "default": 0.0, "smooth": 0.05, "midi_cc": 74, "osc_addr": "/scheng/node/mtx/uniform/u_offset", "description": "DC bias offset" }, + { "name": "u_clip", "min": 0.0, "max": 1.0, "default": 1.0, "smooth": 0.0, "midi_cc": 75, "osc_addr": "/scheng/node/mtx/uniform/u_clip", "description": "Enable output clipping" } + ] + }, + + "feedback": { + "node_label": "fb", + "description": "Video feedback with zoom, rotation, and drift — LZX Cadet VI style", + "params": [ + { "name": "u_decay", "min": 0.0, "max": 0.99, "default": 0.85, "smooth": 0.05, "midi_cc": 80, "osc_addr": "/scheng/node/fb/uniform/u_decay", "description": "Feedback decay (0=off, 0.99=long tail)" }, + { "name": "u_zoom", "min": 0.9, "max": 1.1, "default": 1.0, "smooth": 0.05, "midi_cc": 81, "osc_addr": "/scheng/node/fb/uniform/u_zoom", "description": "Zoom factor per frame" }, + { "name": "u_rotation", "min": -5, "max": 5, "default": 0.0, "smooth": 0.05, "midi_cc": 82, "osc_addr": "/scheng/node/fb/uniform/u_rotation", "description": "Rotation degrees per frame" }, + { "name": "u_offset_x", "min": -0.1,"max": 0.1, "default": 0.0, "smooth": 0.05, "midi_cc": 83, "osc_addr": "/scheng/node/fb/uniform/u_offset_x", "description": "Horizontal drift per frame" }, + { "name": "u_offset_y", "min": -0.1,"max": 0.1, "default": 0.0, "smooth": 0.05, "midi_cc": 84, "osc_addr": "/scheng/node/fb/uniform/u_offset_y", "description": "Vertical drift per frame" }, + { "name": "u_blend_mode", "min": 0.0, "max": 1.0, "default": 0.0, "smooth": 0.0, "midi_cc": 85, "osc_addr": "/scheng/node/fb/uniform/u_blend_mode", "description": "0=additive, 1=mix" } + ] + }, + + "pattern-generator": { + "node_label": "pat", + "description": "SMPTE colour bars, grids, test cards, circles, checkerboard", + "params": [ + { "name": "u_mode", "min": 0, "max": 6, "default": 0.0, "smooth": 0.0, "midi_cc": 90, "osc_addr": "/scheng/node/pat/uniform/u_mode", "description": "0=SMPTE75 1=full-bars 2=grid 3=crosshatch 4=testcard 5=circle 6=checker" }, + { "name": "u_freq", "min": 1, "max": 32, "default": 8.0, "smooth": 0.05, "midi_cc": 91, "osc_addr": "/scheng/node/pat/uniform/u_freq", "description": "Pattern frequency / density" }, + { "name": "u_line_w", "min": 0, "max": 0.1,"default": 0.02,"smooth": 0.05, "midi_cc": 92, "osc_addr": "/scheng/node/pat/uniform/u_line_w", "description": "Grid line width" }, + { "name": "u_phase", "min": 0, "max": 1, "default": 0.0, "smooth": 0.05, "midi_cc": 93, "osc_addr": "/scheng/node/pat/uniform/u_phase", "description": "Pattern phase / offset" } + ] + }, + + "waveform-monitor": { + "node_label": "wfm", + "description": "Broadcast waveform monitor: luma parade, RGB parade, overlay", + "params": [ + { "name": "u_mode", "min": 0, "max": 2, "default": 0.0, "smooth": 0.0, "midi_cc": 100, "osc_addr": "/scheng/node/wfm/uniform/u_mode", "description": "0=luma 1=RGB parade 2=overlay" }, + { "name": "u_intensity", "min": 0, "max": 1, "default": 0.8, "smooth": 0.05, "midi_cc": 101, "osc_addr": "/scheng/node/wfm/uniform/u_intensity", "description": "Trace brightness" }, + { "name": "u_persistence", "min": 0, "max": 32, "default": 1.5, "smooth": 0.05, "midi_cc": 102, "osc_addr": "/scheng/node/wfm/uniform/u_persistence", "description": "Trace thickness" } + ] + }, + + "vectorscope": { + "node_label": "vec", + "description": "Cb/Cr vectorscope with SMPTE targets, graticule, and skin-tone line", + "params": [ + { "name": "u_gain", "min": 0.1, "max": 4, "default": 1.0, "smooth": 0.05, "midi_cc": 110, "osc_addr": "/scheng/node/vec/uniform/u_gain", "description": "Plot gain" }, + { "name": "u_graticule", "min": 0.0, "max": 1, "default": 1.0, "smooth": 0.0, "midi_cc": 111, "osc_addr": "/scheng/node/vec/uniform/u_graticule", "description": "Show reference targets and axes" }, + { "name": "u_skin_line", "min": 0.0, "max": 1, "default": 1.0, "smooth": 0.0, "midi_cc": 112, "osc_addr": "/scheng/node/vec/uniform/u_skin_line", "description": "Show broadcast skin-tone line" } + ] + } + } +} diff --git a/assets/shaders/chroma-keyer.frag b/assets/shaders/chroma-keyer.frag new file mode 100644 index 0000000..c0232b5 --- /dev/null +++ b/assets/shaders/chroma-keyer.frag @@ -0,0 +1,80 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: chroma-keyer.frag +// +// Chroma Keyer +// Keys on a target hue (green screen, blue screen, or any colour). +// Generates a matte from colour distance and composites fg over bg. +// +// Reference: Broadcast chroma key, LZX colour difference keyer techniques. +// +// Node role: Processor / Mixer (2 inputs) +// iChannel0 = foreground + key colour source (same signal, split internally) +// iChannel1 = background +// +// Uniforms: +// u_key_hue [0, 360] target hue to key out default: 120.0 (green) +// u_hue_range [0, 180] hue acceptance range (±) default: 30.0 +// u_saturation [0, 1] min saturation to trigger key default: 0.2 +// u_softness [0, 1] edge feather default: 0.1 +// u_spill_reduce [0, 1] green/blue spill suppression default: 0.5 +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform sampler2D iChannel0; // foreground (with chroma key colour) +uniform sampler2D iChannel1; // background +uniform float u_key_hue; +uniform float u_hue_range; +uniform float u_saturation; +uniform float u_softness; +uniform float u_spill_reduce; + +// RGB → HSV for hue-based keying +vec3 rgb_to_hsv(vec3 c) { + float cmax = max(c.r, max(c.g, c.b)); + float cmin = min(c.r, min(c.g, c.b)); + float delta = cmax - cmin; + + float h = 0.0; + if (delta > 0.0001) { + if (cmax == c.r) h = 60.0 * mod((c.g - c.b) / delta, 6.0); + else if (cmax == c.g) h = 60.0 * ((c.b - c.r) / delta + 2.0); + else h = 60.0 * ((c.r - c.g) / delta + 4.0); + } + if (h < 0.0) h += 360.0; + + float s = (cmax < 0.0001) ? 0.0 : delta / cmax; + return vec3(h, s, cmax); +} + +void main() { + vec4 fg = texture(iChannel0, v_uv); + vec4 bg = texture(iChannel1, v_uv); + + vec3 hsv = rgb_to_hsv(fg.rgb); + float h = hsv.x; + float s = hsv.y; + + // Hue distance (circular, handles wraparound at 0/360) + float hue_dist = abs(mod(h - u_key_hue + 180.0, 360.0) - 180.0); + + // Key matte: 1 = keep foreground, 0 = show background + // Only key pixels that are saturated enough (avoid keying grey/white) + float sat_mask = smoothstep(u_saturation - 0.05, u_saturation + 0.05, s); + float hue_mask = 1.0 - smoothstep( + u_hue_range - u_softness * 20.0, + u_hue_range + u_softness * 20.0, + hue_dist + ); + float key = sat_mask * hue_mask; // 1 = is the key colour + + // Spill suppression: desaturate fg pixels near the key hue + // (prevents green/blue fringe on edges — standard broadcast technique) + float spill = clamp(1.0 - hue_dist / (u_hue_range + 0.001), 0.0, 1.0); + float luma = dot(fg.rgb, vec3(0.2126, 0.7152, 0.0722)); + vec3 fg_nospill = mix(fg.rgb, vec3(luma), spill * u_spill_reduce * sat_mask); + + // Composite: bg where key colour detected, fg elsewhere + fragColor = mix(vec4(fg_nospill, fg.a), bg, key); +} diff --git a/assets/shaders/colorizer.frag b/assets/shaders/colorizer.frag new file mode 100644 index 0000000..126adff --- /dev/null +++ b/assets/shaders/colorizer.frag @@ -0,0 +1,62 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: colorizer.frag +// +// Colorizer +// Maps input luma to a configurable colour gradient — the LZX Chroma Key / +// Colour Encoder style module. Takes a greyscale or colour signal and +// recolours it by mapping the luminance value through a hue cycle. +// +// In analog hardware this is done with a voltage-controlled oscillator +// sweeping the colour subcarrier phase proportional to luma voltage. +// +// Node role: Processor (iChannel0 → out) +// Uniforms: +// u_hue_start [0, 360] starting hue for luma=0 default: 0.0 +// u_hue_range [-360,360] hue sweep across luma range default: 360.0 +// u_saturation [0, 1] output chroma saturation default: 1.0 +// u_luminance [0, 1] output luma (brightness) default: 0.5 +// u_invert [0, 1] invert luma before mapping default: 0.0 +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform sampler2D iChannel0; +uniform float u_hue_start; // hue at luma=0 (degrees) +uniform float u_hue_range; // degrees of hue swept across full luma range +uniform float u_saturation; // output saturation +uniform float u_luminance; // output luminance +uniform float u_invert; // 0=normal, 1=invert luma before mapping + +// HSL → RGB (classic formula, faithful to colour encoder hardware output) +vec3 hsl_to_rgb(float h, float s, float l) { + float c = (1.0 - abs(2.0 * l - 1.0)) * s; + h = mod(h / 60.0, 6.0); + float x = c * (1.0 - abs(mod(h, 2.0) - 1.0)); + vec3 rgb; + if (h < 1.0) rgb = vec3(c, x, 0); + else if (h < 2.0) rgb = vec3(x, c, 0); + else if (h < 3.0) rgb = vec3(0, c, x); + else if (h < 4.0) rgb = vec3(0, x, c); + else if (h < 5.0) rgb = vec3(x, 0, c); + else rgb = vec3(c, 0, x); + float m = l - 0.5 * c; + return clamp(rgb + m, 0.0, 1.0); +} + +void main() { + vec4 src = texture(iChannel0, v_uv); + + // Extract luma (Rec.709 coefficients) + float luma = dot(src.rgb, vec3(0.2126, 0.7152, 0.0722)); + + // Optional inversion + luma = mix(luma, 1.0 - luma, u_invert); + + // Map luma → hue + float hue = mod(u_hue_start + luma * u_hue_range, 360.0); + + vec3 colour = hsl_to_rgb(hue, u_saturation, u_luminance); + + fragColor = vec4(colour, src.a); +} diff --git a/assets/shaders/crossfader.frag b/assets/shaders/crossfader.frag new file mode 100644 index 0000000..f56e5af --- /dev/null +++ b/assets/shaders/crossfader.frag @@ -0,0 +1,63 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: crossfader.frag +// +// T-Bar Crossfader +// Blends between two input signals with multiple transition modes. +// Reference: LZX Cadet VIII / video mixer T-bar faders. +// +// Mode 0 = dissolve (linear mix — the broadcast standard T-bar) +// Mode 1 = additive (A + B, clipped — for layering/accretion) +// Mode 2 = multiply (A × B — for texture mapping / gating) +// Mode 3 = hard wipe (left→right at u_tbar position, no softness) +// Mode 4 = soft wipe (left→right with feathered edge) +// +// Node role: Mixer (iChannel0=A, iChannel1=B) +// Uniforms: +// u_tbar [0, 1] crossfade position (0=full A, 1=full B) default: 0.5 +// u_mode [0, 4] transition type default: 0 +// u_softness [0,0.5] wipe edge softness (modes 3–4) default: 0.05 +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform sampler2D iChannel0; // input A +uniform sampler2D iChannel1; // input B +uniform float u_tbar; +uniform float u_mode; +uniform float u_softness; + +void main() { + vec4 a = texture(iChannel0, v_uv); + vec4 b = texture(iChannel1, v_uv); + int mode = int(u_mode); + + vec4 out_col; + + if (mode == 0) { + // Dissolve — standard linear crossfade + out_col = mix(a, b, u_tbar); + + } else if (mode == 1) { + // Additive — both signals add, clipped to 1.0 + out_col = clamp(a * (1.0 - u_tbar) + b * u_tbar + a * b * u_tbar, 0.0, 1.0); + + } else if (mode == 2) { + // Multiply — A gates B (useful for keying shapes onto sources) + out_col = mix(a, a * b, u_tbar); + + } else if (mode == 3) { + // Hard wipe: left portion shows A, right shows B + // u_tbar = wipe position (0=all A, 1=all B) + float edge = step(u_tbar, v_uv.x); + out_col = mix(a, b, edge); + + } else { + // Soft wipe: feathered transition + float soft = max(u_softness, 0.001); + float edge = smoothstep(u_tbar - soft, u_tbar + soft, v_uv.x); + out_col = mix(a, b, edge); + } + + fragColor = out_col; +} diff --git a/assets/shaders/default.frag b/assets/shaders/default.frag new file mode 100644 index 0000000..5fd2095 --- /dev/null +++ b/assets/shaders/default.frag @@ -0,0 +1,71 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: ramp-generator.frag +// +// Ramp Generator +// Produces geometric voltage ramps — the foundational waveform of analog +// video synthesis. LZX Visionary, Cadet I (H Ramp), Cadet II (V Ramp). +// +// A "ramp" is a linearly increasing voltage that sweeps across the frame, +// used as a control voltage (CV) input to modulators, oscillators, and +// keyers to create geometric shapes and patterns. +// +// Four ramp types selectable via u_mode: +// 0 = horizontal (left→right) +// 1 = vertical (top→bottom) +// 2 = radial (centre→edge, circular) +// 3 = angular (rotation around centre, 0–1 = 0°–360°) +// +// Node role: Source (no input — generates signal) +// Uniforms: +// u_mode [0,3] ramp type default: 0 +// u_freq [0.1,16] tile frequency default: 1.0 +// u_phase [0,1] phase offset default: 0.0 +// u_invert [0,1] invert output default: 0.0 +// u_center_x [0,1] centre X for radial/angular default: 0.5 +// u_center_y [0,1] centre Y for radial/angular default: 0.5 +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform float u_mode; +uniform float u_freq; +uniform float u_phase; +uniform float u_invert; +uniform float u_center_x; +uniform float u_center_y; + +void main() { + vec2 uv = v_uv; + vec2 ctr = vec2(u_center_x, u_center_y); + int mode = int(u_mode); + + float ramp; + + if (mode == 0) { + // Horizontal: left→right sawtooth + ramp = fract(uv.x * u_freq + u_phase); + + } else if (mode == 1) { + // Vertical: top→bottom sawtooth + ramp = fract(uv.y * u_freq + u_phase); + + } else if (mode == 2) { + // Radial: distance from centre, tiled + // Matches LZX Cadet III / Visual Cortex radial output + float dist = length(uv - ctr) * 2.0; // 0 at centre, 1 at corners + ramp = fract(dist * u_freq + u_phase); + + } else { + // Angular: angle around centre, 0→1 = 0°→360° + // Matches LZX phase/angle modulation inputs + vec2 d = uv - ctr; + float angle = atan(d.y, d.x); // -π to π + ramp = fract((angle / (2.0 * 3.14159265)) + 0.5 + u_phase); + } + + // Optional invert (flip the ramp polarity — common analog patch) + ramp = mix(ramp, 1.0 - ramp, u_invert); + + fragColor = vec4(ramp, ramp, ramp, 1.0); +} diff --git a/assets/shaders/feedback.frag b/assets/shaders/feedback.frag new file mode 100644 index 0000000..251591e --- /dev/null +++ b/assets/shaders/feedback.frag @@ -0,0 +1,78 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: feedback.frag +// +// Video Feedback +// Mixes the current frame with the previous frame at reduced gain, creating +// temporal echo / feedback trails. The foundational texture of analog video +// feedback systems (pointing a camera at a monitor, simulated digitally). +// +// Reference: LZX Cadet VI (Lag Processor), video feedback installations, +// Steina & Woody Vasulka feedback works. +// +// In hardware this is achieved by mixing the live signal with a delayed +// version (via a frame buffer or physical camera-monitor loop) at a gain +// less than unity so the feedback converges rather than exploding. +// +// Node role: Processor (iChannel0=live, iChannel1=previous frame) +// Uniforms: +// u_decay [0, 0.99] feedback decay (0=no feedback, 0.99=long tail) +// u_zoom [0.9, 1.1] zoom factor applied to previous frame +// u_rotation [-5, 5] rotation in degrees per frame +// u_offset_x [-0.1,0.1] horizontal drift per frame +// u_offset_y [-0.1,0.1] vertical drift per frame +// u_blend_mode [0, 1] 0=additive, 1=mix +// +// Note: iChannel1 must be connected to the PreviousFrame node in the graph +// for this shader to create actual feedback loops. +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform sampler2D iChannel0; // live source +uniform sampler2D iChannel1; // previous frame (connect PreviousFrame node here) +uniform float u_decay; +uniform float u_zoom; +uniform float u_rotation; +uniform float u_offset_x; +uniform float u_offset_y; +uniform float u_blend_mode; + +void main() { + vec2 uv = v_uv; + + // Transform UV for the previous frame sample — + // zoom, rotate, and offset create the characteristic feedback spiral + vec2 ctr = vec2(0.5, 0.5); + vec2 d = uv - ctr; + + // Rotation + float angle = radians(u_rotation); + float cosA = cos(angle); + float sinA = sin(angle); + d = vec2(d.x * cosA - d.y * sinA, d.x * sinA + d.y * cosA); + + // Zoom + d /= max(u_zoom, 0.001); + + // Drift offset + vec2 fb_uv = ctr + d + vec2(u_offset_x, u_offset_y); + + vec4 live = texture(iChannel0, uv); + vec4 previous = texture(iChannel1, fb_uv); + + // Decay: previous frame contributes at reduced amplitude + vec4 decayed = previous * u_decay; + + // Blend modes + vec4 result; + if (u_blend_mode < 0.5) { + // Additive: live + decayed previous (creates bloom / trails) + result = clamp(live + decayed, 0.0, 1.0); + } else { + // Mix: crossfade between live and decayed previous + result = mix(live, decayed, u_decay); + } + + fragColor = result; +} diff --git a/assets/shaders/luma-keyer.frag b/assets/shaders/luma-keyer.frag new file mode 100644 index 0000000..d972ccc --- /dev/null +++ b/assets/shaders/luma-keyer.frag @@ -0,0 +1,57 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: luma-keyer.frag +// +// Luma Keyer +// Generates a key signal (matte) from the luminance of iChannel0, then +// composites foreground (iChannel1) over background (iChannel2). +// +// Reference: LZX Cadet IV (Key Generator), broadcast luma keyers. +// In analog hardware, the key signal is a derived voltage that gates the +// downstream mix — above the threshold lets the foreground through, below +// passes the background. Softness controls the edge transition width. +// +// Node role: Processor / Mixer (3 inputs) +// iChannel0 = key source (luma extracted from this) +// iChannel1 = foreground (shown where key is HIGH) +// iChannel2 = background (shown where key is LOW) +// +// Uniforms: +// u_thresh [0, 1] key clip point default: 0.5 +// u_softness [0, 0.5] edge softness (feather) default: 0.05 +// u_gain [0, 4] pre-key luma amplifier default: 1.0 +// u_invert [0, 1] invert the key matte default: 0.0 +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform sampler2D iChannel0; // key source +uniform sampler2D iChannel1; // foreground +uniform sampler2D iChannel2; // background +uniform float u_thresh; +uniform float u_softness; +uniform float u_gain; +uniform float u_invert; + +void main() { + vec4 key_src = texture(iChannel0, v_uv); + vec4 fg = texture(iChannel1, v_uv); + vec4 bg = texture(iChannel2, v_uv); + + // Extract luma (Rec.709) + float luma = dot(key_src.rgb, vec3(0.2126, 0.7152, 0.0722)); + + // Apply pre-key gain (amplify signal before clipping — analog hardware behaviour) + luma = clamp(luma * u_gain, 0.0, 1.0); + + // Soft key: smoothstep over [thresh - softness, thresh + softness] + // Hard key when softness = 0 (step function, matching a comparator circuit) + float soft = max(u_softness, 0.0001); // prevent div-by-zero + float key = smoothstep(u_thresh - soft, u_thresh + soft, luma); + + // Invert the key matte (flip foreground/background) + key = mix(key, 1.0 - key, u_invert); + + // Composite: fg where key=1, bg where key=0 + fragColor = mix(bg, fg, key); +} diff --git a/assets/shaders/matrix-mixer.frag b/assets/shaders/matrix-mixer.frag new file mode 100644 index 0000000..d04f662 --- /dev/null +++ b/assets/shaders/matrix-mixer.frag @@ -0,0 +1,56 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: matrix-mixer.frag +// +// Matrix Mixer (4→1) +// Weighted sum of up to 4 input channels with individual gain controls. +// Reference: LZX Matrix Mixer, video mixing consoles, Fairlight CMI fader banks. +// +// This is the core routing primitive of analog video synthesis — every +// signal can feed into every output channel with variable gain. Full matrix +// mixing enables any signal topology: summation, inversion, polarisation. +// +// Node role: Mixer (iChannel0–3) +// Uniforms: +// u_gain0 [-2, 2] gain for iChannel0 default: 1.0 +// u_gain1 [-2, 2] gain for iChannel1 default: 0.0 +// u_gain2 [-2, 2] gain for iChannel2 default: 0.0 +// u_gain3 [-2, 2] gain for iChannel3 default: 0.0 +// u_offset [0, 1] DC offset (bias) default: 0.0 +// u_clip [0, 1] enable output clipping default: 1.0 +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform sampler2D iChannel0; +uniform sampler2D iChannel1; +uniform sampler2D iChannel2; +uniform sampler2D iChannel3; +uniform float u_gain0; +uniform float u_gain1; +uniform float u_gain2; +uniform float u_gain3; +uniform float u_offset; +uniform float u_clip; + +void main() { + vec4 c0 = texture(iChannel0, v_uv); + vec4 c1 = texture(iChannel1, v_uv); + vec4 c2 = texture(iChannel2, v_uv); + vec4 c3 = texture(iChannel3, v_uv); + + // Weighted sum — negative gains invert the signal (phase reversal) + vec3 mix_out = c0.rgb * u_gain0 + + c1.rgb * u_gain1 + + c2.rgb * u_gain2 + + c3.rgb * u_gain3 + + vec3(u_offset); + + // Optional hard clip (disable for "hot" overload / bloom effect) + if (u_clip > 0.5) { + mix_out = clamp(mix_out, 0.0, 1.0); + } + + // Alpha: take from channel 0 (primary source) + fragColor = vec4(mix_out, c0.a); +} diff --git a/assets/shaders/pattern-generator.frag b/assets/shaders/pattern-generator.frag new file mode 100644 index 0000000..afaf620 --- /dev/null +++ b/assets/shaders/pattern-generator.frag @@ -0,0 +1,117 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: pattern-generator.frag +// +// Pattern Generator +// Produces standard test patterns and geometric signal sources. +// Reference: SMPTE EG 1 (colour bars), EIA RS-189 (colour bars), +// LZX Visual Cortex sync/pattern outputs, Tektronix 1910 signal generator. +// +// Mode 0 = SMPTE 75% colour bars (broadcast alignment standard) +// Mode 1 = Full-field colour bars (100% amplitude) +// Mode 2 = Grid (H + V lines, configurable frequency) +// Mode 3 = Crosshatch (diagonal grid) +// Mode 4 = Ramp + colour field test card +// Mode 5 = Circle / disc generator +// Mode 6 = Checkerboard +// +// Node role: Source (no input) +// Uniforms: +// u_mode [0, 6] pattern type default: 0 +// u_freq [1, 32] grid/pattern frequency default: 8.0 +// u_line_w [0, 0.1] line width default: 0.02 +// u_phase [0, 1] pattern phase offset default: 0.0 +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform float u_mode; +uniform float u_freq; +uniform float u_line_w; +uniform float u_phase; + +// Standard SMPTE 75% colour bar colours (normalised to 0-1) +const vec3 BARS_75[8] = vec3[8]( + vec3(0.75, 0.75, 0.75), // White 75% + vec3(0.75, 0.75, 0.00), // Yellow + vec3(0.00, 0.75, 0.75), // Cyan + vec3(0.00, 0.75, 0.00), // Green + vec3(0.75, 0.00, 0.75), // Magenta + vec3(0.75, 0.00, 0.00), // Red + vec3(0.00, 0.00, 0.75), // Blue + vec3(0.00, 0.00, 0.00) // Black +); + +// Standard 100% colour bar colours +const vec3 BARS_100[8] = vec3[8]( + vec3(1.00, 1.00, 1.00), // White + vec3(1.00, 1.00, 0.00), // Yellow + vec3(0.00, 1.00, 1.00), // Cyan + vec3(0.00, 1.00, 0.00), // Green + vec3(1.00, 0.00, 1.00), // Magenta + vec3(1.00, 0.00, 0.00), // Red + vec3(0.00, 0.00, 1.00), // Blue + vec3(0.00, 0.00, 0.00) // Black +); + +void main() { + vec2 uv = v_uv; + int mode = int(u_mode); + vec3 col = vec3(0.0); + + if (mode == 0 || mode == 1) { + // Colour bars: 8 equal-width vertical bars + int bar_idx = int(uv.x * 8.0); + bar_idx = clamp(bar_idx, 0, 7); + if (mode == 0) col = BARS_75[bar_idx]; + else col = BARS_100[bar_idx]; + + } else if (mode == 2) { + // Grid: H and V lines at u_freq intervals + vec2 grid = fract(uv * u_freq + u_phase); + float line_h = step(1.0 - u_line_w * u_freq, grid.x); + float line_v = step(1.0 - u_line_w * u_freq, grid.y); + col = vec3(max(line_h, line_v)); + + } else if (mode == 3) { + // Crosshatch: diagonal lines at ±45° + vec2 d1 = fract((uv.x + uv.y) * u_freq * 0.5 + u_phase); + vec2 d2 = fract((uv.x - uv.y) * u_freq * 0.5 + u_phase); + float lw = u_line_w * u_freq * 0.5; + float l1 = step(1.0 - lw, d1.x); + float l2 = step(1.0 - lw, d2.x); + col = vec3(max(l1, l2)); + + } else if (mode == 4) { + // Test card: luma ramp on top half, chroma ramp on bottom half + if (uv.y < 0.5) { + // Top: greyscale ramp + float luma = fract(uv.x * u_freq + u_phase); + col = vec3(luma); + } else { + // Bottom: hue ramp (R→G→B→R) + float h = fract(uv.x + u_phase); + // HSV to RGB for a pure hue sweep at full sat/val + float r = clamp(abs(mod(h * 6.0 - 3.0, 6.0) - 3.0) - 1.0, 0.0, 1.0); + float g = clamp(2.0 - abs(mod(h * 6.0 - 2.0, 6.0) - 2.0), 0.0, 1.0); + float b = clamp(2.0 - abs(mod(h * 6.0 - 4.0, 6.0) - 2.0), 0.0, 1.0); + col = vec3(r, g, b); + } + + } else if (mode == 5) { + // Circle / disc: filled circle at centre + vec2 d = uv - vec2(0.5 + u_phase, 0.5); + float r = length(d); + float freq_r = 0.5 / max(u_freq * 0.125, 0.01); + float disc = 1.0 - smoothstep(freq_r - 0.005, freq_r + 0.005, r); + col = vec3(disc); + + } else { + // Checkerboard + vec2 cell = floor(uv * u_freq + u_phase); + float chk = mod(cell.x + cell.y, 2.0); + col = vec3(chk); + } + + fragColor = vec4(col, 1.0); +} diff --git a/assets/shaders/proc-amp.frag b/assets/shaders/proc-amp.frag new file mode 100644 index 0000000..b65c312 --- /dev/null +++ b/assets/shaders/proc-amp.frag @@ -0,0 +1,72 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: proc-amp.frag +// +// Proc Amp (Processing Amplifier) +// Broadcast-standard video processing: brightness, contrast, saturation, hue. +// Reference: Extron, Kramer, and analog video sync processors. +// +// In analog video hardware, a proc amp regenerates sync, adjusts luma +// amplitude (brightness/contrast) and adjusts chroma (saturation/hue). +// This shader replicates the signal-chain math in GLSL. +// +// Node role: Processor (iChannel0 → out) +// Uniforms: +// u_brightness [-1, 1] additive luma offset default: 0.0 +// u_contrast [0, 3] luma gain multiplier default: 1.0 +// u_saturation [0, 3] chroma amplitude scale default: 1.0 +// u_hue [-180,180] hue rotation in degrees default: 0.0 +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform sampler2D iChannel0; +uniform float u_brightness; // additive offset to luma +uniform float u_contrast; // luma gain (1.0 = unity) +uniform float u_saturation; // chroma scale (1.0 = unity) +uniform float u_hue; // hue rotation in degrees + +// ── RGB ↔ YIQ (NTSC colour space) ──────────────────────────────────────── +// YIQ separates luma (Y) from chroma (I,Q) exactly as broadcast proc amps do. +// Using YIQ rather than HSV matches analog hardware more closely. + +vec3 rgb_to_yiq(vec3 c) { + return vec3( + 0.2990 * c.r + 0.5870 * c.g + 0.1140 * c.b, + 0.5959 * c.r - 0.2746 * c.g - 0.3213 * c.b, + 0.2115 * c.r - 0.5227 * c.g + 0.3112 * c.b + ); +} + +vec3 yiq_to_rgb(vec3 yiq) { + return clamp(vec3( + yiq.x + 0.9563 * yiq.y + 0.6210 * yiq.z, + yiq.x - 0.2721 * yiq.y - 0.6474 * yiq.z, + yiq.x - 1.1070 * yiq.y + 1.7046 * yiq.z + ), 0.0, 1.0); +} + +void main() { + vec4 src = texture(iChannel0, v_uv); + vec3 yiq = rgb_to_yiq(src.rgb); + + // 1. Contrast: scale luma around mid-grey (0.5), matching analog gain stage + yiq.x = (yiq.x - 0.5) * u_contrast + 0.5; + + // 2. Brightness: additive luma offset (IRE shift) + yiq.x = yiq.x + u_brightness; + + // 3. Saturation: scale chroma vector amplitude + yiq.yz *= u_saturation; + + // 4. Hue rotation: rotate the IQ chroma vector + // Hardware hue control rotates the colour subcarrier phase + float angle = radians(u_hue); + float cosA = cos(angle); + float sinA = sin(angle); + float i = yiq.y * cosA - yiq.z * sinA; + float q = yiq.y * sinA + yiq.z * cosA; + yiq.yz = vec2(i, q); + + fragColor = vec4(yiq_to_rgb(yiq), src.a); +} diff --git a/assets/shaders/ramp-generator.frag b/assets/shaders/ramp-generator.frag new file mode 100644 index 0000000..5fd2095 --- /dev/null +++ b/assets/shaders/ramp-generator.frag @@ -0,0 +1,71 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: ramp-generator.frag +// +// Ramp Generator +// Produces geometric voltage ramps — the foundational waveform of analog +// video synthesis. LZX Visionary, Cadet I (H Ramp), Cadet II (V Ramp). +// +// A "ramp" is a linearly increasing voltage that sweeps across the frame, +// used as a control voltage (CV) input to modulators, oscillators, and +// keyers to create geometric shapes and patterns. +// +// Four ramp types selectable via u_mode: +// 0 = horizontal (left→right) +// 1 = vertical (top→bottom) +// 2 = radial (centre→edge, circular) +// 3 = angular (rotation around centre, 0–1 = 0°–360°) +// +// Node role: Source (no input — generates signal) +// Uniforms: +// u_mode [0,3] ramp type default: 0 +// u_freq [0.1,16] tile frequency default: 1.0 +// u_phase [0,1] phase offset default: 0.0 +// u_invert [0,1] invert output default: 0.0 +// u_center_x [0,1] centre X for radial/angular default: 0.5 +// u_center_y [0,1] centre Y for radial/angular default: 0.5 +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform float u_mode; +uniform float u_freq; +uniform float u_phase; +uniform float u_invert; +uniform float u_center_x; +uniform float u_center_y; + +void main() { + vec2 uv = v_uv; + vec2 ctr = vec2(u_center_x, u_center_y); + int mode = int(u_mode); + + float ramp; + + if (mode == 0) { + // Horizontal: left→right sawtooth + ramp = fract(uv.x * u_freq + u_phase); + + } else if (mode == 1) { + // Vertical: top→bottom sawtooth + ramp = fract(uv.y * u_freq + u_phase); + + } else if (mode == 2) { + // Radial: distance from centre, tiled + // Matches LZX Cadet III / Visual Cortex radial output + float dist = length(uv - ctr) * 2.0; // 0 at centre, 1 at corners + ramp = fract(dist * u_freq + u_phase); + + } else { + // Angular: angle around centre, 0→1 = 0°→360° + // Matches LZX phase/angle modulation inputs + vec2 d = uv - ctr; + float angle = atan(d.y, d.x); // -π to π + ramp = fract((angle / (2.0 * 3.14159265)) + 0.5 + u_phase); + } + + // Optional invert (flip the ramp polarity — common analog patch) + ramp = mix(ramp, 1.0 - ramp, u_invert); + + fragColor = vec4(ramp, ramp, ramp, 1.0); +} diff --git a/assets/shaders/vectorscope.frag b/assets/shaders/vectorscope.frag new file mode 100644 index 0000000..a57a91a --- /dev/null +++ b/assets/shaders/vectorscope.frag @@ -0,0 +1,117 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: vectorscope.frag +// +// Vectorscope +// Plots each pixel as a point on a Cb/Cr (or IQ) colour plane. +// The angle = hue, the radius = saturation. Used for calibrating colour +// balance, checking skin tone line, and legality in broadcast work. +// +// Reference: Tektronix 1760 vectorscope, TSL PTM001, Leader VC-5800. +// +// The vectorscope reads the entire source frame and accumulates each pixel's +// chroma onto the plot plane — bright spots = common colours in the image. +// Reference targets (colour bar dots) are shown at their standard positions. +// +// Node role: Processor (iChannel0 = signal to analyse) +// Uniforms: +// u_gain [0.1, 4] plot gain (brighten sparse plots) default: 1.0 +// u_graticule [0, 1] show reference targets and axes default: 1.0 +// u_skin_line [0, 1] show the broadcast skin-tone line default: 1.0 +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform sampler2D iChannel0; +uniform float u_gain; +uniform float u_graticule; +uniform float u_skin_line; +uniform vec2 uResolution; + +// RGB → YCbCr (BT.601 digital) +vec3 rgb_to_ycbcr(vec3 c) { + float y = 0.2990 * c.r + 0.5870 * c.g + 0.1140 * c.b; + float cb = -0.16875 * c.r - 0.33126 * c.g + 0.50001 * c.b; + float cr = 0.50001 * c.r - 0.41869 * c.g - 0.08132 * c.b; + return vec3(y, cb, cr); +} + +// Draw a small dot at a given Cb/Cr position +float dot_at(vec2 uv_plot, vec2 target, float size) { + return smoothstep(size, size * 0.5, length(uv_plot - target)); +} + +void main() { + // Map this fragment's UV to the Cb/Cr plot plane [-0.5, 0.5] + vec2 uv_plot = (v_uv - 0.5); // -0.5 to 0.5 + + // ── Accumulate: sample the source image to plot chroma points ───────── + // We sample a grid of source pixels and check if their Cb/Cr falls + // near this plot position. This is a simplified single-pass approach. + float intensity = 0.0; + const int SAMPLES = 32; + for (int i = 0; i < SAMPLES; i++) { + for (int j = 0; j < SAMPLES; j++) { + vec2 src_uv = vec2(float(i), float(j)) / float(SAMPLES); + vec3 ycbcr = rgb_to_ycbcr(texture(iChannel0, src_uv).rgb); + vec2 chroma = ycbcr.yz; // Cb, Cr + + // Distance from this chroma point to current plot position + float d = length(chroma - uv_plot); + // Gaussian accumulation + intensity += exp(-d * d * uResolution.x * 0.5 / u_gain); + } + } + intensity = clamp(intensity / float(SAMPLES * SAMPLES) * u_gain * 8.0, 0.0, 1.0); + + // Plot colour: false colour based on chroma angle (hue) of this plot position + float hue_angle = atan(uv_plot.y, uv_plot.x); + float hue_norm = hue_angle / (2.0 * 3.14159265) + 0.5; + float r = clamp(abs(mod(hue_norm * 6.0 - 3.0, 6.0) - 3.0) - 1.0, 0.0, 1.0); + float g = clamp(2.0 - abs(mod(hue_norm * 6.0 - 2.0, 6.0) - 2.0), 0.0, 1.0); + float b = clamp(2.0 - abs(mod(hue_norm * 6.0 - 4.0, 6.0) - 2.0), 0.0, 1.0); + vec3 plot_col = mix(vec3(intensity), vec3(r, g, b) * intensity, 0.7); + + // ── Graticule ───────────────────────────────────────────────────────── + float grat = 0.0; + if (u_graticule > 0.5) { + // Crosshairs (axes) + float axis_w = 0.003; + grat += smoothstep(axis_w, 0.0, abs(uv_plot.x)); + grat += smoothstep(axis_w, 0.0, abs(uv_plot.y)); + + // Outer circle at 0.5 radius (100% saturation) + float ring_r = length(uv_plot); + grat += smoothstep(0.005, 0.0, abs(ring_r - 0.5)) * 0.4; + // Inner circle at 0.25 (50% saturation) + grat += smoothstep(0.005, 0.0, abs(ring_r - 0.25)) * 0.2; + + // SMPTE colour bar reference targets (Cb/Cr positions) + // Yellow, Cyan, Green, Magenta, Red, Blue + grat += dot_at(uv_plot, vec2(-0.166, 0.417), 0.015) * 0.8; // Yellow + grat += dot_at(uv_plot, vec2(-0.425, -0.095), 0.015) * 0.8; // Cyan + grat += dot_at(uv_plot, vec2(-0.259, -0.512), 0.015) * 0.8; // Green + grat += dot_at(uv_plot, vec2( 0.259, 0.512), 0.015) * 0.8; // Magenta + grat += dot_at(uv_plot, vec2( 0.425, 0.095), 0.015) * 0.8; // Red + grat += dot_at(uv_plot, vec2( 0.166, -0.417), 0.015) * 0.8; // Blue + + grat = clamp(grat, 0.0, 0.5); + } + + // ── Skin tone line ──────────────────────────────────────────────────── + float skin = 0.0; + if (u_skin_line > 0.5) { + // Broadcast skin tone line: approximately 123° hue in IQ / YCbCr + // Represented as a line from origin at this angle + vec2 skin_dir = normalize(vec2(0.382, -0.261)); // ~123° in Cb/Cr + float d_skin = abs(dot(uv_plot, vec2(-skin_dir.y, skin_dir.x))); + float along = dot(uv_plot, skin_dir); + skin = smoothstep(0.006, 0.0, d_skin) * step(0.0, along) * 0.6; + } + + vec3 final_col = plot_col + + vec3(0.15, 0.15, 0.15) * grat + + vec3(1.0, 0.8, 0.3) * skin; + + fragColor = vec4(clamp(final_col, 0.0, 1.0), 1.0); +} diff --git a/assets/shaders/waveform-monitor.frag b/assets/shaders/waveform-monitor.frag new file mode 100644 index 0000000..cd918a1 --- /dev/null +++ b/assets/shaders/waveform-monitor.frag @@ -0,0 +1,95 @@ +// ───────────────────────────────────────────────────────────────────────────── +// scheng shader: waveform-monitor.frag +// +// Waveform Monitor +// Displays the luma or RGB signal level of each column of pixels as a +// waveform trace, exactly as a broadcast waveform monitor does. +// +// Reference: Tektronix 1740 series, Leader LV-5800, broadcast QC practice. +// Used for checking IRE levels, clipping, luma distribution, and legality. +// +// Mode 0 = Luma parade (single trace, grey on black) +// Mode 1 = RGB parade (R, G, B traces side by side, false colour) +// Mode 2 = Overlay (all traces overlaid on the input image) +// +// Node role: Processor (iChannel0 = signal to analyse) +// Uniforms: +// u_mode [0, 2] display mode default: 0 +// u_intensity [0, 1] trace brightness default: 0.8 +// u_persistence [0,32] vertical trace thickness default: 1.5 +// ───────────────────────────────────────────────────────────────────────────── +#version 330 core +in vec2 v_uv; +out vec4 fragColor; + +uniform sampler2D iChannel0; +uniform float u_mode; +uniform float u_intensity; +uniform float u_persistence; +uniform vec2 uResolution; + +void main() { + int mode = int(u_mode); + vec2 uv = v_uv; + float px_h = 1.0 / uResolution.y; // one pixel in UV space + + // ── Mode 0/1: Parade modes ──────────────────────────────────────────── + // Read the source pixel at (this column, scaled from waveform Y position) + // and check if the signal level falls at this Y position. + + // Map waveform Y: bottom = 0.0, top = 1.0 (IRE 0 to 100) + float wave_y = uv.y; // 0=black(bottom), 1=white(top) + + if (mode == 0) { + // Luma parade — single column analysis + // Sample the source at this X column, Y = 50% (representative row) + // then scatter the luma value vertically + vec2 src_uv = vec2(uv.x, 0.5); + vec3 src = texture(iChannel0, src_uv).rgb; + float luma = dot(src, vec3(0.2126, 0.7152, 0.0722)); + + // Trace: bright where |wave_y - luma| < thickness + float dist = abs(wave_y - luma); + float trace = exp(-dist * dist * uResolution.y / max(u_persistence, 0.1)); + float bright = trace * u_intensity; + + // Draw on black background + fragColor = vec4(bright, bright, bright, 1.0); + + } else if (mode == 1) { + // RGB parade — three columns (R left, G centre, B right) + float col_w = 1.0 / 3.0; + int col_idx = int(uv.x / col_w); + float col_uv_x = mod(uv.x, col_w) / col_w; // local X within each section + + // Sample source at this local X position + vec2 src_uv = vec2(col_uv_x, 0.5); + vec3 src = texture(iChannel0, src_uv).rgb; + + float level; + vec3 trace_col; + if (col_idx == 0) { + level = src.r; + trace_col = vec3(1.0, 0.2, 0.2); // Red trace + } else if (col_idx == 1) { + level = src.g; + trace_col = vec3(0.2, 1.0, 0.2); // Green trace + } else { + level = src.b; + trace_col = vec3(0.2, 0.5, 1.0); // Blue trace + } + + float dist = abs(wave_y - level); + float trace = exp(-dist * dist * uResolution.y / max(u_persistence, 0.1)); + fragColor = vec4(trace_col * trace * u_intensity, 1.0); + + } else { + // Overlay mode: draw source image with luma trace overlaid + vec4 src_px = texture(iChannel0, uv); + float luma = dot(src_px.rgb, vec3(0.2126, 0.7152, 0.0722)); + float dist = abs(wave_y - luma); + float trace = exp(-dist * dist * uResolution.y / max(u_persistence * 0.5, 0.1)); + vec3 overlay = mix(src_px.rgb, vec3(0.0, 1.0, 0.8), trace * u_intensity); + fragColor = vec4(overlay, 1.0); + } +} diff --git a/crates.zip b/crates.zip deleted file mode 100644 index 83dbec0..0000000 Binary files a/crates.zip and /dev/null differ diff --git a/crates/.DS_Store b/crates/.DS_Store index d882e9c..192a5b2 100644 Binary files a/crates/.DS_Store and b/crates/.DS_Store differ diff --git a/crates/SCHENG_DEV.md b/crates/SCHENG_DEV.md new file mode 100644 index 0000000..881dab6 --- /dev/null +++ b/crates/SCHENG_DEV.md @@ -0,0 +1,574 @@ +# scheng — Development Reference +> Living document. Update as work progresses. +> Last updated: 2026-03-15 + +--- + +## Project North Star + +**scheng** is a Rust SDK for building GPU-accelerated video synthesis instruments. +It provides a node-graph execution model, explicit shader contracts, and a modular I/O surface. +Instruments built on the SDK ship as cross-platform native applications via Tauri. +The shader layer is intentionally low-level — enabling developers to implement any signal +processing topology, including faithful reproductions of historical analog video synthesis hardware. + +**shadecore** = the proven single-binary reference implementation. Features that work there are the target spec for scheng's SDK crates. + +--- + +## The wgpu Backend Decision + +### Decision: Dual Backend — Keep glow, Add wgpu + +**Don't delete `scheng-runtime-glow`. Add `scheng-runtime-wgpu` alongside it.** + +This is not a migration — it's an extension. The architecture already supports it because +`scheng-runtime` is backend-agnostic. The `OutputSink` trait, `Plan`, `FrameCtx`, and +`NodeProps` don't change at all. + +--- + +### Why Keep glow + +- **shadecore validated it.** Syphon, Spout, NDI, FFmpeg, MIDI — everything works end-to-end in glow. +- **GLSL shaders run as-is.** Your entire shader library is native GLSL. No translation needed. +- **Linux/Windows still work fine with OpenGL 3.3+.** No deprecation concern there. +- **It's the regression baseline.** Contract tests run against it. Keep it green. + +--- + +### Why Add wgpu + +| Problem | glow | wgpu | +|---|---|---| +| macOS long-term | Deprecated (10.14+), translation layer | Metal native | +| Tauri embedding | Separate GL window, hostile | First-class via `wgpu-core` | +| Cross-platform one backend | No (GL quirks per platform) | Yes (Metal/DX12/Vulkan/WebGPU) | +| WebGPU/browser future | No | Yes (same API) | +| Shader language | GLSL only | WGSL + **GLSL via naga** | + +The GLSL → wgpu path works via **naga**, wgpu's shader translation library. Naga compiles +GLSL fragment shaders to WGSL/SPIR-V/Metal at load time. This means your existing shader +library stays GLSL — naga handles the translation transparently. + +``` +your .frag file (GLSL 330 core) + │ + ▼ naga::front::glsl + naga IR (intermediate) + │ + ├──▶ WGSL (wgpu native / WebGPU) + ├──▶ MSL (Metal / macOS / iOS) + ├──▶ HLSL (DX12 / Windows) + └──▶ SPIR-V (Vulkan / Linux) +``` + +**Known naga GLSL limitation:** naga's GLSL frontend targets GLSL 450 / Vulkan-style GLSL, +not the exact `#version 330 core` profile used in shadecore. The delta is small: +- `gl_FragCoord` works fine +- `texture()` calls work fine +- Extensions and legacy builtins may need minor adjustment +- The `v_uv` vertex output convention is compatible + +Mitigation: write a small compatibility header injected at the top of every shader before +naga compilation. This normalizes any 330→450 gaps transparently. + +--- + +### How wgpu Slots Into the Crate Structure + +No existing crate changes. One new crate added: + +``` +crates/ +├── scheng-core (unchanged) +├── scheng-graph (unchanged) +├── scheng-runtime (unchanged — abstract contracts) +├── scheng-runtime-glow (unchanged — proven baseline) +├── scheng-runtime-wgpu (NEW — Metal/DX12/Vulkan backend) +│ ├── Depends on: scheng-runtime, scheng-graph, wgpu +│ ├── Implements: execute_plan_to_sink() via wgpu render passes +│ ├── Manages: wgpu Device, Queue, bind groups, render pipelines +│ └── GLSL shaders compiled via naga at load time +├── scheng-bridge (unchanged) +... +``` + +**Cargo feature flags on the host binary:** + +```toml +[features] +default = ["backend-wgpu"] +gl = ["scheng-runtime-glow"] +wgpu = ["scheng-runtime-wgpu"] # default going forward +``` + +--- + +### Tauri Integration Model + +``` +Tauri Binary +├── Rust backend +│ ├── scheng-runtime-wgpu running on dedicated render thread +│ ├── wgpu renders into offscreen texture (not visible yet) +│ ├── OutputSink implementations: Syphon, Spout, NDI, FFmpeg +│ └── Tauri commands: set_param, load_graph, set_output_mode +│ +└── WebView (WKWebView on macOS, WebView2 on Windows) + ├── Instrument UI: parameter sliders, graph editor (future) + ├── Calls Tauri commands via invoke() + └── Receives preview frames via IPC (optional, downscaled JPEG/PNG) +``` + +The render loop runs entirely in native Rust. The WebView is a control surface only. +Preview frames can be sent via Tauri's event system as base64-encoded images at 15-30fps — +enough for monitoring without impacting GPU performance. + +--- + +### wgpu Backend Implementation Checklist + +- [ ] Add `scheng-runtime-wgpu` crate to workspace +- [ ] Add wgpu, naga as dependencies +- [ ] Implement `RenderTarget` (wgpu Texture + TextureView + Framebuffer equivalent) +- [ ] Implement shader compilation: GLSL → naga → wgpu `ShaderModule` +- [ ] Implement GLSL compat header (330→450 normalization) +- [ ] Implement uniform binding: uTime, uResolution, uFrame, custom u_ params +- [ ] Implement texture binding: iChannel0..iChannel3 via bind groups +- [ ] Implement `execute_plan_to_sink()` — iterate Plan, dispatch render passes +- [ ] Implement ping-pong buffers for PreviousFrame / Feedback nodes +- [ ] Port `OutputSink` implementations: preview window, Syphon, Spout +- [ ] Add contract tests running against wgpu backend (same golden fixtures) +- [ ] Feature-flag the backend selection in the host binary + +--- + +## Master Punchlist + +Status key: `[ ]` todo · `[~]` in progress · `[x]` done · `[!]` blocked + +--- + +### Phase 0 — Orientation & Baseline ✓ + +- [x] Read and understand scheng codebase + docs +- [x] Read and understand shadecore features +- [x] Map shadecore features → scheng SDK gaps +- [x] Make wgpu backend decision +- [ ] Verify `cargo build --workspace` passes clean on current main +- [ ] Verify `cargo test -p scheng-contract-tests` passes +- [ ] Verify shadecore `cargo run` produces a render window with default shader + +--- + +### Phase 1 — wgpu Backend (current focus) + +- [x] Create `crates/scheng-runtime-wgpu/` with Cargo.toml +- [~] Add to workspace Cargo.toml members list ← YOU ARE HERE: add "crates/scheng-runtime-wgpu" to members +- [x] Scaffold: lib.rs, context.rs, compat.rs, shader.rs, render_target.rs, uniforms.rs, pipeline.rs, executor.rs +- [x] Wire up wgpu Device + Queue initialization (headless) — context.rs +- [x] Implement GLSL compat header for naga — compat.rs (bindings 0-5, split texture/sampler, #define aliases) +- [~] Compile a single hardcoded frag shader through naga → wgpu pipeline ← verify with: cargo test test_naga_glsl_compile +- [~] Render one frame to offscreen texture — verify pixel readback ← cargo test test_single_node_renders +- [x] Implement execute_frame() with full Plan iteration — executor.rs (ShaderSource → PixelsOut) +- [x] Implement iChannel0..3 via bind groups (split texture+sampler, blank fallback) +- [x] Implement FrameBlock uniform buffer (uTime, uResolution, uFrame) — uniforms.rs +- [ ] Implement custom u_ uniform injection — Phase 1.2 +- [ ] Port PingPongTarget for Feedback/PreviousFrame nodes — Phase 1.3 +- [~] Run integration tests against wgpu backend ← cargo test -p scheng-runtime-wgpu -- --nocapture +- [~] CLI test: single shader, pixel readback ← cargo test test_custom_frag_shader + +--- + +### Phase 2 — Input Layer (port from shadecore) + +- [ ] `scheng-input-video`: audit current state, verify gapless decode → GL texture +- [ ] `scheng-input-video`: add wgpu texture upload path +- [ ] `scheng-input-webcam`: verify native feature builds on macOS + Windows +- [ ] `scheng-input-webcam`: wgpu upload path +- [ ] `scheng-input-midi`: new crate — port from shadecore's midir integration + - [ ] CoreMIDI on macOS + - [ ] cross-platform via midir + - [ ] JSON mapping: CC → NodeId + param name (matches shadecore params.json model) +- [ ] `scheng-input-ndi` (receive side): NDI frames → texture +- [ ] `scheng-input-syphon` (macOS receive): Syphon client → texture +- [ ] `scheng-input-spout` (Windows receive): Spout receiver → texture + +--- + +### Phase 3 — Output Layer (port from shadecore) + +- [ ] `scheng-output-syphon`: port syphon_bridge.m from shadecore, wire OutputSink +- [ ] `scheng-output-spout`: port C++ Spout2 bridge from shadecore, wire OutputSink +- [ ] `scheng-output-ffmpeg`: port recording.rs FFmpeg worker from shadecore + - [ ] Bounded queue model (drop frames rather than stall render) + - [ ] RTSP / RTMP streaming mode + - [ ] Local file recording mode + - [ ] JSON config: codec, bitrate, preset, framerate, resolution +- [ ] `scheng-output-ndi`: formalize NDI sender as OutputSink +- [ ] Headless / offscreen OutputSink (pixel readback only — for testing + CLI tools) + +--- + +### Phase 4 — Control Layer + +- [ ] Consolidate `scheng-control-osc` (exists, verify it works) +- [ ] Port MIDI from shadecore into `scheng-input-midi` +- [ ] `scrubbable_controls`: verify keymap.json / osc_map.json hot-reload +- [ ] State ownership model: match shadecore's authority table + - render.json → active shader + - params.json → parameter defaults + MIDI map + - output.json → output mode + hotkeys + - recording.json → recording profiles +- [ ] Hot-reload: file watcher → reload shader source without restart (port hotreload.rs) + +--- + +### Phase 5 — Tauri Shell + +- [ ] Create `crates/scheng-tauri` — the Tauri integration crate +- [ ] Embed scheng-runtime-wgpu in Tauri backend +- [ ] Define IPC command surface: + - `set_param(node_id, param_name, value: f32)` + - `load_graph(graph_json: String)` + - `set_output_mode(mode: String)` + - `get_params() -> NodePropsSnapshot` + - `start_recording() / stop_recording()` +- [ ] Preview frame IPC: readback → JPEG → Tauri event at 15fps +- [ ] Create minimal instrument template (fork-ready) +- [ ] Test: macOS `.app` build via `tauri build` +- [ ] Test: Windows `.exe` build via `tauri build` +- [ ] Test: Linux build via `tauri build` + +--- + +### Phase 6 — Shader Library (LZX-inspired) + +> Goal: reverse-engineer historical analog video synthesis hardware as GLSL shaders. +> Each shader = a documented module with its own params.json profile. + +- [ ] Colorizer (hue rotation, RGB→component, component→RGB) +- [ ] Ramp generator (horizontal, vertical, radial, angular) +- [ ] Key generator (luma key, chroma key, threshold) +- [ ] Hard keyer +- [ ] Soft keyer +- [ ] Fade to black / white +- [ ] Proc amp (brightness, contrast, saturation, hue) +- [ ] Sync processor (H-sync, V-sync simulation) +- [ ] Video feedback (ping-pong, convergence modes) +- [ ] Matrix mixer 4→1 (weighted sum) +- [ ] Crossfader (T-bar, soft cuts) +- [ ] Waveform monitor (parade, overlay) +- [ ] Vectorscope +- [ ] Pattern generator (color bars, test card, grid) + +--- + +### Phase 7 — GUI / Editor (deferred — design carefully) + +> Don't build until the SDK surface is stable and fully tested CLI-first. + +- [ ] Research: node graph editor options (egui, iced, or Tauri-based) +- [ ] Define the prototyping → export workflow +- [ ] Define the JSON graph format for save/load +- [ ] Design the parameter UI contract (sliders, toggles, XY pads) +- [ ] Design the preview display model in GUI context + +--- + +## CLI Testing Guide + +### Prerequisites + +```bash +# Install Rust stable +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# macOS: Xcode CLI tools +xcode-select --install + +# macOS + Syphon output: place Syphon.framework at vendor/Syphon.framework +# Windows + Spout: C++ build tools required +``` + +### 1. Build everything + +```bash +cd scheng +cargo build --workspace +``` + +Expected: all crates compile, no errors. + +### 2. Run contract tests + +```bash +cargo test -p scheng-contract-tests +``` + +Expected: all golden fixture tests pass. These pin the public SDK surface. +If these fail after any change, the SDK has a regression. + +### 3. Run all tests + +```bash +cargo test --workspace +``` + +### 4. Run the bridge (engine + browser editor) + +```bash +cargo run -p scheng-bridge + +# With debug logging: +RUST_LOG=scheng_bridge=debug cargo run -p scheng-bridge + +# Custom WebSocket address: +SCHENG_BRIDGE_ADDR=0.0.0.0:7777 cargo run -p scheng-bridge +``` + +Then open `crates/scheng-bridge/scheng-editor.html` in Chrome/Firefox → Connect → load a template → Compile. + +### 5. Test OSC control + +```bash +# Start bridge in one terminal +cargo run -p scheng-bridge + +# Send OSC from another terminal (requires oscsend or similar) +oscsend 127.0.0.1 9000 /scheng/node/xfad/uniform/u_tbar f 0.75 +oscsend 127.0.0.1 9000 /scheng/node/key/uniform/u_thresh f 0.35 +``` + +### 6. Test contract tests with verbose output + +```bash +cargo test -p scheng-contract-tests -- --nocapture +``` + +### 7. Validate sdk-compat (API regression check) + +```bash +cargo build -p sdk-compat +``` + +This is a compile-only witness. If it doesn't compile, the public API has broken. + +### 8. Build with Syphon (macOS only) + +```bash +cargo build -p scheng-runtime-glow --features syphon +``` + +### 9. Build with webcam input + +```bash +cargo build -p scheng-input-webcam --features native +``` + +--- + +## shadecore Reference Commands + +> shadecore is the proven single-binary reference. Use these to verify features work +> before porting to scheng. + +```bash +cd shadecore + +# Standard run +cargo run + +# With NDI output +cargo run --features ndi + +# Observe render hotkeys at runtime: +# 1 = preview only +# 2 = Syphon (macOS) +# 3 = Spout (Windows) +# 4 = FFmpeg stream +# 6 = NDI +``` + +--- + +## Key Architecture Rules (Never Violate) + +1. **The engine does not own time.** FrameCtx is always supplied by the host. +2. **Topology is static after compile().** NodeProps is dynamic. Structural changes require recompile. +3. **No layer reaches backward.** Graph → Runtime → Backend. Never reverse. +4. **Control updates NodeProps only.** OSC/MIDI never touches the graph or render loop directly. +5. **The runtime is single-threaded.** GL/wgpu context must be on the calling thread. +6. **Errors are returned immediately.** No silent recovery anywhere in the engine. +7. **Optional features are truly optional.** Syphon, Spout, webcam never bleed into core crates. + +--- + +## GLSL Shader Contract (v_uv convention) + +All fragment shaders must follow this interface: + +```glsl +#version 330 core +in vec2 v_uv; // lowercase — matches vertex shader out +out vec4 fragColor; + +uniform sampler2D iChannel0; // input textures, up to iChannel3 +uniform float uTime; // seconds since start +uniform vec2 uResolution; // output dimensions +uniform int uFrame; // monotonic frame counter + +// Custom params — u_ prefix by convention +uniform float u_myParam; +``` + +Port mappings → texture units: +| Port name | iChannel | +|---|---| +| `in`, `in0`, `a`, `src` | iChannel0 | +| `in1`, `b`, `src1` | iChannel1 | +| `in2`, `c`, `src2` | iChannel2 | +| `in3`, `d`, `src3` | iChannel3 | + +--- + +## Dependency Graph (current) + +``` +scheng-editor.html + │ WebSocket JSON + ▼ +scheng-bridge (tokio + tungstenite) + graph_manager.rs + │ + ├── scheng-runtime-glow (OpenGL / glow) ← proven baseline + │ ├── scheng-runtime (abstract ops, params, banks) + │ │ └── scheng-graph (node/port/edge/plan) + │ │ └── scheng-core (error, config, events) + │ └── scheng-input-video + │ + └── [scheng-runtime-wgpu] ← to be added (Metal/DX12/Vulkan) + +scheng-passes (ping-pong, temporal ring) +scheng-buffers (GPU ring buffer) +scheng-host-winit (window + GL context) +scheng-input-webcam (camera, optional) +scheng-control-osc (UDP OSC) +scrubbable_controls (keyboard + OSC layer) +scheng-contract-tests +sdk-compat +``` + +--- + +## Open Questions (resolve before building) + +- [ ] **naga GLSL compatibility**: Run a shadecore shader through naga today. Document any + adjustments needed for the compat header. +- [ ] **wgpu + winit on macOS**: Verify wgpu + winit event loop works without the OpenGL + path. The winit version in scheng-host-winit may need updating. +- [ ] **Tauri + wgpu surface sharing**: Can wgpu render into a surface that Tauri's WebView + can composite over, or does the preview need to go through IPC frames? + Research: `wgpu` + `raw-window-handle` + Tauri's `setup` hook. +- [ ] **shadecore Vosk resources**: Speech recognition library present in repo. + Intentional? If yes, add `scheng-input-voice` to Phase 2. +- [ ] **Max/MSP integration**: `max/` directory in scheng repo. Define the IPC boundary. + +--- + +## Phase 1 Integration Notes + +### Files Created in This Session + +``` +crates/scheng-runtime-wgpu/ +├── Cargo.toml ← dependencies: wgpu=22, naga=22, bytemuck, pollster, regex +├── src/ +│ ├── lib.rs ← public API, re-exports, WgpuError enum +│ ├── context.rs ← WgpuContext::new() — headless Device+Queue init +│ ├── compat.rs ← GLSL 330→450 preprocessor (strip, rewrite, inject header) +│ ├── shader.rs ← ShaderCache: GLSL→naga→wgpu::ShaderModule +│ ├── render_target.rs ← RenderTarget: offscreen RGBA8 texture + readback +│ ├── uniforms.rs ← UniformManager: FrameBlock buffer (uTime/uResolution/uFrame) +│ ├── pipeline.rs ← PipelineCache: RenderPipeline per shader hash +│ └── executor.rs ← WgpuRuntime::execute_frame(), OutputSink trait, NodeConfig +└── tests/ + └── headless.rs ← 8 integration tests (compat, naga, GPU rendering, readback) +``` + +### Step 1: Add to Workspace + +In the root `Cargo.toml`, add to the `[workspace]` members list: +```toml +[workspace] +members = [ + "crates/scheng-core", + "crates/scheng-graph", + "crates/scheng-runtime", + "crates/scheng-runtime-glow", + "crates/scheng-runtime-wgpu", # ← ADD THIS + # ... other crates +] +``` + +### Step 2: Verify Plan API (CRITICAL — do this before running tests) + +The executor uses `plan.node_ids()`, `plan.node_kind(id)`, and `plan.inputs_for(id)`. +These method names are assumed from the README. Check the actual scheng-graph source: + +```bash +grep -n "pub fn" crates/scheng-graph/src/lib.rs +``` + +Adjust `executor.rs` to match. The three key calls: +- Iterating plan nodes in order +- Getting a node's `NodeKind` +- Getting the upstream `(NodeId, channel_index)` pairs for a node's inputs + +### Step 3: Run the CPU-only tests first (no GPU needed) + +```bash +cargo test -p scheng-runtime-wgpu test_compat_preprocessing -- --nocapture +cargo test -p scheng-runtime-wgpu test_naga_glsl_compile -- --nocapture +``` + +These must pass before any GPU tests. If `test_naga_glsl_compile` fails: +- The naga GLSL 450 source in compat.rs has a syntax error +- Check the processed source printed by `--nocapture` for the exact error line + +### Step 4: Run GPU tests + +```bash +cargo test -p scheng-runtime-wgpu -- --nocapture +``` + +If no adapter is found in CI: `WGPU_BACKEND=gl cargo test -p scheng-runtime-wgpu` + +### Step 5: Fix `once_cell` dependency + +`compat.rs` uses `once_cell::sync::Lazy` for compiled regexes. +Add to Cargo.toml if not already present in workspace: +```toml +once_cell = "1" +``` +Or replace with `std::sync::OnceLock` (Rust 1.70+): +```rust +use std::sync::OnceLock; +static RE_VERSION: OnceLock = OnceLock::new(); +// in the function: +RE_VERSION.get_or_init(|| Regex::new(...).unwrap()) +``` + +### Known Phase 1 Gaps (to fix in 1.2/1.3) + +| Gap | Location | Phase | +|---|---|---| +| Custom u_ uniforms stripped with warning | compat.rs, executor.rs | 1.2 | +| plan.inputs_for() API needs verification | executor.rs line ~140 | NOW | +| plan.node_kind() API needs verification | executor.rs line ~105 | NOW | +| Y-axis flip vs OpenGL convention | render_target.rs, shader.rs | 1.5 | +| Vertex module created per-pipeline (wasteful) | pipeline.rs | 1.2 | +| PingPong / Feedback node support | executor.rs | 1.3 | +| Hot-reload: recompile on source change | executor.rs | Phase 4 | +| scheng-runtime NodeProps integration | executor.rs NodeConfig | After API verified | diff --git a/crates/scheng-control-osc-wgpu/.DS_Store b/crates/scheng-control-osc-wgpu/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/crates/scheng-control-osc-wgpu/.DS_Store differ diff --git a/crates/scheng-control-osc-wgpu/Cargo.toml b/crates/scheng-control-osc-wgpu/Cargo.toml new file mode 100644 index 0000000..3a4c86a --- /dev/null +++ b/crates/scheng-control-osc-wgpu/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "scheng-control-osc-wgpu" +version = "0.1.0" +edition = "2021" +description = "OSC → ParamStore adapter for scheng-runtime-wgpu" +license = "MIT" + +# NOTE: This crate wires the existing scheng-control-osc receiver (rosc-based) +# to ParamStore. It is a thin adapter — all OSC receive logic lives in +# scheng-control-osc; this crate only adds the ParamStore routing layer. + +[dependencies] +scheng-param-store = { path = "../scheng-param-store" } + +# rosc: OSC packet parsing (same as scheng-control-osc) +rosc = "0.10" +log = "0.4" +thiserror = "1" diff --git a/crates/scheng-control-osc-wgpu/src/lib.rs b/crates/scheng-control-osc-wgpu/src/lib.rs new file mode 100644 index 0000000..6e16dbe --- /dev/null +++ b/crates/scheng-control-osc-wgpu/src/lib.rs @@ -0,0 +1,48 @@ +//! `scheng-control-osc-wgpu` +//! +//! OSC UDP receiver that writes to `ParamStore`. Runs non-blocking +//! via `poll()` called each frame in the render loop. +//! +//! # Address conventions (matches scheng-control-osc and shadecore) +//! +//! Full form: +//! `/scheng/node//uniform/ ` +//! +//! Short form (param name only): +//! `/param/ ` +//! `/ ` +//! +//! Both forms are resolved to the param name in the schema. +//! +//! # Quick start +//! +//! ```rust,ignore +//! use scheng_control_osc_wgpu::OscReceiver; +//! +//! let mut osc = OscReceiver::bind("127.0.0.1:9000").unwrap(); +//! +//! // In render loop (each frame): +//! osc.poll(&mut store); +//! store.step_frame(); +//! ``` +//! +//! # OSC address tooltip convention +//! +//! Each param in params.json should have an `osc_addr` field, e.g.: +//! `"osc_addr": "/scheng/node/proc/uniform/u_brightness"` +//! +//! This address is shown as a tooltip in the editor and used for routing here. + +pub mod receiver; +pub use receiver::OscReceiver; + +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum OscError { + #[error("Failed to bind UDP socket to '{addr}': {source}")] + Bind { addr: String, #[source] source: std::io::Error }, + + #[error("OSC packet parse error: {0}")] + Parse(String), +} diff --git a/crates/scheng-control-osc-wgpu/src/receiver.rs b/crates/scheng-control-osc-wgpu/src/receiver.rs new file mode 100644 index 0000000..835c5e1 --- /dev/null +++ b/crates/scheng-control-osc-wgpu/src/receiver.rs @@ -0,0 +1,157 @@ +//! `receiver.rs` — non-blocking UDP OSC receiver writing to ParamStore. + +use std::net::UdpSocket; + +use rosc::{OscMessage, OscPacket, OscType}; +use scheng_param_store::ParamStore; + +use crate::OscError; + +/// Non-blocking OSC UDP receiver. +/// +/// Drain all pending messages each frame with `poll()`. +/// Compatible with the scheng-control-osc address convention. +pub struct OscReceiver { + socket: UdpSocket, + buf: Vec, +} + +impl OscReceiver { + /// Bind to a UDP address (e.g. `"127.0.0.1:9000"`). + pub fn bind(addr: &str) -> Result { + let socket = UdpSocket::bind(addr) + .map_err(|e| OscError::Bind { addr: addr.into(), source: e })?; + socket.set_nonblocking(true) + .map_err(|e| OscError::Bind { addr: addr.into(), source: e })?; + log::info!("OSC receiver bound to {}", addr); + Ok(Self { socket, buf: vec![0u8; 4096] }) + } + + /// Drain all pending OSC messages and apply them to the ParamStore. + /// + /// Call once per frame before `store.step_frame()`. + /// Returns the number of messages processed this frame. + pub fn poll(&mut self, store: &mut ParamStore) -> usize { + let mut count = 0; + loop { + match self.socket.recv_from(&mut self.buf) { + Ok((len, _src)) => { + if let Ok(packet) = rosc::decoder::decode_udp(&self.buf[..len]) { + count += dispatch_packet(&packet.1, store); + } + } + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => break, + Err(e) => { + log::warn!("OSC recv error: {}", e); + break; + } + } + } + count + } + + /// The local address this receiver is bound to. + pub fn local_addr(&self) -> String { + self.socket.local_addr() + .map(|a| a.to_string()) + .unwrap_or_else(|_| "unknown".into()) + } +} + +// ── Packet dispatch ─────────────────────────────────────────────────────── + +fn dispatch_packet(packet: &OscPacket, store: &mut ParamStore) -> usize { + match packet { + OscPacket::Message(msg) => dispatch_message(msg, store) as usize, + OscPacket::Bundle(bundle) => { + bundle.content.iter() + .map(|p| dispatch_packet(p, store)) + .sum() + } + } +} + +fn dispatch_message(msg: &OscMessage, store: &mut ParamStore) -> bool { + let Some(value) = extract_float(&msg.args) else { + log::trace!("OSC: no float arg in {}", msg.addr); + return false; + }; + + let param_name = resolve_addr(&msg.addr); + log::trace!("OSC: {} → '{}' = {:.4}", msg.addr, param_name, value); + + match store.set_by_osc_addr(&msg.addr, value) + .or_else(|_| store.set_by_name(param_name, value)) + { + Ok(()) => true, + Err(e) => { + log::trace!("OSC: unrouted message {} — {}", msg.addr, e); + false + } + } +} + +/// Extract the first float-like argument from an OSC message. +/// Coerces Int, Long, and Double to f32 — matches scheng-control-osc behaviour. +fn extract_float(args: &[OscType]) -> Option { + args.first().and_then(|a| match a { + OscType::Float(f) => Some(*f), + OscType::Double(d) => Some(*d as f32), + OscType::Int(i) => Some(*i as f32), + OscType::Long(l) => Some(*l as f32), + _ => None, + }) +} + +/// Resolve an OSC address to a param name. +/// +/// Supported forms: +/// - `/scheng/node/