diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..824f010 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,38 @@ +name: pages + +on: + push: + branches: + - main + +permissions: + contents: write + +env: + CARGO_TERM_COLOR: always + +jobs: + gh_pages: + name: Github Pages + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Setup toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + + - name: Make docs + run: cargo run -p make + + - name: Deploy + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: gh-pages + folder: target/shader_docs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fdd8195 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/.todo diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f1e440f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1707 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "askama" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" +dependencies = [ + "askama_derive", + "askama_escape", + "humansize", + "num-traits", + "percent-encoding", +] + +[[package]] +name = "askama_derive" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a0fc7dcf8bd4ead96b1d36b41df47c14beedf7b0301fc543d8f2384e66a2ec0" +dependencies = [ + "askama_parser", + "basic-toml", + "mime", + "mime_guess", + "proc-macro2", + "quote", + "serde", + "syn", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "askama_parser" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c268a96e01a4c47c8c5c2472aaa570707e006a875ea63e819f75474ceedaf7b4" +dependencies = [ + "nom", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "basic-toml" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f2139706359229bfa8f19142ac1155b4b80beafb7a60471ac5dd109d4a19778" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[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 = "compiler" +version = "0.1.0" +dependencies = [ + "cargo_metadata", + "docs", + "flate2", + "naga 0.12.3", + "naga 0.13.0", + "naga 0.14.1", + "naga_oil 0.10.1", + "naga_oil 0.11.0", + "naga_oil 0.8.2", + "regex", + "reqwest", + "tar", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "docs" +version = "0.1.0" +dependencies = [ + "indexmap 2.1.0", + "semver", +] + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "filetime" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "windows-sys 0.48.0", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generator" +version = "0.1.0" +dependencies = [ + "askama", + "docs", + "serde", + "serde_json", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "make" +version = "0.1.0" +dependencies = [ + "compiler", + "docs", + "generator", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "naga" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbcc2e0513220fd2b598e6068608d4462db20322c0e77e47f6f488dfcfc279cb" +dependencies = [ + "bit-set", + "bitflags 1.3.2", + "codespan-reporting", + "hexf-parse", + "indexmap 1.9.3", + "log", + "num-traits", + "pp-rs", + "rustc-hash", + "termcolor", + "thiserror", + "unicode-xid", +] + +[[package]] +name = "naga" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ceaaa4eedaece7e4ec08c55c640ba03dbb73fb812a6570a59bcf1930d0f70e" +dependencies = [ + "bit-set", + "bitflags 2.4.1", + "codespan-reporting", + "hexf-parse", + "indexmap 1.9.3", + "log", + "num-traits", + "pp-rs", + "rustc-hash", + "termcolor", + "thiserror", + "unicode-xid", +] + +[[package]] +name = "naga" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cd05939c491da968a42986204b7431678be21fdcd4b10cc84997ba130ada5a4" +dependencies = [ + "bit-set", + "bitflags 2.4.1", + "codespan-reporting", + "hexf-parse", + "indexmap 2.1.0", + "log", + "num-traits", + "pp-rs", + "rustc-hash", + "termcolor", + "thiserror", + "unicode-xid", +] + +[[package]] +name = "naga_oil" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be942a5c21c58b9b0bf4d9b99db3634ddb7a916f8e1d1d0b71820cc4150e56b" +dependencies = [ + "bit-set", + "codespan-reporting", + "data-encoding", + "indexmap 1.9.3", + "naga 0.12.3", + "once_cell", + "regex", + "regex-syntax 0.6.29", + "rustc-hash", + "thiserror", + "tracing", + "unicode-ident", +] + +[[package]] +name = "naga_oil" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac54c77b3529887f9668d3dd81e955e58f252b31a333f836e3548c06460b958" +dependencies = [ + "bit-set", + "codespan-reporting", + "data-encoding", + "indexmap 1.9.3", + "naga 0.13.0", + "once_cell", + "regex", + "regex-syntax 0.7.5", + "rustc-hash", + "thiserror", + "tracing", + "unicode-ident", +] + +[[package]] +name = "naga_oil" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff3f369dd665ee365daeab786466a6f70ff53e4a95a76117363b1077e1b0492" +dependencies = [ + "bit-set", + "codespan-reporting", + "data-encoding", + "indexmap 2.1.0", + "naga 0.14.1", + "once_cell", + "regex", + "regex-syntax 0.7.5", + "rustc-hash", + "thiserror", + "tracing", + "unicode-ident", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl" +version = "0.10.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[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 = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.4.1", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2 0.5.5", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "xattr" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +dependencies = [ + "libc", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a848b85 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +resolver = "2" +members = ["crates/*"] diff --git a/crates/compiler/Cargo.toml b/crates/compiler/Cargo.toml new file mode 100644 index 0000000..67f4d49 --- /dev/null +++ b/crates/compiler/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "compiler" +version = "0.1.0" +edition = "2021" + +[dependencies] +docs = { path = "../docs" } + +reqwest = { version = "0.11.22", features = ["blocking"] } +cargo_metadata = "0.18.1" +tar = "0.4.40" +flate2 = "1.0.28" +regex = "1.10.2" + +naga_oil_v0_11 = { package = "naga_oil", version = "0.11.0", optional = true } +naga_oil_v0_10 = { package = "naga_oil", version = "0.10.1", optional = true } +naga_oil_v0_08 = { package = "naga_oil", version = "0.8.2", optional = true } + +naga_v0_14 = { package = "naga", version = "0.14.1", optional = true } +naga_v0_13 = { package = "naga", version = "0.13.0", optional = true } +naga_v0_12 = { package = "naga", version = "0.12.3", optional = true } + +[features] +default = [] + +backend_v0_11 = ["dep:naga_oil_v0_11", "dep:naga_v0_14"] # ... +backend_v0_10 = ["dep:naga_oil_v0_10", "dep:naga_v0_13"] # bevy 0.12.x +backend_v0_08 = ["dep:naga_oil_v0_08", "dep:naga_v0_12"] # bevy 0.11.x diff --git a/crates/compiler/src/backend_v0_08/mod.rs b/crates/compiler/src/backend_v0_08/mod.rs new file mode 100644 index 0000000..a04e617 --- /dev/null +++ b/crates/compiler/src/backend_v0_08/mod.rs @@ -0,0 +1,249 @@ +mod to_wgsl; +mod util; + +use naga_oil_v0_08 as naga_oil; +use naga_v0_12 as naga; + +use crate::{common, download::ShaderSource}; +use docs::*; +use naga::TypeInner; +use naga_oil::compose::{ + self, ComposableModuleDescriptor, Composer, ImportDefinition, NagaModuleDescriptor, + ShaderLanguage, +}; +use std::collections::HashMap; +use to_wgsl::{build_ty, build_ty_inner}; +use util::{build_address_space, build_binding, build_expression, build_resource_binding}; + +const NAGA_OIL_DECORATION_PRE: &str = "_naga_oil_mod_"; +const NAGA_OIL_DECORATION_POST: &str = "_member"; + +pub fn compile( + root_crate_name: &str, + root_crate_version: Version, + shader_def_values: IndexMap, + shader_sources: Vec, +) -> Result> { + let (shaders, mut composer) = compile_shaders(shader_sources)?; + + let mut doc = common::doc_new( + root_crate_name.to_string(), + root_crate_version, + shader_def_values.clone(), + ); + + for (import_path, shader) in &shaders { + let (module_path, module) = common::find_or_create_module(&mut doc, import_path); + + module + .shader_defs + .extend(shader.source.shader_defs.iter().cloned()); + module.source_url = Some(shader.source.docsrs_url.clone()); + + let desc = NagaModuleDescriptor { + source: &shader.source.source, + shader_defs: shader_def_values + .iter() + .map(|(key, value)| { + ( + key.clone(), + match *value { + ShaderDefValue::Bool(value) => compose::ShaderDefValue::Bool(value), + ShaderDefValue::Int(value) => compose::ShaderDefValue::Int(value), + ShaderDefValue::UInt(value) => compose::ShaderDefValue::UInt(value), + }, + ) + }) + .collect(), + ..Default::default() + }; + let naga_module = composer.make_naga_module(desc)?; + + let mut def_paths = HashMap::new(); + for import in &shader.imports { + let module_path = import + .import + .split("::") + .map(str::to_owned) + .collect::>(); + if let Some(items) = &import.items { + for item in items { + def_paths.insert(item.clone(), module_path.clone()); + } + } + } + for (_handle, ty) in naga_module.types.iter() { + if !contains_pre(ty.name.as_deref()) { + if let TypeInner::Struct { .. } = &ty.inner { + if let Some(name) = &ty.name { + def_paths.insert(name.clone(), module_path.clone()); + } + } + } + } + + for (_handle, constant) in naga_module.constants.iter() { + if !contains_pre(constant.name.as_deref()) && constant.name.is_some() { + module.constants.push(Constant { + name: Ident::from(constant.name.clone()), + ty: { + match constant.inner.resolve_type() { + naga::proc::TypeResolution::Handle(ty_handle) => build_ty( + &naga_module.types[ty_handle], + &naga_module.types, + &naga_module.constants, + &def_paths, + ), + naga::proc::TypeResolution::Value(ty_inner) => build_ty_inner( + &ty_inner, + &naga_module.types, + &naga_module.constants, + &def_paths, + ), + } + }, + init: build_expression(&constant.inner), + }); + } + } + + for (_handle, var) in naga_module.global_variables.iter() { + if !contains_pre(var.name.as_deref()) { + module.global_variables.push(GlobalVariable { + name: Ident::from(var.name.clone()), + space: build_address_space(&var.space), + binding: var.binding.as_ref().map(build_resource_binding), + ty: build_ty( + &naga_module.types[var.ty], + &naga_module.types, + &naga_module.constants, + &def_paths, + ), + init: var + .init + .map(|init| build_expression(&naga_module.constants[init].inner)), + }); + } + } + + for (_handle, ty) in naga_module.types.iter() { + if !contains_pre(ty.name.as_deref()) { + if let TypeInner::Struct { members, .. } = &ty.inner { + module.structs.push(Struct { + name: Ident::from(ty.name.clone()), + members: members + .iter() + .map(|member| StructMember { + name: Ident::from(member.name.clone()), + ty: build_ty( + &naga_module.types[member.ty], + &naga_module.types, + &naga_module.constants, + &def_paths, + ), + binding: member.binding.as_ref().map(build_binding), + }) + .collect(), + }); + } + } + } + + for (_handle, function) in naga_module.functions.iter() { + if !contains_pre(function.name.as_deref()) { + module.functions.push(Function { + name: Ident::from(function.name.clone()), + arguments: function + .arguments + .iter() + .map(|arg| FunctionArgument { + name: Ident::from(arg.name.clone()), + ty: build_ty( + &naga_module.types[arg.ty], + &naga_module.types, + &naga_module.constants, + &def_paths, + ), + binding: arg.binding.as_ref().map(build_binding), + }) + .collect(), + ret: function.result.as_ref().map(|res| { + build_ty( + &naga_module.types[res.ty], + &naga_module.types, + &naga_module.constants, + &def_paths, + ) + }), + }); + } + } + } + + Ok(doc) +} + +fn contains_pre(name: Option<&str>) -> bool { + name.map(|name| name.contains(NAGA_OIL_DECORATION_PRE)) + .unwrap_or(false) +} + +struct Shader { + source: ShaderSource, + imports: Vec, + defines: HashMap, +} + +fn compile_shaders( + shader_sources: Vec, +) -> Result<(HashMap, Composer), Box> { + let mut composer = Composer::default(); + let mut shaders = HashMap::new(); + + for shader_source in shader_sources { + let (import_path, imports, defines) = compose::get_preprocessor_data(&shader_source.source); + if let Some(import_path) = import_path { + shaders.insert( + import_path, + Shader { + source: shader_source, + imports, + defines, + }, + ); + } + } + + fn add_to_composer( + composer: &mut Composer, + name: &str, + shaders: &HashMap, + ) -> Result<(), Box> { + if !composer.contains_module(name) { + let this = match shaders.get(name) { + Some(this) => this, + None => return Err(format!("shader not found: {}", name).into()), + }; + + for import in &this.imports { + add_to_composer(composer, &import.import, shaders)?; + } + + composer.add_composable_module(ComposableModuleDescriptor { + source: &this.source.source, + file_path: &this.source.path.to_string_lossy(), + language: ShaderLanguage::Wgsl, + additional_imports: Default::default(), + shader_defs: this.defines.clone(), + as_name: None, + })?; + } + + Ok(()) + } + for name in shaders.keys() { + add_to_composer(&mut composer, name, &shaders)?; + } + + Ok((shaders, composer)) +} diff --git a/crates/compiler/src/backend_v0_08/to_wgsl.rs b/crates/compiler/src/backend_v0_08/to_wgsl.rs new file mode 100644 index 0000000..7934fa2 --- /dev/null +++ b/crates/compiler/src/backend_v0_08/to_wgsl.rs @@ -0,0 +1,249 @@ +use super::naga::{self, TypeInner}; +use super::{NAGA_OIL_DECORATION_POST, NAGA_OIL_DECORATION_PRE}; +use std::collections::HashMap; + +pub fn build_ty( + ty: &naga::Type, + types: &naga::UniqueArena, + constants: &naga::Arena, + def_paths: &HashMap>, +) -> docs::Type { + match &ty.name { + Some(name) => { + // let pre_pos = name.find(NAGA_OIL_DECORATION_PRE); + // let ends_with_post = name.ends_with(NAGA_OIL_DECORATION_POST); + // + // let name = match (pre_pos, ends_with_post) { + // (Some(pre_pos), true) => name[..pre_pos].to_string(), + // _ => name.clone(), + // }; + + let pre_pos = name.find(NAGA_OIL_DECORATION_PRE); + let end_pos = name.find(NAGA_OIL_DECORATION_POST); + + let name = match (pre_pos, end_pos) { + (Some(pre_pos), Some(end_pos)) if end_pos > pre_pos => { + let mut res = name[..pre_pos].to_string(); + res.push_str(&name[end_pos + NAGA_OIL_DECORATION_POST.len()..]); + res + } + _ => name.clone(), + }; + + let def_path = def_paths.get(&name).cloned(); + docs::Type::Named { name, def_path } + } + None => build_ty_inner(&ty.inner, types, constants, def_paths), + } +} + +// Copy-pasted and adapted from: naga-0.14.1 + +pub fn build_ty_inner( + type_inner: &TypeInner, + types: &naga::UniqueArena, + constants: &naga::Arena, + def_paths: &HashMap>, +) -> docs::Type { + use naga::TypeInner as Ti; + + let name = + match *type_inner { + Ti::Scalar { kind, width } => kind.to_wgsl(width), + Ti::Vector { size, kind, width } => { + format!("vec{}<{}>", size as u32, kind.to_wgsl(width)) + } + Ti::Matrix { + columns, + rows, + width, + } => { + format!( + "mat{}x{}<{}>", + columns as u32, + rows as u32, + naga::ScalarKind::Float.to_wgsl(width), + ) + } + Ti::Atomic { kind, width } => { + format!("atomic<{}>", kind.to_wgsl(width)) + } + Ti::Pointer { base, .. } => { + let base = &types[base]; + return docs::Type::Pointer(Box::new(build_ty(base, types, constants, def_paths))); + } + Ti::ValuePointer { kind, width, .. } => { + format!("ptr<{}>", kind.to_wgsl(width)) + } + Ti::Array { base, size, .. } => { + let member_type = &types[base]; + return match size { + naga::ArraySize::Constant(size) => { + let constant = &constants[size]; + let size = match &constant.inner { + naga::ConstantInner::Scalar { value, .. } => match *value { + naga::ScalarValue::Sint(v) => u32::try_from(v).ok(), + naga::ScalarValue::Uint(v) => u32::try_from(v).ok(), + naga::ScalarValue::Float(_) => None, + naga::ScalarValue::Bool(_) => None, + }, + _ => None, + }; + + docs::Type::ArrayConstant( + Box::new(build_ty(member_type, types, constants, def_paths)), + size, + ) + } + naga::ArraySize::Dynamic => docs::Type::ArrayDynamic(Box::new(build_ty( + member_type, + types, + constants, + def_paths, + ))), + }; + } + Ti::Struct { .. } => { + // TODO: Actually output the struct? + "struct".to_string() + } + Ti::Image { + dim, + arrayed, + class, + } => { + let dim_suffix = match dim { + naga::ImageDimension::D1 => "_1d", + naga::ImageDimension::D2 => "_2d", + naga::ImageDimension::D3 => "_3d", + naga::ImageDimension::Cube => "_cube", + }; + let array_suffix = if arrayed { "_array" } else { "" }; + + let class_suffix = match class { + naga::ImageClass::Sampled { multi: true, .. } => "_multisampled", + naga::ImageClass::Depth { multi: false } => "_depth", + naga::ImageClass::Depth { multi: true } => "_depth_multisampled", + naga::ImageClass::Sampled { multi: false, .. } + | naga::ImageClass::Storage { .. } => "", + }; + + let type_in_brackets = match class { + naga::ImageClass::Sampled { kind, .. } => { + // Note: The only valid widths are 4 bytes wide. + // The lexer has already verified this, so we can safely assume it here. + // https://gpuweb.github.io/gpuweb/wgsl/#sampled-texture-type + let element_type = kind.to_wgsl(4); + format!("<{element_type}>") + } + naga::ImageClass::Depth { multi: _ } => String::new(), + naga::ImageClass::Storage { format, access } => { + if access.contains(naga::StorageAccess::STORE) { + format!("<{},write>", format.to_wgsl()) + } else { + format!("<{}>", format.to_wgsl()) + } + } + }; + + format!("texture{class_suffix}{dim_suffix}{array_suffix}{type_in_brackets}") + } + Ti::Sampler { .. } => "sampler".to_string(), + Ti::AccelerationStructure => "acceleration_structure".to_string(), + Ti::RayQuery => "ray_query".to_string(), + Ti::BindingArray { base, size, .. } => { + let member_type = &types[base]; + return match size { + naga::ArraySize::Constant(size) => { + let constant = &constants[size]; + let size = match &constant.inner { + naga::ConstantInner::Scalar { value, .. } => match *value { + naga::ScalarValue::Sint(v) => u32::try_from(v).ok(), + naga::ScalarValue::Uint(v) => u32::try_from(v).ok(), + naga::ScalarValue::Float(_) => None, + naga::ScalarValue::Bool(_) => None, + }, + _ => None, + }; + + docs::Type::BindingArrayConstant( + Box::new(build_ty(member_type, types, constants, def_paths)), + size, + ) + } + naga::ArraySize::Dynamic => docs::Type::BindingArrayDynamic(Box::new( + build_ty(member_type, types, constants, def_paths), + )), + }; + } + }; + + let def_path = def_paths.get(&name).cloned(); + docs::Type::Named { name, def_path } +} + +trait ScalarKindToWgsl { + fn to_wgsl(self, width: u8) -> String; +} + +impl ScalarKindToWgsl for naga::ScalarKind { + fn to_wgsl(self, width: u8) -> String { + let prefix = match self { + naga::ScalarKind::Sint => "i", + naga::ScalarKind::Uint => "u", + naga::ScalarKind::Float => "f", + naga::ScalarKind::Bool => return "bool".to_string(), + }; + format!("{}{}", prefix, width * 8) + } +} + +trait StorageFormatToWgsl { + fn to_wgsl(self) -> &'static str; +} + +impl StorageFormatToWgsl for naga::StorageFormat { + fn to_wgsl(self) -> &'static str { + use naga::StorageFormat as Sf; + match self { + Sf::R8Unorm => "r8unorm", + Sf::R8Snorm => "r8snorm", + Sf::R8Uint => "r8uint", + Sf::R8Sint => "r8sint", + Sf::R16Uint => "r16uint", + Sf::R16Sint => "r16sint", + Sf::R16Float => "r16float", + Sf::Rg8Unorm => "rg8unorm", + Sf::Rg8Snorm => "rg8snorm", + Sf::Rg8Uint => "rg8uint", + Sf::Rg8Sint => "rg8sint", + Sf::R32Uint => "r32uint", + Sf::R32Sint => "r32sint", + Sf::R32Float => "r32float", + Sf::Rg16Uint => "rg16uint", + Sf::Rg16Sint => "rg16sint", + Sf::Rg16Float => "rg16float", + Sf::Rgba8Unorm => "rgba8unorm", + Sf::Rgba8Snorm => "rgba8snorm", + Sf::Rgba8Uint => "rgba8uint", + Sf::Rgba8Sint => "rgba8sint", + Sf::Rgb10a2Unorm => "rgb10a2unorm", + Sf::Rg11b10Float => "rg11b10float", + Sf::Rg32Uint => "rg32uint", + Sf::Rg32Sint => "rg32sint", + Sf::Rg32Float => "rg32float", + Sf::Rgba16Uint => "rgba16uint", + Sf::Rgba16Sint => "rgba16sint", + Sf::Rgba16Float => "rgba16float", + Sf::Rgba32Uint => "rgba32uint", + Sf::Rgba32Sint => "rgba32sint", + Sf::Rgba32Float => "rgba32float", + Sf::R16Unorm => "r16unorm", + Sf::R16Snorm => "r16snorm", + Sf::Rg16Unorm => "rg16unorm", + Sf::Rg16Snorm => "rg16snorm", + Sf::Rgba16Unorm => "rgba16unorm", + Sf::Rgba16Snorm => "rgba16snorm", + } + } +} diff --git a/crates/compiler/src/backend_v0_08/util.rs b/crates/compiler/src/backend_v0_08/util.rs new file mode 100644 index 0000000..5394ef5 --- /dev/null +++ b/crates/compiler/src/backend_v0_08/util.rs @@ -0,0 +1,98 @@ +use super::naga; +use docs::{ + AddressSpace, Binding, BuiltIn, Expression, Interpolation, Literal, ResourceBinding, Sampling, +}; + +pub fn build_expression(constant_inner: &naga::ConstantInner) -> Expression { + match constant_inner { + naga::ConstantInner::Scalar { value, .. } => Expression::Literal(match *value { + naga::ScalarValue::Sint(v) => Literal::I64(v), + naga::ScalarValue::Uint(v) => Literal::U64(v), + naga::ScalarValue::Float(v) => Literal::F64(v), + naga::ScalarValue::Bool(v) => Literal::Bool(v), + }), + _ => Expression::Unknown, + } +} + +pub fn build_resource_binding(binding: &naga::ResourceBinding) -> ResourceBinding { + ResourceBinding { + group: binding.group, + binding: binding.binding, + } +} + +pub fn build_address_space(address_space: &naga::AddressSpace) -> AddressSpace { + match address_space { + naga::AddressSpace::Function => AddressSpace::Function, + naga::AddressSpace::Private => AddressSpace::Private, + naga::AddressSpace::WorkGroup => AddressSpace::WorkGroup, + naga::AddressSpace::Uniform => AddressSpace::Uniform, + naga::AddressSpace::Storage { access } => AddressSpace::Storage { + load: access.contains(naga::StorageAccess::LOAD), + store: access.contains(naga::StorageAccess::STORE), + }, + naga::AddressSpace::Handle => AddressSpace::Handle, + naga::AddressSpace::PushConstant => AddressSpace::PushConstant, + } +} + +pub fn build_binding(binding: &naga::Binding) -> Binding { + match binding { + naga::Binding::BuiltIn(builtin) => Binding::BuiltIn(build_builtin(builtin)), + naga::Binding::Location { + location, + interpolation, + sampling, + } => Binding::Location { + location: *location, + second_blend_source: false, + interpolation: interpolation.as_ref().map(build_interpolation), + sampling: sampling.as_ref().map(build_sampling), + }, + } +} + +pub fn build_builtin(builtin: &naga::BuiltIn) -> BuiltIn { + match builtin { + naga::BuiltIn::Position { invariant } => BuiltIn::Position { + invariant: *invariant, + }, + naga::BuiltIn::ViewIndex => BuiltIn::ViewIndex, + naga::BuiltIn::BaseInstance => BuiltIn::BaseInstance, + naga::BuiltIn::BaseVertex => BuiltIn::BaseVertex, + naga::BuiltIn::ClipDistance => BuiltIn::ClipDistance, + naga::BuiltIn::CullDistance => BuiltIn::CullDistance, + naga::BuiltIn::InstanceIndex => BuiltIn::InstanceIndex, + naga::BuiltIn::PointSize => BuiltIn::PointSize, + naga::BuiltIn::VertexIndex => BuiltIn::VertexIndex, + naga::BuiltIn::FragDepth => BuiltIn::FragDepth, + naga::BuiltIn::PointCoord => BuiltIn::PointCoord, + naga::BuiltIn::FrontFacing => BuiltIn::FrontFacing, + naga::BuiltIn::PrimitiveIndex => BuiltIn::PrimitiveIndex, + naga::BuiltIn::SampleIndex => BuiltIn::SampleIndex, + naga::BuiltIn::SampleMask => BuiltIn::SampleMask, + naga::BuiltIn::GlobalInvocationId => BuiltIn::GlobalInvocationId, + naga::BuiltIn::LocalInvocationId => BuiltIn::LocalInvocationId, + naga::BuiltIn::LocalInvocationIndex => BuiltIn::LocalInvocationIndex, + naga::BuiltIn::WorkGroupId => BuiltIn::WorkGroupId, + naga::BuiltIn::WorkGroupSize => BuiltIn::WorkGroupSize, + naga::BuiltIn::NumWorkGroups => BuiltIn::NumWorkGroups, + } +} + +pub fn build_interpolation(interpolation: &naga::Interpolation) -> Interpolation { + match interpolation { + naga::Interpolation::Perspective => Interpolation::Perspective, + naga::Interpolation::Linear => Interpolation::Linear, + naga::Interpolation::Flat => Interpolation::Flat, + } +} + +pub fn build_sampling(sampling: &naga::Sampling) -> Sampling { + match sampling { + naga::Sampling::Center => Sampling::Center, + naga::Sampling::Centroid => Sampling::Centroid, + naga::Sampling::Sample => Sampling::Sample, + } +} diff --git a/crates/compiler/src/backend_v0_10/mod.rs b/crates/compiler/src/backend_v0_10/mod.rs new file mode 100644 index 0000000..05ebf75 --- /dev/null +++ b/crates/compiler/src/backend_v0_10/mod.rs @@ -0,0 +1,214 @@ +mod to_wgsl; +mod util; + +use naga_oil_v0_10 as naga_oil; +use naga_v0_13 as naga; + +use crate::{common, download::ShaderSource}; +use docs::*; +use naga::TypeInner; +use naga_oil::compose::{ + self, ComposableModuleDescriptor, Composer, ImportDefinition, NagaModuleDescriptor, + ShaderLanguage, +}; +use std::collections::HashMap; +use to_wgsl::build_ty; +use util::{build_address_space, build_binding, build_expression, build_resource_binding}; + +const NAGA_OIL_DECORATION_PRE: &str = "X_naga_oil_mod_X"; +const NAGA_OIL_DECORATION_POST: &str = "X"; + +pub fn compile( + root_crate_name: &str, + root_crate_version: Version, + shader_def_values: IndexMap, + shader_sources: Vec, +) -> Result> { + let (shaders, mut composer) = compile_shaders(shader_sources)?; + + let mut doc = common::doc_new( + root_crate_name.to_string(), + root_crate_version, + shader_def_values.clone(), + ); + + for (import_path, shader) in &shaders { + let (module_path, module) = common::find_or_create_module(&mut doc, import_path); + + module + .shader_defs + .extend(shader.source.shader_defs.iter().cloned()); + module.source_url = Some(shader.source.docsrs_url.clone()); + + let desc = NagaModuleDescriptor { + source: &shader.source.source, + shader_defs: shader_def_values + .iter() + .map(|(key, value)| { + ( + key.clone(), + match *value { + ShaderDefValue::Bool(value) => compose::ShaderDefValue::Bool(value), + ShaderDefValue::Int(value) => compose::ShaderDefValue::Int(value), + ShaderDefValue::UInt(value) => compose::ShaderDefValue::UInt(value), + }, + ) + }) + .collect(), + ..Default::default() + }; + let naga_module = composer.make_naga_module(desc)?; + let gctx = naga_module.to_ctx(); + + let mut def_paths = HashMap::new(); + for import in &shader.imports { + let module_path = import + .import + .split("::") + .map(str::to_owned) + .collect::>(); + for item in &import.items { + def_paths.insert(item.clone(), module_path.clone()); + } + } + for (_handle, ty) in naga_module.types.iter() { + if !contains_pre(ty.name.as_deref()) { + if let TypeInner::Struct { .. } = &ty.inner { + if let Some(name) = &ty.name { + def_paths.insert(name.clone(), module_path.clone()); + } + } + } + } + + for (_handle, constant) in naga_module.constants.iter() { + if !contains_pre(constant.name.as_deref()) { + module.constants.push(Constant { + name: Ident::from(constant.name.clone()), + ty: build_ty(&naga_module.types[constant.ty], gctx, &def_paths), + init: build_expression(&naga_module.const_expressions[constant.init]), + }); + } + } + + for (_handle, var) in naga_module.global_variables.iter() { + if !contains_pre(var.name.as_deref()) { + module.global_variables.push(GlobalVariable { + name: Ident::from(var.name.clone()), + space: build_address_space(&var.space), + binding: var.binding.as_ref().map(build_resource_binding), + ty: build_ty(&naga_module.types[var.ty], gctx, &def_paths), + init: var + .init + .map(|init| build_expression(&naga_module.const_expressions[init])), + }); + } + } + + for (_handle, ty) in naga_module.types.iter() { + if !contains_pre(ty.name.as_deref()) { + if let TypeInner::Struct { members, .. } = &ty.inner { + module.structs.push(Struct { + name: Ident::from(ty.name.clone()), + members: members + .iter() + .map(|member| StructMember { + name: Ident::from(member.name.clone()), + ty: build_ty(&naga_module.types[member.ty], gctx, &def_paths), + binding: member.binding.as_ref().map(build_binding), + }) + .collect(), + }); + } + } + } + + for (_handle, function) in naga_module.functions.iter() { + if !contains_pre(function.name.as_deref()) { + module.functions.push(Function { + name: Ident::from(function.name.clone()), + arguments: function + .arguments + .iter() + .map(|arg| FunctionArgument { + name: Ident::from(arg.name.clone()), + ty: build_ty(&naga_module.types[arg.ty], gctx, &def_paths), + binding: arg.binding.as_ref().map(build_binding), + }) + .collect(), + ret: function + .result + .as_ref() + .map(|res| build_ty(&naga_module.types[res.ty], gctx, &def_paths)), + }); + } + } + } + + Ok(doc) +} + +fn contains_pre(name: Option<&str>) -> bool { + name.map(|name| name.contains(NAGA_OIL_DECORATION_PRE)) + .unwrap_or(false) +} + +struct Shader { + source: ShaderSource, + imports: Vec, + defines: HashMap, +} + +fn compile_shaders( + shader_sources: Vec, +) -> Result<(HashMap, Composer), Box> { + let mut composer = Composer::default(); + let mut shaders = HashMap::new(); + + for shader_source in shader_sources { + let (import_path, imports, defines) = compose::get_preprocessor_data(&shader_source.source); + if let Some(import_path) = import_path { + shaders.insert( + import_path, + Shader { + source: shader_source, + imports, + defines, + }, + ); + } + } + + fn add_to_composer( + composer: &mut Composer, + name: &str, + shaders: &HashMap, + ) -> Result<(), Box> { + if !composer.contains_module(name) { + let this = match shaders.get(name) { + Some(this) => this, + None => return Err(format!("shader not found: {}", name).into()), + }; + + for import in &this.imports { + add_to_composer(composer, &import.import, shaders)?; + } + + composer.add_composable_module(ComposableModuleDescriptor { + source: &this.source.source, + file_path: &this.source.path.to_string_lossy(), + language: ShaderLanguage::Wgsl, + additional_imports: Default::default(), + shader_defs: this.defines.clone(), + as_name: None, + })?; + } + + Ok(()) + } + for name in shaders.keys() { + add_to_composer(&mut composer, name, &shaders)?; + } + + Ok((shaders, composer)) +} diff --git a/crates/compiler/src/backend_v0_10/to_wgsl.rs b/crates/compiler/src/backend_v0_10/to_wgsl.rs new file mode 100644 index 0000000..282798d --- /dev/null +++ b/crates/compiler/src/backend_v0_10/to_wgsl.rs @@ -0,0 +1,205 @@ +use super::naga::{self, proc::GlobalCtx, TypeInner}; +use super::{NAGA_OIL_DECORATION_POST, NAGA_OIL_DECORATION_PRE}; +use std::collections::HashMap; + +pub fn build_ty( + ty: &naga::Type, + gctx: GlobalCtx, + def_paths: &HashMap>, +) -> docs::Type { + match &ty.name { + Some(name) => { + let pre_pos = name.find(NAGA_OIL_DECORATION_PRE); + let ends_with_post = name.ends_with(NAGA_OIL_DECORATION_POST); + + let name = match (pre_pos, ends_with_post) { + (Some(pre_pos), true) => name[..pre_pos].to_string(), + _ => name.clone(), + }; + let def_path = def_paths.get(&name).cloned(); + docs::Type::Named { name, def_path } + } + None => build_ty_inner(&ty.inner, gctx, def_paths), + } +} + +// Copy-pasted and adapted from: naga-0.14.1 + +pub fn build_ty_inner( + type_inner: &TypeInner, + gctx: GlobalCtx, + def_paths: &HashMap>, +) -> docs::Type { + use naga::TypeInner as Ti; + + let name = + match *type_inner { + Ti::Scalar { kind, width } => kind.to_wgsl(width), + Ti::Vector { size, kind, width } => { + format!("vec{}<{}>", size as u32, kind.to_wgsl(width)) + } + Ti::Matrix { + columns, + rows, + width, + } => { + format!( + "mat{}x{}<{}>", + columns as u32, + rows as u32, + naga::ScalarKind::Float.to_wgsl(width), + ) + } + Ti::Atomic { kind, width } => { + format!("atomic<{}>", kind.to_wgsl(width)) + } + Ti::Pointer { base, .. } => { + let base = &gctx.types[base]; + return docs::Type::Pointer(Box::new(build_ty(base, gctx, def_paths))); + } + Ti::ValuePointer { kind, width, .. } => { + format!("ptr<{}>", kind.to_wgsl(width)) + } + Ti::Array { base, size, .. } => { + let member_type = &gctx.types[base]; + return match size { + naga::ArraySize::Constant(size) => docs::Type::ArrayConstant( + Box::new(build_ty(member_type, gctx, def_paths)), + Some(size.get()), + ), + naga::ArraySize::Dynamic => { + docs::Type::ArrayDynamic(Box::new(build_ty(member_type, gctx, def_paths))) + } + }; + } + Ti::Struct { .. } => { + // TODO: Actually output the struct? + "struct".to_string() + } + Ti::Image { + dim, + arrayed, + class, + } => { + let dim_suffix = match dim { + naga::ImageDimension::D1 => "_1d", + naga::ImageDimension::D2 => "_2d", + naga::ImageDimension::D3 => "_3d", + naga::ImageDimension::Cube => "_cube", + }; + let array_suffix = if arrayed { "_array" } else { "" }; + + let class_suffix = match class { + naga::ImageClass::Sampled { multi: true, .. } => "_multisampled", + naga::ImageClass::Depth { multi: false } => "_depth", + naga::ImageClass::Depth { multi: true } => "_depth_multisampled", + naga::ImageClass::Sampled { multi: false, .. } + | naga::ImageClass::Storage { .. } => "", + }; + + let type_in_brackets = match class { + naga::ImageClass::Sampled { kind, .. } => { + // Note: The only valid widths are 4 bytes wide. + // The lexer has already verified this, so we can safely assume it here. + // https://gpuweb.github.io/gpuweb/wgsl/#sampled-texture-type + let element_type = kind.to_wgsl(4); + format!("<{element_type}>") + } + naga::ImageClass::Depth { multi: _ } => String::new(), + naga::ImageClass::Storage { format, access } => { + if access.contains(naga::StorageAccess::STORE) { + format!("<{},write>", format.to_wgsl()) + } else { + format!("<{}>", format.to_wgsl()) + } + } + }; + + format!("texture{class_suffix}{dim_suffix}{array_suffix}{type_in_brackets}") + } + Ti::Sampler { .. } => "sampler".to_string(), + Ti::AccelerationStructure => "acceleration_structure".to_string(), + Ti::RayQuery => "ray_query".to_string(), + Ti::BindingArray { base, size, .. } => { + let member_type = &gctx.types[base]; + return match size { + naga::ArraySize::Constant(size) => docs::Type::BindingArrayConstant( + Box::new(build_ty(member_type, gctx, def_paths)), + Some(size.get()), + ), + naga::ArraySize::Dynamic => docs::Type::BindingArrayDynamic(Box::new( + build_ty(member_type, gctx, def_paths), + )), + }; + } + }; + + let def_path = def_paths.get(&name).cloned(); + docs::Type::Named { name, def_path } +} + +trait ScalarKindToWgsl { + fn to_wgsl(self, width: u8) -> String; +} + +impl ScalarKindToWgsl for naga::ScalarKind { + fn to_wgsl(self, width: u8) -> String { + let prefix = match self { + naga::ScalarKind::Sint => "i", + naga::ScalarKind::Uint => "u", + naga::ScalarKind::Float => "f", + naga::ScalarKind::Bool => return "bool".to_string(), + }; + format!("{}{}", prefix, width * 8) + } +} + +trait StorageFormatToWgsl { + fn to_wgsl(self) -> &'static str; +} + +impl StorageFormatToWgsl for naga::StorageFormat { + fn to_wgsl(self) -> &'static str { + use naga::StorageFormat as Sf; + match self { + Sf::R8Unorm => "r8unorm", + Sf::R8Snorm => "r8snorm", + Sf::R8Uint => "r8uint", + Sf::R8Sint => "r8sint", + Sf::R16Uint => "r16uint", + Sf::R16Sint => "r16sint", + Sf::R16Float => "r16float", + Sf::Rg8Unorm => "rg8unorm", + Sf::Rg8Snorm => "rg8snorm", + Sf::Rg8Uint => "rg8uint", + Sf::Rg8Sint => "rg8sint", + Sf::R32Uint => "r32uint", + Sf::R32Sint => "r32sint", + Sf::R32Float => "r32float", + Sf::Rg16Uint => "rg16uint", + Sf::Rg16Sint => "rg16sint", + Sf::Rg16Float => "rg16float", + Sf::Rgba8Unorm => "rgba8unorm", + Sf::Rgba8Snorm => "rgba8snorm", + Sf::Rgba8Uint => "rgba8uint", + Sf::Rgba8Sint => "rgba8sint", + Sf::Rgb10a2Unorm => "rgb10a2unorm", + Sf::Rg11b10Float => "rg11b10float", + Sf::Rg32Uint => "rg32uint", + Sf::Rg32Sint => "rg32sint", + Sf::Rg32Float => "rg32float", + Sf::Rgba16Uint => "rgba16uint", + Sf::Rgba16Sint => "rgba16sint", + Sf::Rgba16Float => "rgba16float", + Sf::Rgba32Uint => "rgba32uint", + Sf::Rgba32Sint => "rgba32sint", + Sf::Rgba32Float => "rgba32float", + Sf::R16Unorm => "r16unorm", + Sf::R16Snorm => "r16snorm", + Sf::Rg16Unorm => "rg16unorm", + Sf::Rg16Snorm => "rg16snorm", + Sf::Rgba16Unorm => "rgba16unorm", + Sf::Rgba16Snorm => "rgba16snorm", + } + } +} diff --git a/crates/compiler/src/backend_v0_10/util.rs b/crates/compiler/src/backend_v0_10/util.rs new file mode 100644 index 0000000..06baa26 --- /dev/null +++ b/crates/compiler/src/backend_v0_10/util.rs @@ -0,0 +1,99 @@ +use super::naga; +use docs::{ + AddressSpace, Binding, BuiltIn, Expression, Interpolation, Literal, ResourceBinding, Sampling, +}; + +pub fn build_expression(expression: &naga::Expression) -> Expression { + match expression { + naga::Expression::Literal(lit) => Expression::Literal(match *lit { + naga::Literal::F64(v) => Literal::F64(v), + naga::Literal::F32(v) => Literal::F32(v), + naga::Literal::U32(v) => Literal::U32(v), + naga::Literal::I32(v) => Literal::I32(v), + naga::Literal::Bool(v) => Literal::Bool(v), + }), + _ => Expression::Unknown, + } +} + +pub fn build_resource_binding(binding: &naga::ResourceBinding) -> ResourceBinding { + ResourceBinding { + group: binding.group, + binding: binding.binding, + } +} + +pub fn build_address_space(address_space: &naga::AddressSpace) -> AddressSpace { + match address_space { + naga::AddressSpace::Function => AddressSpace::Function, + naga::AddressSpace::Private => AddressSpace::Private, + naga::AddressSpace::WorkGroup => AddressSpace::WorkGroup, + naga::AddressSpace::Uniform => AddressSpace::Uniform, + naga::AddressSpace::Storage { access } => AddressSpace::Storage { + load: access.contains(naga::StorageAccess::LOAD), + store: access.contains(naga::StorageAccess::STORE), + }, + naga::AddressSpace::Handle => AddressSpace::Handle, + naga::AddressSpace::PushConstant => AddressSpace::PushConstant, + } +} + +pub fn build_binding(binding: &naga::Binding) -> Binding { + match binding { + naga::Binding::BuiltIn(builtin) => Binding::BuiltIn(build_builtin(builtin)), + naga::Binding::Location { + location, + interpolation, + sampling, + } => Binding::Location { + location: *location, + second_blend_source: false, + interpolation: interpolation.as_ref().map(build_interpolation), + sampling: sampling.as_ref().map(build_sampling), + }, + } +} + +pub fn build_builtin(builtin: &naga::BuiltIn) -> BuiltIn { + match builtin { + naga::BuiltIn::Position { invariant } => BuiltIn::Position { + invariant: *invariant, + }, + naga::BuiltIn::ViewIndex => BuiltIn::ViewIndex, + naga::BuiltIn::BaseInstance => BuiltIn::BaseInstance, + naga::BuiltIn::BaseVertex => BuiltIn::BaseVertex, + naga::BuiltIn::ClipDistance => BuiltIn::ClipDistance, + naga::BuiltIn::CullDistance => BuiltIn::CullDistance, + naga::BuiltIn::InstanceIndex => BuiltIn::InstanceIndex, + naga::BuiltIn::PointSize => BuiltIn::PointSize, + naga::BuiltIn::VertexIndex => BuiltIn::VertexIndex, + naga::BuiltIn::FragDepth => BuiltIn::FragDepth, + naga::BuiltIn::PointCoord => BuiltIn::PointCoord, + naga::BuiltIn::FrontFacing => BuiltIn::FrontFacing, + naga::BuiltIn::PrimitiveIndex => BuiltIn::PrimitiveIndex, + naga::BuiltIn::SampleIndex => BuiltIn::SampleIndex, + naga::BuiltIn::SampleMask => BuiltIn::SampleMask, + naga::BuiltIn::GlobalInvocationId => BuiltIn::GlobalInvocationId, + naga::BuiltIn::LocalInvocationId => BuiltIn::LocalInvocationId, + naga::BuiltIn::LocalInvocationIndex => BuiltIn::LocalInvocationIndex, + naga::BuiltIn::WorkGroupId => BuiltIn::WorkGroupId, + naga::BuiltIn::WorkGroupSize => BuiltIn::WorkGroupSize, + naga::BuiltIn::NumWorkGroups => BuiltIn::NumWorkGroups, + } +} + +pub fn build_interpolation(interpolation: &naga::Interpolation) -> Interpolation { + match interpolation { + naga::Interpolation::Perspective => Interpolation::Perspective, + naga::Interpolation::Linear => Interpolation::Linear, + naga::Interpolation::Flat => Interpolation::Flat, + } +} + +pub fn build_sampling(sampling: &naga::Sampling) -> Sampling { + match sampling { + naga::Sampling::Center => Sampling::Center, + naga::Sampling::Centroid => Sampling::Centroid, + naga::Sampling::Sample => Sampling::Sample, + } +} diff --git a/crates/compiler/src/backend_v0_11/mod.rs b/crates/compiler/src/backend_v0_11/mod.rs new file mode 100644 index 0000000..8b9536f --- /dev/null +++ b/crates/compiler/src/backend_v0_11/mod.rs @@ -0,0 +1,214 @@ +mod to_wgsl; +mod util; + +use naga_oil_v0_11 as naga_oil; +use naga_v0_14 as naga; + +use crate::{common, download::ShaderSource}; +use docs::*; +use naga::TypeInner; +use naga_oil::compose::{ + self, ComposableModuleDescriptor, Composer, ImportDefinition, NagaModuleDescriptor, + ShaderLanguage, +}; +use std::collections::HashMap; +use to_wgsl::build_ty; +use util::{build_address_space, build_binding, build_expression, build_resource_binding}; + +const NAGA_OIL_DECORATION_PRE: &str = "X_naga_oil_mod_X"; +const NAGA_OIL_DECORATION_POST: &str = "X"; + +pub fn compile( + root_crate_name: &str, + root_crate_version: Version, + shader_def_values: IndexMap, + shader_sources: Vec, +) -> Result> { + let (shaders, mut composer) = compile_shaders(shader_sources)?; + + let mut doc = common::doc_new( + root_crate_name.to_string(), + root_crate_version, + shader_def_values.clone(), + ); + + for (import_path, shader) in &shaders { + let (module_path, module) = common::find_or_create_module(&mut doc, import_path); + + module + .shader_defs + .extend(shader.source.shader_defs.iter().cloned()); + module.source_url = Some(shader.source.docsrs_url.clone()); + + let desc = NagaModuleDescriptor { + source: &shader.source.source, + shader_defs: shader_def_values + .iter() + .map(|(key, value)| { + ( + key.clone(), + match *value { + ShaderDefValue::Bool(value) => compose::ShaderDefValue::Bool(value), + ShaderDefValue::Int(value) => compose::ShaderDefValue::Int(value), + ShaderDefValue::UInt(value) => compose::ShaderDefValue::UInt(value), + }, + ) + }) + .collect(), + ..Default::default() + }; + let naga_module = composer.make_naga_module(desc)?; + let gctx = naga_module.to_ctx(); + + let mut def_paths = HashMap::new(); + for import in &shader.imports { + let module_path = import + .import + .split("::") + .map(str::to_owned) + .collect::>(); + for item in &import.items { + def_paths.insert(item.clone(), module_path.clone()); + } + } + for (_handle, ty) in naga_module.types.iter() { + if !contains_pre(ty.name.as_deref()) { + if let TypeInner::Struct { .. } = &ty.inner { + if let Some(name) = &ty.name { + def_paths.insert(name.clone(), module_path.clone()); + } + } + } + } + + for (_handle, constant) in naga_module.constants.iter() { + if !contains_pre(constant.name.as_deref()) { + module.constants.push(Constant { + name: Ident::from(constant.name.clone()), + ty: build_ty(&naga_module.types[constant.ty], gctx, &def_paths), + init: build_expression(&naga_module.const_expressions[constant.init]), + }); + } + } + + for (_handle, var) in naga_module.global_variables.iter() { + if !contains_pre(var.name.as_deref()) { + module.global_variables.push(GlobalVariable { + name: Ident::from(var.name.clone()), + space: build_address_space(&var.space), + binding: var.binding.as_ref().map(build_resource_binding), + ty: build_ty(&naga_module.types[var.ty], gctx, &def_paths), + init: var + .init + .map(|init| build_expression(&naga_module.const_expressions[init])), + }); + } + } + + for (_handle, ty) in naga_module.types.iter() { + if !contains_pre(ty.name.as_deref()) { + if let TypeInner::Struct { members, .. } = &ty.inner { + module.structs.push(Struct { + name: Ident::from(ty.name.clone()), + members: members + .iter() + .map(|member| StructMember { + name: Ident::from(member.name.clone()), + ty: build_ty(&naga_module.types[member.ty], gctx, &def_paths), + binding: member.binding.as_ref().map(build_binding), + }) + .collect(), + }); + } + } + } + + for (_handle, function) in naga_module.functions.iter() { + if !contains_pre(function.name.as_deref()) { + module.functions.push(Function { + name: Ident::from(function.name.clone()), + arguments: function + .arguments + .iter() + .map(|arg| FunctionArgument { + name: Ident::from(arg.name.clone()), + ty: build_ty(&naga_module.types[arg.ty], gctx, &def_paths), + binding: arg.binding.as_ref().map(build_binding), + }) + .collect(), + ret: function + .result + .as_ref() + .map(|res| build_ty(&naga_module.types[res.ty], gctx, &def_paths)), + }); + } + } + } + + Ok(doc) +} + +fn contains_pre(name: Option<&str>) -> bool { + name.map(|name| name.contains(NAGA_OIL_DECORATION_PRE)) + .unwrap_or(false) +} + +struct Shader { + source: ShaderSource, + imports: Vec, + defines: HashMap, +} + +fn compile_shaders( + shader_sources: Vec, +) -> Result<(HashMap, Composer), Box> { + let mut composer = Composer::default(); + let mut shaders = HashMap::new(); + + for shader_source in shader_sources { + let (import_path, imports, defines) = compose::get_preprocessor_data(&shader_source.source); + if let Some(import_path) = import_path { + shaders.insert( + import_path, + Shader { + source: shader_source, + imports, + defines, + }, + ); + } + } + + fn add_to_composer( + composer: &mut Composer, + name: &str, + shaders: &HashMap, + ) -> Result<(), Box> { + if !composer.contains_module(name) { + let this = match shaders.get(name) { + Some(this) => this, + None => return Err(format!("shader not found: {}", name).into()), + }; + + for import in &this.imports { + add_to_composer(composer, &import.import, shaders)?; + } + + composer.add_composable_module(ComposableModuleDescriptor { + source: &this.source.source, + file_path: &this.source.path.to_string_lossy(), + language: ShaderLanguage::Wgsl, + additional_imports: Default::default(), + shader_defs: this.defines.clone(), + as_name: None, + })?; + } + + Ok(()) + } + for name in shaders.keys() { + add_to_composer(&mut composer, name, &shaders)?; + } + + Ok((shaders, composer)) +} diff --git a/crates/compiler/src/backend_v0_11/to_wgsl.rs b/crates/compiler/src/backend_v0_11/to_wgsl.rs new file mode 100644 index 0000000..3bbb193 --- /dev/null +++ b/crates/compiler/src/backend_v0_11/to_wgsl.rs @@ -0,0 +1,207 @@ +use super::naga::{self, proc::GlobalCtx, TypeInner}; +use super::{NAGA_OIL_DECORATION_POST, NAGA_OIL_DECORATION_PRE}; +use std::collections::HashMap; + +pub fn build_ty( + ty: &naga::Type, + gctx: GlobalCtx, + def_paths: &HashMap>, +) -> docs::Type { + match &ty.name { + Some(name) => { + let pre_pos = name.find(NAGA_OIL_DECORATION_PRE); + let ends_with_post = name.ends_with(NAGA_OIL_DECORATION_POST); + + let name = match (pre_pos, ends_with_post) { + (Some(pre_pos), true) => name[..pre_pos].to_string(), + _ => name.clone(), + }; + let def_path = def_paths.get(&name).cloned(); + docs::Type::Named { name, def_path } + } + None => build_ty_inner(&ty.inner, gctx, def_paths), + } +} + +// Copy-pasted and adapted from: naga-0.14.1 + +pub fn build_ty_inner( + type_inner: &TypeInner, + gctx: GlobalCtx, + def_paths: &HashMap>, +) -> docs::Type { + use naga::TypeInner as Ti; + + let name = + match *type_inner { + Ti::Scalar { kind, width } => kind.to_wgsl(width), + Ti::Vector { size, kind, width } => { + format!("vec{}<{}>", size as u32, kind.to_wgsl(width)) + } + Ti::Matrix { + columns, + rows, + width, + } => { + format!( + "mat{}x{}<{}>", + columns as u32, + rows as u32, + naga::ScalarKind::Float.to_wgsl(width), + ) + } + Ti::Atomic { kind, width } => { + format!("atomic<{}>", kind.to_wgsl(width)) + } + Ti::Pointer { base, .. } => { + let base = &gctx.types[base]; + return docs::Type::Pointer(Box::new(build_ty(base, gctx, def_paths))); + } + Ti::ValuePointer { kind, width, .. } => { + format!("ptr<{}>", kind.to_wgsl(width)) + } + Ti::Array { base, size, .. } => { + let member_type = &gctx.types[base]; + return match size { + naga::ArraySize::Constant(size) => docs::Type::ArrayConstant( + Box::new(build_ty(member_type, gctx, def_paths)), + Some(size.get()), + ), + naga::ArraySize::Dynamic => { + docs::Type::ArrayDynamic(Box::new(build_ty(member_type, gctx, def_paths))) + } + }; + } + Ti::Struct { .. } => { + // TODO: Actually output the struct? + "struct".to_string() + } + Ti::Image { + dim, + arrayed, + class, + } => { + let dim_suffix = match dim { + naga::ImageDimension::D1 => "_1d", + naga::ImageDimension::D2 => "_2d", + naga::ImageDimension::D3 => "_3d", + naga::ImageDimension::Cube => "_cube", + }; + let array_suffix = if arrayed { "_array" } else { "" }; + + let class_suffix = match class { + naga::ImageClass::Sampled { multi: true, .. } => "_multisampled", + naga::ImageClass::Depth { multi: false } => "_depth", + naga::ImageClass::Depth { multi: true } => "_depth_multisampled", + naga::ImageClass::Sampled { multi: false, .. } + | naga::ImageClass::Storage { .. } => "", + }; + + let type_in_brackets = match class { + naga::ImageClass::Sampled { kind, .. } => { + // Note: The only valid widths are 4 bytes wide. + // The lexer has already verified this, so we can safely assume it here. + // https://gpuweb.github.io/gpuweb/wgsl/#sampled-texture-type + let element_type = kind.to_wgsl(4); + format!("<{element_type}>") + } + naga::ImageClass::Depth { multi: _ } => String::new(), + naga::ImageClass::Storage { format, access } => { + if access.contains(naga::StorageAccess::STORE) { + format!("<{},write>", format.to_wgsl()) + } else { + format!("<{}>", format.to_wgsl()) + } + } + }; + + format!("texture{class_suffix}{dim_suffix}{array_suffix}{type_in_brackets}") + } + Ti::Sampler { .. } => "sampler".to_string(), + Ti::AccelerationStructure => "acceleration_structure".to_string(), + Ti::RayQuery => "ray_query".to_string(), + Ti::BindingArray { base, size, .. } => { + let member_type = &gctx.types[base]; + return match size { + naga::ArraySize::Constant(size) => docs::Type::BindingArrayConstant( + Box::new(build_ty(member_type, gctx, def_paths)), + Some(size.get()), + ), + naga::ArraySize::Dynamic => docs::Type::BindingArrayDynamic(Box::new( + build_ty(member_type, gctx, def_paths), + )), + }; + } + }; + + let def_path = def_paths.get(&name).cloned(); + docs::Type::Named { name, def_path } +} + +trait ScalarKindToWgsl { + fn to_wgsl(self, width: u8) -> String; +} + +impl ScalarKindToWgsl for naga::ScalarKind { + fn to_wgsl(self, width: u8) -> String { + let prefix = match self { + naga::ScalarKind::Sint => "i", + naga::ScalarKind::Uint => "u", + naga::ScalarKind::Float => "f", + naga::ScalarKind::Bool => return "bool".to_string(), + }; + format!("{}{}", prefix, width * 8) + } +} + +trait StorageFormatToWgsl { + fn to_wgsl(self) -> &'static str; +} + +impl StorageFormatToWgsl for naga::StorageFormat { + fn to_wgsl(self) -> &'static str { + use naga::StorageFormat as Sf; + match self { + Sf::R8Unorm => "r8unorm", + Sf::R8Snorm => "r8snorm", + Sf::R8Uint => "r8uint", + Sf::R8Sint => "r8sint", + Sf::R16Uint => "r16uint", + Sf::R16Sint => "r16sint", + Sf::R16Float => "r16float", + Sf::Rg8Unorm => "rg8unorm", + Sf::Rg8Snorm => "rg8snorm", + Sf::Rg8Uint => "rg8uint", + Sf::Rg8Sint => "rg8sint", + Sf::R32Uint => "r32uint", + Sf::R32Sint => "r32sint", + Sf::R32Float => "r32float", + Sf::Rg16Uint => "rg16uint", + Sf::Rg16Sint => "rg16sint", + Sf::Rg16Float => "rg16float", + Sf::Rgba8Unorm => "rgba8unorm", + Sf::Rgba8Snorm => "rgba8snorm", + Sf::Rgba8Uint => "rgba8uint", + Sf::Rgba8Sint => "rgba8sint", + Sf::Bgra8Unorm => "bgra8unorm", + Sf::Rgb10a2Uint => "rgb10a2uint", + Sf::Rgb10a2Unorm => "rgb10a2unorm", + Sf::Rg11b10Float => "rg11b10float", + Sf::Rg32Uint => "rg32uint", + Sf::Rg32Sint => "rg32sint", + Sf::Rg32Float => "rg32float", + Sf::Rgba16Uint => "rgba16uint", + Sf::Rgba16Sint => "rgba16sint", + Sf::Rgba16Float => "rgba16float", + Sf::Rgba32Uint => "rgba32uint", + Sf::Rgba32Sint => "rgba32sint", + Sf::Rgba32Float => "rgba32float", + Sf::R16Unorm => "r16unorm", + Sf::R16Snorm => "r16snorm", + Sf::Rg16Unorm => "rg16unorm", + Sf::Rg16Snorm => "rg16snorm", + Sf::Rgba16Unorm => "rgba16unorm", + Sf::Rgba16Snorm => "rgba16snorm", + } + } +} diff --git a/crates/compiler/src/backend_v0_11/util.rs b/crates/compiler/src/backend_v0_11/util.rs new file mode 100644 index 0000000..d146d23 --- /dev/null +++ b/crates/compiler/src/backend_v0_11/util.rs @@ -0,0 +1,100 @@ +use super::naga; +use docs::{ + AddressSpace, Binding, BuiltIn, Expression, Interpolation, Literal, ResourceBinding, Sampling, +}; + +pub fn build_expression(expression: &naga::Expression) -> Expression { + match expression { + naga::Expression::Literal(lit) => Expression::Literal(match *lit { + naga::Literal::F64(v) => Literal::F64(v), + naga::Literal::F32(v) => Literal::F32(v), + naga::Literal::U32(v) => Literal::U32(v), + naga::Literal::I32(v) => Literal::I32(v), + naga::Literal::Bool(v) => Literal::Bool(v), + }), + _ => Expression::Unknown, + } +} + +pub fn build_resource_binding(binding: &naga::ResourceBinding) -> ResourceBinding { + ResourceBinding { + group: binding.group, + binding: binding.binding, + } +} + +pub fn build_address_space(address_space: &naga::AddressSpace) -> AddressSpace { + match address_space { + naga::AddressSpace::Function => AddressSpace::Function, + naga::AddressSpace::Private => AddressSpace::Private, + naga::AddressSpace::WorkGroup => AddressSpace::WorkGroup, + naga::AddressSpace::Uniform => AddressSpace::Uniform, + naga::AddressSpace::Storage { access } => AddressSpace::Storage { + load: access.contains(naga::StorageAccess::LOAD), + store: access.contains(naga::StorageAccess::STORE), + }, + naga::AddressSpace::Handle => AddressSpace::Handle, + naga::AddressSpace::PushConstant => AddressSpace::PushConstant, + } +} + +pub fn build_binding(binding: &naga::Binding) -> Binding { + match binding { + naga::Binding::BuiltIn(builtin) => Binding::BuiltIn(build_builtin(builtin)), + naga::Binding::Location { + location, + second_blend_source, + interpolation, + sampling, + } => Binding::Location { + location: *location, + second_blend_source: *second_blend_source, + interpolation: interpolation.as_ref().map(build_interpolation), + sampling: sampling.as_ref().map(build_sampling), + }, + } +} + +pub fn build_builtin(builtin: &naga::BuiltIn) -> BuiltIn { + match builtin { + naga::BuiltIn::Position { invariant } => BuiltIn::Position { + invariant: *invariant, + }, + naga::BuiltIn::ViewIndex => BuiltIn::ViewIndex, + naga::BuiltIn::BaseInstance => BuiltIn::BaseInstance, + naga::BuiltIn::BaseVertex => BuiltIn::BaseVertex, + naga::BuiltIn::ClipDistance => BuiltIn::ClipDistance, + naga::BuiltIn::CullDistance => BuiltIn::CullDistance, + naga::BuiltIn::InstanceIndex => BuiltIn::InstanceIndex, + naga::BuiltIn::PointSize => BuiltIn::PointSize, + naga::BuiltIn::VertexIndex => BuiltIn::VertexIndex, + naga::BuiltIn::FragDepth => BuiltIn::FragDepth, + naga::BuiltIn::PointCoord => BuiltIn::PointCoord, + naga::BuiltIn::FrontFacing => BuiltIn::FrontFacing, + naga::BuiltIn::PrimitiveIndex => BuiltIn::PrimitiveIndex, + naga::BuiltIn::SampleIndex => BuiltIn::SampleIndex, + naga::BuiltIn::SampleMask => BuiltIn::SampleMask, + naga::BuiltIn::GlobalInvocationId => BuiltIn::GlobalInvocationId, + naga::BuiltIn::LocalInvocationId => BuiltIn::LocalInvocationId, + naga::BuiltIn::LocalInvocationIndex => BuiltIn::LocalInvocationIndex, + naga::BuiltIn::WorkGroupId => BuiltIn::WorkGroupId, + naga::BuiltIn::WorkGroupSize => BuiltIn::WorkGroupSize, + naga::BuiltIn::NumWorkGroups => BuiltIn::NumWorkGroups, + } +} + +pub fn build_interpolation(interpolation: &naga::Interpolation) -> Interpolation { + match interpolation { + naga::Interpolation::Perspective => Interpolation::Perspective, + naga::Interpolation::Linear => Interpolation::Linear, + naga::Interpolation::Flat => Interpolation::Flat, + } +} + +pub fn build_sampling(sampling: &naga::Sampling) -> Sampling { + match sampling { + naga::Sampling::Center => Sampling::Center, + naga::Sampling::Centroid => Sampling::Centroid, + naga::Sampling::Sample => Sampling::Sample, + } +} diff --git a/crates/compiler/src/common.rs b/crates/compiler/src/common.rs new file mode 100644 index 0000000..852406d --- /dev/null +++ b/crates/compiler/src/common.rs @@ -0,0 +1,57 @@ +use docs::{Doc, IndexMap, IndexSet, Module, ShaderDefValue, Version}; + +pub fn doc_new( + root_crate_name: String, + root_crate_version: Version, + shader_def_values: IndexMap, +) -> Doc { + Doc { + version: root_crate_version, + root: Module { + name: root_crate_name, + source_url: None, + modules: Vec::new(), + constants: Vec::new(), + global_variables: Vec::new(), + structs: Vec::new(), + functions: Vec::new(), + shader_defs: IndexSet::new(), + }, + compiled_with: shader_def_values, + } +} + +pub fn find_or_create_module<'a>( + doc: &'a mut Doc, + import_path: &str, +) -> (Vec, &'a mut Module) { + let mut module = &mut doc.root; + let path = import_path + .split("::") + .map(str::to_owned) + .collect::>(); + for segment in &path { + let idx = module + .modules + .iter() + .position(|module| &module.name == segment); + module = match idx { + Some(idx) => &mut module.modules[idx], + None => { + module.modules.push(Module { + name: segment.to_string(), + source_url: None, + modules: Vec::new(), + constants: Vec::new(), + global_variables: Vec::new(), + structs: Vec::new(), + functions: Vec::new(), + shader_defs: IndexSet::new(), + }); + module.modules.last_mut().unwrap() + } + }; + } + + (path, module) +} diff --git a/crates/compiler/src/download.rs b/crates/compiler/src/download.rs new file mode 100644 index 0000000..5e6fcdd --- /dev/null +++ b/crates/compiler/src/download.rs @@ -0,0 +1,181 @@ +use crate::CompilerBackend; +use cargo_metadata::MetadataCommand; +use docs::Version; +use regex::Regex; +use reqwest::blocking::Client; +use std::{ + collections::HashSet, + fs::{self, OpenOptions}, + io::Write, + path::{Path, PathBuf}, + sync::OnceLock, +}; +use tar::Archive; + +pub struct ShaderSource { + pub path: PathBuf, + pub source: String, + pub shader_defs: HashSet, + pub docsrs_url: String, +} + +pub fn download_shaders( + root_crate_name: &str, + root_crate_version: &Version, + package_filter: impl Fn(&str) -> bool, + cache_path: &Path, + backend: CompilerBackend, +) -> Result, Box> { + let manifest_path = download_crate(cache_path, root_crate_name, root_crate_version)?; + let metadata = MetadataCommand::new().manifest_path(manifest_path).exec()?; + + let mut shaders = Vec::new(); + + for package in &metadata.packages { + if package.name == "naga_oil" && package.version.minor != backend.naga_oil_minor() { + println!( + "Warning: naga_oil version mismatch: compiling with {}, found {}", + backend.naga_oil_minor(), + package.version.minor + ); + } + + if package_filter(&package.name) { + let crate_path = package + .manifest_path + .parent() + .unwrap() + .to_path_buf() + .into_std_path_buf(); + + let mut dirs = vec![crate_path.clone()]; + while let Some(parent) = dirs.pop() { + for entry in fs::read_dir(parent)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + dirs.push(path); + } else if path.is_file() && path.extension() == Some("wgsl".as_ref()) { + let source = fs::read_to_string(&path)?; + let shader_defs = find_defs(&source); + + let docsrs_url = { + let mut url = format!( + "https://docs.rs/crate/{}/{}/source", + package.name, package.version + ); + + let local = path.strip_prefix(&crate_path)?; + for segment in local.components() { + url.push('/'); + url.push_str(&segment.as_os_str().to_string_lossy()); + } + + url + }; + + shaders.push(ShaderSource { + path, + source, + shader_defs, + docsrs_url, + }); + } + } + } + } + } + + Ok(shaders) +} + +fn download_crate( + cache_path: &Path, + name: &str, + version: &Version, +) -> Result> { + let crate_path = cache_path.join(format!("{name}@{version}")); + let manifest_path = crate_path.join(format!("{name}-{version}/Cargo.toml")); + + if manifest_path.exists() { + return Ok(manifest_path); + } + + let client = Client::builder().no_gzip().build()?; + let url = crates_io_download_url(name, version); + + let response = client.get(url).send()?; + let decoded = flate2::read::GzDecoder::new(response); + + let mut archive = Archive::new(decoded); + archive.unpack(&crate_path)?; + + // Add empty workspace section to manifest, so that cargo does not complain about any workspace + let mut file = OpenOptions::new().append(true).open(&manifest_path)?; + file.write_all(b"\n[workspace]\n")?; + + Ok(manifest_path) +} + +fn crates_io_download_url(name: &str, version: &Version) -> String { + format!("https://static.crates.io/crates/{name}/{name}-{version}.crate") +} + +fn find_defs(source: &str) -> HashSet { + fn ifdef_regex() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + fn init() -> Regex { + Regex::new(r"^\s*#\s*(else\s+)?\s*ifdef\s+([\w|\d|_]+)").unwrap() + } + RE.get_or_init(init) + } + fn ifndef_regex() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + fn init() -> Regex { + Regex::new(r"^\s*#\s*(else\s+)?\s*ifndef\s+([\w|\d|_]+)").unwrap() + } + RE.get_or_init(init) + } + fn ifop_regex() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + fn init() -> Regex { + Regex::new(r"^\s*#\s*(else\s+)?\s*if\s+([\w|\d|_]+)\s*([=!<>]*)\s*([-\w|\d]+)").unwrap() + } + RE.get_or_init(init) + } + fn def_regex_delimited() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + fn init() -> Regex { + Regex::new(r"#\s*\{([\w|\d|_]+)\}").unwrap() + } + RE.get_or_init(init) + } + + let ifdef_regex = ifdef_regex(); + let ifndef_regex = ifndef_regex(); + let ifop_regex = ifop_regex(); + let def_regex_delimited = def_regex_delimited(); + + let mut defs = HashSet::new(); + + for line in source.lines() { + if let Some(caps) = ifdef_regex.captures(line) { + let def = caps.get(2).unwrap().as_str(); + defs.insert(def.to_string()); + } + if let Some(caps) = ifndef_regex.captures(line) { + let def = caps.get(2).unwrap().as_str(); + defs.insert(def.to_string()); + } + if let Some(caps) = ifop_regex.captures(line) { + let def = caps.get(2).unwrap().as_str(); + defs.insert(def.to_string()); + } + for caps in def_regex_delimited.captures_iter(line) { + let def = caps.get(1).unwrap().as_str(); + defs.insert(def.to_string()); + } + } + + defs +} diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs new file mode 100644 index 0000000..f4da2e6 --- /dev/null +++ b/crates/compiler/src/lib.rs @@ -0,0 +1,90 @@ +mod common; +mod download; +mod post_process; + +#[cfg(feature = "backend_v0_11")] +mod backend_v0_11; + +#[cfg(feature = "backend_v0_10")] +mod backend_v0_10; + +#[cfg(feature = "backend_v0_08")] +mod backend_v0_08; + +use docs::{Doc, IndexMap, ShaderDefValue, Version}; + +use std::path::Path; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum CompilerBackend { + #[cfg(feature = "backend_v0_11")] + V0_11, + #[cfg(feature = "backend_v0_10")] + V0_10, + #[cfg(feature = "backend_v0_08")] + V0_08, +} + +impl CompilerBackend { + fn naga_oil_minor(self) -> u64 { + match self { + #[cfg(feature = "backend_v0_11")] + CompilerBackend::V0_11 => 11, + #[cfg(feature = "backend_v0_10")] + CompilerBackend::V0_10 => 10, + #[cfg(feature = "backend_v0_08")] + CompilerBackend::V0_08 => 8, + } + } +} + +pub fn compile( + root_crate_name: &str, + root_crate_version: Version, + package_filter: impl Fn(&str) -> bool, + shader_def_values: IndexMap, + cache_path: &Path, + backend: CompilerBackend, +) -> Result> { + let shader_sources = download::download_shaders( + root_crate_name, + &root_crate_version, + package_filter, + cache_path, + backend, + )?; + + // CompileFn type is necessary to avoid compiler error if no backend is enabled + let compile: CompileFn = match backend { + #[cfg(feature = "backend_v0_11")] + CompilerBackend::V0_11 => backend_v0_11::compile, + #[cfg(feature = "backend_v0_10")] + CompilerBackend::V0_10 => backend_v0_10::compile, + #[cfg(feature = "backend_v0_08")] + CompilerBackend::V0_08 => backend_v0_08::compile, + }; + + let mut doc = compile( + root_crate_name, + root_crate_version, + shader_def_values.clone(), + shader_sources, + )?; + + post_process::post_process(&mut doc); + + for key in shader_def_values.keys() { + if !doc.root.shader_defs.contains(key) { + println!("Warning: shader def `{}` was provived but not used", key); + } + } + + Ok(doc) +} + +type CompileFn = fn( + &str, + Version, + IndexMap, + Vec, +) -> Result>; diff --git a/crates/compiler/src/post_process.rs b/crates/compiler/src/post_process.rs new file mode 100644 index 0000000..75eaf91 --- /dev/null +++ b/crates/compiler/src/post_process.rs @@ -0,0 +1,37 @@ +use docs::{Doc, IndexSet, Module}; + +pub fn post_process(doc: &mut Doc) { + // Sort compiled_with shader defs + doc.compiled_with.sort_keys(); + + // Post process modules + post_process_module(&mut doc.root); +} + +fn post_process_module(module: &mut Module) -> IndexSet { + // Inner modules + for inner in &mut module.modules { + let res_inner = post_process_module(inner); + module.shader_defs.extend(res_inner); + } + + // Sort modules + module.modules.sort_by(|a, b| a.name.cmp(&b.name)); + + // Sort constants + module.constants.sort_by(|a, b| a.name.cmp(&b.name)); + + // Sort global variables + module.global_variables.sort_by(|a, b| a.name.cmp(&b.name)); + + // Sort structs + module.structs.sort_by(|a, b| a.name.cmp(&b.name)); + + // Sort functions + module.functions.sort_by(|a, b| a.name.cmp(&b.name)); + + // Sort shader defs + module.shader_defs.sort(); + + module.shader_defs.clone() +} diff --git a/crates/docs/Cargo.toml b/crates/docs/Cargo.toml new file mode 100644 index 0000000..32a9db0 --- /dev/null +++ b/crates/docs/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "docs" +version = "0.1.0" +edition = "2021" + +[dependencies] +semver = "1.0.20" +indexmap = "2.1.0" diff --git a/crates/docs/src/lib.rs b/crates/docs/src/lib.rs new file mode 100644 index 0000000..88ffecf --- /dev/null +++ b/crates/docs/src/lib.rs @@ -0,0 +1,237 @@ +use std::fmt; + +pub use indexmap::{IndexMap, IndexSet}; +pub use semver::Version; + +pub struct Doc { + pub version: Version, + pub root: Module, + pub compiled_with: IndexMap, +} + +pub struct Module { + pub name: String, + pub source_url: Option, + pub modules: Vec, + pub constants: Vec, + pub global_variables: Vec, + pub structs: Vec, + pub functions: Vec, + pub shader_defs: IndexSet, +} + +#[derive(Debug, Clone, Copy)] +pub enum ShaderDefValue { + Bool(bool), + Int(i32), + UInt(u32), +} + +impl fmt::Display for ShaderDefValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ShaderDefValue::Bool(value) => write!(f, "{}", value), + ShaderDefValue::Int(value) => write!(f, "{}i", value), + ShaderDefValue::UInt(value) => write!(f, "{}u", value), + } + } +} + +pub struct Constant { + pub name: Ident, + pub ty: Type, + pub init: Expression, +} + +pub struct GlobalVariable { + pub name: Ident, + pub space: AddressSpace, + pub binding: Option, + pub ty: Type, + pub init: Option, +} + +pub enum AddressSpace { + Function, + Private, + WorkGroup, + Uniform, + Storage { load: bool, store: bool }, + Handle, + PushConstant, +} + +impl fmt::Display for AddressSpace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AddressSpace::Function => write!(f, ""), + AddressSpace::Private => write!(f, ""), + AddressSpace::WorkGroup => write!(f, ""), + AddressSpace::Uniform => write!(f, ""), + AddressSpace::Storage { store, .. } => { + if *store { + write!(f, "") + } else { + write!(f, "") + } + } + AddressSpace::Handle => write!(f, ""), + AddressSpace::PushConstant => write!(f, ""), + } + } +} + +pub struct ResourceBinding { + pub group: u32, + pub binding: u32, +} + +pub enum Expression { + Literal(Literal), + Unknown, +} + +impl fmt::Display for Expression { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Expression::Literal(literal) => write!(f, "{}", literal), + Expression::Unknown => write!(f, ".."), + } + } +} + +pub enum Literal { + F64(f64), + F32(f32), + U32(u32), + I32(i32), + U64(u64), + I64(i64), + Bool(bool), +} + +impl fmt::Display for Literal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Literal::F64(value) => write!(f, "{}", value), + Literal::F32(value) => write!(f, "{}", value), + Literal::U32(value) => write!(f, "{}", value), + Literal::I32(value) => write!(f, "{}", value), + Literal::U64(value) => write!(f, "{}", value), + Literal::I64(value) => write!(f, "{}", value), + Literal::Bool(value) => write!(f, "{}", value), + } + } +} + +pub struct Struct { + pub name: Ident, + pub members: Vec, +} + +pub struct StructMember { + pub name: Ident, + pub ty: Type, + pub binding: Option, +} + +pub enum Type { + Named { + name: String, + def_path: Option>, + }, + Pointer(Box), + ArrayConstant(Box, Option), + ArrayDynamic(Box), + BindingArrayConstant(Box, Option), + BindingArrayDynamic(Box), + Unnamed, +} + +pub struct Function { + pub name: Ident, + pub arguments: Vec, + pub ret: Option, +} + +pub struct FunctionArgument { + pub name: Ident, + pub ty: Type, + pub binding: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Ident { + Named(String), + Unnamed, +} + +impl From> for Ident +where + T: Into, +{ + fn from(name: Option) -> Self { + match name { + Some(name) => Ident::Named(name.into()), + None => Ident::Unnamed, + } + } +} + +impl fmt::Display for Ident { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Ident::Named(name) => write!(f, "{}", name), + Ident::Unnamed => write!(f, "unknown"), + } + } +} + +pub enum Binding { + BuiltIn(BuiltIn), + Location { + location: u32, + second_blend_source: bool, + interpolation: Option, + sampling: Option, + }, +} + +#[derive(Debug)] +pub enum BuiltIn { + Position { invariant: bool }, + ViewIndex, + BaseInstance, + BaseVertex, + ClipDistance, + CullDistance, + InstanceIndex, + PointSize, + VertexIndex, + FragDepth, + PointCoord, + FrontFacing, + PrimitiveIndex, + SampleIndex, + SampleMask, + GlobalInvocationId, + LocalInvocationId, + LocalInvocationIndex, + WorkGroupId, + WorkGroupSize, + NumWorkGroups, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Interpolation { + Perspective, + Linear, + Flat, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Sampling { + Center, + Centroid, + Sample, +} diff --git a/crates/generator/Cargo.toml b/crates/generator/Cargo.toml new file mode 100644 index 0000000..d22ee0e --- /dev/null +++ b/crates/generator/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "generator" +version = "0.1.0" +edition = "2021" + +[dependencies] +docs = { path = "../docs" } +askama = "0.12.1" +serde = { version = "1.0.193", features = ["derive"] } +serde_json = "1.0.108" diff --git a/crates/generator/src/all_items.rs b/crates/generator/src/all_items.rs new file mode 100644 index 0000000..bba2ea4 --- /dev/null +++ b/crates/generator/src/all_items.rs @@ -0,0 +1,91 @@ +use docs::{Doc, Ident, Module}; +use serde::Serialize; + +pub fn all_items(doc: &Doc) -> Vec { + let mut items = Vec::new(); + all_items_module(&doc.root, &[], &mut items); + items.sort(); + items +} + +fn all_items_module(module: &Module, parent: &[String], items: &mut Vec) { + let path = parent + .iter() + .cloned() + .chain([module.name.clone()]) + .collect::>(); + for inner in &module.modules { + all_items_module(inner, &path, items); + + items.push(Item::new( + path.clone(), + inner.name.clone(), + ItemKind::Module, + )); + } + + for item in &module.constants { + if let Ident::Named(name) = &item.name { + items.push(Item::new(path.clone(), name.clone(), ItemKind::Constant)); + } + } + + for item in &module.global_variables { + if let Ident::Named(name) = &item.name { + items.push(Item::new( + path.clone(), + name.clone(), + ItemKind::GlobalVariable, + )); + } + } + + for item in &module.structs { + if let Ident::Named(name) = &item.name { + items.push(Item::new(path.clone(), name.clone(), ItemKind::Struct)); + } + } + + for item in &module.functions { + if let Ident::Named(name) = &item.name { + items.push(Item::new(path.clone(), name.clone(), ItemKind::Function)); + } + } +} + +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] +pub struct Item { + path: Vec, + name: String, + kind: ItemKind, + url: String, +} + +impl Item { + fn new(path: Vec, name: String, kind: ItemKind) -> Self { + let mut url = path.join("/"); + match kind { + ItemKind::Module => url.push_str(&format!("/{}/index.html", name)), + ItemKind::Constant => url.push_str(&format!("/const.{}.html", name)), + ItemKind::GlobalVariable => url.push_str(&format!("/var.{}.html", name)), + ItemKind::Struct => url.push_str(&format!("/struct.{}.html", name)), + ItemKind::Function => url.push_str(&format!("/fn.{}.html", name)), + } + + Self { + path, + name, + kind, + url, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)] +enum ItemKind { + Module, + Constant, + GlobalVariable, + Struct, + Function, +} diff --git a/crates/generator/src/lib.rs b/crates/generator/src/lib.rs new file mode 100644 index 0000000..55fbc1a --- /dev/null +++ b/crates/generator/src/lib.rs @@ -0,0 +1,461 @@ +mod all_items; + +use askama::Template; +use docs::{ + Binding, BuiltIn, Constant, Doc, Function, GlobalVariable, Interpolation, Module, Sampling, + Struct, Type, Version, +}; +use serde_json::Value; +use std::{ + cmp::Ordering, + collections::HashSet, + fs::{self, File}, + ops::Deref, + path::Path, +}; + +pub fn generate(doc: &Doc, base_path: &Path) -> Result<(), Box> { + let base_path = base_path.join(&doc.root.name); + + // Prepare directories + fs::create_dir_all(&base_path)?; + + // Versions + let existing_versions = existing_versions(&base_path)?; + let is_latest = + existing_versions + .iter() + .all(|version| match doc.version.cmp_precedence(version) { + Ordering::Equal | Ordering::Greater => true, + Ordering::Less => false, + }); + let all_versions = { + let mut versions = existing_versions.clone(); + versions.insert(doc.version.clone()); + + let mut versions = versions.into_iter().collect::>(); + versions.sort_by(|a, b| a.cmp_precedence(b).reverse()); + + versions + }; + + // Gen docs + gen_doc(doc, false, &base_path)?; + if is_latest { + gen_doc(doc, true, &base_path)?; + } + + // Store versions + let mut common = load_common_json(&base_path)?; + common["versions"] = Value::Array( + all_versions + .iter() + .map(|version| Value::String(version.to_string())) + .collect(), + ); + store_common_json(&base_path, &common)?; + + Ok(()) +} + +fn load_common_json(base_path: &Path) -> Result> { + let common_path = base_path.join("common.js"); + let source = if common_path.exists() { + fs::read_to_string(&common_path)? + } else { + return Ok(Value::Object(Default::default())); + }; + + let source = source.trim(); + let source = source + .trim_start_matches("window.DOCS_COMMON") + .trim_start() + .trim_start_matches('=') + .trim_start(); + let source = source.trim_end_matches(';').trim_end(); + + Ok(serde_json::de::from_str(source)?) +} + +fn store_common_json(base_path: &Path, value: &Value) -> Result<(), Box> { + let common_path = base_path.join("common.js"); + let source = format!( + "window.DOCS_COMMON = {};\n", + serde_json::ser::to_string_pretty(value)? + ); + fs::write(common_path, source)?; + Ok(()) +} + +fn existing_versions(path: &Path) -> Result, Box> { + let mut versions = HashSet::new(); + for entry in fs::read_dir(path)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + if let Ok(version) = Version::parse(path.file_name().unwrap().to_str().unwrap()) { + versions.insert(version); + } + } + } + Ok(versions) +} + +fn gen_doc( + doc: &Doc, + build_as_latest: bool, + base_path: &Path, +) -> Result<(), Box> { + let base_path = if build_as_latest { + base_path.join("latest").join(&doc.root.name) + } else { + base_path.join(doc.version.to_string()).join(&doc.root.name) + }; + + // Prepare directories + fs::remove_dir_all(&base_path).ok(); + fs::create_dir_all(&base_path)?; + + // Gen modules + gen_module( + &Base { + doc, + build_as_latest, + }, + &ModulePath { + segments: vec![( + doc.root.name.clone(), + "index.html".to_string(), + ItemKind::Module, + )], + level: 0, + }, + &doc.root, + &base_path, + )?; + + // Store items + let items = all_items::all_items(doc); + let source = format!( + "window.DOCS_ITEMS = {};\n", + serde_json::ser::to_string(&items)? + ); + fs::write(base_path.join("items.js"), source)?; + + Ok(()) +} + +fn gen_module( + base: &Base, + module_path: &ModulePath, + module: &Module, + base_path: &Path, +) -> Result<(), Box> { + let template = OverviewTemplate { + base, + title: &module.name, + module_path, + module, + }; + template.write_into(&mut File::create(base_path.join("index.html"))?)?; + + for module in &module.modules { + let module_path = module_path.extend(&module.name, "index.html", ItemKind::Module, true); + + let base_path = base_path.join(&module.name); + fs::create_dir(&base_path)?; + + gen_module(base, &module_path, module, &base_path)?; + } + + for constant in &module.constants { + let module_path = + module_path.extend(constant.name.to_string(), "#", ItemKind::Constant, false); + let template = ConstantTemplate { + base, + title: &constant.name.to_string(), + module_path: &module_path, + module, + constant, + }; + template.write_into(&mut File::create( + base_path.join(format!("const.{}.html", constant.name)), + )?)?; + } + + for var in &module.global_variables { + let module_path = + module_path.extend(var.name.to_string(), "#", ItemKind::GlobalVariable, false); + let template = GlobalVariableTemplate { + base, + title: &var.name.to_string(), + module_path: &module_path, + module, + var, + }; + template.write_into(&mut File::create( + base_path.join(format!("var.{}.html", var.name)), + )?)?; + } + + for struct_ in &module.structs { + let module_path = + module_path.extend(struct_.name.to_string(), "#", ItemKind::Struct, false); + let template = StructTemplate { + base, + title: &struct_.name.to_string(), + module_path: &module_path, + module, + struct_, + }; + template.write_into(&mut File::create( + base_path.join(format!("struct.{}.html", struct_.name)), + )?)?; + } + + for function in &module.functions { + let module_path = + module_path.extend(&function.name.to_string(), "#", ItemKind::Function, false); + let template = FunctionTemplate { + base, + title: &function.name.to_string(), + module_path: &module_path, + module, + function, + }; + template.write_into(&mut File::create( + base_path.join(format!("fn.{}.html", function.name)), + )?)?; + } + + Ok(()) +} + +#[derive(Debug, Clone, Copy)] +enum ItemKind { + Module, + Constant, + GlobalVariable, + Struct, + Function, +} + +#[derive(Debug, Clone)] +struct ModulePath { + segments: Vec<(String, String, ItemKind)>, + level: usize, +} + +impl ModulePath { + fn extend( + &self, + name: impl Into, + path: impl Into, + kind: ItemKind, + is_child: bool, + ) -> Self { + Self { + segments: self + .segments + .iter() + .map(|(name, path, kind)| { + if is_child { + (name.clone(), format!("../{}", path), *kind) + } else { + (name.clone(), path.clone(), *kind) + } + }) + .chain([(name.into(), path.into(), kind)]) + .collect(), + level: if is_child { self.level + 1 } else { self.level }, + } + } +} + +#[derive(Template)] +#[template(path = "overview.html")] +struct OverviewTemplate<'a> { + base: &'a Base<'a>, + title: &'a str, + module_path: &'a ModulePath, + module: &'a Module, +} + +#[derive(Template)] +#[template(path = "constant.html")] +struct ConstantTemplate<'a> { + base: &'a Base<'a>, + title: &'a str, + module_path: &'a ModulePath, + module: &'a Module, + constant: &'a Constant, +} + +#[derive(Template)] +#[template(path = "global_variable.html")] +struct GlobalVariableTemplate<'a> { + base: &'a Base<'a>, + title: &'a str, + module_path: &'a ModulePath, + module: &'a Module, + var: &'a GlobalVariable, +} + +#[derive(Template)] +#[template(path = "struct.html")] +struct StructTemplate<'a> { + base: &'a Base<'a>, + title: &'a str, + module_path: &'a ModulePath, + module: &'a Module, + struct_: &'a Struct, +} + +#[derive(Template)] +#[template(path = "function.html")] +struct FunctionTemplate<'a> { + base: &'a Base<'a>, + title: &'a str, + module_path: &'a ModulePath, + module: &'a Module, + function: &'a Function, +} + +#[derive(Template)] +#[template(path = "render_type.html")] +struct RenderTypeTemplate<'a> { + ty: &'a Type, + module_path_level: usize, +} + +impl RenderTypeTemplate<'_> { + fn render_rec(&self, ty: &Type) -> String { + RenderTypeTemplate { + ty, + module_path_level: self.module_path_level, + } + .to_string() + } +} + +fn render_type(ty: &Type, module_path_level: &usize) -> String { + RenderTypeTemplate { + ty, + module_path_level: *module_path_level, + } + .to_string() +} + +struct Base<'a> { + doc: &'a Doc, + build_as_latest: bool, +} + +fn module_path_class(kind: &ItemKind, last: &bool) -> &'static str { + if !*last { + return ""; + } + + match kind { + ItemKind::Module => "module", + ItemKind::Constant => "const", + ItemKind::GlobalVariable => "var", + ItemKind::Struct => "struct", + ItemKind::Function => "fn", + } +} + +fn display_array_size(size: &Option) -> String { + match size { + Some(size) => size.to_string(), + None => "?".to_string(), + } +} + +// Copy-pasted and adapted from: naga-0.14.1 + +fn display_binding(binding: Option<&Binding>) -> String { + let Some(binding) = binding else { + return "".to_string(); + }; + + match binding { + Binding::BuiltIn(builtin) => { + let builtin = builtin_str(builtin).unwrap(); + format!("@builtin({}) ", builtin) + } + Binding::Location { + location, + second_blend_source, + interpolation, + sampling, + } => { + let mut res = String::new(); + + res += &format!("@location({}) ", location); + if *second_blend_source { + res += "@second_blend_source "; + } + + if sampling.is_some() && *sampling != Some(Sampling::Center) { + res += &format!( + "@interpolate({}, {}) ", + interpolation_str(interpolation.unwrap_or(Interpolation::Perspective)), + sampling_str(sampling.unwrap_or(Sampling::Center)) + ); + } else if interpolation.is_some() && *interpolation != Some(Interpolation::Perspective) + { + res += &format!( + "@interpolate({}) ", + interpolation_str(interpolation.unwrap_or(Interpolation::Perspective)) + ); + } + + res + } + } +} + +fn builtin_str(built_in: &BuiltIn) -> Result<&'static str, Box> { + Ok(match built_in { + BuiltIn::Position { .. } => "position", + BuiltIn::ViewIndex => "view_index", + BuiltIn::InstanceIndex => "instance_index", + BuiltIn::VertexIndex => "vertex_index", + BuiltIn::FragDepth => "frag_depth", + BuiltIn::FrontFacing => "front_facing", + BuiltIn::PrimitiveIndex => "primitive_index", + BuiltIn::SampleIndex => "sample_index", + BuiltIn::SampleMask => "sample_mask", + BuiltIn::GlobalInvocationId => "global_invocation_id", + BuiltIn::LocalInvocationId => "local_invocation_id", + BuiltIn::LocalInvocationIndex => "local_invocation_index", + BuiltIn::WorkGroupId => "workgroup_id", + BuiltIn::NumWorkGroups => "num_workgroups", + BuiltIn::BaseInstance + | BuiltIn::BaseVertex + | BuiltIn::ClipDistance + | BuiltIn::CullDistance + | BuiltIn::PointSize + | BuiltIn::PointCoord + | BuiltIn::WorkGroupSize => { + return Err(format!("unsupported built-in: {:?}", built_in).into()) + } + }) +} + +const fn interpolation_str(interpolation: Interpolation) -> &'static str { + match interpolation { + Interpolation::Perspective => "perspective", + Interpolation::Linear => "linear", + Interpolation::Flat => "flat", + } +} + +const fn sampling_str(sampling: Sampling) -> &'static str { + match sampling { + Sampling::Center => "", + Sampling::Centroid => "centroid", + Sampling::Sample => "sample", + } +} diff --git a/crates/generator/templates/base.html b/crates/generator/templates/base.html new file mode 100644 index 0000000..44fbbdd --- /dev/null +++ b/crates/generator/templates/base.html @@ -0,0 +1,340 @@ +{% let this_version %} +{% if base.build_as_latest %} +{% let this_version = "latest".to_string() %} +{% else %} +{% let this_version = base.doc.version.to_string() %} +{% endif %} + + + + + + + + + {% if title != base.doc.root.name %} + {{ title }} - {{ base.doc.root.name }} + {% else %} + {{ title }} + {% endif %} + + + + + {% block head %}{% endblock %} + + + + +
+ + +
+

+ {% for (name, path, kind) in module_path.segments -%} + {% if !loop.first %}::{% endif -%} + {{ name }} + {%- endfor %} +

+ + {% if let Some(source_url) = module.source_url %} + source + {% endif %} + + {% block content %}{% endblock %} +
+
+ + + + + + + + diff --git a/crates/generator/templates/constant.html b/crates/generator/templates/constant.html new file mode 100644 index 0000000..d72b0f1 --- /dev/null +++ b/crates/generator/templates/constant.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} + +{% block content %} +
+const {{ constant.name }}: {{ self::render_type(constant.ty, module_path.level)|safe }} = {{ constant.init }};
+
+{% endblock %} diff --git a/crates/generator/templates/function.html b/crates/generator/templates/function.html new file mode 100644 index 0000000..b1bbcd5 --- /dev/null +++ b/crates/generator/templates/function.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} + +{% block content %} +
+
+{%- let ws_pre %}
+{%- let ws_post %}
+{%- if function.arguments.len() > 3 %}
+{%- let ws_pre = "\n    " %}
+{%- let ws_post = "\n" %}
+{%- else %}
+{%- let ws_pre = "" %}
+{%- let ws_post = "" %}
+{%- endif -%}
+
+fn {{ function.name }}(
+    {%- for arg in function.arguments %}
+    {{- ws_pre }}
+    {{- self::display_binding(arg.binding.as_ref()) }}{{ arg.name }}: {{ self::render_type(arg.ty, module_path.level)|safe }}
+    {%- if !loop.last %}, {% endif %}
+    {%- endfor %}
+    {{- ws_post -}}
+) {% if let Some(ret) = function.ret %}-> {{ self::render_type(ret, module_path.level)|safe }}{% endif %}
+
+{% endblock %} diff --git a/crates/generator/templates/global_variable.html b/crates/generator/templates/global_variable.html new file mode 100644 index 0000000..8f80e69 --- /dev/null +++ b/crates/generator/templates/global_variable.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} + +{% block content %} +
+
+    {%- if let Some(binding) = var.binding %}@group({{ binding.group }}) @binding({{ binding.binding }}) {% endif -%}
+    var{{ var.space }} {{ var.name }}: {{ self::render_type(var.ty, module_path.level)|safe }}
+    {%- if let Some(init) = var.init %} = {{ init }}{% endif -%};
+
+{% endblock %} diff --git a/crates/generator/templates/overview.html b/crates/generator/templates/overview.html new file mode 100644 index 0000000..41f99ec --- /dev/null +++ b/crates/generator/templates/overview.html @@ -0,0 +1,69 @@ +{% extends "base.html" %} + +{% block content %} + +{% if !module.modules.is_empty() %} +

Modules

+ +{% endif %} + +{% if !module.constants.is_empty() %} +

Constants

+ +{% endif %} + +{% if !module.global_variables.is_empty() %} +

Global Variables

+
    + {% for var in module.global_variables %} +
  • {{ var.name }}
  • + {% endfor %} +
+{% endif %} + +{% if !module.structs.is_empty() %} +

Structs

+ +{% endif %} + +{% if !module.functions.is_empty() %} +

Functions

+ +{% endif %} + +{% if !module.shader_defs.is_empty() %} +

Shader Definitions

+
    + {% for shader_def in module.shader_defs %} +
  • {{ shader_def }}
  • + {% endfor %} +
+{% endif %} + +{% if module_path.level == 0 && !base.doc.compiled_with.is_empty() %} +

+
Compiled With:
+
    + {% for (key, value) in base.doc.compiled_with %} +
  • {{ key }}: {{ value }}
  • + {% endfor %} +
+{% endif %} + +{% endblock %} diff --git a/crates/generator/templates/render_type.html b/crates/generator/templates/render_type.html new file mode 100644 index 0000000..c257a7b --- /dev/null +++ b/crates/generator/templates/render_type.html @@ -0,0 +1,23 @@ +{% match ty %} +{%- when Type::Named with { name, def_path } %} +{%- if let Some(def_path) = def_path -%} + + {{- name -}} + +{%- else %} +{{- name }} +{%- endif %} +{%- when Type::Pointer with (inner) -%} +ptr<{{ Self::render_rec(self, inner.deref())|safe }}> +{%- when Type::ArrayConstant with (inner, size) -%} +array<{{ Self::render_rec(self, inner.deref())|safe }}, {{ self::display_array_size(size) }}> +{%- when Type::ArrayDynamic with (inner) -%} +array<{{ Self::render_rec(self, inner.deref())|safe }}> +{%- when Type::BindingArrayConstant with (inner, size) -%} +binding_array<{{ Self::render_rec(self, inner.deref())|safe }}, {{ self::display_array_size(size) }}> +{%- when Type::BindingArrayDynamic with (inner) -%} +binding_array<{{ Self::render_rec(self, inner.deref())|safe }}> +{%- when Type::Unnamed -%} +unknown +{%- endmatch -%} diff --git a/crates/generator/templates/struct.html b/crates/generator/templates/struct.html new file mode 100644 index 0000000..52dea72 --- /dev/null +++ b/crates/generator/templates/struct.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% block content %} +
+struct {{ struct_.name }} {
+    {%- for member in struct_.members %}
+    {{ self::display_binding(member.binding.as_ref()) }}{{ member.name }}: {{ self::render_type(member.ty, module_path.level)|safe }}{% if !loop.last %},{% endif %}
+    {%- endfor %}
+}
+
+{% endblock %} diff --git a/crates/make/Cargo.toml b/crates/make/Cargo.toml new file mode 100644 index 0000000..ff39e11 --- /dev/null +++ b/crates/make/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "make" +version = "0.1.0" +edition = "2021" + +[dependencies] +docs = { path = "../docs" } +compiler = { path = "../compiler", features = [ + "backend_v0_11", + "backend_v0_10", + "backend_v0_08", +] } +generator = { path = "../generator" } diff --git a/crates/make/src/main.rs b/crates/make/src/main.rs new file mode 100644 index 0000000..6e43613 --- /dev/null +++ b/crates/make/src/main.rs @@ -0,0 +1,128 @@ +use docs::{IndexMap, ShaderDefValue, Version}; +use std::path::Path; + +fn main() -> Result<(), Box> { + // Compile docs + let cache_path = Path::new("target/shader_docs_cache"); + let docs = vec![ + compiler::compile( + "bevy", + Version::new(0, 12, 1), + |name| name.starts_with("bevy"), + shader_def_values_0_12(), + cache_path, + compiler::CompilerBackend::V0_10, + )?, + compiler::compile( + "bevy", + Version::new(0, 12, 0), + |name| name.starts_with("bevy"), + shader_def_values_0_12(), + cache_path, + compiler::CompilerBackend::V0_10, + )?, + compiler::compile( + "bevy", + Version::new(0, 11, 3), + |name| name.starts_with("bevy"), + shader_def_values_0_11(), + cache_path, + compiler::CompilerBackend::V0_08, + )?, + compiler::compile( + "bevy", + Version::new(0, 11, 2), + |name| name.starts_with("bevy"), + shader_def_values_0_11(), + cache_path, + compiler::CompilerBackend::V0_08, + )?, + compiler::compile( + "bevy", + Version::new(0, 11, 1), + |name| name.starts_with("bevy"), + shader_def_values_0_11(), + cache_path, + compiler::CompilerBackend::V0_08, + )?, + compiler::compile( + "bevy", + Version::new(0, 11, 0), + |name| name.starts_with("bevy"), + shader_def_values_0_11(), + cache_path, + compiler::CompilerBackend::V0_08, + )?, + ]; + + // Generate docs + for doc in docs { + generator::generate(&doc, Path::new("target/shader_docs"))?; + } + + Ok(()) +} + +fn shader_def_values_0_12() -> IndexMap { + use ShaderDefValue::*; + + [ + ("AVAILABLE_STORAGE_BUFFER_BINDINGS", UInt(3)), + ("BLEND_MULTIPLY", Bool(true)), + ("BLEND_PREMULTIPLIED_ALPHA", Bool(true)), + ("DEFERRED_PREPASS", Bool(true)), + ("DEPTH_PREPASS", Bool(true)), + ("ENVIRONMENT_MAP", Bool(true)), + ("MAX_CASCADES_PER_LIGHT", UInt(4)), + ("MAX_DIRECTIONAL_LIGHTS", UInt(10)), + ("MORPH_TARGETS", Bool(true)), + ("MOTION_VECTOR_PREPASS", Bool(true)), + ("MULTISAMPLED", Bool(true)), + ("NORMAL_PREPASS", Bool(true)), + ("NORMAL_PREPASS_OR_DEFERRED_PREPASS", Bool(true)), + ("PBR_TRANSMISSION_TEXTURES_SUPPORTED", Bool(true)), + ("PREPASS_FRAGMENT", Bool(true)), + ("PREPASS_PIPELINE", Bool(true)), + ("SKINNED", Bool(true)), + ("STANDARDMATERIAL_NORMAL_MAP", Bool(true)), + ("TONEMAPPING_PASS", Bool(true)), + ("TONEMAP_METHOD_TONY_MC_MAPFACE", Bool(true)), + ("VERTEX_COLORS", Bool(true)), + ("VERTEX_NORMALS", Bool(true)), + ("VERTEX_OUTPUT_INSTANCE_INDEX", Bool(true)), + ("VERTEX_POSITIONS", Bool(true)), + ("VERTEX_TANGENTS", Bool(true)), + ("VERTEX_UVS", Bool(true)), + ] + .into_iter() + .map(|(key, value)| (key.to_string(), value)) + .collect() +} + +fn shader_def_values_0_11() -> IndexMap { + use ShaderDefValue::*; + + [ + ("AVAILABLE_STORAGE_BUFFER_BINDINGS", UInt(3)), + ("BLEND_MULTIPLY", Bool(true)), + ("BLEND_PREMULTIPLIED_ALPHA", Bool(true)), + ("DEPTH_PREPASS", Bool(true)), + ("ENVIRONMENT_MAP", Bool(true)), + ("MAX_CASCADES_PER_LIGHT", UInt(4)), + ("MAX_DIRECTIONAL_LIGHTS", UInt(10)), + ("MORPH_TARGETS", Bool(true)), + ("MOTION_VECTOR_PREPASS", Bool(true)), + ("MULTISAMPLED", Bool(true)), + ("NORMAL_PREPASS", Bool(true)), + ("SKINNED", Bool(true)), + ("STANDARDMATERIAL_NORMAL_MAP", Bool(true)), + ("TONEMAPPING_PASS", Bool(true)), + ("TONEMAP_METHOD_TONY_MC_MAPFACE", Bool(true)), + ("VERTEX_COLORS", Bool(true)), + ("VERTEX_TANGENTS", Bool(true)), + ("VERTEX_UVS", Bool(true)), + ] + .into_iter() + .map(|(key, value)| (key.to_string(), value)) + .collect() +}