diff --git a/Cargo.lock b/Cargo.lock index 4a4d1e8..554fda9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -37,6 +46,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + [[package]] name = "arrayref" version = "0.3.7" @@ -64,6 +79,17 @@ dependencies = [ "libloading", ] +[[package]] +name = "async-channel" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + [[package]] name = "async-executor" version = "1.5.0" @@ -78,6 +104,41 @@ dependencies = [ "slab", ] +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if 1.0.0", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix", + "slab", + "socket2", + "waker-fn", +] + [[package]] name = "async-lock" version = "2.7.0" @@ -87,6 +148,24 @@ dependencies = [ "event-listener", ] +[[package]] +name = "async-process" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" +dependencies = [ + "async-io", + "async-lock", + "autocfg", + "blocking", + "cfg-if 1.0.0", + "event-listener", + "futures-lite", + "rustix", + "signal-hook", + "windows-sys 0.48.0", +] + [[package]] name = "async-recursion" version = "1.0.4" @@ -98,19 +177,63 @@ dependencies = [ "syn 2.0.13", ] +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + [[package]] name = "async-task" version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -121,6 +244,45 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -160,12 +322,63 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "blocking" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "log", +] + [[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "bytemuck" version = "1.13.1" @@ -192,6 +405,35 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cacache" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "205ae2da91f7633dd4ecc1b12fac60c991a48b61b2cd801ad533085b15392537" +dependencies = [ + "async-std", + "digest 0.9.0", + "either", + "futures", + "hex 0.4.3", + "memmap", + "serde", + "serde_derive", + "serde_json", + "sha-1 0.9.8", + "sha2 0.9.9", + "ssri", + "tempfile", + "thiserror", + "walkdir", +] + [[package]] name = "cached" version = "0.42.0" @@ -263,6 +505,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "iana-time-zone", + "num-integer", + "num-traits", + "winapi", +] + [[package]] name = "cmake" version = "0.1.50" @@ -319,6 +573,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "com-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642" + [[package]] name = "concurrent-queue" version = "2.1.0" @@ -401,6 +661,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -442,12 +711,66 @@ dependencies = [ "winapi", ] +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "cty" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" +[[package]] +name = "cxx" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.13", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + [[package]] name = "d3d12" version = "0.5.0" @@ -459,6 +782,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "d3d12" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f0de2f5a8e7bd4a9eec0e3c781992a4ce1724f68aec7d7a3715344de8b39da" +dependencies = [ + "bitflags", + "libloading", + "winapi", +] + [[package]] name = "darling" version = "0.13.4" @@ -529,6 +863,24 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -570,6 +922,15 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "env_logger" version = "0.9.3" @@ -583,6 +944,27 @@ dependencies = [ "termcolor", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -599,6 +981,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fastrand" version = "1.9.0" @@ -666,6 +1054,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + [[package]] name = "freetype-rs" version = "0.26.0" @@ -695,12 +1092,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] -name = "futures-channel" +name = "futures" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ + "futures-channel", "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", ] [[package]] @@ -709,6 +1122,17 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-intrusive" version = "0.4.2" @@ -741,12 +1165,47 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + [[package]] name = "futures-sink" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "fxhash" version = "0.2.1" @@ -756,6 +1215,25 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.9" @@ -767,6 +1245,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + [[package]] name = "gloo-net" version = "0.2.6" @@ -787,6 +1271,18 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "gloo-utils" version = "0.1.6" @@ -812,6 +1308,18 @@ dependencies = [ "web-sys", ] +[[package]] +name = "glow" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e007a07a24de5ecae94160f141029e9a347282cfe25d1d58d85d845cf3130f1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gpu-alloc" version = "0.5.3" @@ -831,6 +1339,19 @@ dependencies = [ "bitflags", ] +[[package]] +name = "gpu-allocator" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce95f9e2e11c2c6fadfce42b5af60005db06576f231f5c92550fdded43c423e8" +dependencies = [ + "backtrace", + "log", + "thiserror", + "winapi", + "windows 0.44.0", +] + [[package]] name = "gpu-descriptor" version = "0.2.3" @@ -851,6 +1372,25 @@ dependencies = [ "bitflags", ] +[[package]] +name = "h2" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -866,6 +1406,21 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +[[package]] +name = "hassle-rs" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90601c6189668c7345fc53842cb3f3a3d872203d523be1b3cb44a36a3e62fb85" +dependencies = [ + "bitflags", + "com-rs", + "libc", + "libloading", + "thiserror", + "widestring", + "winapi", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -875,24 +1430,179 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +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 = "http-cache-semantics" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486d14f0f5e6981578cc683e01c5f50e2be31c984a75ff5517949af2e4268519" +dependencies = [ + "chrono", + "http", + "http-serde", + "reqwest", + "serde", +] + +[[package]] +name = "http-serde" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e272971f774ba29341db2f686255ff8a979365a26fb9e4277f6b6d9ec0cdd5e" +dependencies = [ + "http", + "serde", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "0.14.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "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 = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows 0.48.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "image" version = "0.24.6" @@ -930,6 +1640,23 @@ dependencies = [ "web-sys", ] +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + [[package]] name = "itertools" version = "0.10.5" @@ -977,6 +1704,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy-regex" version = "2.5.0" @@ -1022,6 +1758,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + [[package]] name = "lock_api" version = "0.4.9" @@ -1039,6 +1790,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if 1.0.0", + "value-bag", ] [[package]] @@ -1056,6 +1808,16 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "memmap2" version = "0.5.10" @@ -1094,6 +1856,22 @@ dependencies = [ "objc", ] +[[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" @@ -1154,11 +1932,30 @@ dependencies = [ "log", "num-traits", "rustc-hash", + "spirv", "termcolor", "thiserror", "unicode-xid", ] +[[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 = "ndk" version = "0.7.0" @@ -1329,8 +2126,18 @@ dependencies = [ ] [[package]] -name = "num_enum" -version = "0.5.11" +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ @@ -1368,12 +2175,77 @@ dependencies = [ "cc", ] +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d2f106ab837a24e03672c59b1239669a0596406ff657c3c0835b6b7f0f35a33" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types 0.3.2", + "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 2.0.13", +] + +[[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.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a20eace9dc2d82904039cb76dcf50fb1a0bba071cfd1629720b5d6f1ddba0fa" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking" version = "2.0.0" @@ -1410,7 +2282,7 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] @@ -1423,7 +2295,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -1460,6 +2332,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +[[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.26" @@ -1478,6 +2356,22 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be1c66a6add46bff50935c313dae30a5030cf8385c5206e8a95e9e9def974aa" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if 1.0.0", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + [[package]] name = "pollster" version = "0.2.5" @@ -1554,6 +2448,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.7.3" @@ -1577,12 +2480,106 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" +[[package]] +name = "reqwest" +version = "0.11.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "reqwest-middleware" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69539cea4148dce683bec9dc95be3f0397a9bb2c248a49c8296a9d21659a8cdd" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "http", + "reqwest", + "serde", + "task-local-extensions", + "thiserror", +] + +[[package]] +name = "reqwest-middleware-cache" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64dbc0a15e4d69f96e9b66ea5ff8102e2f4cce287bc26562363013d1c39d8f66" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "cacache", + "http", + "http-cache-semantics", + "httpdate", + "reqwest", + "reqwest-middleware", + "serde", + "task-local-extensions", + "url", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" + [[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.37.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "ryu" version = "1.0.13" @@ -1598,6 +2595,24 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1610,6 +2625,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + [[package]] name = "sctk-adwaita" version = "0.4.3" @@ -1622,11 +2643,37 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" @@ -1650,6 +2697,18 @@ dependencies = [ "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 = "servo-fontconfig" version = "0.5.1" @@ -1671,6 +2730,75 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "signal-hook" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.8" @@ -1724,6 +2852,16 @@ dependencies = [ "unicode_categories", ] +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "spirv" version = "0.2.0+1.5.4" @@ -1734,6 +2872,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ssri" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9cec0d388f39fbe79d7aa600e8d38053bf97b1bc8d350da7c0ba800d0f423f2" +dependencies = [ + "base64 0.10.1", + "digest 0.8.1", + "hex 0.3.2", + "serde", + "sha-1 0.8.2", + "sha2 0.8.2", + "thiserror", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1774,6 +2927,28 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "task-local-extensions" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba323866e5d033818e3240feeb9f7db2c4296674e4d9e16b97b7bf8f490434e8" +dependencies = [ + "pin-utils", +] + +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -1828,6 +3003,21 @@ dependencies = [ "bytemuck", ] +[[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.27.0" @@ -1835,7 +3025,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot 0.12.1", "pin-project-lite", + "signal-hook-registry", + "socket2", "tokio-macros", "windows-sys 0.45.0", ] @@ -1851,6 +3048,30 @@ dependencies = [ "syn 2.0.13", ] +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml_datetime" version = "0.6.1" @@ -1868,12 +3089,74 @@ dependencies = [ "winnow", ] +[[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.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +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 = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +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.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +[[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.10" @@ -1892,6 +3175,34 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "value-bag" +version = "1.0.0-alpha.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" +dependencies = [ + "ctor", + "version_check", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "vec_map" version = "0.8.2" @@ -1910,6 +3221,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2118,9 +3449,33 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "wgpu-core", - "wgpu-hal", - "wgpu-types", + "wgpu-core 0.14.2", + "wgpu-hal 0.14.1", + "wgpu-types 0.14.1", +] + +[[package]] +name = "wgpu" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d745a1b6d91d85c33defbb29f0eee0450e1d2614d987e14bf6baf26009d132d7" +dependencies = [ + "arrayvec 0.7.2", + "cfg-if 1.0.0", + "js-sys", + "log", + "naga 0.11.0", + "parking_lot 0.12.1", + "profiling", + "raw-window-handle 0.5.2", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core 0.15.1", + "wgpu-hal 0.15.4", + "wgpu-types 0.15.2", ] [[package]] @@ -2143,8 +3498,31 @@ dependencies = [ "smallvec", "thiserror", "web-sys", - "wgpu-hal", - "wgpu-types", + "wgpu-hal 0.14.1", + "wgpu-types 0.14.1", +] + +[[package]] +name = "wgpu-core" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7131408d940e335792645a98f03639573b0480e9e2e7cddbbab74f7c6d9f3fff" +dependencies = [ + "arrayvec 0.7.2", + "bit-vec", + "bitflags", + "codespan-reporting", + "fxhash", + "log", + "naga 0.11.0", + "parking_lot 0.12.1", + "profiling", + "raw-window-handle 0.5.2", + "smallvec", + "thiserror", + "web-sys", + "wgpu-hal 0.15.4", + "wgpu-types 0.15.2", ] [[package]] @@ -2160,10 +3538,10 @@ dependencies = [ "bitflags", "block", "core-graphics-types", - "d3d12", + "d3d12 0.5.0", "foreign-types 0.3.2", "fxhash", - "glow", + "glow 0.11.2", "gpu-alloc", "gpu-descriptor", "js-sys", @@ -2182,7 +3560,49 @@ dependencies = [ "thiserror", "wasm-bindgen", "web-sys", - "wgpu-types", + "wgpu-types 0.14.1", + "winapi", +] + +[[package]] +name = "wgpu-hal" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdcf61a283adc744bb5453dd88ea91f3f86d5ca6b027661c6c73c7734ae0288b" +dependencies = [ + "android_system_properties", + "arrayvec 0.7.2", + "ash", + "bit-set", + "bitflags", + "block", + "core-graphics-types", + "d3d12 0.6.0", + "foreign-types 0.3.2", + "fxhash", + "glow 0.12.1", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hassle-rs", + "js-sys", + "khronos-egl", + "libc", + "libloading", + "log", + "metal", + "naga 0.11.0", + "objc", + "parking_lot 0.12.1", + "profiling", + "range-alloc", + "raw-window-handle 0.5.2", + "renderdoc-sys", + "smallvec", + "thiserror", + "wasm-bindgen", + "web-sys", + "wgpu-types 0.15.2", "winapi", ] @@ -2195,11 +3615,21 @@ dependencies = [ "bitflags", ] +[[package]] +name = "wgpu-types" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32444e121b0bd00cb02c0de32fde457a9491bd44e03e7a5db6df9b1da2f6f110" +dependencies = [ + "bitflags", + "js-sys", + "web-sys", +] + [[package]] name = "wgputoy" version = "0.1.0" dependencies = [ - "async-executor", "async-recursion", "bitvec", "bytemuck", @@ -2221,16 +3651,29 @@ dependencies = [ "num", "pollster", "regex", + "reqwest", + "reqwest-middleware", + "reqwest-middleware-cache", + "serde", + "serde_json", "snailquote", + "tokio", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test", "web-sys", "wee_alloc", - "wgpu", + "wgpu 0.14.2", + "wgpu 0.15.1", "winit", ] +[[package]] +name = "widestring" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" + [[package]] name = "winapi" version = "0.3.9" @@ -2262,6 +3705,24 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-sys" version = "0.36.1" @@ -2275,13 +3736,37 @@ dependencies = [ "windows_x86_64_msvc 0.36.1", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[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.0", ] [[package]] @@ -2290,21 +3775,42 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -2317,6 +3823,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -2329,6 +3841,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -2341,6 +3859,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -2353,12 +3877,24 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -2371,6 +3907,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winit" version = "0.27.5" @@ -2413,6 +3955,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "wio" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index c91a751..5f1d303 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,10 +12,10 @@ default = ["console_error_panic_hook"] [dependencies] wasm-bindgen = "0.2.84" +wasm-bindgen-futures = "0.4.31" winit = "0.27.1" pollster = "0.2.5" -async-executor = "1.4.1" bytemuck = { version = "1.9.1", features = ["derive"] } log = "0.4.17" env_logger = "0.9.0" @@ -29,13 +29,6 @@ futures-intrusive = "0.4.0" async-recursion = "1.0.0" snailquote = "0.3.1" -console_log = "0.2.0" -js-sys = "0.3.57" -wasm-bindgen-futures = "0.4.31" -web-sys = "0.3.57" -gloo-utils = "0.1.4" -gloo-net = "0.2.1" - # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for @@ -49,16 +42,35 @@ console_error_panic_hook = { version = "0.1.7", optional = true } # Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. wee_alloc = { version = "0.4.5", optional = true } -[dependencies.wgpu] -#git = "https://github.com/gfx-rs/wgpu" +[target.'cfg(target_arch = "wasm32")'.dependencies] +console_log = "0.2.0" +js-sys = "0.3.57" +web-sys = "0.3.57" +gloo-utils = "0.1.4" +gloo-net = "0.2.1" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +reqwest = "0.11.16" +reqwest-middleware = "0.1" +reqwest-middleware-cache = "0.1" +tokio = { version = "1.27.0", features = ["full"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +# https://github.com/gfx-rs/wgpu/issues/3430 +[target.'cfg(target_arch = "wasm32")'.dependencies.wgpu] version = "0.14.2" +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgpu] +version = "0.15.1" [dependencies.naga] -git = "https://github.com/gfx-rs/naga" -rev = "99a7773e65f855ecdefc6fc0fffa6f0aacd3f867" version = "0.11.0" features = ["wgsl-in"] +[patch.crates-io.naga] +git = "https://github.com/gfx-rs/naga" +rev = "99a7773e65f855ecdefc6fc0fffa6f0aacd3f867" + [dependencies.image] version = "0.24.2" default-features = false diff --git a/examples/assert.wgsl b/examples/assert.wgsl new file mode 100644 index 0000000..7c02956 --- /dev/null +++ b/examples/assert.wgsl @@ -0,0 +1,34 @@ +#include + +#include "Dave_Hoskins/hash" + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(screen_size.y - id.y) - .5); + + // Normalised pixel coordinates (from 0 to 1) + let uv = fragCoord / float2(screen_size); + + // Time varying pixel colour + var col = .5 + .5 * cos(time.elapsed + uv.xyxx + float4(0.,2.,4.,0.)); + + col.x /= floor(3. * hash12(fragCoord)); + col.z -= hash12(fragCoord); + col.z = log(col.z); + if (time.frame % 199u == 0u) { col.w = col.x; } + + assert(0, !isinf(col.x)); + assert(1, isfinite(col.y)); + assert(2, !isnan(col.z)); + assert(3, isnormal(col.w)); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), col); +} diff --git a/examples/cornusammonis/3d-colorspace-projection.wgsl b/examples/cornusammonis/3d-colorspace-projection.wgsl new file mode 100644 index 0000000..7d23dcf --- /dev/null +++ b/examples/cornusammonis/3d-colorspace-projection.wgsl @@ -0,0 +1,136 @@ +#storage atomic_storage array> + +alias float4x4 = mat4x4; + +fn rotXW(t: float) -> float4x4 { + return float4x4( + 1.0, 0.0, 0.0, 0.0, + 0.0, cos(t), sin(t), 0.0, + 0.0, - sin(t), cos(t), 0.0, + 0.0, 0.0, 0.0, 1.0 + ); +} +fn rotXY(t: float) -> float4x4 { + return float4x4( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, cos(t), sin(t), + 0.0, 0.0, - sin(t), cos(t) + ); +} +fn rotXZ(t: float) -> float4x4 { + return float4x4( + 1.0, 0.0, 0.0, 0.0, + 0.0, cos(t), 0.0, sin(t), + 0.0, 0.0, 1.0, 0.0, + 0.0, - sin(t), 0.0, cos(t) + ); +} +fn rotYZ(t: float) -> float4x4 { + return float4x4( + cos(t), 0.0, 0.0, sin(t), + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + - sin(t), 0.0, 0.0, cos(t) + ); +} +fn rotYW(t: float) -> float4x4 { + return float4x4( + cos(t), 0.0, sin(t), 0.0, + 0.0, 1.0, 0.0, 0.0, + - sin(t), 0.0, cos(t), 0.0, + 0.0, 0.0, 0.0, 1.0 + ); +} +fn rotZW(t: float) -> float4x4 { + return float4x4( + cos(t), sin(t), 0.0, 0.0, + - sin(t), cos(t), 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); +} + +fn matrot(p: float4) -> float4 { + let t = float(time.frame)/240.; + let m = rotZW(t) * rotXW(t*1.3) * rotYW(t*1.6); + return m * p; +} + +fn toScreen(p : float4, R : float2) -> float2 { + let q = matrot(p - 0.5).xy; + return (0.5*q+0.5) * (R) * float2(R.y/R.x,1.) + float2(0.25*R.x,0.); +} + +@compute @workgroup_size(16, 16) +fn clear(@builtin(global_invocation_id) id: uint3) { + let screen_size = int2(textureDimensions(screen)); + let idx0 = int(id.x) + int(screen_size.x * int(id.y)); + + atomicStore(&atomic_storage[idx0*4+0], 0); + atomicStore(&atomic_storage[idx0*4+1], 0); + atomicStore(&atomic_storage[idx0*4+2], 0); + atomicStore(&atomic_storage[idx0*4+3], 0); +} + +@compute @workgroup_size(16, 16) +fn project(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = int2(textureDimensions(screen)); + let screen_size_f = float2(screen_size); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= uint(screen_size.x) || id.y >= uint(screen_size.y)) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(id.y) + .5); + + // Normalised pixel coordinates (from 0 to 1) + let uv = fragCoord / screen_size_f; + + let col = textureSampleLevel(channel0, bilinear, uv, 0.).xyz; + let pos = sqrt(col); + + let screenCoord = clamp(int2(toScreen(float4(pos,0.), screen_size_f)),int2(0,0),screen_size); + + let idx1 = int(screenCoord.x) + int(screen_size.x * screenCoord.y); + + atomicAdd(&atomic_storage[idx1*4+0], int(256. * col.x)); + atomicAdd(&atomic_storage[idx1*4+1], int(256. * col.y)); + atomicAdd(&atomic_storage[idx1*4+2], int(256. * col.z)); + atomicAdd(&atomic_storage[idx1*4+3], 1); +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(id.y) + .5); + + let idx = int(id.x) + int(screen_size.x * id.y); + + let count = atomicLoad(&atomic_storage[idx*4+3]); + let x = float(atomicLoad(&atomic_storage[idx*4+0]))/(float(count)*256.0); + let y = float(atomicLoad(&atomic_storage[idx*4+1]))/(float(count)*256.0); + let z = float(atomicLoad(&atomic_storage[idx*4+2]))/(float(count)*256.0); + + + let projectCol = float3(x,y,z); + + // Normalised pixel coordinates (from 0 to 1) + let uv = fragCoord / float2(screen_size); + + let col = textureSampleLevel(channel0, bilinear, uv, 0.).xyz; + + let result = select(col, projectCol, count > 0); + + //let result = col; + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(result, 1.)); +} diff --git a/examples/cornusammonis/3d-colorspace-projection.wgsl.json b/examples/cornusammonis/3d-colorspace-projection.wgsl.json new file mode 100644 index 0000000..301beff --- /dev/null +++ b/examples/cornusammonis/3d-colorspace-projection.wgsl.json @@ -0,0 +1,11 @@ +{ + "uniforms": [], + "textures": [ + { + "img": "/textures/london.jpg" + }, + { + "img": "/textures/blank.png" + } + ] +} \ No newline at end of file diff --git a/examples/davidar/boids-predation-4d.wgsl b/examples/davidar/boids-predation-4d.wgsl new file mode 100644 index 0000000..cae33a3 --- /dev/null +++ b/examples/davidar/boids-predation-4d.wgsl @@ -0,0 +1,270 @@ +// A four-dimensional boids predator/prey model. The particles are projected to three dimensions (click and drag the mouse to rotate) with the hue indicating the position in the fourth dimension. + +#storage atomic_storage array> + +// sqrt of particle count +const PREY = 100; +const PREDATORS = 20; + +fn hue(v: float) -> float4 { + return .6 + .6 * cos(6.3 * v + float4(0.,23.,21.,0.)); +} + +fn hash42(p: float2) -> float4 { + var p4 = fract(float4(p.xyxy) * float4(.1031, .1030, .0973, .1099)); + p4 += dot(p4, p4.wzxy+33.33); + return fract((p4.xxyz+p4.yzzw)*p4.zywx); +} + +fn clamp_length(v: float4, r: float) -> float4 { + if (length(v) > r) { + return r * normalize(v); + } else { + return v; + } +} + +fn normz(v: float4) -> float4 { + if (length(v) == 0.) { + return float4(0.); + } else { + return normalize(v); + } +} + +// particle rendering code based on "3D atomic rasterizer" by michael0884 +// https://compute.toys/view/21 + +alias float3x3 = mat3x3; + +struct Camera { + pos: float3, + cam: float3x3, + fov: float, + size: float2, +} + +fn GetCameraMatrix(ang: float2) -> float3x3 +{ + let x_dir = float3(cos(ang.x)*sin(ang.y), cos(ang.y), sin(ang.x)*sin(ang.y)); + let y_dir = normalize(cross(x_dir, float3(0.,1.,0.))); + let z_dir = normalize(cross(y_dir, x_dir)); + return float3x3(-x_dir, y_dir, z_dir); +} + +fn RasterizePoint(cam: Camera, p: float3, color: float3, r: int) { + let screen_size = int2(textureDimensions(screen)); + + // project to clip space + let dir = normalize(p - cam.pos); + let screen = dir * cam.cam; + let pos = float3(screen.yz * cam.size.y / (cam.fov * screen.x) + .5 * cam.size, screen.x * distance(cam.pos, p)); + if (pos.x < 0. || pos.x > cam.size.x || pos.y < 0. || pos.y > cam.size.y || pos.z < 0.) { + return; + } + + for (var i = -r; i <= r; i += 1) { + for (var j = -r; j <= r; j += 1) { + let idx = int(pos.x) + i + screen_size.x * (int(pos.y) + j); + let c = 255. * color / pos.z; + atomicAdd(&atomic_storage[idx*4+0], int(c.x)); + atomicAdd(&atomic_storage[idx*4+1], int(c.y)); + atomicAdd(&atomic_storage[idx*4+2], int(c.z)); + } + } +} + +fn Sample(pos: int2) -> float3 { + let screen_size = int2(textureDimensions(screen)); + let idx = pos.x + screen_size.x * pos.y; + let x = float(atomicLoad(&atomic_storage[idx*4+0])); + let y = float(atomicLoad(&atomic_storage[idx*4+1])); + let z = float(atomicLoad(&atomic_storage[idx*4+2])); + return float3(x,y,z) / 255.; +} + +struct Particle { + position: float4, + velocity: float4, +} + +fn LoadParticle(id: int2) -> Particle { + var p = Particle(); + p.position = passLoad(0, id, 0); + p.velocity = passLoad(1, id, 0); + return p; +} + +fn SaveParticle(id: int2, p: Particle) { + passStore(0, id, p.position); + passStore(1, id, p.velocity); +} + +@compute @workgroup_size(16, 16) +fn SimulatePrey(@builtin(global_invocation_id) id: uint3) { + var p = LoadParticle(int2(id.xy)); + + if(int(id.x) > PREY || int(id.y) > PREY) { + return; + } + + if(time.frame == 0u) { + p.position = hash42(float2(id.xy)); + } + + var separation = float4(); + var mean = Particle(); + var count = 0; + + // other prey + for(var i = 0; i < PREY; i += 1) { + for(var j = 0; j < PREY; j += 1) { + let q = LoadParticle(int2(i,j)); + let d = p.position - q.position; + if (length(d) < 1e-3) { + continue; + } + if (length(d) < custom.DistSeparation * 30.) { + separation += d / pow(length(d), 2.); + } + if (length(d) < custom.DistCohesion * 30.) { + mean.position += q.position; + mean.velocity += q.velocity; + count += 1; + } + } + } + + // predators + for(var i = PREY; i < PREY + PREDATORS; i += 1) { + for(var j = 0; j < PREDATORS; j += 1) { + let q = LoadParticle(int2(i,j)); + let d = p.position - q.position; + if (length(d) < 1e-3) { + continue; + } + if (length(d) < custom.DistPrey * 30.) + { + separation += d / pow(length(d), 1.); + } + } + } + + if (count > 0) { + mean.position /= float(count); + mean.velocity /= float(count); + } + + var cohesion = mean.position - p.position; + var alignment = mean.velocity; + + let v = p.velocity; + p.velocity += clamp_length(normz(separation) - v, custom.MaxForce * .1) * 1.5; + p.velocity += clamp_length(normz(cohesion) - v, custom.MaxForce * .1); + p.velocity += clamp_length(normz(alignment) - v, custom.MaxForce * .1); + + if (length(p.position) > 20.) { + p.velocity -= pow(10., -10. * custom.Homing) * normalize(p.position); + } + + p.velocity = clamp_length(p.velocity, custom.MaxSpeed); + + p.position += custom.Timescale * p.velocity; + + SaveParticle(int2(id.xy), p); +} + +@compute @workgroup_size(16, 16) +fn SimulatePredators(@builtin(global_invocation_id) id: uint3) { + var p = LoadParticle(int2(id.xy)); + + if(int(id.x) <= PREY || int(id.x) > PREY + PREDATORS || int(id.y) > PREDATORS) { + return; + } + + if(time.frame == 0u) { + p.position = hash42(float2(id.xy)); + } + + var separation = float4(); + + // other predators + for(var i = PREY; i < PREY + PREDATORS; i += 1) { + for(var j = 0; j < PREDATORS; j += 1) { + let q = LoadParticle(int2(i,j)); + let d = p.position - q.position; + if (length(d) < 1e-3) { + continue; + } + if (length(d) < custom.DistSeparationPred * 30.) { + separation += d / pow(length(d), 1.); + } + } + } + + // prey + for(var i = 0; i < PREY; i += 1) { + for(var j = 0; j < PREY; j += 1) { + let q = LoadParticle(int2(i,j)); + let d = p.position - q.position; + if (length(d) < 1e-3) { + continue; + } + if (length(d) < custom.DistPredation * 30.) { + separation -= d / pow(length(d), 1.); + } + } + } + + p.velocity = clamp_length(p.velocity + clamp_length(separation, custom.PredForce * .1), custom.PredSpeed); + + p.position += custom.Timescale * p.velocity; + + SaveParticle(int2(id.xy), p); +} + +@compute @workgroup_size(16, 16) +fn Clear(@builtin(global_invocation_id) id: uint3) { + let screen_size = uint2(textureDimensions(screen)); + let idx = int(id.x + screen_size.x * id.y); + atomicStore(&atomic_storage[idx*4+0], 0); + atomicStore(&atomic_storage[idx*4+1], 0); + atomicStore(&atomic_storage[idx*4+2], 0); + atomicStore(&atomic_storage[idx*4+3], 0); +} + +@compute @workgroup_size(16, 16) +fn Rasterize(@builtin(global_invocation_id) id: uint3) { + let screen_size = int2(textureDimensions(screen)); + + var ang = float2(mouse.pos) * float2(-radians(360.), radians(180.)) / float2(screen_size) + 1e-4; + ang.x += custom.Rotation * time.elapsed; + + var camera = Camera(); + camera.fov = .8; + camera.cam = GetCameraMatrix(ang); + camera.pos = camera.cam * -float3(100. * custom.Zoom, 0., 0.); + camera.size = float2(textureDimensions(screen)); + + var r = 0; + if (int(id.x) <= PREY && int(id.y) <= PREY) { + r = 1; + } else if (int(id.x) <= PREY + PREDATORS && int(id.y) <= PREDATORS) { + r = 3; + } else { + return; + } + + let p = LoadParticle(int2(id.xy)); + RasterizePoint(camera, p.position.xyz, hue(.5 + .5 * p.position.w / 20.).rgb, r); +} + +@compute @workgroup_size(16, 16) +fn Accumulate(@builtin(global_invocation_id) id: uint3) { + var col = 20. * Sample(int2(id.xy)); + if (mouse.click == 0) { + col += passLoad(2, int2(id.xy), 0).rgb * custom.Accumulation; + } + passStore(2, int2(id.xy), float4(col, 1.)); + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/davidar/boids-predation-4d.wgsl.json b/examples/davidar/boids-predation-4d.wgsl.json new file mode 100644 index 0000000..c87610b --- /dev/null +++ b/examples/davidar/boids-predation-4d.wgsl.json @@ -0,0 +1,69 @@ +{ + "uniforms": [ + { + "name": "Zoom", + "value": 0.646 + }, + { + "name": "Accumulation", + "value": 0.723 + }, + { + "name": "DistSeparation", + "value": 0.103 + }, + { + "name": "DistCohesion", + "value": 0.142 + }, + { + "name": "MaxForce", + "value": 0.295 + }, + { + "name": "MaxSpeed", + "value": 0.266 + }, + { + "name": "DistPredation", + "value": 0.579 + }, + { + "name": "PredSpeed", + "value": 0.5 + }, + { + "name": "PredForce", + "value": 0.422 + }, + { + "name": "DistPrey", + "value": 0.547 + }, + { + "name": "Homing", + "value": 0.165 + }, + { + "name": "Timescale", + "value": 0.495 + }, + { + "name": "Rotation", + "value": 0.135 + }, + { + "name": "DistSeparationPred", + "value": 0.218 + } + ], + "textures": [ + { + "img": "/textures/blank.png" + }, + { + "img": "/textures/blank.png" + } + ], + "float32Enabled": false +} \ No newline at end of file diff --git a/examples/davidar/buddhabrot.wgsl b/examples/davidar/buddhabrot.wgsl new file mode 100644 index 0000000..b9bed0b --- /dev/null +++ b/examples/davidar/buddhabrot.wgsl @@ -0,0 +1,67 @@ +#storage atomic_storage array> + +// 2022 David A Roberts + +// https://www.jcgt.org/published/0009/03/02/ +// https://www.pcg-random.org/ +fn pcg(seed: ptr) -> float { + *seed = *seed * 747796405u + 2891336453u; + let word = ((*seed >> ((*seed >> 28u) + 4u)) ^ *seed) * 277803737u; + return float((word >> 22u) ^ word) / float(0xffffffffu); +} + +@compute @workgroup_size(16, 16) +fn main_hist(@builtin(global_invocation_id) id: uint3) { + let screen_size = uint2(textureDimensions(screen)); + let resolution = float2(screen_size); + var seed = id.x + id.y * screen_size.x + time.frame * screen_size.x * screen_size.y; + for (var iter = 0; iter < 8; iter = iter + 1) { + let aspect = resolution.xy / resolution.y; + let uv = float2(float(id.x) + pcg(&seed), float(id.y) + pcg(&seed)) / resolution; + let uv0 = float2(float(id.x) + pcg(&seed), float(id.y) + pcg(&seed)) / resolution; + let c = (uv * 2. - 1.) * aspect * 1.5; + let z0 = (uv0 * 2. - 1.) * aspect * 1.5; + var z = z0; + var n = 0; + for (n = 0; n < 2500; n = n + 1) { + z = float2(z.x * z.x - z.y * z.y, 2. * z.x * z.y) + c; + if (dot(z,z) > 4.) { break; } + } + z = z0; + for (var i = 0; i < 2500; i = i + 1) { + z = float2(z.x * z.x - z.y * z.y, 2. * z.x * z.y) + c; + if (dot(z,z) > 4.) { break; } + let t = float(time.frame) / 60.; + let p = (cos(.3*t) * z + sin(.3*t) * c) / 1.5 / aspect * .5 + .5; + if (p.x < 0. || p.x > 1. || p.y < 0. || p.y > 1.) { continue; } + let idx1 = int(resolution.x * p.x) + int(resolution.y * p.y) * int(screen_size.x); + let idx2 = int(resolution.x * p.x) + int(resolution.y * (1. - p.y)) * int(screen_size.x); + if (n < 25) { + atomicAdd(&atomic_storage[idx1*4+2], 1); + atomicAdd(&atomic_storage[idx2*4+2], 1); + } else if (n < 250) { + atomicAdd(&atomic_storage[idx1*4+1], 1); + atomicAdd(&atomic_storage[idx2*4+1], 1); + } else if (n < 2500) { + atomicAdd(&atomic_storage[idx1*4+0], 1); + atomicAdd(&atomic_storage[idx2*4+0], 1); + } + } + } +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + let screen_size = uint2(textureDimensions(screen)); + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + let idx = int(id.x + id.y * screen_size.x); + let x = float(atomicLoad(&atomic_storage[idx*4+0])); + let y = float(atomicLoad(&atomic_storage[idx*4+1])); + let z = float(atomicLoad(&atomic_storage[idx*4+2])); + var r = float3(x + y + z, y + z, z) / 3e3; + r = smoothstep(float3(0.), float3(1.), 2.5 * pow(r, float3(.9, .8, .7))); + textureStore(screen, int2(id.xy), float4(r, 1.)); + atomicStore(&atomic_storage[idx*4+0], int(x * .7)); + atomicStore(&atomic_storage[idx*4+1], int(y * .7)); + atomicStore(&atomic_storage[idx*4+2], int(z * .7)); +} diff --git a/examples/davidar/caustics.wgsl b/examples/davidar/caustics.wgsl new file mode 100644 index 0000000..0ec3905 --- /dev/null +++ b/examples/davidar/caustics.wgsl @@ -0,0 +1,91 @@ +#storage atomic_storage array> + +// 2022 David A Roberts + +// https://www.shadertoy.com/view/4djSRW +fn hash44(p: float4) -> float4 { + var p4 = fract(p * float4(.1031, .1030, .0973, .1099)); + p4 = p4 + dot(p4, p4.wzxy+33.33); + return fract((p4.xxyz+p4.yzzw)*p4.zywx); +} + +const dt = 1.; +const n = float2(0., 1.); +const e = float2(1., 0.); +const s = float2(0., -1.); +const w = float2(-1., 0.); + +fn A(fragCoord: float2) -> float4 { + return passLoad(0, int2(fragCoord), 0); +} + +fn B(fragCoord: float2) -> float4 { + return passSampleLevelBilinearRepeat(1, fragCoord / float2(textureDimensions(screen)), 0.); +} + +fn T(fragCoord: float2) -> float4 { + return B(fragCoord - dt * B(fragCoord).xy); +} + +@compute @workgroup_size(16, 16) +fn main_velocity(@builtin(global_invocation_id) id: uint3) { + let screen_size = uint2(textureDimensions(screen)); + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + let u = float2(id.xy) + 0.5; + var r = T(u); + r.x = r.x - dt * 0.25 * (T(u+e).z - T(u+w).z); + r.y = r.y - dt * 0.25 * (T(u+n).z - T(u+s).z); + + if (time.frame < 3u) { r = float4(0.); } + passStore(0, int2(id.xy), r); +} + +@compute @workgroup_size(16, 16) +fn main_pressure(@builtin(global_invocation_id) id: uint3) { + let screen_size = uint2(textureDimensions(screen)); + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + let u = float2(id.xy) + 0.5; + var r = A(u); + r.z = r.z - dt * 0.25 * (A(u+e).x - A(u+w).x + A(u+n).y - A(u+s).y); + + let t = float(time.frame) / 120.; + let o = float2(screen_size)/2. * (1. + .75 * float2(cos(t/15.), sin(2.7*t/15.))); + r = mix(r, float4(0.5 * sin(dt * 2. * t) * sin(dt * t), 0., r.z, 1.), exp(-0.2 * length(u - o))); + passStore(1, int2(id.xy), r); +} + +@compute @workgroup_size(16, 16) +fn main_caustics(@builtin(global_invocation_id) id: uint3) { + let screen_size = uint2(textureDimensions(screen)); + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + for (var i = 0; i < 25; i = i+1) { + let h = hash44(float4(float2(id.xy), float(time.frame), float(i))); + var p = float2(id.xy) + h.xy; + let z = mix(.3, 1., h.z); + let c = max(cos(z*6.2+float4(1.,2.,3.,4.)),float4(0.)); + let grad = 0.25 * float2(B(p+e).z - B(p+w).z, B(p+n).z - B(p+s).z); + p = p + 1e5 * grad * z; + p = fract(p / float2(screen_size)) * float2(screen_size); + let idx = int(p.x) + int(p.y) * int(screen_size.x); + atomicAdd(&atomic_storage[idx*4+0], int(c.x * 256.)); + atomicAdd(&atomic_storage[idx*4+1], int(c.y * 256.)); + atomicAdd(&atomic_storage[idx*4+2], int(c.z * 256.)); + } +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + let screen_size = uint2(textureDimensions(screen)); + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + let idx = int(id.x) + int(id.y) * int(screen_size.x); + let x = float(atomicLoad(&atomic_storage[idx*4+0])); + let y = float(atomicLoad(&atomic_storage[idx*4+1])); + let z = float(atomicLoad(&atomic_storage[idx*4+2])); + var r = float3(x, y, z) / 256.; + r = r * sqrt(r) / 5e3; + r = r * float3(.5, .75, 1.); + textureStore(screen, int2(id.xy), float4(r, 1.)); + atomicStore(&atomic_storage[idx*4+0], int(x * .9)); + atomicStore(&atomic_storage[idx*4+1], int(y * .9)); + atomicStore(&atomic_storage[idx*4+2], int(z * .9)); +} diff --git a/examples/davidar/environment-sampling.wgsl b/examples/davidar/environment-sampling.wgsl new file mode 100644 index 0000000..4374f48 --- /dev/null +++ b/examples/davidar/environment-sampling.wgsl @@ -0,0 +1,55 @@ +// 2022 David A Roberts +// A simple example of randomly sampling locations from a HDR environment map according to lighting intensity + +fn pcg_random(seed: ptr) -> float { + *seed = *seed * 747796405u + 2891336453u; + let word = ((*seed >> ((*seed >> 28u) + 4u)) ^ *seed) * 277803737u; + return float((word >> 22u) ^ word) / float(0xffffffffu); +} + +// weighted coin flip (bernoulli) +fn flip(state: ptr, p: float) -> bool { + return pcg_random(state) <= p; +} + +// relative weight of given region of image +fn weight(pos: int2, mip: int) -> float { + return length(textureLoad(channel0, pos, mip).rgb); +} + +// sample location from image according to pixel weights +fn sample_coord(state: ptr, mipmax: int) -> int2 { + var pos = int2(0,0); + for (var mip = mipmax - 1; mip >= 0; mip -= 1) { + pos *= 2; + let w00 = weight(pos + int2(0,0), mip); + let w01 = weight(pos + int2(0,1), mip); + let w10 = weight(pos + int2(1,0), mip); + let w11 = weight(pos + int2(1,1), mip); + let w0 = w00 + w01; // weight of column 0 + let w1 = w10 + w11; // weight of column 1 + let w = w0 + w1; // total weight + pos += select( + int2(0, select(0, 1, flip(state, w01 / w0))), // cond prob of row 1 given col 0 + int2(1, select(0, 1, flip(state, w11 / w1))), // cond prob of row 1 given col 1 + flip(state, w1 / w)); // prob of col 1 + } + return pos; +} + +@compute @workgroup_size(16, 16) +fn blank(@builtin(global_invocation_id) id: uint3) { + textureStore(screen, int2(id.xy), float4(0.,0.,0.,1.)); +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + let screen_size = uint2(textureDimensions(screen)); + var seed = id.x + id.y * screen_size.x + time.frame * screen_size.x * screen_size.y; + let mipmax = int(textureNumLevels(channel0)) - 1; + let uv = float2(sample_coord(&seed, mipmax)) / float2(textureDimensions(channel0)); + if (uv.y > 1.) { return; } + if (flip(&seed, .25)) { // splat 25% of sampled points to screen + textureStore(screen, int2(uv * float2(textureDimensions(screen))), float4(1.)); + } +} diff --git a/examples/davidar/environment-sampling.wgsl.json b/examples/davidar/environment-sampling.wgsl.json new file mode 100644 index 0000000..68bd376 --- /dev/null +++ b/examples/davidar/environment-sampling.wgsl.json @@ -0,0 +1,14 @@ +{ + "uniforms": [], + "textures": [ + { + "img": "https://dl.polyhaven.org/file/ph-assets/HDRIs/hdr/2k/spruit_sunrise_2k.hdr", + "thumb": "https://dl.polyhaven.org/file/ph-assets/HDRIs/extra/Tonemapped%20JPG/spruit_sunrise.jpg", + "url": "https://polyhaven.com/a/spruit_sunrise" + }, + { + "img": "/textures/blank.png" + } + ], + "float32Enabled": false +} \ No newline at end of file diff --git a/examples/davidar/neighbourhood-grid.wgsl b/examples/davidar/neighbourhood-grid.wgsl new file mode 100644 index 0000000..ad16699 --- /dev/null +++ b/examples/davidar/neighbourhood-grid.wgsl @@ -0,0 +1,410 @@ +// This is a fork of boids-predation-4d.wgsl using a neighbourhood grid for faster nearest-neighbour lookups + +#include "Dave_Hoskins/hash" + +// sqrt of particle count +#define PREY 250 +#define PREDATORS 20 + +// number of grid cells per dimension +#define GRID_RES 16 + +// total number of grid cells = pow(GRID_RES, 3) +#define GRID_SIZE 4096 + +// maximum number of particles per cell +#define GRID_CAP 1000 + +// world size of grid extents +#define GRID_WIDTH 50. + +struct Atoms { + pixels: array,3>, SCREEN_HEIGHT>, SCREEN_WIDTH>, + count: array, GRID_SIZE>, +} + +#storage atoms Atoms + +struct Particle { + position: float3, + velocity: float3, +} + +struct Store { + particles: array, GRID_SIZE>, + count: array, +} + +#storage store Store + + + +fn hue(v: float) -> float4 { + return .6 + .6 * cos(6.3 * v + float4(0.,23.,21.,0.)); +} + +fn clamp_length(v: float3, r: float) -> float3 { + if (length(v) > r) { + return r * normalize(v); + } else { + return v; + } +} + +fn normz(v: float3) -> float3 { + if (length(v) == 0.) { + return float3(0.); + } else { + return normalize(v); + } +} + +// particle rendering code based on "3D atomic rasterizer" by michael0884 +// https://compute.toys/view/21 + +alias float3x3 = mat3x3; + +struct Camera { + pos: float3, + cam: float3x3, + fov: float, + size: float2, +} + +fn GetCameraMatrix(ang: float2) -> float3x3 +{ + let x_dir = float3(cos(ang.x)*sin(ang.y), cos(ang.y), sin(ang.x)*sin(ang.y)); + let y_dir = normalize(cross(x_dir, float3(0.,1.,0.))); + let z_dir = normalize(cross(y_dir, x_dir)); + return float3x3(-x_dir, y_dir, z_dir); +} + +fn camera() -> Camera { + let screen_size = int2(textureDimensions(screen)); + + var ang = float2(mouse.pos) * float2(-radians(360.), radians(180.)) / float2(screen_size); + if (ang.y == 0.) { + ang.y = radians(120.); + } + ang += 1e-4; + ang.x += custom.Rotation * time.elapsed; + + var camera = Camera(); + camera.fov = .8; + camera.cam = GetCameraMatrix(ang); + camera.pos = camera.cam * -float3(100. * custom.Zoom, 0., 0.); + camera.size = float2(textureDimensions(screen)); + return camera; +} + +fn RasterizePoint(p: float3, color: float3, r: int) { + let screen_size = int2(textureDimensions(screen)); + let cam = camera(); + + // project to clip space + let dir = normalize(p - cam.pos); + let screen = dir * cam.cam; + let pos = float3(screen.yz * cam.size.y / (cam.fov * screen.x) + .5 * cam.size, screen.x * distance(cam.pos, p)); + if (pos.x < 0. || pos.x > cam.size.x || pos.y < 0. || pos.y > cam.size.y || pos.z < 0.) { + return; + } + + for (var i = -r; i <= r; i += 1) { + for (var j = -r; j <= r; j += 1) { + let idx = int(pos.x) + i + screen_size.x * (int(pos.y) + j); + //let c = 255. * color / pos.z; + let c = 255. * hue(pos.z / GRID_WIDTH).rgb; + atomicAdd(&atoms.pixels[int(pos.x) + i][int(pos.y) + j][0], uint(c.x)); + atomicAdd(&atoms.pixels[int(pos.x) + i][int(pos.y) + j][1], uint(c.y)); + atomicAdd(&atoms.pixels[int(pos.x) + i][int(pos.y) + j][2], uint(c.z)); + } + } +} + +fn Sample(pos: int2) -> float3 { + let screen_size = int2(textureDimensions(screen)); + let idx = pos.x + screen_size.x * pos.y; + let x = float(atomicLoad(&atoms.pixels[pos.x][pos.y][0])); + let y = float(atomicLoad(&atoms.pixels[pos.x][pos.y][1])); + let z = float(atomicLoad(&atoms.pixels[pos.x][pos.y][2])); + return float3(x,y,z) / 255.; +} + +fn LoadParticle(id: int2) -> Particle { + var p = Particle(); + p.position = passLoad(0, id, 0).xyz; + p.velocity = passLoad(1, id, 0).xyz; + return p; +} + +fn SaveParticle(id: int2, p: Particle) { + passStore(0, id, float4(p.position, 0.)); + passStore(1, id, float4(p.velocity, 0.)); +} + +fn grid_cell(pos: float3) -> int3 { + return clamp(int3((.5 + pos / GRID_WIDTH) * float(GRID_RES)), int3(0), int3(GRID_RES - 1)); +} + +fn grid_cell_id(cell: int3) -> int { + return cell.x + cell.y * GRID_RES + cell.z * GRID_RES * GRID_RES; +} + +fn grid_id_to_cell(i: int) -> int3 { + return int3(i, i / GRID_RES, i / GRID_RES / GRID_RES) % GRID_RES; +} + +fn grid_cell_dist_min(cell1: int3, cell2: int3) -> float { + let cell_width = GRID_WIDTH / float(GRID_RES); + let cdist = distance(float3(cell1), float3(cell2)) * cell_width; + return max(0., cdist - sqrt(3.) * cell_width); +} + +fn grid_increment(cell_id: int) -> int { + return int(atomicAdd(&atoms.count[cell_id], 1u)); +} + +fn grid_clear(cell_id: int) { + atomicStore(&atoms.count[cell_id], 0u); +} + +fn grid_insert(p: Particle) { + let cell = grid_cell(p.position); + let cell_id = grid_cell_id(cell); + + let idx = grid_increment(cell_id); + if (idx >= GRID_CAP) { return; } + + store.particles[cell_id][idx] = p; +} + +fn grid_neighbours() -> array { + var r: array; + var n = 0; + for (var i = -1; i <= 1; i += 1) { + for (var j = -1; j <= 1; j += 1) { + for (var k = -1; k <= 1; k += 1) { + r[n] = int3(i,j,k); + n += 1; + } + } + } + return r; +} + +@compute @workgroup_size(16, 16) +fn SimulatePrey(@builtin(global_invocation_id) id: uint3) { + var p = LoadParticle(int2(id.xy)); + + if(int(id.x) > PREY || int(id.y) > PREY) { + return; + } + + if(time.frame == 0u) { + p.position = (hash42(float2(id.xy)).xyz - .5) * GRID_WIDTH; + } + + var separation = float3(); + var mean = Particle(); + var count = 0; + + let offsets = grid_neighbours(); + + let cell = grid_cell(p.position); + + // other prey + for(var o = 0; o < 27; o += 1) { + let offset = offsets[o]; + let ncell = cell + offset; + if (any(ncell < int3(0)) || any(ncell >= int3(GRID_RES))) { + continue; + } + let i = grid_cell_id(ncell); + let gcount = int(store.count[i]); + for(var j = 0; j < gcount; j += 1) { + let q = store.particles[i][j]; + let d = p.position - q.position; + if (length(d) < 1e-3) { + continue; + } + if (length(d) < custom.DistSeparation * 30.) { + separation += d / pow(length(d), 2.); + } + if (length(d) < custom.DistCohesion * 30.) { + mean.position += q.position; + mean.velocity += q.velocity; + count += 1; + } + } + } + + // predators + for(var i = PREY; i < PREY + PREDATORS; i += 1) { + for(var j = 0; j < PREDATORS; j += 1) { + let q = LoadParticle(int2(i,j)); + let d = p.position - q.position; + if (length(d) < 1e-3) { + continue; + } + if (length(d) < custom.DistPrey * 30.) { + separation += d / pow(length(d), 1.); + } + } + } + + if (count > 0) { + mean.position /= float(count); + mean.velocity /= float(count); + } + + var cohesion = mean.position - p.position; + var alignment = mean.velocity; + + let v = p.velocity; + p.velocity += clamp_length(normz(separation) - v, custom.MaxForce * .1) * 1.5; + p.velocity += clamp_length(normz(cohesion) - v, custom.MaxForce * .1); + p.velocity += clamp_length(normz(alignment) - v, custom.MaxForce * .1); + + if (length(p.position) > 20.) { + p.velocity -= pow(10., -10. * custom.Homing) * normalize(p.position); + } + + p.velocity = clamp_length(p.velocity, custom.MaxSpeed); + + p.position += custom.Timescale * p.velocity; + + SaveParticle(int2(id.xy), p); +} + +@compute @workgroup_size(16, 16) +fn SimulatePredators(@builtin(global_invocation_id) id: uint3) { + var p = LoadParticle(int2(id.xy)); + + if(int(id.x) <= PREY || int(id.x) > PREY + PREDATORS || int(id.y) > PREDATORS) { + return; + } + + if(time.frame == 0u) { + p.position = (hash42(float2(id.xy)).xyz - .5) * GRID_WIDTH; + } + + var separation = float3(); + + // other predators + for(var i = PREY; i < PREY + PREDATORS; i += 1) { + for(var j = 0; j < PREDATORS; j += 1) { + let q = LoadParticle(int2(i,j)); + let d = p.position - q.position; + if (length(d) < 1e-3) { + continue; + } + if (length(d) < custom.DistSeparationPred * 30.) { + separation += d / pow(length(d), 1.); + } + } + } + + let cell = grid_cell(p.position); + + // prey + for(var i = 0; i < GRID_SIZE; i += 1) { + let ncell = grid_id_to_cell(i); + var gcount = int(store.count[i]); + for(var j = 0; j < 10; j += 1) { + if (j >= gcount) { + break; + } + let q = store.particles[i][j]; + let d = p.position - q.position; + if (length(d) < 1e-3) { + continue; + } + if (length(d) < custom.DistPredation * 30.) { + separation -= d / pow(length(d), 1.); + } + } + } + + p.velocity = clamp_length(p.velocity + clamp_length(separation, custom.PredForce * .1), custom.PredSpeed); + + p.position += custom.Timescale * p.velocity; + + SaveParticle(int2(id.xy), p); +} + +@compute @workgroup_size(16, 16) +fn Clear(@builtin(global_invocation_id) id: uint3) { + let screen_size = uint2(textureDimensions(screen)); + let idx = int(id.x + screen_size.x * id.y); + atomicStore(&atoms.pixels[id.x][id.y][0], 0u); + atomicStore(&atoms.pixels[id.x][id.y][1], 0u); + atomicStore(&atoms.pixels[id.x][id.y][2], 0u); + + if (id.x == 0u && id.y == 0u && id.z == 0u) { + for (var i = 0; i < GRID_SIZE; i += 1) { + grid_clear(i); + } + } +} + +@compute @workgroup_size(16, 16) +fn BuildGrid(@builtin(global_invocation_id) id: uint3) { + var p = LoadParticle(int2(id.xy)); + + if(int(id.x) > PREY || int(id.y) > PREY) { + return; + } + + grid_insert(p); +} + +@compute @workgroup_size(256) +#workgroup_count FreezeGrid 16 1 1 +fn FreezeGrid(@builtin(global_invocation_id) id: uint3) { + if(int(id.x) >= GRID_SIZE || int(id.y) > 0) { + return; + } + let cell_id = int(id.x); + store.count[cell_id] = uint(atomicLoad(&atoms.count[cell_id])); +} + +@compute @workgroup_size(16, 16) +#workgroup_count RasterizePrey 64 256 1 +fn RasterizePrey(@builtin(global_invocation_id) id: uint3) { + if (int(id.y) < GRID_SIZE && id.x < store.count[id.y]) { + let p = store.particles[id.y][id.x]; + RasterizePoint(p.position.xyz, float3(1.), 1); + } +} + +@compute @workgroup_size(16, 16) +fn RasterizePredators(@builtin(global_invocation_id) id: uint3) { + var r = 0; + if (int(id.x) <= PREY && int(id.y) <= PREY) { + return; + } else if (int(id.x) <= PREY + PREDATORS && int(id.y) <= PREDATORS) { + r = 3; + } else { + return; + } + + let p = LoadParticle(int2(id.xy)); + RasterizePoint(p.position.xyz, float3(1.), r); +} + +@compute @workgroup_size(16, 16) +fn Accumulate(@builtin(global_invocation_id) id: uint3) { + let screen_size = uint2(textureDimensions(screen)); + var col = .1 * Sample(int2(id.xy)); + if (mouse.click == 0) { + col += passLoad(2, int2(id.xy), 0).rgb * custom.Accumulation; + } + passStore(2, int2(id.xy), float4(col, 1.)); + + if (int(id.x) < GRID_SIZE && screen_size.y - id.y < store.count[id.x]) { + let p = store.particles[id.x][id.y]; + col += .1; + } + + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/davidar/neighbourhood-grid.wgsl.json b/examples/davidar/neighbourhood-grid.wgsl.json new file mode 100644 index 0000000..1609d18 --- /dev/null +++ b/examples/davidar/neighbourhood-grid.wgsl.json @@ -0,0 +1,69 @@ +{ + "uniforms": [ + { + "name": "Zoom", + "value": 1 + }, + { + "name": "Accumulation", + "value": 0.434 + }, + { + "name": "DistSeparation", + "value": 0.087 + }, + { + "name": "DistCohesion", + "value": 0.113 + }, + { + "name": "MaxForce", + "value": 0.295 + }, + { + "name": "MaxSpeed", + "value": 0.266 + }, + { + "name": "DistPredation", + "value": 0.579 + }, + { + "name": "PredSpeed", + "value": 0.5 + }, + { + "name": "PredForce", + "value": 0.422 + }, + { + "name": "DistPrey", + "value": 0.547 + }, + { + "name": "Homing", + "value": 0.19 + }, + { + "name": "Timescale", + "value": 0.495 + }, + { + "name": "Rotation", + "value": 0.5 + }, + { + "name": "DistSeparationPred", + "value": 0.218 + } + ], + "textures": [ + { + "img": "/textures/blank.png" + }, + { + "img": "/textures/blank.png" + } + ], + "float32Enabled": false +} \ No newline at end of file diff --git a/examples/davidar/pbrt-with-ibl.wgsl b/examples/davidar/pbrt-with-ibl.wgsl new file mode 100644 index 0000000..ad01bfc --- /dev/null +++ b/examples/davidar/pbrt-with-ibl.wgsl @@ -0,0 +1,1150 @@ +/* Partial port of PBRT v3 to WGSL + with support for image based lighting. Quite slow to compile and still a bit buggy. + https://pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Path_Tracing + +BSD 2-Clause License + +Copyright (c) 1998-2015, Matt Pharr, Greg Humphreys, and Wenzel Jakob +Copyright (c) 2022, David A Roberts + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +struct BSDF { + Rd: float3, + Rs: float3, + roughness: float, +} + +fn isfinite(x: f32) -> bool { + return clamp(x, -3.4e38, 3.4e38) == x; +} + +// A.1 Main Include File + +const Pi = 3.14159265358979323846; + +// A.5 Mathematical Routines + +fn Quadratic(a: f32, b: f32, c: f32, t0: ptr, t1: ptr) -> bool { + // Find quadratic discriminant + let discrim = b * b - 4. * a * c; + if (discrim < 0.) { return false; } + let rootDiscrim = sqrt(discrim); + + // Compute quadratic t values + var q = 0.; + if (b < 0.) { + q = -.5 * (b - rootDiscrim); + } else { + q = -.5 * (b + rootDiscrim); + } + *t0 = q / a; + *t1 = c / q; + if (*t0 > *t1) { + let swap = *t0; + *t0 = *t1; + *t1 = swap; + } + return true; +} + +// 2.2 Vectors + +alias Vector3f = vec3; + +fn MinComponent(v: Vector3f) -> f32 { + return min(v.x, min(v.y, v.z)); +} + +fn MaxComponent(v: Vector3f) -> f32 { + return max(v.x, max(v.y, v.z)); +} + +fn MaxDimension(v: Vector3f) -> i32 { + if (v.x > v.y) { + if (v.x > v.z) { + return 0; + } else { + return 2; + } + } else { + if (v.y > v.z) { + return 1; + } else { + return 2; + } + } +} + +fn Permute(v: Vector3f, x: i32, y: i32, z: i32) -> Vector3f { + return Vector3f(v[x], v[y], v[z]); +} + +fn CoordinateSystem(v1: Vector3f, v2: ptr, v3: ptr) { + if (abs(v1.x) > abs(v1.y)) { + *v2 = Vector3f(-v1.z, 0., v1.x) / sqrt(v1.x * v1.x + v1.z * v1.z); + } else { + *v2 = Vector3f(0., v1.z, -v1.y) / sqrt(v1.y * v1.y + v1.z * v1.z); + } + *v3 = cross(v1, *v2); +} + +// 2.3 Points + +alias Point2f = vec2; +alias Point3f = vec3; + +// 2.4 Normals + +alias Normal3f = vec3; + +// 2.5 Rays + +struct Ray { + o: Point3f, + d: Vector3f, +} + +// 2.6 Bounding Boxes + +struct Bounds2f { + pMin: Point2f, + pMax: Point2f, +} + +struct Bounds3f { + pMin: Point3f, + pMax: Point3f, +} + +// 2.7 Transformations + +alias Matrix4x4 = mat4x4; + +struct Transform { + m: Matrix4x4, + mInv: Matrix4x4, +} + +fn Translate(delta: Vector3f) -> Transform { + let m = transpose(Matrix4x4( + 1., 0., 0., delta.x, + 0., 1., 0., delta.y, + 0., 0., 1., delta.z, + 0., 0., 0., 1., + )); + let mInv = transpose(Matrix4x4( + 1., 0., 0., -delta.x, + 0., 1., 0., -delta.y, + 0., 0., 1., -delta.z, + 0., 0., 0., 1., + )); + return Transform(m, mInv); +} + +fn Scale(x: f32, y: f32, z: f32) -> Transform { + let m = transpose(Matrix4x4( + x, 0., 0., 0., + 0., y, 0., 0., + 0., 0., z, 0., + 0., 0., 0., 1.)); + let mInv = transpose(Matrix4x4( + 1./x, 0., 0., 0., + 0., 1./y, 0., 0., + 0., 0., 1./z, 0., + 0., 0., 0., 1.)); + return Transform(m, mInv); +} + +fn RotateX(theta: f32) -> Transform { + let sinTheta = sin(radians(theta)); + let cosTheta = cos(radians(theta)); + let m = transpose(Matrix4x4( + 1., 0., 0., 0., + 0., cosTheta, -sinTheta, 0., + 0., sinTheta, cosTheta, 0., + 0., 0., 0., 1.)); + return Transform(m, transpose(m)); +} + +fn RotateY(theta: f32) -> Transform { + let sinTheta = sin(radians(theta)); + let cosTheta = cos(radians(theta)); + let m = transpose(Matrix4x4( + cosTheta, 0., sinTheta, 0., + 0., 1., 0., 0., + -sinTheta, 0., cosTheta, 0., + 0., 0., 0., 1.)); + return Transform(m, transpose(m)); +} + +fn RotateZ(theta: f32) -> Transform { + let sinTheta = sin(radians(theta)); + let cosTheta = cos(radians(theta)); + let m = transpose(Matrix4x4( + cosTheta, -sinTheta, 0., 0., + sinTheta, cosTheta, 0., 0., + 0., 0., 1., 0., + 0., 0., 0., 1.)); + return Transform(m, transpose(m)); +} + +fn Transform_Inverse(t: Transform) -> Transform { + return Transform(t.mInv, t.m); +} + +// 2.8 Applying Transformations + +fn Transform_Point3f(t: Transform, p: Point3f) -> Point3f { + let q = t.m * vec4(p, 1.); + return q.xyz / q.w; +} + +fn Transform_Vector3f(t: Transform, v: Vector3f) -> Vector3f { + return (t.m * vec4(v, 0.)).xyz; +} + +fn Transform_Normal3f(t: Transform, n: Normal3f) -> Normal3f { + return (transpose(t.mInv) * vec4(n, 0.)).xyz; +} + +fn Transform_Ray(t: Transform, r: Ray) -> Ray { + let o = Transform_Point3f(t, r.o); + let d = Transform_Vector3f(t, r.d); + return Ray(o, d); +} + +fn Compose(t: Transform, t2: Transform) -> Transform { + return Transform(t.m * t2.m, t2.mInv * t.mInv); +} + +fn Transform_SwapsHandedness(t: Transform) -> bool { + let m = mat3x3(t.m[0].xyz, t.m[1].xyz, t.m[2].xyz); + return determinant(m) < 0.; +} + +// 2.10 Interactions + +struct TangentSpace { + n: Normal3f, + dpdu: Vector3f, + dpdv: Vector3f, +} + +struct SurfaceInteraction { + p: Point3f, + wo: Vector3f, + + uv: Point2f, + t: TangentSpace, + shading: TangentSpace, +} + +fn Transform_TangentSpace(t: Transform, s: TangentSpace) -> TangentSpace { + return TangentSpace( + normalize(Transform_Normal3f(t, s.n)), + Transform_Vector3f(t, s.dpdu), + Transform_Vector3f(t, s.dpdv)); +} + +fn Transform_SurfaceInteraction(t: Transform, si: SurfaceInteraction) -> SurfaceInteraction { + return SurfaceInteraction( + Transform_Point3f(t, si.p), + Transform_Vector3f(t, si.wo), + si.uv, + Transform_TangentSpace(t, si.t), + Transform_TangentSpace(t, si.shading)); +} + +// 3.2 Spheres + +struct Sphere { + ObjectToWorld: Transform, + radius: f32, + zMin: f32, + zMax: f32, + phiMax: f32, +} + +fn Sphere_ObjectBound(s: Sphere) -> Bounds3f { + return Bounds3f(Point3f(-s.radius, -s.radius, s.zMin), + Point3f( s.radius, s.radius, s.zMax)); +} + +fn Sphere_Intersect(s: Sphere, r: Ray, isect: ptr) -> bool { + var phi = 0.; + var pHit = Point3f(0.); + + // Transform Ray to object space + let ray = Transform_Ray(Transform_Inverse(s.ObjectToWorld), r); + + // Compute quadratic sphere coefficients + let o = ray.o; + let d = ray.d; + let a = dot(d,d); + let b = 2. * dot(d,o); + let c = dot(o,o) - s.radius * s.radius; + + // Solve quadratic equation for t values + var t0 = 0.; + var t1 = 0.; + if (!Quadratic(a, b, c, &t0, &t1)) { + return false; + } + + // Check quadric shape t0 and t1 for nearest intersection + if (/* t0 > ray.tMax || */ t1 <= 0.) { + return false; + } + var tShapeHit = t0; + if (tShapeHit <= 0.) { + tShapeHit = t1; + //if (tShapeHit > ray.tMax) { return false; } + } + + // Compute sphere hit position and phi + pHit = ray.o + ray.d * tShapeHit; + // Refine sphere intersection point + pHit *= s.radius / length(pHit); + if (pHit.x == 0. && pHit.y == 0.) { pHit.x = 1e-5 * s.radius; } + phi = atan2(pHit.y, pHit.x); + if (phi < 0.) { + phi += 2. * Pi; + } + + // Test sphere intersection against clipping parameters + if ((s.zMin > -s.radius && pHit.z < s.zMin) || + (s.zMax < s.radius && pHit.z > s.zMax) || phi > s.phiMax) { + if (tShapeHit == t1) { return false; } + //if (t1 > ray.tMax) { return false; } + tShapeHit = t1; + + // Compute sphere hit position and phi + pHit = ray.o + ray.d * tShapeHit; + // Refine sphere intersection point + pHit *= s.radius / length(pHit); + if (pHit.x == 0. && pHit.y == 0.) { pHit.x = 1e-5 * s.radius; } + phi = atan2(pHit.y, pHit.x); + if (phi < 0.) { + phi += 2. * Pi; + } + + if ((s.zMin > -s.radius && pHit.z < s.zMin) || + (s.zMax < s.radius && pHit.z > s.zMax) || phi > s.phiMax) { + return false; + } + } + + // Find parametric representation of sphere hit + let u = phi / s.phiMax; + let theta = acos(clamp(pHit.z / s.radius, -1., 1.)); + let thetaMin = acos(clamp(s.zMin / s.radius, -1., 1.)); + let thetaMax = acos(clamp(s.zMax / s.radius, -1., 1.)); + let v = (theta - thetaMin) / (thetaMax - thetaMin); + + // Compute sphere dpdu and dpdv + let zRadius = length(pHit.xy); + let invZRadius = 1. / zRadius; + let cosPhi = pHit.x * invZRadius; + let sinPhi = pHit.y * invZRadius; + let dpdu = Vector3f(-s.phiMax * pHit.y, s.phiMax * pHit.x, 0.); + let dpdv = (thetaMax - thetaMin) * + Vector3f(pHit.z * cosPhi, pHit.z * sinPhi, + -s.radius * sin(theta)); + + // Initialize SurfaceInteraction from parametric information + let n = normalize(cross(dpdu, dpdv)); + let t = TangentSpace(n, dpdu, dpdv); + *isect = Transform_SurfaceInteraction(s.ObjectToWorld, SurfaceInteraction(pHit, -ray.d, Point2f(u,v), t, t)); + + // Update tHit for quadric intersection + //*tHit = tShapeHit; + + return true; +} + +fn Sphere_Area(s: Sphere) -> f32 { + return s.phiMax * s.radius * (s.zMax - s.zMin); +} + +// 3.6 Triangle Meshes + +struct Triangle { + ObjectToWorld: Transform, + p: array, + uv: array, +} + +fn Triangle_Intersect(tri: Triangle, ray: Ray, isect: ptr) -> bool { + // Get triangle vertices in p0, p1, and p2 + let p0 = tri.p[0]; + let p1 = tri.p[1]; + let p2 = tri.p[2]; + let uv = tri.uv; + + // Perform ray–triangle intersection test + // Transform triangle vertices to ray coordinate space + // Translate vertices based on ray origin + var p0t = p0 - Vector3f(ray.o); + var p1t = p1 - Vector3f(ray.o); + var p2t = p2 - Vector3f(ray.o); + + // Permute components of triangle vertices and ray direction + let kz = MaxDimension(abs(ray.d)); + var kx = kz + 1; if (kx == 3) { kx = 0; } + var ky = kx + 1; if (ky == 3) { ky = 0; } + let d = Permute(ray.d, kx, ky, kz); + p0t = Permute(p0t, kx, ky, kz); + p1t = Permute(p1t, kx, ky, kz); + p2t = Permute(p2t, kx, ky, kz); + + // Apply shear transformation to translated vertex positions + let Sx = -d.x / d.z; + let Sy = -d.y / d.z; + let Sz = 1. / d.z; + p0t.x += Sx * p0t.z; + p0t.y += Sy * p0t.z; + p1t.x += Sx * p1t.z; + p1t.y += Sy * p1t.z; + p2t.x += Sx * p2t.z; + p2t.y += Sy * p2t.z; + + // Compute edge function coefficients e0, e1, and e2 + let e0 = p1t.x * p2t.y - p1t.y * p2t.x; + let e1 = p2t.x * p0t.y - p2t.y * p0t.x; + let e2 = p0t.x * p1t.y - p0t.y * p1t.x; + + // TODO: Fall back to double-precision test at triangle edges + + // Perform triangle edge and determinant tests + if ((e0 < 0. || e1 < 0. || e2 < 0.) && (e0 > 0. || e1 > 0. || e2 > 0.)) { + return false; + } + let det = e0 + e1 + e2; + if (det == 0.) { + return false; + } + + // Compute scaled hit distance to triangle and test against ray t range + p0t.z *= Sz; + p1t.z *= Sz; + p2t.z *= Sz; + let tScaled = e0 * p0t.z + e1 * p1t.z + e2 * p2t.z; + if (det < 0. && (tScaled >= 0. /* || tScaled < ray.tMax * det */)) { + return false; + } else if (det > 0. && (tScaled <= 0. /* || tScaled > ray.tMax * det */)) { + return false; + } + + // Compute barycentric coordinates and t value for triangle intersection + let invDet = 1. / det; + let b0 = e0 * invDet; + let b1 = e1 * invDet; + let b2 = e2 * invDet; + let t = tScaled * invDet; + + // TODO: Ensure that computed triangle t is conservatively greater than zero + + // Compute triangle partial derivatives + var dpdu = Vector3f(0.); + var dpdv = Vector3f(0.); + // Compute deltas for triangle partial derivatives + let duv02 = uv[0] - uv[2]; + let duv12 = uv[1] - uv[2]; + let dp02 = p0 - p2; + let dp12 = p1 - p2; + + let determinant = duv02[0] * duv12[1] - duv02[1] * duv12[0]; + if (determinant == 0.) { + // Handle zero determinant for triangle partial derivative matrix + CoordinateSystem(normalize(cross(p2 - p0, p1 - p0)), &dpdu, &dpdv); + } else { + let invdet = 1. / determinant; + dpdu = ( duv12[1] * dp02 - duv02[1] * dp12) * invdet; + dpdv = (-duv12[0] * dp02 + duv02[0] * dp12) * invdet; + } + + // TODO: Compute error bounds for triangle intersection + + // Interpolate uv parametric coordinates and hit point + let pHit = b0 * p0 + b1 * p1 + b2 * p2; + let uvHit = b0 * uv[0] + b1 * uv[1] + b2 * uv[2]; + + // TODO: Test intersection against alpha texture, if present + + // Fill in SurfaceInteraction from triangle hit + let n = Normal3f(normalize(cross(dp02, dp12))); + let ts = TangentSpace(n, dpdu, dpdv); + *isect = SurfaceInteraction(pHit, -ray.d, uvHit, ts, ts); + + // TODO: Initialize Triangle shading geometry + // TODO: Ensure correct orientation of the geometric normal + + //*tHit = t; + return true; +} + +fn Quad_Intersect(ray: Ray, isect: ptr, a: Point3f, b: Point3f, c: Point3f, d: Point3f) -> bool { + let Identity = Matrix4x4( + 1., 0., 0., 0., + 0., 1., 0., 0., + 0., 0., 1., 0., + 0., 0., 0., 1., + ); + let uv = array(Point2f(0., 0.), Point2f(1., 0.), Point2f(1., 1.)); + let t = Transform(Identity, Identity); + return Triangle_Intersect(Triangle(t, array(a,b,c), uv), ray, isect) + || Triangle_Intersect(Triangle(t, array(c,d,a), uv), ray, isect); +} + +// 3.9 Managing Rounding Error + +fn SurfaceInteraction_SpawnRay(si: SurfaceInteraction, d: Vector3f) -> Ray { + return Ray(si.p + si.t.n * 1e-3, d); +} + +// 5.1 Spectral Representation + +alias RGBSpectrum = vec3; +alias Spectrum = RGBSpectrum; + +fn Spectrum_IsBlack(f: Spectrum) -> bool { + return f.r == 0. && f.g == 0. && f.b == 0.; +} + +fn Spectrum_y(f: Spectrum) -> float { + // TODO: compute y coefficient of XYZ colour + return MaxComponent(f); +} + +// 5.5 Working with Radiometric Integrals + +fn SphericalDirection(sinTheta: f32, cosTheta: f32, phi: f32) -> Vector3f { + return Vector3f(sinTheta * cos(phi), + sinTheta * sin(phi), + cosTheta); +} + +fn SphericalTheta(v: Vector3f) -> f32 { + return acos(clamp(v.z, -1., 1.)); +} + +fn SphericalPhi(v: Vector3f) -> f32 { + let p = atan2(v.y, v.x); + return select(p, p + 2. * Pi, p < 0.); +} + +// 7.2 Sampling Interface + +alias Sampler = ptr; + +fn pcg_random(seed: Sampler) -> f32 { + *seed = *seed * 747796405u + 2891336453u; + let word = ((*seed >> ((*seed >> 28u) + 4u)) ^ *seed) * 277803737u; + return f32((word >> 22u) ^ word) / f32(0xffffffffu); +} + +fn Sampler_Get1D(samp: Sampler) -> f32 { + return pcg_random(samp); +} + +fn Sampler_Get2D(samp: Sampler) -> Point2f { + return Point2f(Sampler_Get1D(samp), Sampler_Get1D(samp)); +} + +// 8 Reflection Models + +fn CosTheta(w: Vector3f) -> f32 { return w.z; } +fn Cos2Theta(w: Vector3f) -> f32 { return w.z * w.z; } +fn AbsCosTheta(w: Vector3f) -> f32 { return abs(w.z); } + +fn Sin2Theta(w: Vector3f) -> f32 { + return max(0., 1. - Cos2Theta(w)); +} +fn SinTheta(w: Vector3f) -> f32 { + return sqrt(Sin2Theta(w)); +} + +fn TanTheta(w: Vector3f) -> f32 { + return SinTheta(w) / CosTheta(w); +} +fn Tan2Theta(w: Vector3f) -> f32 { + return Sin2Theta(w) / Cos2Theta(w); +} + +fn CosPhi(w: Vector3f) -> f32 { + let sinTheta = SinTheta(w); + if (sinTheta == 0.) { + return 1.; + } else { + return clamp(w.x / sinTheta, -1., 1.); + } +} +fn SinPhi(w: Vector3f) -> f32 { + let sinTheta = SinTheta(w); + if (sinTheta == 0.) { + return 0.; + } else { + return clamp(w.y / sinTheta, -1., 1.); + } +} + +fn Cos2Phi(w: Vector3f) -> f32 { + return CosPhi(w) * CosPhi(w); +} +fn Sin2Phi(w: Vector3f) -> f32 { + return SinPhi(w) * SinPhi(w); +} + +fn CosDPhi(wa: Vector3f, wb: Vector3f) -> f32 { + return clamp(dot(wa,wb) / (length(wa) * length(wb)), -1., 1.); +} + +// 8.2 Specular Reflection and Transmission + +fn Reflect(wo: Vector3f, n: Vector3f) -> Vector3f { + //return -wo + 2. * dot(wo, n) * n; + return -reflect(wo, n); +} + +// 8.4 Microfacet Models + +fn BeckmannDistribution_RoughnessToAlpha(roughness: f32) -> f32 { + return pow(max(roughness, 1e-3), 2.); + //let x = log(max(roughness, 1e-3)); + //return 1.62142 + 0.819955 * x + 0.1734 * x * x + + // 0.0171201 * x * x * x + 0.000640711 * x * x * x * x; +} + +fn BeckmannDistribution_D(alpha: f32, wh: Vector3f) -> f32 { + let tan2Theta = Tan2Theta(wh); + if (!isfinite(tan2Theta)) { return 0.; } + let cos4Theta = Cos2Theta(wh) * Cos2Theta(wh); + return exp(-tan2Theta * (Cos2Phi(wh) / (alpha * alpha) + + Sin2Phi(wh) / (alpha * alpha))) / + (Pi * alpha * alpha * cos4Theta); +} + +// 8.5 Fresnel Incidence Effects + +fn SchlickFresnel(Rs: Spectrum, cosTheta: f32) -> Spectrum { + return Rs + pow(1. - cosTheta, 5.) * (Spectrum(1.) - Rs); +} + +fn FresnelBlend_f(bsdf: BSDF, wo: Vector3f, wi: Vector3f) -> Spectrum { + let alpha = BeckmannDistribution_RoughnessToAlpha(bsdf.roughness); + + let diffuse = (28./(23.*Pi)) * bsdf.Rd * + (Spectrum(1.) - bsdf.Rs) * + (1. - pow(1. - .5 * AbsCosTheta(wi), 5.)) * + (1. - pow(1. - .5 * AbsCosTheta(wo), 5.)); + var wh = wi + wo; + if (wh.x == 0. && wh.y == 0. && wh.z == 0.) { return Spectrum(0.); } + wh = normalize(wh); + let specular = 1. / //BeckmannDistribution_D(alpha, wh) / + (4. * abs(dot(wi, wh)) * + max(AbsCosTheta(wi), AbsCosTheta(wo))) * + SchlickFresnel(bsdf.Rs, dot(wi, wh)); + return diffuse + specular; +} + +// 9.1 BSDFs + +fn BSDF_WorldToLocal(si: SurfaceInteraction, v: Vector3f) -> Vector3f { + let ns = si.shading.n; + let ss = normalize(si.shading.dpdu); + let ts = cross(ns, ss); + return Vector3f(dot(v, ss), dot(v, ts), dot(v, ns)); +} + +fn BSDF_LocalToWorld(si: SurfaceInteraction, v: Vector3f) -> Vector3f { + let ns = si.shading.n; + let ss = normalize(si.shading.dpdu); + let ts = cross(ns, ss); + return Vector3f(ss.x * v.x + ts.x * v.y + ns.x * v.z, + ss.y * v.x + ts.y * v.y + ns.y * v.z, + ss.z * v.x + ts.z * v.y + ns.z * v.z); +} + +// 12.6 Infinite Area Lights + +fn SurfaceInteraction_Le(isect: SurfaceInteraction, w: float3) -> Spectrum { + return Spectrum(0.); +} + +fn InfiniteAreaLight_Le(LightToWorld: Transform, ray: Ray) -> Spectrum { + let w = normalize(Transform_Vector3f(Transform_Inverse(LightToWorld), ray.d)); + let st = Point2f(SphericalPhi(w) / (2.*Pi), SphericalTheta(w) / Pi); + return Spectrum(textureSampleLevel(channel0, bilinear, st, 0.).rgb); +} + +// 13.6 2D Sampling with Multidimensional Transformations + +fn ConcentricSampleDisk(u: Point2f) -> Point2f { + // Map uniform random numbers to [-1, 1]^2 + let uOffset = 2. * u - 1.; + + // Handle degeneracy at the origin + if (uOffset.x == 0. && uOffset.y == 0.) { + return Point2f(0.); + } + + // Apply concentric mapping to point + var theta = 0.; + var r = 0.; + if (abs(uOffset.x) > abs(uOffset.y)) { + r = uOffset.x; + theta = Pi/4. * (uOffset.y / uOffset.x); + } else { + r = uOffset.y; + theta = Pi/2. - Pi/4. * (uOffset.x / uOffset.y); + } + return r * Point2f(cos(theta), sin(theta)); +} + +fn InfiniteAreaLight_img(pos: int2, mip: int) -> float { + // Compute scalar-valued image img from environment map + return Spectrum_y(textureLoad(channel0, pos, mip).rgb); +} + +fn Distribution2D_SampleContinuous(samp: Sampler, u: Point2f, pdf: ptr) -> Point2f { + let mipmax = int(textureNumLevels(channel0)) - 1; + let res = float2(textureDimensions(channel0)); + var prob = 1.; + var pos = int2(0,0); + for (var mip = mipmax - 1; mip >= 0; mip -= 1) { + pos *= 2; + let w00 = InfiniteAreaLight_img(pos + int2(0,0), mip); + let w01 = InfiniteAreaLight_img(pos + int2(0,1), mip); + let w10 = InfiniteAreaLight_img(pos + int2(1,0), mip); + let w11 = InfiniteAreaLight_img(pos + int2(1,1), mip); + let w0 = w00 + w01; // weight of column 0 + let w1 = w10 + w11; // weight of column 1 + let w = w0 + w1; // total weight + let offset = select( + int2(0, select(0, 1, Sampler_Get1D(samp) <= w01 / w0)), // cond prob of row 1 given col 0 + int2(1, select(0, 1, Sampler_Get1D(samp) <= w11 / w1)), // cond prob of row 1 given col 1 + Sampler_Get1D(samp) <= w1 / w); // prob of col 1 + pos += offset; + prob *= select( + w0 / w * select(w00 / w0, w01 / w0, offset.y == 1), + w1 / w * select(w10 / w1, w11 / w1, offset.y == 1), + offset.x == 1); + } + let uv = (float2(pos) + Sampler_Get2D(samp)) / res; + *pdf = prob * res.x * res.y; + return uv; +} + +fn CosineSampleHemisphere(u: Point2f) -> Vector3f { + let d = ConcentricSampleDisk(u); + let z = sqrt(max(0., 1. - dot(d,d))); + return Vector3f(d.xy, z); +} + +// 13.10 Importance Sampling + +fn PowerHeuristic(nf: i32, fPdf: f32, ng: i32, gPdf: f32) -> f32 { + let f = f32(nf) * fPdf; + let g = f32(ng) * gPdf; + return (f * f) / (f * f + g * g); +} + +// 14.1 Sampling Reflection Functions + +fn SameHemisphere(w: Vector3f, wp: Vector3f) -> bool { + return w.z * wp.z > 0.; +} + +fn BeckmannDistribution_Sample_wh(alpha: f32, wo: Vector3f, u: Point2f) -> Vector3f { + // Sample full distribution of normals for Beckmann distribution + // Compute tan2Theta and phi for Beckmann distribution sample + var logSample = log(1. - u[0]); + if (!isfinite(logSample)) { + logSample = 0.; + } + let tan2Theta = -alpha * alpha * logSample; + let phi = u[1] * 2. * Pi; + + // TODO: Compute tan2Theta and phi for anisotropic Beckmann distribution + + // Map sampled Beckmann angles to normal direction wh>> + let cosTheta = 1. / sqrt(1. + tan2Theta); + let sinTheta = sqrt(max(0., 1. - cosTheta * cosTheta)); + var wh = SphericalDirection(sinTheta, cosTheta, phi); + if (!SameHemisphere(wo, wh)) { + wh = -wh; + } + + return wh; + + // TODO: Sample visible area of normals for Beckmann distribution>> +} + +fn BeckmannDistribution_Pdf(alpha: f32, wo: Vector3f, wh: Vector3f) -> f32 { + //if (sampleVisibleArea) + // return D(wh) * G1(wo) * AbsDot(wo, wh) / AbsCosTheta(wo); + //else + return //BeckmannDistribution_D(alpha, wh) * + AbsCosTheta(wh); +} + +fn FresnelBlend_Pdf(bsdf: BSDF, wo: Vector3f, wi: Vector3f) -> f32 { + let alpha = BeckmannDistribution_RoughnessToAlpha(bsdf.roughness); + + if (!SameHemisphere(wo, wi)) { return 0.; } + let wh = normalize(wo + wi); + let pdf_wh = BeckmannDistribution_Pdf(alpha, wo, wh); + return .5 * (AbsCosTheta(wi) / Pi + pdf_wh / (4. * dot(wo, wh))); +} + +fn FresnelBlend_Sample_f(bsdf: BSDF, wo: Vector3f, wi: ptr, uOrig: Point2f, pdf: ptr) -> Spectrum { + let alpha = BeckmannDistribution_RoughnessToAlpha(bsdf.roughness); + + var u = uOrig; + if (u[0] < .5) { // LambertianReflection + u[0] = 2. * u[0]; + // Cosine-sample the hemisphere, flipping the direction if necessary + *wi = CosineSampleHemisphere(u); + if (wo.z < 0.) { + (*wi).z *= -1.; + } + } else { // MicrofacetReflection + u[0] = 2. * (u[0] - .5); + // Sample microfacet orientation wh and reflected direction wi + let wh = BeckmannDistribution_Sample_wh(alpha, wo, u); + *wi = Reflect(wo, wh); + if (!SameHemisphere(wo, *wi)) { + return Spectrum(0.); + } + } + *pdf = FresnelBlend_Pdf(bsdf, wo, *wi); + return FresnelBlend_f(bsdf, wo, *wi); +} + +fn BSDF_Sample_f(si: SurfaceInteraction, bsdf: BSDF, woWorld: Vector3f, wiWorld: ptr, u: Point2f, pdf: ptr) -> Spectrum { + // TODO: Choose which BxDF to sample + // TODO: Remap BxDF sample u + + // Sample chosen BxDF + var wi = Vector3f(0.); + let wo = BSDF_WorldToLocal(si, woWorld); + *pdf = 0.; + let f = FresnelBlend_Sample_f(bsdf, wo, &wi, u, pdf); + if (*pdf == 0.) { + return Spectrum(0.); + } + *wiWorld = BSDF_LocalToWorld(si, wi); + + // TODO: Compute overall PDF with all matching BxDFs + // TODO: Compute value of BSDF for sampled direction + + return f; +} + +// 14.2 Sampling Light Sources + +fn InfiniteAreaLight_Sample_Li( + LightToWorld: Transform, + si: SurfaceInteraction, + u: Point2f, + wi: ptr, + pdf: ptr, + vis_p: ptr, + samp: Sampler) + -> Spectrum +{ + // Find uv sample coordinates in infinite light texture + var mapPdf = 0.; + let uv = Distribution2D_SampleContinuous(samp, u, &mapPdf); + if (mapPdf == 0.) { return Spectrum(0.); } + + // Convert infinite light sample point to direction + let theta = uv[1] * Pi; + let phi = uv[0] * 2. * Pi; + let cosTheta = cos(theta); + let sinTheta = sin(theta); + let sinPhi = sin(phi); + let cosPhi = cos(phi); + *wi = Transform_Vector3f(LightToWorld, Vector3f(sinTheta * cosPhi, sinTheta * sinPhi, cosTheta)); + + // Compute PDF for sampled infinite light direction + *pdf = mapPdf / (2. * Pi * Pi * sinTheta); + if (sinTheta == 0.) { *pdf = 0.; } + + // Return radiance value for infinite light direction + let worldRadius = 1e3; // TODO + *vis_p = si.p + *wi * (2. * worldRadius); + return Spectrum(textureSampleLevel(channel0, bilinear, uv, 0.).rgb); +} + +// Hardcoded Scene + +fn Scene_Intersect(ray: Ray, isect: ptr, bsdf: ptr) -> bool { + var foundIntersection = false; + + let A = float3(-12.5, 25., -12.5); + let B = float3( 12.5, 25., -12.5); + let C = float3( 12.5, 25., 12.5); + let D = float3(-12.5, 25., 12.5); + let a = float3(-12.5, 15., -12.5); + let b = float3( 12.5, 15., -12.5); + let c = float3( 12.5, 15., 12.5); + let d = float3(-12.5, 15., 12.5); + + // back wall + if (Quad_Intersect(ray, isect, A, B, C, D)) { + *bsdf = BSDF(float3(1.), float3(0.), 1.); + foundIntersection = true; + } + + // floor + if (Quad_Intersect(ray, isect, a, b, B, A)) { + *bsdf = BSDF(float3(1.), float3(0.), 1.); + foundIntersection = true; + } + + // ceiling + if (Quad_Intersect(ray, isect, D, C, c, d) && !Quad_Intersect(ray, isect, + float3(-5.0, 22.5, 12.5), + float3( 5.0, 22.5, 12.5), + float3( 5.0, 17.5, 12.5), + float3(-5.0, 17.5, 12.5))) { + *bsdf = BSDF(float3(1.), float3(0.), 1.); + foundIntersection = true; + } + + // left wall + if (Quad_Intersect(ray, isect, a, A, D, d)) { + *bsdf = BSDF(float3(1., 0., 0.), float3(0.), 1.); + foundIntersection = true; + } + + // right wall + if (Quad_Intersect(ray, isect, B, b, c, C)) { + *bsdf = BSDF(float3(0., 1., 0.), float3(0.), 1.); + foundIntersection = true; + } + + if (Sphere_Intersect(Sphere(Translate(float3(-9., 20., -9.)), 3., -1e6, 1e6, 1e6), ray, isect)) { + *bsdf = BSDF(float3(.9, .9, .5), float3(.9, .9, .5), .2); + foundIntersection = true; + } + + if (Sphere_Intersect(Sphere(Translate(float3(0., 20., -9.)), 3., -1e6, 1e6, 1e6), ray, isect)) { + *bsdf = BSDF(float3(.5, .9, .9), float3(.5, .9, .9), .2); + foundIntersection = true; + } + + if (Sphere_Intersect(Sphere(Translate(float3(9., 20., -9.)), 3., -1e6, 1e6, 1e6), ray, isect)) { + *bsdf = BSDF(float3(0., 0., 1.), float3(1., 0., 0.), .4); + foundIntersection = true; + } + + for(var i = 0; i < 5; i += 1) { + if (Sphere_Intersect(Sphere(Translate(float3(float(5*i - 10), 23., 0.)), 1.75, -1e6, 1e6, 1e6), ray, isect)) { + *bsdf = BSDF(float3(.3, 1., .3), float3(.3, 1., .3), float(i*i) / 16.); + foundIntersection = true; + } + } + + return foundIntersection; +} + +fn Scene_IntersectP(ray: Ray) -> bool { + var isect = SurfaceInteraction(); + var bsdf = BSDF(); + return Scene_Intersect(ray, &isect, &bsdf); +} + +// 14.3 Direct Lighting + +fn EstimateDirect( + isect: SurfaceInteraction, + bsdf: BSDF, + uScattering: Point2f, + LightToWorld: Transform, + uLight: Point2f, + samp: Sampler) + -> Spectrum +{ + let specular = false; + //let bsdfFlags = select(BSDF_ALL & ~BSDF_SPECULAR, BSDF_ALL, specular); + var Ld = Spectrum(0.); + // Sample light source with multiple importance sampling + var wi = Vector3f(0.); + var lightPdf = 0.; + var scatteringPdf = 0.; + var visibility_p = Point3f(0.); + var Li = InfiniteAreaLight_Sample_Li(LightToWorld, isect, uLight, &wi, &lightPdf, &visibility_p, samp); + if (lightPdf > 0. && !Spectrum_IsBlack(Li)) { + // Compute BSDF or phase function's value for light sample + // Evaluate BSDF for light sampling strategy + let f = FresnelBlend_f(bsdf, isect.wo, wi) * abs(dot(wi, isect.shading.n)); + scatteringPdf = FresnelBlend_Pdf(bsdf, isect.wo, wi); + + // TODO: Evaluate phase function for light sampling strategy + + if (!Spectrum_IsBlack(f)) { + // Compute effect of visibility for light source sample + if (Scene_IntersectP(Ray(isect.p, visibility_p - isect.p)) + || dot(isect.t.n, visibility_p - isect.p) < 0.) { + Li = Spectrum(0.); + } + + // Add light's contribution to reflected radiance + if (!Spectrum_IsBlack(Li)) { + //if (IsDeltaLight(light.flags)) + // Ld += f * Li / lightPdf; + //else { + let weight = PowerHeuristic(1, lightPdf, 1, scatteringPdf); + Ld += f * Li * weight / lightPdf; + //} + } + } + } + + // TODO: Sample BSDF with multiple importance sampling + + return Ld; +} + +fn UniformSampleOneLight(isect: SurfaceInteraction, bsdf: BSDF, LightToWorld: Transform, samp: Sampler, handleMedia: bool) -> Spectrum { + // TODO: Randomly choose a single light to sample + + let uLight = Sampler_Get2D(samp); + let uScattering = Sampler_Get2D(samp); + let nLights = 1; + return f32(nLights) * EstimateDirect(isect, bsdf, uScattering, LightToWorld, uLight, samp); +} + +// 14.5 Path Tracing + +fn PathIntegrator_Li(maxDepth: i32, r: Ray, samp: Sampler) -> Spectrum { + let Identity = Matrix4x4( + 1., 0., 0., 0., + 0., 1., 0., 0., + 0., 0., 1., 0., + 0., 0., 0., 1., + ); + var L = Spectrum(0.); + var beta = Spectrum(1.); + var ray = r; + var specularBounce = false; + for (var bounces = 0; bounces <= maxDepth; bounces += 1) { + // Find next path vertex and accumulate contribution + // Intersect ray with scene and store intersection in isect + var isect = SurfaceInteraction(); + var bsdf = BSDF(); + let foundIntersection = Scene_Intersect(ray, &isect, &bsdf); + + // Possibly add emitted light at intersection + if (bounces == 0 || specularBounce) { + // Add emitted light at path vertex or from the environment + if (foundIntersection) { + L += beta * SurfaceInteraction_Le(isect, -ray.d); + } else { + L += beta * InfiniteAreaLight_Le(Transform(Identity, Identity), ray); + } + } + + // Terminate path if ray escaped or maxDepth was reached + if (!foundIntersection || bounces >= maxDepth) { + break; + } + + // TODO: Compute scattering functions and skip over medium boundaries + + // Sample illumination from lights to find path contribution + L += beta * UniformSampleOneLight(isect, bsdf, Transform(Identity, Identity), samp, false); + + // Sample BSDF to get new path direction + let wo = -ray.d; + var wi = Vector3f(0.); + var pdf = 0.; + //var flags = BSDF_ALL; + let f = BSDF_Sample_f(isect, bsdf, wo, &wi, Sampler_Get2D(samp), &pdf); + if (Spectrum_IsBlack(f) || pdf == 0.) { + break; + } + beta *= f * abs(dot(wi, isect.shading.n)) / pdf; + //specularBounce = (flags & BSDF_SPECULAR) != 0u; + ray = SurfaceInteraction_SpawnRay(isect, wi); + + // TODO: Account for subsurface scattering, if applicable + + // Possibly terminate the path with Russian roulette + if (bounces > 3) { + let q = max(.05, 1. - Spectrum_y(beta)); + if (Sampler_Get1D(samp) < q) { + break; + } + beta /= max(1e-3, 1. - q); + } + } + return L; +} + +// Main Rendering + +// https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ +fn ACESFilm(x: float3) -> float3 { + let a = 2.51; let b = 0.03; let c = 2.43; let d = 0.59; let e = 0.14; + return clamp((x*(a*x + b)) / (x*(c*x + d) + e), float3(0.), float3(1.)); +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + let resolution = float2(screen_size); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Seed PRNG + var seed = id.x + id.y * screen_size.x + time.frame * screen_size.x * screen_size.y; + + // Camera + let uv = (float2(id.xy) + Sampler_Get2D(&seed)) / resolution; + var dir = Vector3f(uv.x * 2. - 1., 1., uv.y * 2. - 1.); + dir.z /= -resolution.x / resolution.y; + let ray = Ray(Point3f(0., -10., 0.), normalize(dir)); + + // Path integration + var col = float3(0.); + let spp = 10; // samples per pixel (per frame) + for (var i = 0; i < spp; i += 1) { + col += PathIntegrator_Li(8, ray, &seed) / float(spp); + } + + // Accumulate pixel samples + col = mix(textureLoad(pass_in, int2(id.xy), 0, 0).rgb, col, 1. / float(time.frame + 1u)); + textureStore(pass_out, int2(id.xy), 0, float4(col, 1.)); + + // Convert from HDR to LDR (tone mapping) + col = ACESFilm(col); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/davidar/pbrt-with-ibl.wgsl.json b/examples/davidar/pbrt-with-ibl.wgsl.json new file mode 100644 index 0000000..68bd376 --- /dev/null +++ b/examples/davidar/pbrt-with-ibl.wgsl.json @@ -0,0 +1,14 @@ +{ + "uniforms": [], + "textures": [ + { + "img": "https://dl.polyhaven.org/file/ph-assets/HDRIs/hdr/2k/spruit_sunrise_2k.hdr", + "thumb": "https://dl.polyhaven.org/file/ph-assets/HDRIs/extra/Tonemapped%20JPG/spruit_sunrise.jpg", + "url": "https://polyhaven.com/a/spruit_sunrise" + }, + { + "img": "/textures/blank.png" + } + ], + "float32Enabled": false +} \ No newline at end of file diff --git a/examples/default.wgsl b/examples/default.wgsl new file mode 100644 index 0000000..3154eff --- /dev/null +++ b/examples/default.wgsl @@ -0,0 +1,23 @@ +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(screen_size.y - id.y) - .5); + + // Normalised pixel coordinates (from 0 to 1) + let uv = fragCoord / float2(screen_size); + + // Time varying pixel colour + var col = .5 + .5 * cos(time.elapsed + uv.xyx + float3(0.,2.,4.)); + + // Convert from gamma-encoded to linear colour space + col = pow(col, float3(2.2)); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/demofox/path-tracing.wgsl b/examples/demofox/path-tracing.wgsl new file mode 100644 index 0000000..74ef419 --- /dev/null +++ b/examples/demofox/path-tracing.wgsl @@ -0,0 +1,485 @@ +// Based on the path tracing tutorial series by demofox: +// https://blog.demofox.org/2020/05/25/casual-shadertoy-path-tracing-1-basic-camera-diffuse-emissive/ +// Ported to WGSL by davidar.io + +const MINIMUM_RAY_HIT_TIME = .1; +const FAR_PLANE = 1e4; +const FOV_DEGREES = 90.; +const NUM_BOUNCES = 8; +const RAY_POS_NORMAL_NUDGE = .01; +const NUM_RENDERS_PER_FRAME = 100; +const EXPOSURE = .5; +const PI = 3.1415926536; + +struct Material { + albedo: float3, + emissive: float3, + specular: float3, + percentSpecular: float, + roughness: float, + IOR: float, +} + +struct RayHitInfo { + dist: float, + normal: float3, + material: Material, +}; + +fn WangHash(seed: ptr) -> uint +{ + *seed = (*seed ^ 61u) ^ (*seed >> 16u); + *seed *= 9u; + *seed = *seed ^ (*seed >> 4u); + *seed *= 0x27d4eb2du; + *seed = *seed ^ (*seed >> 15u); + return *seed; +} + +fn RandomFloat01(seed: ptr) -> float +{ + return float(WangHash(seed)) / 4294967296.0; +} + +fn RandomUnitVector(seed: ptr) -> float3 +{ + let z = RandomFloat01(seed) * 2. - 1.; + let a = RandomFloat01(seed) * 2. * PI; + let r = sqrt(1. - z * z); + let x = r * cos(a); + let y = r * sin(a); + return float3(x, y, z); +} + +// ACES tone mapping curve fit to go from HDR to LDR +// https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ +fn ACESFilm(x: float3) -> float3 +{ + let a = 2.51; + let b = 0.03; + let c = 2.43; + let d = 0.59; + let e = 0.14; + return clamp((x*(a*x + b)) / (x*(c*x + d) + e), float3(0.), float3(1.)); +} + +fn FresnelReflectAmount( + n1: float, + n2: float, + normal: float3, + incident: float3, + f0: float, + f90: float) + -> float +{ + // Schlick aproximation + var r0 = (n1-n2) / (n1+n2); + r0 *= r0; + var cosX = -dot(normal, incident); + if (n1 > n2) + { + let n = n1/n2; + let sinT2 = n*n*(1.0-cosX*cosX); + // Total internal reflection + if (sinT2 > 1.0) + { + return f90; + } + cosX = sqrt(1.0-sinT2); + } + let x = 1.0-cosX; + let ret = r0+(1.0-r0)*x*x*x*x*x; + + // adjust reflect multiplier for object reflectivity + return mix(f0, f90, ret); +} + +fn ScalarTriple(u: float3, v: float3, w: float3) -> float +{ + return dot(cross(u, v), w); +} + +fn TestQuadTrace( + rayPos: float3, + rayDir: float3, + info: ptr, + _a: float3, + _b: float3, + _c: float3, + _d: float3) + -> bool +{ + var a = _a; + var b = _b; + var c = _c; + var d = _d; + // calculate normal and flip vertices order if needed + var normal = normalize(cross(c-a, c-b)); + if (dot(normal, rayDir) > 0.) + { + normal *= -1.; + + var temp = d; + d = a; + a = temp; + + temp = b; + b = c; + c = temp; + } + + let p = rayPos; + let q = rayPos + rayDir; + let pq = q - p; + let pa = a - p; + let pb = b - p; + let pc = c - p; + + // determine which triangle to test against by testing against diagonal first + let m = cross(pc, pq); + var v = dot(pa, m); + var intersectPos = float3(0.); + if (v >= 0.) + { + // test against triangle a,b,c + var u = -dot(pb, m); + if (u < 0.) { return false; } + var w = ScalarTriple(pq, pb, pa); + if (w < 0.) { return false; } + let denom = 1. / (u+v+w); + u*=denom; + v*=denom; + w*=denom; + intersectPos = u*a+v*b+w*c; + } + else + { + let pd = d - p; + var u = dot(pd, m); + if (u < 0.) { return false; } + var w = ScalarTriple(pq, pa, pd); + if (w < 0.) { return false; } + v = -v; + let denom = 1. / (u+v+w); + u*=denom; + v*=denom; + w*=denom; + intersectPos = u*a+v*d+w*c; + } + + var dist = 0.; + if (abs(rayDir.x) > 0.) + { + dist = (intersectPos.x - rayPos.x) / rayDir.x; + } + else if (abs(rayDir.y) > 0.) + { + dist = (intersectPos.y - rayPos.y) / rayDir.y; + } + else + { + dist = (intersectPos.z - rayPos.z) / rayDir.z; + } + + if (dist > MINIMUM_RAY_HIT_TIME && dist < (*info).dist) + { + (*info).dist = dist; + (*info).normal = normal; + return true; + } + + return false; +} + +fn TestSphereTrace( + rayPos: float3, + rayDir: float3, + info: ptr, + sphere: float4) + -> bool +{ + //get the vector from the center of this sphere to where the ray begins. + let m = rayPos - sphere.xyz; + + //get the dot product of the above vector and the ray's vector + let b = dot(m, rayDir); + + let c = dot(m, m) - sphere.w * sphere.w; + + //exit if r's origin outside s (c > 0) and r pointing away from s (b > 0) + if(c > 0.0 && b > 0.0) + { + return false; + } + + //calculate discriminant + let discr = b * b - c; + + //a negative discriminant corresponds to ray missing sphere + if(discr < 0.0) + { + return false; + } + + //ray now found to intersect sphere, compute smallest t value of intersection + var fromInside = false; + var dist = -b - sqrt(discr); + if (dist < 0.) + { + fromInside = true; + dist = -b + sqrt(discr); + } + + if (dist > MINIMUM_RAY_HIT_TIME && dist < (*info).dist) + { + (*info).dist = dist; + (*info).normal = normalize((rayPos+rayDir*dist) - sphere.xyz) * select(1., -1., fromInside); + return true; + } + + return false; +} + +fn TestSceneTrace(rayPos: float3, rayDir: float3, hitInfo: ptr) +{ + // to move the scene around, since we can't move the camera yet + let sceneTranslation = float3(0., 0., 10.); + let sceneTranslation4 = float4(sceneTranslation, 0.); + + // back wall + { + let A = float3(-12.6, -12.6, 25.) + sceneTranslation; + let B = float3( 12.6, -12.6, 25.) + sceneTranslation; + let C = float3( 12.6, 12.6, 25.) + sceneTranslation; + let D = float3(-12.6, 12.6, 25.) + sceneTranslation; + if (TestQuadTrace(rayPos, rayDir, hitInfo, A, B, C, D)) + { + (*hitInfo).material.albedo = float3(.7); + (*hitInfo).material.emissive = float3(0.); + } + } + + // floor + { + let A = float3(-12.6, -12.45, 25.) + sceneTranslation; + let B = float3( 12.6, -12.45, 25.) + sceneTranslation; + let C = float3( 12.6, -12.45, 15.) + sceneTranslation; + let D = float3(-12.6, -12.45, 15.) + sceneTranslation; + if (TestQuadTrace(rayPos, rayDir, hitInfo, A, B, C, D)) + { + (*hitInfo).material.albedo = float3(.7); + (*hitInfo).material.emissive = float3(0.); + } + } + + // ceiling + { + let A = float3(-12.6, 12.5, 25.0) + sceneTranslation; + let B = float3( 12.6, 12.5, 25.0) + sceneTranslation; + let C = float3( 12.6, 12.5, 15.0) + sceneTranslation; + let D = float3(-12.6, 12.5, 15.0) + sceneTranslation; + if (TestQuadTrace(rayPos, rayDir, hitInfo, A, B, C, D)) + { + (*hitInfo).material.albedo = float3(.7); + (*hitInfo).material.emissive = float3(0.); + } + } + + // left wall + { + let A = float3(-12.5, -12.6, 25.0) + sceneTranslation; + let B = float3(-12.5, -12.6, 15.0) + sceneTranslation; + let C = float3(-12.5, 12.6, 15.0) + sceneTranslation; + let D = float3(-12.5, 12.6, 25.0) + sceneTranslation; + if (TestQuadTrace(rayPos, rayDir, hitInfo, A, B, C, D)) + { + (*hitInfo).material.albedo = float3(.5, 0., 0.); + (*hitInfo).material.emissive = float3(0.); + } + } + + // right wall + { + let A = float3( 12.5, -12.6, 25.) + sceneTranslation; + let B = float3( 12.5, -12.6, 15.) + sceneTranslation; + let C = float3( 12.5, 12.6, 15.) + sceneTranslation; + let D = float3( 12.5, 12.6, 25.) + sceneTranslation; + if (TestQuadTrace(rayPos, rayDir, hitInfo, A, B, C, D)) + { + (*hitInfo).material.albedo = float3(0., .5, 0.); + (*hitInfo).material.emissive = float3(0.); + } + } + + // light + { + let A = float3(-5.0, 12.4, 22.5) + sceneTranslation; + let B = float3( 5.0, 12.4, 22.5) + sceneTranslation; + let C = float3( 5.0, 12.4, 17.5) + sceneTranslation; + let D = float3(-5.0, 12.4, 17.5) + sceneTranslation; + if (TestQuadTrace(rayPos, rayDir, hitInfo, A, B, C, D)) + { + (*hitInfo).material.albedo = float3(0.); + (*hitInfo).material.emissive = float3(1., .8, .5) * 20.; + } + } + + if (TestSphereTrace(rayPos, rayDir, hitInfo, vec4(-9., -9., 20., 3.)+sceneTranslation4)) + { + (*hitInfo).material.albedo = float3(.9, .9, .5); + (*hitInfo).material.emissive = float3(0.); + (*hitInfo).material.specular = float3(.9); + (*hitInfo).material.percentSpecular = .1; + (*hitInfo).material.roughness = .2; + (*hitInfo).material.IOR = 1.; + } + + if (TestSphereTrace(rayPos, rayDir, hitInfo, vec4(0., -9., 20., 3.)+sceneTranslation4)) + { + (*hitInfo).material.albedo = float3(.9, .5, .9); + (*hitInfo).material.emissive = float3(0.); + (*hitInfo).material.specular = float3(.9); + (*hitInfo).material.percentSpecular = .3; + (*hitInfo).material.roughness = .2; + (*hitInfo).material.IOR = 1.; + } + + if (TestSphereTrace(rayPos, rayDir, hitInfo, vec4(9., -9., 20., 3.)+sceneTranslation4)) + { + (*hitInfo).material.albedo = float3(0., 0., 1.); + (*hitInfo).material.emissive = float3(0.); + (*hitInfo).material.specular = float3(1., 0., 0.); + (*hitInfo).material.percentSpecular = .5; + (*hitInfo).material.roughness = .4; + (*hitInfo).material.IOR = 1.; + } + + for(var i = 0; i < 5; i += 1) { + if (TestSphereTrace(rayPos, rayDir, hitInfo, vec4(float(5*i - 10), 0., 23., 1.75)+sceneTranslation4)) + { + (*hitInfo).material.albedo = float3(1.); + (*hitInfo).material.emissive = float3(0.); + (*hitInfo).material.specular = float3(.3, 1., .3); + (*hitInfo).material.percentSpecular = 1.; + (*hitInfo).material.roughness = float(i*i) / 16.; + (*hitInfo).material.IOR = 1.; + } + } +} + +fn GetColorForRay(startRayPos: float3, startRayDir: float3, rngState: ptr) -> float3 +{ + + var ret = float3(0.); + var throughput = float3(1.); + var rayPos = startRayPos; + var rayDir = startRayDir; + + for (var bounceIndex = 0; bounceIndex <= NUM_BOUNCES; bounceIndex += 1) + { + var hitInfo = RayHitInfo(); + hitInfo.dist = FAR_PLANE; + TestSceneTrace(rayPos, rayDir, &hitInfo); + + if (hitInfo.dist == FAR_PLANE) + { + let uv = float2(atan2(rayDir.x, rayDir.z) / (2.*PI), acos(rayDir.y) / PI); + ret += textureSampleLevel(channel0, bilinear, fract(uv), 0.).rgb * throughput; + break; + } + + rayPos = rayPos + rayDir * hitInfo.dist + hitInfo.normal * RAY_POS_NORMAL_NUDGE; + + var specularChance = hitInfo.material.percentSpecular; + if (specularChance > 0.) + { + specularChance = FresnelReflectAmount( + 1., hitInfo.material.IOR, rayDir, hitInfo.normal, + hitInfo.material.percentSpecular, 1.); + } + let doSpecular = (RandomFloat01(rngState) < specularChance); + + let rayProbability = select(1. - specularChance, specularChance, doSpecular); + + let diffuseRayDir = normalize(hitInfo.normal + RandomUnitVector(rngState)); + var specularRayDir = reflect(rayDir, hitInfo.normal); + specularRayDir = normalize(mix(specularRayDir, diffuseRayDir, hitInfo.material.roughness)); + rayDir = select(diffuseRayDir, specularRayDir, doSpecular); + + ret += hitInfo.material.emissive * throughput; + + throughput *= select(hitInfo.material.albedo, hitInfo.material.specular, doSpecular); + + throughput /= max(1e-3, rayProbability); + + let p = max(throughput.r, max(throughput.g, throughput.b)); + if (RandomFloat01(rngState) > p) { + break; + } + throughput /= max(1e-3, p); + } + + return ret; +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(screen_size.y - id.y) - .5); + + // Normalised pixel coordinates (from 0 to 1) + let uv = fragCoord / float2(screen_size); + + // initialize a random number state based on frag coord and frame + var rngState = (id.x * 1973u + id.y * 9277u + time.frame * 26699u) | 1u; + + // The ray starts at the camera position (the origin) + let rayPosition = float3(0.); + + // calculate the camera distance + let cameraDistance = 1. / tan(FOV_DEGREES * .5 * PI / 180.); + + // calculate subpixel camera jitter for anti aliasing + let jitter = float2(RandomFloat01(&rngState), RandomFloat01(&rngState)) - .5; + + // calculate coordinates of the ray target on the imaginary pixel plane. + // -1 to +1 on x,y axis. 1 unit away on the z axis + var rayTarget = float3((fragCoord + jitter) / float2(screen_size) * 2. - 1., cameraDistance); + + // correct for aspect ratio + let aspectRatio = float(screen_size.x) / float(screen_size.y); + rayTarget.y /= aspectRatio; + + // calculate a normalized vector for the ray direction. + // it's pointing from the ray position to the ray target. + let rayDir = normalize(rayTarget - rayPosition); + + // raytrace for this pixel + var col = float3(0.); + for (var index = 0; index < NUM_RENDERS_PER_FRAME; index += 1) + { + col += GetColorForRay(rayPosition, rayDir, &rngState) / float(NUM_RENDERS_PER_FRAME); + } + + // average the frames together + let lastFrameCol = textureLoad(pass_in, int2(id.xy), 0, 0).rgb; + col = mix(lastFrameCol, col, 1. / float(time.frame + 1u)); + + // Store to Buffer 0 + textureStore(pass_out, int2(id.xy), 0, float4(col, 1.)); + + // apply exposure (how long the shutter is open) + col *= EXPOSURE; + + // convert unbounded HDR color range to SDR color range + col = ACESFilm(col); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/dispatch_count.wgsl b/examples/dispatch_count.wgsl new file mode 100644 index 0000000..9cbac65 --- /dev/null +++ b/examples/dispatch_count.wgsl @@ -0,0 +1,25 @@ +// This allows you to dispatch an entrypoint multiple times. The uniform `dispatch.id` tells you which iteration is currently executing + +#dispatch_count main_image 3 + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(screen_size.y - id.y) - .5); + + // Normalised pixel coordinates (from 0 to 1) + let uv = fragCoord / float2(screen_size); + + // draw a colour band for each dispatch + var col = float3(0.); + if (uint(3. * uv.x) == dispatch.id) { + col[dispatch.id] = 1.; + textureStore(screen, int2(id.xy), float4(col, 1.)); + } +} diff --git a/examples/fabrice/utils.wgsl b/examples/fabrice/utils.wgsl new file mode 100644 index 0000000..54ea366 --- /dev/null +++ b/examples/fabrice/utils.wgsl @@ -0,0 +1,198 @@ +//=== original link for citation: https://www.shadertoy.com/view/llySRh +//find many other tricks here: https://shadertoyunofficial.wordpress.com/ + +// --- short approx hue -------------- https://www.shadertoy.com/view/ll2cDc + +fn hue(v: float) -> float4 { + return .6 + .6 * cos( 6.3*(v) + float4(0.,23.,21.,0.) ); +} + + +// --- printing chars, integers and floats --------------------------- + +// --- access to the image of ascii code c + +fn char_(p: float2, c: int) -> float4 { + if (p.x<.0|| p.x>1. || p.y<0.|| p.y>1.) { + return float4(0.,0.,0.,1e5); + } + return textureSampleLevel( channel0, trilinear, + float2(p.x,1.-p.y)/16. + fract( float2(float(c), float(c/16)) / 16. ), 0.); +} + +// --- display int4 + +fn pInt(p: float2, _n: float) -> float4 { + var n = _n; + var v = float4(); + if (n < 0.) { + v += char_(p - float2(-.5,0.), 45 ); + n = -n; + } + + for (var i = 3.; i>=0.; i -= 1.) { + n /= 9.999999; // 10., // for windows :-( + v += char_(p - .5*float2(i,0.), 48+ int(fract(n)*10.) ); + } + return v; +} + +// --- display float4.4 + +fn pFloat(_p: float2, _n: float) -> float4 { + var p = _p; + var n = _n; + var v = float4(); + if (n < 0.) { v += char_(p - float2(-.5,0.), 45 ); n = -n; } + var upper = floor(n); + var lower = fract(n)*1e4 + .5; // mla fix for rounding lost decimals + if (lower >= 1e4) { lower -= 1e4; upper += 1.; } + v += pInt(p,upper); p.x -= 2.; + v += char_(p, 46); p.x -= .5; + v += pInt(p,lower); + return v; +} + +// printing full IEEE floats (right or left justified): see https://www.shadertoy.com/view/7dfyRH , https://www.shadertoy.com/view/7sscz7 + +// --- chars + +var O: float4; +var U: float2; +var CAPS: int; + +fn C(c: int) { + U.x -= .5; + O+= char_(U, 64 + 32 * (1-CAPS) + c); +} + +// NB: use either char.x ( pixel mask ) or char.w ( distance field + 0.5 ) + +// --- antialiased line drawing ------ https://www.shadertoy.com/view/4dcfW8 + +fn S(d: float, r: float, pix: float) -> float { + return smoothstep( .75, -.75, (d)/(pix)-(r)); // antialiased draw. r >= 1. +} +// segment with disc ends: seamless distance to segment +fn line_(_p: float2, a: float2, _b: float2) -> float { + let p = _p - a; + let b = _b - a; + let h = clamp(dot(p, b) / dot(b, b), 0., 1.); // proj coord on line + return length(p - b * h); // dist to segment +} +// line segment without disc ends ( sometime useful with semi-transparency ) +fn line0(_p: float2, a: float2, _b: float2) -> float { + let p = _p - a; + let b = _b - a; + let h = dot(p, b) / dot(b, b); // proj coord on line + let c = clamp(h, 0., 1.); + return select(1e5, length(p - b * h), h==c); // dist to strict segment +} + // You might directly return smoothstep( 3./R.y, 0., dist), + // but more efficient to factor all lines. + // Indeed we can even return dot(,) and take sqrt at the end of polyline: + // p -= b*h; return dot(p,p); + + +// for polylines with acute angles, see: https://www.shadertoy.com/view/fdVXRh + + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(screen_size.y - id.y) - .5); + + // Normalised pixel coordinates (from 0 to 1) + let uv = fragCoord / float2(screen_size.yy); + + // Time varying pixel colour + var col = .5 + .5 * cos(time.elapsed + uv.xyx + float3(0.,2.,4.)); + + let R = float2(screen_size); + + let lod = int(time.elapsed % 10.); + + U = ( uv - float2(.0,.9) ) * 16.; CAPS=1; C(18); CAPS=0; C(5);C(19);C(15);C(12); CAPS=1; C(-6); // "Resol" + U.x-=1.; CAPS=0; C(19);C(3);C(18);C(5);C(5);C(14); // "screen" + U = ( uv - float2(.6,.9) ) * 16.; CAPS=0; C(20);C(5);C(24);C(20); // "text" + U = ( uv - float2(.85,.9) ) * 16.; CAPS=0; C(12);C(15);C(4); U.x-=.5; C(-48+lod); // "lod" + + U = ( uv - float2(.0,.6) ) * 16.; CAPS=1; C(13); CAPS=0; C(15);C(21);C(19);C(5); CAPS=1; C(-6); // "mouse" + U = ( uv - float2(.5,.6) ) * 16.; CAPS=1; C(20); CAPS=0; C(9);C(13);C(5); CAPS=1; C(-6); // "Time" + U = ( uv - float2(1.45,.55) ) * 16.; CAPS=1; C(11); CAPS=0; C(5);C(25); CAPS=1; C(-6); // "Key" + + U = ( uv - float2(.1,.8) ) * 8.; // --- column 1 + O += pInt(U, R.x); U.y += .8; // window resolution + O += pInt(U, R.y); U.y += .8; + O += pFloat((U-float2(-1.,.35))*1.5, R.x/R.y); U.y += .8; + U.y += .8; + + O += pInt(U, float(mouse.pos.x)); U.y += .8; // mouse location + O += pInt(U, float(mouse.pos.y)); U.y += .8; + U.y += .4; + O += pInt(U, float(mouse.click)); U.y += .8; + + U = ( uv - float2(.5,.8) ) * 8.; // --- column 2 + + //if ( !(textureDimensions(channel1).x > 0) ) { // texture not loaded yet + // if (U.x>0. && U.y>-1.5 && U.x<2.5 && U.y<1.5) { O.r+= .5; } + //} + O += pInt(U, float(textureDimensions(channel1).x)); U.y += .8; // texture ( video ) + O += pInt(U, float(textureDimensions(channel1).y)); U.y += .8; // see LOD in column 2b + U.y += .8; + + O += pFloat(U, time.elapsed); U.y += .8; // time + O += pInt(U, float(time.frame)); U.y += .8; // iFrame + + U = ( uv - float2(.8,.8) ) * 8.; // --- column 2b + + let Sz = textureDimensions(channel1,lod); + O += pInt(U, float(Sz.x)); U.y += .8; // texture LOD + O += pInt(U, float(Sz.y)); U.y += .4; + + U = ( uv - float2(1.4,.45) ) * 8.; // --- column 4 + + var b = false; + for (var i=0; i<256; i++) { + if (keyDown(uint(i)) ) { + O += pInt(U, float(i)); // keypressed ascii + b=true; U.y += .1 *8.; + } + } + if (!b) { O += pInt(U, -1.); } + + O = O.xxxx; + + // --- non-fonts stuff + + U = (uv*R.y/R- .9)/.1; + if (min(U.x,U.y)>0.) { + O = hue(U.x); // --- hue (already in sRGB final space) + } + + U = (uv -float2(.9*R.x/R.y,.8))*10.; // --- line drawing + let pix = 10./R.y; // pixel size + O+= S( line_( U,float2(0.,0.),float2(1.1,.85)), 3., pix); + O+= S( line0(U,float2(0.5,0.),float2(1.6,.85)), 3., pix); + + U = (uv - .8*R/R.y)*10.; // --- circle, discs, transp and blend + O += S( abs(length(U-float2(.2,1.)) - .5), 1., pix); // disc. -.5: relative units. 1: pixel units + O += S( length(U-float2(1.1,1.)) - .5, 0., pix) * float4(1.,0.,0.,1.)*.5; // float4(pureCol)*opacity + O += (1.-O.a)*S( length(U-float2(1.1,.3)) - .5, 0., pix) * float4(0.,1.,0.,1.); // blend below prevs + let C = S( length(U-float2(1.1,-.3)) - .5, 0., pix) * float4(0.,0.,1.,1.)*.5; // blend above prevs + O = C + (1.-C.a)*O; + + col = O.rgb; + + // Convert from gamma-encoded to linear colour space + col = pow(col, float3(2.2)); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/fabrice/utils.wgsl.json b/examples/fabrice/utils.wgsl.json new file mode 100644 index 0000000..afd384c --- /dev/null +++ b/examples/fabrice/utils.wgsl.json @@ -0,0 +1,12 @@ +{ + "uniforms": [], + "textures": [ + { + "img": "/textures/font0.png" + }, + { + "img": "/textures/london.jpg" + } + ], + "float32Enabled": false +} \ No newline at end of file diff --git a/examples/hash.wgsl b/examples/hash.wgsl new file mode 100644 index 0000000..86647b1 --- /dev/null +++ b/examples/hash.wgsl @@ -0,0 +1,51 @@ +#include "Dave_Hoskins/hash" + +#define noise_simplex_2d_hash hash22 +#include "iq/noise_simplex_2d" + +#define noise_simplex_3d_hash hash33 +#include "nikat/noise_simplex_3d" + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + let screen_size = uint2(textureDimensions(screen)); + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + let fragCoord = float2(id.xy) + .5; + let resolution = float2(screen_size); + let p = fragCoord / resolution.xy; + let p3 = float3(fragCoord / resolution.x, time.elapsed*0.025); + + var uv = p * float2(resolution.x / resolution.y, 1.) + time.elapsed * .25; + var f = 0.; + if (p.x < .6) { // left: value noise + if (p.y < .5) { + f = noise_simplex_2d( 16.0*uv ); + } else { + f = noise_simplex_3d(p3*32.0); + } + } else { // right: fractal noise (4 octaves) + if (p.y < .5) { + uv *= 5.0; + let m = mat2x2( 1.6, 1.2, -1.2, 1.6 ); + f = 0.5000*noise_simplex_2d( uv ); uv = m*uv; + f += 0.2500*noise_simplex_2d( uv ); uv = m*uv; + f += 0.1250*noise_simplex_2d( uv ); uv = m*uv; + f += 0.0625*noise_simplex_2d( uv ); uv = m*uv; + } else { + let m = p3*8.0+8.0; + // directional artifacts can be reduced by rotating each octave + let rot1 = mat3x3(-0.37, 0.36, 0.85,-0.14,-0.93, 0.34,0.92, 0.01,0.4); + let rot2 = mat3x3(-0.55,-0.39, 0.74, 0.33,-0.91,-0.24,0.77, 0.12,0.63); + let rot3 = mat3x3(-0.71, 0.52,-0.47,-0.08,-0.72,-0.68,-0.7,-0.45,0.56); + f = 0.5333333*noise_simplex_3d(m*rot1) + + 0.2666667*noise_simplex_3d(2.0*m*rot2) + + 0.1333333*noise_simplex_3d(4.0*m*rot3) + + 0.0666667*noise_simplex_3d(8.0*m); + } + } + f = 0.5 + 0.5*f; + f *= smoothstep( 0.0, 0.005, abs(p.x - 0.6) ); + + f = pow(f, 2.2); // perceptual gradient to linear colour space + textureStore(screen, int2(id.xy), float4(f, f, f, 1.)); +} diff --git a/examples/michael0884/2d-field-lagrangian-integrator.wgsl b/examples/michael0884/2d-field-lagrangian-integrator.wgsl new file mode 100644 index 0000000..193d73a --- /dev/null +++ b/examples/michael0884/2d-field-lagrangian-integrator.wgsl @@ -0,0 +1,205 @@ +#define LENGTH 512 + +struct SimData +{ + data: array, LENGTH>, LENGTH>,2>, +} + +#storage sim SimData +#define ITERATIONS 16 +#define IN_BUF ((uint(ITERATIONS)*time.frame+dispatch.id)%2u) +#define OUT_BUF ((uint(ITERATIONS)*time.frame+dispatch.id+1u)%2u) + +fn Load(i: uint2) -> float3 { return sim.data[IN_BUF][i.x][i.y]; } +fn Store(i: uint2, data: float3) { sim.data[OUT_BUF][i.x][i.y] = data; } + +fn sdSegment(p: float2, a: float2, b: float2) -> float +{ + let pa = p-a; + let ba = b-a; + let h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); + return length( pa - ba*h ); +} + +alias Dual = vec2; + +fn dC(a: float) -> Dual +{ + return Dual(a, 0.0); +} + +fn dV(a: float) -> Dual +{ + return Dual(a, 1.0); +} + +fn dMul(a: Dual, b: Dual) -> Dual +{ + return Dual( + a.x * b.x, + a.y * b.x + a.x * b.y, + ); +} + +fn dChain(a: Dual, s: float, ds: float) -> Dual +{ + return Dual( + s, + ds * a.y, + ); +} + +fn dSqrt(a: Dual) -> Dual +{ + let s = sqrt(abs(a.x)); + let ds = (0.5 / (s+0.0001)); + return dChain(a, s, ds); +} + +fn dSin(a: Dual) -> Dual +{ + let s = sin(a.x); + let ds = cos(a.x); + return dChain(a, s, ds); +} + +fn dCos(a: Dual) -> Dual +{ + let s = cos(a.x); + let ds = -sin(a.x); + return dChain(a, s, ds); +} + +fn dSqr(a: Dual) -> Dual +{ + let s = a.x*a.x; + let ds = 2.0*a.x; + return dChain(a, s, ds); +} + +fn Ltime(qt: Dual) -> Dual +{ + return dSqr(qt); + //return -dSqrt(dC(1.0) - dSqr(qt)); +} + +fn Lspace(qx: Dual) -> Dual +{ + return -0.5*dSqr(qx); +} + +fn Lpotential(q: Dual) -> Dual +{ + return custom.CosK*dCos(custom.CosA*q); +} + +//a is the value we are varying +//at0 - previous timestep +//ax0,ax2 - neighbor space samples +//the part of the action that does not depend on at2 +fn Action0(a: Dual, at0: Dual, ax0: Dual, ax2: Dual, ay0: Dual, ay2: Dual) -> Dual +{ + return Ltime(a - at0) + + Lspace(a - ax0) + Lspace(ax2 - a) + + Lspace(a - ay0) + Lspace(ay2 - a) + + 2.0*Lpotential(a); +} + +//at2 - the value we want to predict +fn Action(a: Dual, at2: Dual, action0: Dual) -> Dual +{ + //all terms of the action integral that contain a + return Ltime(at2 - a) + action0; +} + +#define NEWTON_STEPS 64 +#define STEP_SIZE 1.0 +fn Solve(i: uint2) -> float3 +{ + let a = Load(i); + let a0 = a.x - a.y; + let a1 = a.x; + var a2 = a.x; //predicted value + + let f = dV(a1); + let ft0 = dC(a0); + let fx0 = dC(Load(i - uint2(1u, 0u)).x); + let fx2 = dC(Load(i + uint2(1u, 0u)).x); + let fy0 = dC(Load(i - uint2(0u, 1u)).x); + let fy2 = dC(Load(i + uint2(0u, 1u)).x); + let action0 = Action0(f, ft0, fx0, fx2, fy0, fy2); + + var dA = 0.0; + var dT = STEP_SIZE; + var j = 0; + for(; j float3 +{ + let screen_size = float2(textureDimensions(screen)); + let id = uint2(float(LENGTH) * ((p - screen_size.xy*0.5)/float(SCREEN_HEIGHT) + 0.5)); + return Load(id); +} + +@compute @workgroup_size(16, 16) +fn MainImage(@builtin(global_invocation_id) id: uint3) +{ + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(screen_size.y - id.y) - .5); + + var col = float3(0.35)*pow(length(Sample(fragCoord)),1.0); + + col = pow(col, float3(2.2)); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/michael0884/2d-field-lagrangian-integrator.wgsl.json b/examples/michael0884/2d-field-lagrangian-integrator.wgsl.json new file mode 100644 index 0000000..10376f5 --- /dev/null +++ b/examples/michael0884/2d-field-lagrangian-integrator.wgsl.json @@ -0,0 +1,25 @@ +{ + "uniforms": [ + { + "name": "CosK", + "value": 1 + }, + { + "name": "CosA", + "value": 0.814 + }, + { + "name": "TimeStep", + "value": 0.398 + } + ], + "textures": [ + { + "img": "/textures/blank.png" + }, + { + "img": "/textures/blank.png" + } + ], + "float32Enabled": false +} \ No newline at end of file diff --git a/examples/michael0884/3d-atomic-rasterizer.wgsl b/examples/michael0884/3d-atomic-rasterizer.wgsl new file mode 100644 index 0000000..5a43fe1 --- /dev/null +++ b/examples/michael0884/3d-atomic-rasterizer.wgsl @@ -0,0 +1,228 @@ +#storage atomic_storage array> + +//Check Uniforms +//Mode 0 - additive blending (atomicAdd) +//Mode 1 - closest sample (atomicMax) + +const MaxSamples = 64.0; +const FOV = 0.8; +const PI = 3.14159265; +const TWO_PI = 6.28318530718; + +const DEPTH_MIN = 0.2; +const DEPTH_MAX = 5.0; +const DEPTH_BITS = 16u; + +alias float3x3 = mat3x3; + +struct Camera +{ + pos: float3, + cam: float3x3, + fov: float, + size: float2 +} + +var camera : Camera; +var state : uint4; + +fn pcg4d(a: uint4) -> uint4 +{ + var v = a * 1664525u + 1013904223u; + v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z; + v = v ^ ( v >> uint4(16u) ); + v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z; + return v; +} + +fn rand4() -> float4 +{ + state = pcg4d(state); + return float4(state)/float(0xffffffffu); +} + +fn nrand4(sigma: float, mean: float4) -> float4 +{ + let Z = rand4(); + return mean + sigma * sqrt(-2.0 * log(Z.xxyy)) * + float4(cos(TWO_PI * Z.z),sin(TWO_PI * Z.z),cos(TWO_PI * Z.w),sin(TWO_PI * Z.w)); +} + +fn GetCameraMatrix(ang: float2) -> float3x3 +{ + let x_dir = float3(cos(ang.x)*sin(ang.y), cos(ang.y), sin(ang.x)*sin(ang.y)); + let y_dir = normalize(cross(x_dir, float3(0.0,1.0,0.0))); + let z_dir = normalize(cross(y_dir, x_dir)); + return float3x3(-x_dir, y_dir, z_dir); +} + +fn SetCamera(ang: float2, fov: float) +{ + camera.fov = fov; + camera.cam = GetCameraMatrix(ang); + camera.pos = - (camera.cam*float3(3.0*custom.Radius+0.5,0.0,0.0)); + camera.size = float2(textureDimensions(screen)); +} + +//project to clip space +fn Project(cam: Camera, p: float3) -> float3 +{ + let td = distance(cam.pos, p); + let dir = (p - cam.pos)/td; + let screen = dir*cam.cam; + return float3(screen.yz*cam.size.y/(cam.fov*screen.x) + 0.5*cam.size,screen.x*td); +} + +@compute @workgroup_size(16, 16) +fn Clear(@builtin(global_invocation_id) id: uint3) { + let screen_size = int2(textureDimensions(screen)); + let idx0 = int(id.x) + int(screen_size.x * int(id.y)); + + atomicStore(&atomic_storage[idx0*4+0], 0); + atomicStore(&atomic_storage[idx0*4+1], 0); + atomicStore(&atomic_storage[idx0*4+2], 0); + atomicStore(&atomic_storage[idx0*4+3], 0); +} + +fn Pack(a: uint, b: uint) -> int +{ + return int(a + (b << (31u - DEPTH_BITS))); +} + +fn Unpack(a: int) -> float +{ + let mask = (1 << (DEPTH_BITS - 1u)) - 1; + return float(a & mask)/256.0; +} + +fn ClosestPoint(color: float3, depth: float, index: int) +{ + let inverseDepth = 1.0/depth; + let scaledDepth = (inverseDepth - 1.0/DEPTH_MAX)/(1.0/DEPTH_MIN - 1.0/DEPTH_MAX); + + if(scaledDepth > 1.0 || scaledDepth < 0.0) + { + return; + } + + let uintDepth = uint(scaledDepth*float((1u << DEPTH_BITS) - 1u)); + let uintColor = uint3(color * 256.0); + + atomicMax(&atomic_storage[index*4+0], Pack(uintColor.x, uintDepth)); + atomicMax(&atomic_storage[index*4+1], Pack(uintColor.y, uintDepth)); + atomicMax(&atomic_storage[index*4+2], Pack(uintColor.z, uintDepth)); +} + +fn AdditiveBlend(color: float3, depth: float, index: int) +{ + let scaledColor = 256.0 * color/depth; + + atomicAdd(&atomic_storage[index*4+0], int(scaledColor.x)); + atomicAdd(&atomic_storage[index*4+1], int(scaledColor.y)); + atomicAdd(&atomic_storage[index*4+2], int(scaledColor.z)); +} + +fn RasterizePoint(pos: float3, color: float3) +{ + let screen_size = int2(camera.size); + let projectedPos = Project(camera, pos); + let screenCoord = int2(projectedPos.xy); + + //outside of our view + if(screenCoord.x < 0 || screenCoord.x >= screen_size.x || + screenCoord.y < 0 || screenCoord.y >= screen_size.y || projectedPos.z < 0.0) + { + return; + } + + let idx = screenCoord.x + screen_size.x * screenCoord.y; + + if(custom.Mode < 0.5) + { + AdditiveBlend(color, projectedPos.z, idx); + } + else + { + ClosestPoint(color, projectedPos.z, idx); + } +} + +@compute @workgroup_size(16, 16) +fn Rasterize(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = int2(textureDimensions(screen)); + let screen_size_f = float2(screen_size); + + let ang = float2(mouse.pos.xy)*float2(-TWO_PI, PI)/screen_size_f + float2(0.4, 0.4); + + SetCamera(ang, FOV); + + //RNG state + state = uint4(id.x, id.y, id.z, 0u*time.frame); + + for(var i: i32 = 0; i < int(custom.Samples*MaxSamples + 1.0); i++) + { + let rand = nrand4(1.0, float4(0.0)); + var pos = 0.2*rand.xyz; + let col = float3(0.5 + 0.5*sin(10.0*pos)); + + let sec = 5.0+custom.Speed*time.elapsed; + //move points along sines + pos += sin(float3(2.0,1.0,1.5)*sec)*0.1*sin(30.0*custom.Sine1*pos); + pos += sin(float3(2.0,1.0,1.5)*sec)*0.02*sin(30.0*custom.Sine2*pos.zxy); + + RasterizePoint(pos, col); + } +} + +fn Sample(pos: int2) -> float3 +{ + let screen_size = int2(textureDimensions(screen)); + let idx = pos.x + screen_size.x * pos.y; + + var color: float3; + if(custom.Mode < 0.5) + { + let x = float(atomicLoad(&atomic_storage[idx*4+0]))/(256.0); + let y = float(atomicLoad(&atomic_storage[idx*4+1]))/(256.0); + let z = float(atomicLoad(&atomic_storage[idx*4+2]))/(256.0); + + color = tanh(0.1*float3(x,y,z)/(custom.Samples*MaxSamples + 1.0)); + } + else + { + let x = Unpack(atomicLoad(&atomic_storage[idx*4+0])); + let y = Unpack(atomicLoad(&atomic_storage[idx*4+1])); + let z = Unpack(atomicLoad(&atomic_storage[idx*4+2])); + + color = float3(x,y,z); + } + + return abs(color); +} + +//to remove canvas aliasing +fn SampleBlur(pos: int2) -> float3 +{ + let avg = Sample(pos+int2(1,0))+Sample(pos+int2(-1,0))+ + Sample(pos+int2(0,1))+Sample(pos+int2(0,-1)); + return mix(Sample(pos), 0.25*avg, custom.Blur); +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) +{ + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(id.y) + .5); + + + let color = SampleBlur(int2(id.xy)); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(color, 1.)); +} diff --git a/examples/michael0884/3d-atomic-rasterizer.wgsl.json b/examples/michael0884/3d-atomic-rasterizer.wgsl.json new file mode 100644 index 0000000..381c486 --- /dev/null +++ b/examples/michael0884/3d-atomic-rasterizer.wgsl.json @@ -0,0 +1,40 @@ +{ + "uniforms": [ + { + "name": "Radius", + "value": 0.082 + }, + { + "name": "Sine1", + "value": 0.513 + }, + { + "name": "Sine2", + "value": 0.534 + }, + { + "name": "Speed", + "value": 0.485 + }, + { + "name": "Blur", + "value": 0 + }, + { + "name": "Samples", + "value": 0.25 + }, + { + "name": "Mode", + "value": 0 + } + ], + "textures": [ + { + "img": "/textures/blank.png" + }, + { + "img": "/textures/blank.png" + } + ] +} \ No newline at end of file diff --git a/examples/michael0884/accretion-disk-simulation.wgsl b/examples/michael0884/accretion-disk-simulation.wgsl new file mode 100644 index 0000000..cccdea5 --- /dev/null +++ b/examples/michael0884/accretion-disk-simulation.wgsl @@ -0,0 +1,654 @@ +// Accretion disk around a Kerr-Newman black hole. A rather simple general relativistic hydrodynamics simulation without EM effects. + +#storage atomic_storage array> + +alias float3x3 = mat3x3; +alias float4x4 = mat4x4; + +struct Camera +{ + pos: float3, + cam: float3x3, + fov: float, + size: float2 +} + +struct GeodesicRay +{ + q: float4, + qt: float4, + p: float4, +}; + +struct Particle +{ + position: float4, + velocity: float4, +} + +struct TraceRes +{ + ro: float3, + ex: float4, + color: float3, + hit: bool, +} + +struct Ray +{ + ro: float3, + rd: float3, +} + +fn isfinite(x: f32) -> bool { + return clamp(x, -3.4e38, 3.4e38) == x; +} + +const MaxSamples = 8.0; +const FOV = 0.8; +const PI = 3.14159265; +const TWO_PI = 6.28318530718; +//sqrt of particle count +const PARTICLE_COUNT = 2000; + +const DEPTH_MIN = 0.2; +const DEPTH_MAX = 5.0; +const DEPTH_BITS = 16u; +const dq = float2(0.0, 1.0); +const eps = 0.01; +const KerrM = 1.0; +const ENCODE_SCALE = 64000.0; +const GridScale = float3(38.0, 38.0, 38.0); + +var GridSize : int3; +var camera : Camera; +var state : uint4; +var bokehRad : float; +var screen_size: int2; + +fn Encode(x: float) -> int +{ + return int(x*ENCODE_SCALE); +} + +fn Decode(x: int) -> float +{ + return float(x)/ENCODE_SCALE; +} + +fn ComputeGridSize() +{ + let screenSize = float2(textureDimensions(screen)); + let maxVoxelCount = screenSize.x*screenSize.y; + let sideSize = pow(maxVoxelCount/(GridScale.x*GridScale.y*GridScale.z), 0.333333); + GridSize = int3(sideSize * GridScale); +} + +fn ToGrid(pos: float3) -> float3 +{ + return float3(GridSize)*(pos/GridScale + 0.5); +} + +fn VoxelID(pos: int3) -> int +{ + return pos.x + (pos.y + pos.z*GridSize.y)*GridSize.x; +} + +fn IndexToPixel(id: int) -> int2 +{ + return int2(id%screen_size.x,id/screen_size.x); +} + +fn Voxel(id: int) -> int3 +{ + var pos: int3; + pos.x = id%GridSize.x; + pos.y = (id/GridSize.x)%GridSize.y; + pos.z = id/(GridSize.x*GridSize.y); + return pos; +} + +fn sqr(x: float) -> float +{ + return x*x; +} + +fn Kernel(dnode: float3) -> float +{ + //box overlaps + let aabb0 = max(dnode - 0.5, float3(-0.5)); + let aabb1 = min(dnode + 0.5, float3(0.5)); + let size = max(aabb1 - aabb0, float3(0.0)); + return size.x*size.y*size.z; +} + +fn diag(a: float4) -> float4x4 +{ + return float4x4( + a.x,0.0,0.0,0.0, + 0.0,a.y,0.0,0.0, + 0.0,0.0,a.z,0.0, + 0.0,0.0,0.0,a.w + ); +} + +fn pcg4d(a: uint4) -> uint4 +{ + var v = a * 1664525u + 1013904223u; + v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z; + v = v ^ ( v >> uint4(16u) ); + v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z; + return v; +} + +fn rand4() -> float4 +{ + state = pcg4d(state); + return float4(state)/float(0xffffffffu); +} + +fn nrand4(sigma: float, mean: float4) -> float4 +{ + let Z = rand4(); + return mean + sigma * sqrt(-2.0 * log(Z.xxyy)) * + float4(cos(TWO_PI * Z.z),sin(TWO_PI * Z.z),cos(TWO_PI * Z.w),sin(TWO_PI * Z.w)); +} + +fn disk(r: float2) -> float2 +{ + return vec2(sin(TWO_PI*r.x), cos(TWO_PI*r.x))*(r.y); +} + +fn GetCameraMatrix(ang: float2) -> float3x3 +{ + let x_dir = float3(cos(ang.x)*sin(ang.y), cos(ang.y), sin(ang.x)*sin(ang.y)); + let y_dir = normalize(cross(x_dir, float3(0.0,1.0,0.0))); + let z_dir = normalize(cross(y_dir, x_dir)); + return float3x3(-x_dir, y_dir, z_dir); +} + +fn SetCamera() +{ + let screen_size = int2(textureDimensions(screen)); + let screen_size_f = float2(screen_size); + let ang = float2(mouse.pos.xy)*float2(-TWO_PI, PI)/screen_size_f + float2(0.4, 0.4); + + camera.fov = FOV; + camera.cam = GetCameraMatrix(ang); + camera.pos = - (camera.cam*float3(1.5*GridScale.x*custom.Radius+0.5,0.0,0.0)); + camera.size = screen_size_f; +} + +//project to clip space +fn Project(cam: Camera, p: float3) -> float3 +{ + let td = distance(cam.pos, p); + let dir = (p - cam.pos)/td; + let screen = dir*cam.cam; + return float3(screen.yz*cam.size.y/(cam.fov*screen.x) + 0.5*cam.size,screen.x*td); +} + +fn CameraRay(pix: float2) -> Ray +{ + let clip = (pix - 0.5*camera.size)/camera.size.y; + var ray: Ray; + ray.ro = camera.pos; + ray.rd = camera.cam * normalize(float3(1.0, camera.fov*clip.xy)); + return ray; +} + +fn LoadParticle(pix: int2) -> Particle +{ + var p: Particle; + p.position = textureLoad(pass_in, pix, 0, 0); + p.velocity = textureLoad(pass_in, pix, 1, 0); + return p; +} + +fn SaveParticle(pix: int2, p: Particle) +{ + textureStore(pass_out, pix, 0, p.position); + textureStore(pass_out, pix, 1, p.velocity); +} + +fn KerrGetR2(p: float3) -> float +{ + let rho = dot(p,p) - sqr(custom.KerrA); + let r2 = 0.5*(rho + sqrt(sqr(rho) + sqr(2.0*custom.KerrA*p.z))); + return r2; +} + +fn KerrGetK(p: float3) -> float4 +{ + let r2 = KerrGetR2(p); + let r = sqrt(r2); + let invr2 = 1.0 / (r2 + sqr(custom.KerrA) + 1e-3); + let k = float3((r*p.x - custom.KerrA*p.y) * invr2, (r*p.y + custom.KerrA*p.x) * invr2, p.z/(r + 1e-4)); + let f = r2 * (2.0 * KerrM * r - sqr(custom.KerrQ)) / (r2 * r2 + sqr(custom.KerrA * p.z) + 1e-3); + return float4(k, f); +} + +fn G(q: float4) -> float4x4 +{ + //Kerr metric in Kerr-Schild coordinates + let k = KerrGetK(q.yzw); + let kf = k.w*float4(1.0, k.xyz); + return diag(float4(-1.0,1.0,1.0,1.0)) + float4x4(kf, k.x*kf, k.y*kf, k.z*kf); +} + +fn Ginv(q: float4) -> float4x4 +{ + //inverse of Kerr metric in Kerr-Schild coordinates + let k = KerrGetK(q.yzw); + let kf = k.w*vec4(1.0, -k.xyz)/dot(k.xyz, k.xyz); + return diag(float4(-1.0,1.0,1.0,1.0)) + float4x4(-kf, k.x*kf, k.y*kf, k.z*kf); +} + +//lagrangian +fn Lmat(qt: float4, g: float4x4) -> float +{ + return g[0][0]*qt.x*qt.x + g[1][1]*qt.y*qt.y + g[2][2]*qt.z*qt.z + g[3][3]*qt.w*qt.w + + 2.0*(g[0][1]*qt.x*qt.y + g[0][2]*qt.x*qt.z + g[0][3]*qt.x*qt.w + + g[1][2]*qt.y*qt.z + g[1][3]*qt.y*qt.w + + g[2][3]*qt.z*qt.w); +} + +fn L(qt: float4, q: float4) -> float +{ + return Lmat(qt, G(q)); +} + +fn H(p: float4, ginv: float4x4) -> float +{ + return Lmat(p, ginv); +} + +fn ToMomentum(ray: GeodesicRay) -> float4 +{ + return G(ray.q)*ray.qt; +} + +fn FromMomentum(ray: GeodesicRay) -> float4 +{ + return Ginv(ray.q)*ray.p; +} + +fn ParticleToGeodesic(particle: Particle) -> GeodesicRay +{ + var ray: GeodesicRay; + ray.q = particle.position; + let vel = particle.velocity.xyz; + ray.p = float4(-sqrt(abs(1.0 - dot(vel,vel))), vel); + return ray; +} + +fn GeodesicToParticle(ray: GeodesicRay) -> Particle +{ + var particle: Particle; + particle.position = ray.q; + let normalized = normalize(ray.p); + particle.velocity = float4(normalized.yzw, 0.0); + return particle; +} + +fn HamiltonianGradient(ray: GeodesicRay) -> float4 +{ + let ginv = Ginv(ray.q); + let H0 = H(ray.p, ginv); + let delta = 0.1; + return (float4( + L(ray.qt,ray.q+delta*dq.yxxx), + L(ray.qt,ray.q+delta*dq.xyxx), + L(ray.qt,ray.q+delta*dq.xxyx), + L(ray.qt,ray.q+delta*dq.xxxy)) - H0)/delta; +} + +fn VelClamp(vel: float4) -> float4 +{ + return vel;//float4(vel.x, vel.yzw / max(1.0, length(vel.yzw))); +} + +@compute @workgroup_size(16, 16) +fn ClearGrid(@builtin(global_invocation_id) id: uint3) +{ + screen_size = int2(textureDimensions(screen)); + let idx0 = int(id.x) + int(screen_size.x * int(id.y)); + + for(var i = 0; i<4; i++) + { + atomicStore(&atomic_storage[idx0*4+i], 0); + } +} + +fn AddToGrid(value: float, index: int) +{ + atomicAdd(&atomic_storage[index], Encode(value)); +} + +fn Scatter(data: float4, gridPos: float3, delta: int3, pressure: float) +{ + let voxel = int3(floor(gridPos)) + delta; + + if(voxel.x < 0 || voxel.y < 0 || voxel.z < 0 || + voxel.x >= GridSize.x || voxel.y >= GridSize.y || voxel.z >= GridSize.z) + { + return; + } + + let dpos = gridPos - float3(voxel); + let weight = Kernel(dpos); + + let index = VoxelID(voxel); + let scatterData = (data + float4(dpos*pressure, 0.0))*weight; + + AddToGrid(scatterData.x, 4*index + 0); + AddToGrid(scatterData.y, 4*index + 1); + AddToGrid(scatterData.z, 4*index + 2); + AddToGrid(scatterData.w, 4*index + 3); +} + +@compute @workgroup_size(16, 16) +fn P2G(@builtin(global_invocation_id) id: uint3) +{ + screen_size = int2(textureDimensions(screen)); + var pix = int2(id.xy); + if(pix.x > PARTICLE_COUNT || pix.y > PARTICLE_COUNT || + pix.x >= screen_size.x || pix.y >= screen_size.y) + { + return; + } + + var p = LoadParticle(pix); + + state = uint4(id.x, id.y, id.z, time.frame); + + let r = sqrt(KerrGetR2(p.position.yzw)); + if(time.frame == 0u || r < 1.05 || any(abs(p.position.yzw) > GridScale.xyz*0.5 - 1.0)) + { + let rng = rand4(); + let rng1 = rand4(); + p.position = float4(1.0, GridScale) * float4(0.0,rng.xyz - 0.5); + + var vel = normalize(cross(p.position.yzw, float3(0.0,1.,1.0))); + let r01 = sqrt(KerrGetR2(p.position.yzw)); + + vel += 0.3*(rng1.xyz * 0.5 - 0.25); + let vscale = clamp(1.0 / (0.2 + 0.08*r01), 0., 1.0); + p.velocity = float4(2.0*(custom.InitSpeed - 0.5)*vel*vscale, 0.0); + + if(r01 < 1.05 || any(abs(p.position.yzw) > GridScale.xyz*0.5 - 1.0)) + { + SaveParticle(pix, p); + return; + } + } + + let density = p.velocity.w; + var ray = ParticleToGeodesic(p); + + //assert(0, isfinite(ray.q.y)); + //assert(1, isfinite(ray.p.y)); + + if(mouse.click == 1) + { + // return; + } + + let steps = custom.Steps*64.0 + 1.0; + for(var i = 0; i < int(steps); i++) + { + ray.qt = FromMomentum(ray); + let qt0 = ray.qt; + let dt = 0.5 * custom.TimeStep / (abs(ray.qt.x) + 0.01); + ray.p += HamiltonianGradient(ray)*dt; + ray.qt = FromMomentum(ray); + ray.q += (ray.qt+qt0)*dt; + } + p = GeodesicToParticle(ray); + SaveParticle(pix, p); + + //P2G + ComputeGridSize(); + + let gridPos = ToGrid(p.position.yzw); + let data = float4(p.velocity.xyz, 1.0); + let pressure = -steps*custom.TimeStep*custom.Pressure*density; + for(var i = 0; i <= 1; i++) + { + for(var j = 0; j <= 1; j++) + { + for(var k = 0; k <= 1; k++) + { + Scatter(data, gridPos, int3(i,j,k), pressure); + } + } + } +} + + +fn SampleGrid(pos: int3) -> float4 +{ + var res: float4; + let index = VoxelID(pos); + return textureLoad(pass_in, IndexToPixel(index), 3, 0); +} + +fn Trilinear(p: float3) -> float4 +{ + let pos = ToGrid(p); + let pi = int3(floor(pos)); + let pf = fract(pos); + + let a000 = SampleGrid(pi + int3(0,0,0)); + let a001 = SampleGrid(pi + int3(0,0,1)); + let a010 = SampleGrid(pi + int3(0,1,0)); + let a011 = SampleGrid(pi + int3(0,1,1)); + let a100 = SampleGrid(pi + int3(1,0,0)); + let a101 = SampleGrid(pi + int3(1,0,1)); + let a110 = SampleGrid(pi + int3(1,1,0)); + let a111 = SampleGrid(pi + int3(1,1,1)); + + let a00 = mix(a000, a001, pf.z); + let a01 = mix(a010, a011, pf.z); + let a10 = mix(a100, a101, pf.z); + let a11 = mix(a110, a111, pf.z); + + let a0 = mix(a00, a01, pf.y); + let a1 = mix(a10, a11, pf.y); + + return mix(a0, a1, pf.x); +} + +fn map(p: float3) -> float4 +{ + return Trilinear(p); +} + +@compute @workgroup_size(16, 16) +fn GridToTexture(@builtin(global_invocation_id) id: uint3) +{ + screen_size = int2(textureDimensions(screen)); + var pix = int2(id.xy); + if(pix.x >= screen_size.x || pix.y >= screen_size.y) + { + return; + } + var res: float4; + let index = int(id.x) + int(screen_size.x * int(id.y)); + + res.x = Decode(atomicLoad(&atomic_storage[index*4+0])); + res.y = Decode(atomicLoad(&atomic_storage[index*4+1])); + res.z = Decode(atomicLoad(&atomic_storage[index*4+2])); + res.w = Decode(atomicLoad(&atomic_storage[index*4+3])); + + textureStore(pass_out, int2(id.xy), 3, res); +} + +fn Gather(gridPos: float3, delta: int3) -> float4 +{ + let voxel = int3(floor(gridPos)) + delta; + + if(voxel.x < 0 || voxel.y < 0 || voxel.z < 0 || + voxel.x >= GridSize.x || voxel.y >= GridSize.y || voxel.z >= GridSize.z) + { + return float4(0.0); + } + + let weight = Kernel(gridPos - float3(voxel)); + + let index = VoxelID(voxel); + + var res: float4; + res.x = Decode(atomicLoad(&atomic_storage[index*4+0])); + res.y = Decode(atomicLoad(&atomic_storage[index*4+1])); + res.z = Decode(atomicLoad(&atomic_storage[index*4+2])); + res.w = Decode(atomicLoad(&atomic_storage[index*4+3])); + + return weight*float4(res.xyz / (res.w + 1e-3), res.w); +} + + +@compute @workgroup_size(16, 16) +fn G2P(@builtin(global_invocation_id) id: uint3) +{ + var pix = int2(id.xy); + var p = LoadParticle(pix); + + screen_size = int2(textureDimensions(screen)); + if(pix.x > PARTICLE_COUNT || pix.y > PARTICLE_COUNT || + pix.x >= screen_size.x || pix.y >= screen_size.y) + { + return; + } + + + state = uint4(id.x, id.y, id.z, time.frame); + + ComputeGridSize(); + let pos = p.position.yzw; + let gridPos = ToGrid(pos); + + var data = float4(0.000); + for(var i = 0; i <= 1; i++) + { + for(var j = 0; j <= 1; j++) + { + for(var k = 0; k <= 1; k++) + { + data += Gather(gridPos, int3(i,j,k)); + } + } + } + let cell = GridScale/float3(GridSize); + let ddx = 0.25*float3(-1.0, 0.0, 1.0); + let gradient = float3( + map(pos + cell*ddx.zyy).w - map(pos + cell*ddx.xyy).w, + map(pos + cell*ddx.yzy).w - map(pos + cell*ddx.yxy).w, + map(pos + cell*ddx.yyz).w - map(pos + cell*ddx.yyx).w + )/0.25; + + p.velocity = float4(mix(p.velocity.xyz, data.xyz, custom.FLIP), data.w); + + SaveParticle(pix, p); +} + +fn Sample(pos: int2) -> float3 +{ + let screen_size = int2(textureDimensions(screen)); + let idx = pos.x + screen_size.x * pos.y; + + var color: float3; + let x = float(atomicLoad(&atomic_storage[idx*4+0]))/(256.0); + let y = float(atomicLoad(&atomic_storage[idx*4+1]))/(256.0); + let z = float(atomicLoad(&atomic_storage[idx*4+2]))/(256.0); + + color = tanh(custom.Exposure*0.03*float(screen_size.x)*abs(float3(x,y,z))/(custom.Samples*MaxSamples + 1.0)); + + return abs(color); +} + +//trace up to a scattering threshold +fn Trace(ro: float3, rd: float3, threshold: float, maxDistance: float) -> TraceRes +{ + var c = float4(0.); //rgb extinction and w scattering + + var dx = 2.0*GridScale.x/float(GridSize.x); + var td = dx*rand4().x; + var color = float3(0.0); + for(var i = 0; i < 256; i++) + { + let cpos = ro.xyz + rd*td; + + if(any(abs(cpos) > GridScale.xzy*0.49 - 1.0)) + { + td += dx; + continue; + } + + let d = map(cpos.xzy); //distance + + + let rho = d.w; + let dc = rho*float4(0.3,0.5,1.0,1.0)*0.1; + + if(c.w + dc.w*dx > threshold) + { + dx = (threshold - c.w)/dc.w; + td += dx; + c += dc*dx; + break; + } + + c += dc*dx; + color += exp(-c.xyz) * dx * length(d.xyz) *rho * 0.4; + td += dx; + + if(td > maxDistance) + { + break; + } + } + + var res: TraceRes; + res.ro = ro + rd*td; + res.ex = exp(-c); + res.hit = c.w >= threshold; + res.color = color; + return res; +} + +@compute @workgroup_size(16, 16) +fn Render(@builtin(global_invocation_id) id: uint3) +{ + screen_size = int2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (int(id.x) >= screen_size.x || int(id.y) >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(id.y) + .5); + + SetCamera(); + ComputeGridSize(); + + state = uint4(id.x, id.y, id.z, time.frame); + var ray = CameraRay(float2(id.xy)); + + let res = Trace(ray.ro, ray.rd, 8.0, 2.0*GridScale.x); + + //var color = float4(Sample(int2(id.xy)),1.0); + + var color = float4(custom.Exposure*res.color, 1.0); + + let oldColor = textureLoad(pass_in, int2(id.xy), 2, 0); + + if(mouse.click == 1 && custom.AnimatedNoise > 0.5) + { + color += oldColor * custom.Accumulation; + } + + // Output to buffer + textureStore(pass_out, int2(id.xy), 2, color); + + textureStore(screen, int2(id.xy), float4(color.xyz/color.w, 1.)); +} diff --git a/examples/michael0884/accretion-disk-simulation.wgsl.json b/examples/michael0884/accretion-disk-simulation.wgsl.json new file mode 100644 index 0000000..d1d6b08 --- /dev/null +++ b/examples/michael0884/accretion-disk-simulation.wgsl.json @@ -0,0 +1,73 @@ +{ + "uniforms": [ + { + "name": "Radius", + "value": 1 + }, + { + "name": "TimeStep", + "value": 0.023 + }, + { + "name": "Samples", + "value": 0 + }, + { + "name": "AnimatedNoise", + "value": 0 + }, + { + "name": "Accumulation", + "value": 0.573 + }, + { + "name": "Exposure", + "value": 0.03 + }, + { + "name": "BlurExponent1", + "value": 0 + }, + { + "name": "BlurRadius", + "value": 0 + }, + { + "name": "BlurExponent2", + "value": 0 + }, + { + "name": "KerrA", + "value": 1 + }, + { + "name": "KerrQ", + "value": 0 + }, + { + "name": "InitSpeed", + "value": 0.767 + }, + { + "name": "Steps", + "value": 0.751 + }, + { + "name": "Pressure", + "value": 0.05 + }, + { + "name": "FLIP", + "value": 0.453 + } + ], + "textures": [ + { + "img": "/textures/blank.png" + }, + { + "img": "/textures/blank.png" + } + ], + "float32Enabled": true +} \ No newline at end of file diff --git a/examples/michael0884/black-hole-particles.wgsl b/examples/michael0884/black-hole-particles.wgsl new file mode 100644 index 0000000..c00bbac --- /dev/null +++ b/examples/michael0884/black-hole-particles.wgsl @@ -0,0 +1,407 @@ +#storage atomic_storage array> + +const MaxSamples = 8.0; +const FOV = 0.8; +const PI = 3.14159265; +const TWO_PI = 6.28318530718; +//sqrt of particle count +const PARTICLE_COUNT = 2000; + +const DEPTH_MIN = 0.2; +const DEPTH_MAX = 5.0; +const DEPTH_BITS = 16u; +const dq = float2(0.0, 1.0); +const eps = 0.01; + +alias float3x3 = mat3x3; +alias float4x4 = mat4x4; + +struct Camera +{ + pos: float3, + cam: float3x3, + fov: float, + size: float2 +} + +const KerrM = 1.0; + +struct GeodesicRay +{ + q: float4, + qt: float4, + p: float4, +}; + +struct Particle +{ + position: float4, + velocity: float4, +} + +var camera : Camera; +var state : uint4; +var bokehRad : float; + +fn sqr(x: float) -> float +{ + return x*x; +} + +fn diag(a: float4) -> float4x4 +{ + return float4x4( + a.x,0.0,0.0,0.0, + 0.0,a.y,0.0,0.0, + 0.0,0.0,a.z,0.0, + 0.0,0.0,0.0,a.w + ); +} + +fn pcg4d(a: uint4) -> uint4 +{ + var v = a * 1664525u + 1013904223u; + v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z; + v = v ^ ( v >> uint4(16u) ); + v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z; + return v; +} + +fn rand4() -> float4 +{ + state = pcg4d(state); + return float4(state)/float(0xffffffffu); +} + +fn nrand4(sigma: float, mean: float4) -> float4 +{ + let Z = rand4(); + return mean + sigma * sqrt(-2.0 * log(Z.xxyy)) * + float4(cos(TWO_PI * Z.z),sin(TWO_PI * Z.z),cos(TWO_PI * Z.w),sin(TWO_PI * Z.w)); +} + +fn disk(r: float2) -> float2 +{ + return vec2(sin(TWO_PI*r.x), cos(TWO_PI*r.x))*(r.y); +} + +fn GetCameraMatrix(ang: float2) -> float3x3 +{ + let x_dir = float3(cos(ang.x)*sin(ang.y), cos(ang.y), sin(ang.x)*sin(ang.y)); + let y_dir = normalize(cross(x_dir, float3(0.0,1.0,0.0))); + let z_dir = normalize(cross(y_dir, x_dir)); + return float3x3(-x_dir, y_dir, z_dir); +} + +fn SetCamera(ang: float2, fov: float) +{ + camera.fov = fov; + camera.cam = GetCameraMatrix(ang); + camera.pos = - (camera.cam*float3(50.0*custom.Radius+0.5,0.0,0.0)); + camera.size = float2(textureDimensions(screen)); +} + +//project to clip space +fn Project(cam: Camera, p: float3) -> float3 +{ + let td = distance(cam.pos, p); + let dir = (p - cam.pos)/td; + let screen = dir*cam.cam; + return float3(screen.yz*cam.size.y/(cam.fov*screen.x) + 0.5*cam.size,screen.x*td); +} + +fn LoadParticle(pix: int2) -> Particle +{ + var p: Particle; + p.position = textureLoad(pass_in, pix, 0, 0); + p.velocity = textureLoad(pass_in, pix, 1, 0); + return p; +} + +fn SaveParticle(pix: int2, p: Particle) +{ + textureStore(pass_out, pix, 0, p.position); + textureStore(pass_out, pix, 1, p.velocity); +} + +fn KerrGetR2(p: float3) -> float +{ + let rho = dot(p,p) - sqr(custom.KerrA); + let r2 = 0.5*(rho + sqrt(sqr(rho) + sqr(2.0*custom.KerrA*p.z))); + return r2; +} + +fn KerrGetK(p: float3) -> float4 +{ + let r2 = KerrGetR2(p); + let r = sqrt(r2); + let invr2 = 1.0 / (r2 + sqr(custom.KerrA) + 1e-3); + let k = float3((r*p.x - custom.KerrA*p.y) * invr2, (r*p.y + custom.KerrA*p.x) * invr2, p.z/(r + 1e-4)); + let f = r2 * (2.0 * KerrM * r - sqr(custom.KerrQ)) / (r2 * r2 + sqr(custom.KerrA * p.z) + 1e-3); + return float4(k, f); +} + +fn G(q: float4) -> float4x4 +{ + //Kerr metric in Kerr-Schild coordinates + let k = KerrGetK(q.yzw); + let kf = k.w*float4(1.0, k.xyz); + return diag(float4(-1.0,1.0,1.0,1.0)) + float4x4(kf, k.x*kf, k.y*kf, k.z*kf); +} + +fn Ginv(q: float4) -> float4x4 +{ + //inverse of Kerr metric in Kerr-Schild coordinates + let k = KerrGetK(q.yzw); + let kf = k.w*vec4(1.0, -k.xyz)/dot(k.xyz, k.xyz); + return diag(float4(-1.0,1.0,1.0,1.0)) + float4x4(-kf, k.x*kf, k.y*kf, k.z*kf); +} + +//lagrangian +fn Lmat(qt: float4, g: float4x4) -> float +{ + return g[0][0]*qt.x*qt.x + g[1][1]*qt.y*qt.y + g[2][2]*qt.z*qt.z + g[3][3]*qt.w*qt.w + + 2.0*(g[0][1]*qt.x*qt.y + g[0][2]*qt.x*qt.z + g[0][3]*qt.x*qt.w + + g[1][2]*qt.y*qt.z + g[1][3]*qt.y*qt.w + + g[2][3]*qt.z*qt.w); +} + +fn L(qt: float4, q: float4) -> float +{ + return Lmat(qt, G(q)); +} + +fn H(p: float4, ginv: float4x4) -> float +{ + return Lmat(p, ginv); +} + +fn ToMomentum(ray: GeodesicRay) -> float4 +{ + return G(ray.q)*ray.qt; +} + +fn FromMomentum(ray: GeodesicRay) -> float4 +{ + return Ginv(ray.q)*ray.p; +} + +fn ParticleToGeodesic(particle: Particle) -> GeodesicRay +{ + var ray: GeodesicRay; + ray.q = particle.position; + ray.p = particle.velocity; + return ray; +} + +fn GeodesicToParticle(ray: GeodesicRay) -> Particle +{ + var particle: Particle; + particle.position = ray.q; + particle.velocity = ray.p/length(ray.qt); + return particle; +} + +fn HamiltonianGradient(ray: GeodesicRay) -> float4 +{ + let ginv = Ginv(ray.q); + let H0 = H(ray.p, ginv); + let delta = 0.1; + return (float4( + L(ray.qt,ray.q+delta*dq.yxxx), + L(ray.qt,ray.q+delta*dq.xyxx), + L(ray.qt,ray.q+delta*dq.xxyx), + L(ray.qt,ray.q+delta*dq.xxxy)) - H0)/delta; +} + +fn VelClamp(vel: float4) -> float4 +{ + return vel;//float4(vel.x, vel.yzw / max(1.0, length(vel.yzw))); +} + +@compute @workgroup_size(16, 16) +fn SimulateParticles(@builtin(global_invocation_id) id: uint3) +{ + var pix = int2(id.xy); + var p = LoadParticle(pix); + + if(pix.x > PARTICLE_COUNT || pix.y > PARTICLE_COUNT) + { + return; + } + + state = uint4(id.x, id.y, id.z, time.frame); + + let r = sqrt(KerrGetR2(p.position.yzw)); + + if(time.frame == 0u || r < 0.9 || r > 30.0) + { + let rng = rand4(); + let rng1 = rand4(); + p.position = 30.0*float4(1.0, 1.0, 1.0, custom.InitThick) * float4(0.0,2.0*rng.xyz - 1.0); + + let r01 = sqrt(KerrGetR2(p.position.yzw)); + if(r01 < 0.9) + { + return; + } + + var vel = normalize(cross(p.position.yzw, float3(0.0,0.0,1.0))); + + vel += 0.3*(rng1.xyz * 0.5 - 0.25); + let vscale = clamp(1.0 / (0.2 + 0.08*r01), 0., 1.0); + p.velocity = float4(-1.0,2.0*(custom.InitSpeed - 0.5)*vel*vscale); + } + + + + var ray = ParticleToGeodesic(p); + + if(mouse.click == 1) + { + // return; + } + + for(var i = 0; i < int(custom.Steps*16.0 + 1.0); i++) + { + ray.qt = FromMomentum(ray); + let qt0 = ray.qt; + let dt = 0.5 * custom.TimeStep / (abs(ray.qt.x) + 0.01); + ray.p += HamiltonianGradient(ray)*dt; + ray.qt = FromMomentum(ray); + ray.q += (ray.qt+qt0)*dt; + } + + SaveParticle(pix, GeodesicToParticle(ray)); +} + +@compute @workgroup_size(16, 16) +fn Clear(@builtin(global_invocation_id) id: uint3) { + let screen_size = int2(textureDimensions(screen)); + let idx0 = int(id.x) + int(screen_size.x * int(id.y)); + + atomicStore(&atomic_storage[idx0*4+0], 0); + atomicStore(&atomic_storage[idx0*4+1], 0); + atomicStore(&atomic_storage[idx0*4+2], 0); + atomicStore(&atomic_storage[idx0*4+3], 0); +} + +fn AdditiveBlend(color: float3, depth: float, index: int) +{ + let scaledColor = 256.0 * color/depth; + + atomicAdd(&atomic_storage[index*4+0], int(scaledColor.x)); + atomicAdd(&atomic_storage[index*4+1], int(scaledColor.y)); + atomicAdd(&atomic_storage[index*4+2], int(scaledColor.z)); +} + + +fn RasterizePoint(pos: float3, color: float3) +{ + let screen_size = int2(camera.size); + let projectedPos = Project(camera, pos); + + let screenCoord = int2(projectedPos.xy); + + //outside of our view + if(screenCoord.x < 0 || screenCoord.x >= screen_size.x || + screenCoord.y < 0 || screenCoord.y >= screen_size.y || projectedPos.z < 0.0) + { + return; + } + + let idx = screenCoord.x + screen_size.x * screenCoord.y; + + AdditiveBlend(color, projectedPos.z, idx); +} + +@compute @workgroup_size(16, 16) +fn Rasterize(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = int2(textureDimensions(screen)); + let screen_size_f = float2(screen_size); + + let ang = float2(mouse.pos.xy)*float2(-TWO_PI, PI)/screen_size_f + 1e-4; + + SetCamera(ang, FOV); + + //RNG state + state = uint4(id.x, id.y, id.z, 0u); + + let rng = rand4(); + bokehRad = pow(rng.x, custom.BlurExponent1); + + if(mouse.click == 1 && custom.AnimatedNoise > 0.5) + { + state.w = time.frame; + } + + var pix = int2(id.xy); + + if(pix.x > PARTICLE_COUNT || pix.y > PARTICLE_COUNT) + { + return; + } + + var p = LoadParticle(pix); + + var pos = p.position.ywz; + let vel = abs(p.velocity.ywz); + var col = clamp(8.5*abs(vel)*dot(vel,vel)+0.1, float3(0.0), float3(5.0)); + col /= (0.1+bokehRad); + let impSample = (col.x + col.y + col.z)*bokehRad; + let sampleCount = clamp(int(impSample*custom.Samples*MaxSamples + 1.0), 1, 1024); + let normalCount = int(custom.Samples*MaxSamples + 1.0); + + col *= float(normalCount)/float(sampleCount); + + for(var i = 0; i < sampleCount; i++) + { + let R = 2.0*custom.BlurRadius*bokehRad; + let rng = rand4(); + let dpos = R*normalize(nrand4(1.0, float4(0.0)).xyz)*pow(rng.x, custom.BlurExponent2); + RasterizePoint(pos + dpos, col); + } +} + +fn Sample(pos: int2) -> float3 +{ + let screen_size = int2(textureDimensions(screen)); + let idx = pos.x + screen_size.x * pos.y; + + var color: float3; + let x = float(atomicLoad(&atomic_storage[idx*4+0]))/(256.0); + let y = float(atomicLoad(&atomic_storage[idx*4+1]))/(256.0); + let z = float(atomicLoad(&atomic_storage[idx*4+2]))/(256.0); + + color = tanh(custom.Exposure*0.03*float(screen_size.x)*float3(x,y,z)/(custom.Samples*MaxSamples + 1.0)); + + return abs(color); +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) +{ + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(id.y) + .5); + + + var color = float4(Sample(int2(id.xy)),1.0); + + let oldColor = textureLoad(pass_in, int2(id.xy), 2, 0); + + if(mouse.click == 1 && custom.AnimatedNoise > 0.5) + { + color += oldColor * custom.Accumulation; + } + + // Output to buffer + textureStore(pass_out, int2(id.xy), 2, color); + + textureStore(screen, int2(id.xy), float4(color.xyz/color.w, 1.)); +} diff --git a/examples/michael0884/black-hole-particles.wgsl.json b/examples/michael0884/black-hole-particles.wgsl.json new file mode 100644 index 0000000..862dbec --- /dev/null +++ b/examples/michael0884/black-hole-particles.wgsl.json @@ -0,0 +1,69 @@ +{ + "uniforms": [ + { + "name": "Radius", + "value": 0.905 + }, + { + "name": "TimeStep", + "value": 0.036 + }, + { + "name": "Samples", + "value": 0.218 + }, + { + "name": "AnimatedNoise", + "value": 0 + }, + { + "name": "Accumulation", + "value": 1 + }, + { + "name": "Exposure", + "value": 0.313 + }, + { + "name": "BlurExponent1", + "value": 0.393 + }, + { + "name": "BlurRadius", + "value": 0.444 + }, + { + "name": "BlurExponent2", + "value": 0.81 + }, + { + "name": "KerrA", + "value": 0.876 + }, + { + "name": "KerrQ", + "value": 0 + }, + { + "name": "InitSpeed", + "value": 0.719 + }, + { + "name": "InitThick", + "value": 0.22 + }, + { + "name": "Steps", + "value": 0.387 + } + ], + "textures": [ + { + "img": "/textures/blank.png" + }, + { + "img": "/textures/blank.png" + } + ], + "float32Enabled": true +} \ No newline at end of file diff --git a/examples/michael0884/forward-volumetric-path-tracer.wgsl b/examples/michael0884/forward-volumetric-path-tracer.wgsl new file mode 100644 index 0000000..d7ce597 --- /dev/null +++ b/examples/michael0884/forward-volumetric-path-tracer.wgsl @@ -0,0 +1,459 @@ +#storage atomic_storage array> + +const MaxSamples = 8.0; +const FOV = 0.8; +const PI = 3.14159265; +const TWO_PI = 6.28318530718; +const STEP = 0.01; +const LARGENUM = 1e10; + +alias float3x3 = mat3x3; +alias float2x2 = mat2x2; + +alias distanceArr = array; + +struct Camera +{ + pos: float3, + cam: float3x3, + fov: float, + size: float2 +} + +struct TraceRes +{ + ro: float3, + ex: float4, + hit: bool, + distances: distanceArr, +} + +struct Ray +{ + ro: float3, + rd: float3, +} + +var camera : Camera; +var state : uint4; + +fn getTresholds() -> distanceArr +{ + var arr: distanceArr; + arr[0] = -log(0.9); + arr[1] = -log(0.6); + arr[2] = -log(0.5); + arr[3] = -log(0.25); + arr[4] = -log(0.15); + arr[5] = -log(0.1); + arr[6] = -log(0.05); + arr[7] = -log(0.005); + return arr; +} + +fn pcg4d(a: uint4) -> uint4 +{ + var v = a * 1664525u + 1013904223u; + v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z; + v = v ^ ( v >> uint4(16u) ); + v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z; + return v; +} + +fn rand4() -> float4 +{ + state = pcg4d(state); + return float4(state)/float(0xffffffffu); +} + +fn nrand4(sigma: float, mean: float4) -> float4 +{ + let Z = rand4(); + return mean + sigma * sqrt(-2.0 * log(Z.xxyy)) * + float4(cos(TWO_PI * Z.z),sin(TWO_PI * Z.z),cos(TWO_PI * Z.w),sin(TWO_PI * Z.w)); +} + +fn udir(rng: float2) -> float3 +{ + let r = float2(2.*PI*rng.x, acos(2.*rng.y - 1.0)); + let c = cos(r); + let s = sin(r); + return float3(c.x*s.y, s.x*s.y, c.y); +} + +fn disk(rng: float2) -> float2 +{ + return float2(sin(TWO_PI*rng.x), cos(TWO_PI*rng.x))*sqrt(rng.y); +} + +fn Rotate(t: float) -> float2x2 +{ + return float2x2( + cos(t), sin(t), + - sin(t), cos(t), + ); +} + +fn RotXY(x: float3, t: float) -> float3 +{ + return float3(Rotate(t)*x.xy, x.z); +} + +fn GetCameraMatrix(ang: float2) -> float3x3 +{ + let x_dir = float3(cos(ang.x)*sin(ang.y), cos(ang.y), sin(ang.x)*sin(ang.y)); + let y_dir = normalize(cross(x_dir, float3(0.0,1.0,0.0))); + let z_dir = normalize(cross(y_dir, x_dir)); + return float3x3(-x_dir, y_dir, z_dir); +} + +fn SetCamera() +{ + let screen_size = int2(textureDimensions(screen)); + let screen_size_f = float2(screen_size); + let ang = float2(mouse.pos.xy)*float2(-TWO_PI, PI)/screen_size_f + float2(0.4, 0.4); + + camera.fov = FOV; + camera.cam = GetCameraMatrix(ang); + camera.pos = - (camera.cam*float3(5.0*custom.Radius+0.5,0.0,0.0)); + camera.size = screen_size_f; +} + + +//recover approximate transmittense from texture stored depths +fn ReTrace(td: float, distances: distanceArr) -> float +{ + let thresholds = getTresholds(); + + if(td < distances[0]) + { + return mix(0.0, thresholds[0], (td - 0.0)/(distances[0] - 0.0)); + } + + for(var i = 1; i < 8; i++) + { + if(td < distances[i]) + { + let t = (td - distances[i - 1]) / (distances[i] - distances[i - 1]); + return mix(thresholds[i - 1], thresholds[i], t); + } + } + + return thresholds[7]; +} + + +fn sdMandelbulb(p: float3) -> float4 +{ + var w = p; + var m = dot(w,w); + + var trap = float4(abs(w),m); + var dz = 1.0; + + for(var i = 0; i < 3; i++) + { + // trigonometric version (MUCH faster than polynomial) + dz = 8.0*pow(m,3.5)*dz + 1.0; + + let r = length(w); + let b = 8.0*acos( w.y/r); + let a = 8.0*atan2( w.x, w.z ); + w = p + pow(r,8.0) * vec3( sin(b)*sin(a), cos(b), sin(b)*cos(a) ); + + trap = min( trap, vec4(abs(w),m) ); + + m = dot(w,w); + if( m > 256.0 ) + { + break; + } + } + // distance estimation (through the Hubbard-Douady potential) + return float4(trap.yzw, 0.25*log(m)*sqrt(m)/dz); +} + +fn map(p: float3) -> float4 +{ + return sdMandelbulb(p); +} + +//trace up to a scattering threshold +fn Trace(ro: float3, rd: float3, threshold: float, maxDistance: float) -> TraceRes +{ + var c = float4(0.); //rgb extinction and w scattering + let r = rand4(); + var td = STEP*r.x; + + var distances: distanceArr; + for(var i = 0; i < 8; i++) + { + distances[i] = 100.0; + } + + var j = 0; + let thresholds = getTresholds(); + for(var i = 0; i < 150; i++) + { + let d = map(ro.xyz + rd*td); //distance + var dx = mix(0.5, 1.0, r.y) * select(d.w+STEP, STEP - d.w, d.w < 0.0); //ray step + + let rho = select(float4(0.0,0.0,0.0,0.1), 40.0*custom.Scatter*float4(d.xyz, 1.0), d.w < 0.0); + + let dc = rho*float4(float3(10.0*custom.Absorption), 1.0); + + if(c.w + dc.w*dx > threshold) + { + dx = (threshold - c.w)/dc.w; + td += dx; + c += dc*dx; + break; + } + + if((c.w + dc.w*dx > thresholds[j]) && (j < 8)) + { + distances[j] = td + (thresholds[j] - c.w)/dc.w; + j++; + } + + c += dc*dx; + td += dx; + + if(td > maxDistance) + { + break; + } + } + + var res: TraceRes; + res.ro = ro + rd*td; + res.ex = exp(-c); + res.hit = c.w >= threshold; + res.distances = distances; + return res; +} + +//project to clip space +fn Project(cam: Camera, p: float3) -> float3 +{ + let td = distance(cam.pos, p); + let dir = (p - cam.pos)/td; + let screen = dir*cam.cam; + return float3(screen.yz*cam.size.y/(cam.fov*screen.x) + 0.5*cam.size,screen.x*td); +} + +@compute @workgroup_size(16, 16) +fn Clear(@builtin(global_invocation_id) id: uint3) { + let screen_size = int2(textureDimensions(screen)); + let idx0 = int(id.x) + int(screen_size.x * int(id.y)); + + atomicStore(&atomic_storage[idx0*4+0], 0); + atomicStore(&atomic_storage[idx0*4+1], 0); + atomicStore(&atomic_storage[idx0*4+2], 0); + atomicStore(&atomic_storage[idx0*4+3], 0); +} + +fn AdditiveBlend(color: float3, depth: float, index: int) +{ + let scaledColor = int3(256.0 * color/(depth*depth + 0.2)); + + if(scaledColor.x>0) + { + atomicAdd(&atomic_storage[index*4+0], scaledColor.x); + } + + if(scaledColor.y>0) + { + atomicAdd(&atomic_storage[index*4+1], scaledColor.y); + } + + if(scaledColor.z>0) + { + atomicAdd(&atomic_storage[index*4+2], scaledColor.z); + } +} + +fn RasterizePoint(pos: float3, color: float3) +{ + let screen_size = int2(camera.size); + let projectedPos = Project(camera, pos); + let screenCoord = int2(projectedPos.xy); + + //outside of our view + if(screenCoord.x < 0 || screenCoord.x >= screen_size.x || + screenCoord.y < 0 || screenCoord.y >= screen_size.y || projectedPos.z < 0.0) + { + return; + } + + var dist: distanceArr; + let camPass1 = textureLoad(pass_in, screenCoord, 1, 0); + for(var i = 0; i < 3; i++) + { + dist[i] = camPass1[i]; + } + + let camPass2 = textureLoad(pass_in, screenCoord, 2, 0); + for(var i = 0; i < 3; i++) + { + dist[i+4] = camPass2[i]; + } + + + let visibility =exp(-ReTrace(distance(camera.pos, pos),dist)); + + let idx = screenCoord.x + screen_size.x * screenCoord.y; + AdditiveBlend(visibility*color, projectedPos.z, idx); +} + + +fn getRay() -> Ray +{ + let r = rand4(); + + var ray: Ray; + + if(r.x Ray +{ + let clip = (pix - 0.5*camera.size)/camera.size.y; + var ray: Ray; + ray.ro = camera.pos; + ray.rd = camera.cam * normalize(float3(1.0, camera.fov*clip.xy)); + return ray; +} + +@compute @workgroup_size(16, 16) +fn CameraPass(@builtin(global_invocation_id) id: uint3) +{ + let screen_size = uint2(textureDimensions(screen)); + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + SetCamera(); + + var ray = CameraRay(float2(id.xy)); + + let res = Trace(ray.ro, ray.rd, 8.0, 10.0); + + let td = res.distances; + + // Output to buffer + textureStore(pass_out, int2(id.xy), 1, float4(td[0],td[1],td[2],td[3])); + + textureStore(pass_out, int2(id.xy), 2, float4(td[4],td[5],td[6],td[7])); +} + + +@compute @workgroup_size(16, 16) +fn Rasterize(@builtin(global_invocation_id) id: uint3) +{ + SetCamera(); + + //RNG state + state = uint4(id.x, id.y, id.z, uint(custom.NoiseAnimation)*time.frame); + + for(var i: i32 = 0; i < int(custom.Samples*MaxSamples + 1.0); i++) + { + ForwardTrace(); + } +} + +fn Sample(pos: int2) -> float3 +{ + let screen_size = int2(textureDimensions(screen)); + let idx = pos.x + screen_size.x * pos.y; + + var color: float3; + + let x = float(atomicLoad(&atomic_storage[idx*4+0]))/(256.0); + let y = float(atomicLoad(&atomic_storage[idx*4+1]))/(256.0); + let z = float(atomicLoad(&atomic_storage[idx*4+2]))/(256.0); + + color = tanh(2.8*float3(x,y,z)/(floor(custom.Samples*MaxSamples) + 1.0)); + + return abs(color); +} + +@compute @workgroup_size(16, 16) +fn FinalPass(@builtin(global_invocation_id) id: uint3) +{ + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(id.y) + .5); + + + let oldColor = textureLoad(pass_in, int2(id.xy), 0, 0); + + var color = float4(Sample(int2(id.xy)), 1.0)*1e-3; + + + + if(mouse.click != 1) + { + color += oldColor * custom.Accumulation; + } + + // Output to buffer + textureStore(pass_out, int2(id.xy), 0, color); + + // Output to screen + //if(id.x >= screen_size.x/2u) + { + textureStore(screen, int2(id.xy), float4(color.xyz/color.w, 1.)); + } + //else + //{ + // var camPass = textureLoad(pass_in, int2(id.xy), 1, 0); + // textureStore(screen, int2(id.xy), float4((camPass.xyz)/10.0, 1.)); + // } +} diff --git a/examples/michael0884/forward-volumetric-path-tracer.wgsl.json b/examples/michael0884/forward-volumetric-path-tracer.wgsl.json new file mode 100644 index 0000000..4cd165a --- /dev/null +++ b/examples/michael0884/forward-volumetric-path-tracer.wgsl.json @@ -0,0 +1,52 @@ +{ + "uniforms": [ + { + "name": "Radius", + "value": 0.638 + }, + { + "name": "Samples", + "value": 0 + }, + { + "name": "Laser", + "value": 0 + }, + { + "name": "LightR", + "value": 0.579 + }, + { + "name": "LightAng", + "value": 0.835 + }, + { + "name": "Accumulation", + "value": 1 + }, + { + "name": "NoiseAnimation", + "value": 1 + }, + { + "name": "Bounces", + "value": 0.402 + }, + { + "name": "Scatter", + "value": 0.231 + }, + { + "name": "Absorption", + "value": 0.352 + } + ], + "textures": [ + { + "img": "/textures/blank.png" + }, + { + "img": "/textures/blank.png" + } + ] +} \ No newline at end of file diff --git a/examples/michael0884/lagrangian-wave-equation.wgsl b/examples/michael0884/lagrangian-wave-equation.wgsl new file mode 100644 index 0000000..a1fb024 --- /dev/null +++ b/examples/michael0884/lagrangian-wave-equation.wgsl @@ -0,0 +1,219 @@ +// 1+1 relativistic Sine Gordon string lagrangian + +#define LENGTH 512 + +struct SimData +{ + data: array, LENGTH>,2>, +} + +#storage sim SimData + +#define IN_BUF (time.frame%2u) +#define OUT_BUF ((time.frame+1u)%2u) + +fn Load(i: uint) -> float3 { return sim.data[IN_BUF][i]; } +fn Store(i: uint, data: float3) { sim.data[OUT_BUF][i] = data; } + +fn sdSegment(p: float2, a: float2, b: float2) -> float +{ + let pa = p-a; + let ba = b-a; + let h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); + return length( pa - ba*h ); +} + +alias Dual = vec2; + +fn dC(a: float) -> Dual +{ + return Dual(a, 0.0); +} + +fn dV(a: float) -> Dual +{ + return Dual(a, 1.0); +} + +fn dMul(a: Dual, b: Dual) -> Dual +{ + return Dual( + a.x * b.x, + a.y * b.x + a.x * b.y, + ); +} + +fn dChain(a: Dual, s: float, ds: float) -> Dual +{ + return Dual( + s, + ds * a.y, + ); +} + +fn dSqrt(a: Dual) -> Dual +{ + let s = sqrt(abs(a.x)); + let ds = (0.5 / (s+0.0001)); + return dChain(a, s, ds); +} + +fn dSin(a: Dual) -> Dual +{ + let s = sin(a.x); + let ds = cos(a.x); + return dChain(a, s, ds); +} + +fn dCos(a: Dual) -> Dual +{ + let s = cos(a.x); + let ds = -sin(a.x); + return dChain(a, s, ds); +} + +fn dSqr(a: Dual) -> Dual +{ + let s = a.x*a.x; + let ds = 2.0*a.x; + return dChain(a, s, ds); +} + +fn Ltime(qt: Dual) -> Dual +{ + //return 0.5*dSqr(qt); + return -dSqrt(dC(1.0) - dSqr(qt)); +} + +fn Lspace(qx: Dual) -> Dual +{ + return -0.5*dSqr(qx); +} + +fn Lpotential(q: Dual) -> Dual +{ + return custom.CosK*dCos(custom.CosA*q); +} + +fn Dt(a0: Dual, a1: Dual) -> Dual +{ + return (a1 - a0)/custom.SpeedLimit; +} + +fn Dx(a0: Dual, a1: Dual) -> Dual +{ + return a1 - a0; +} + +//a is the value we are varying +//at0 - previous timestep +//at2 - the value we want to predict +//ax0,ax2 - neighbor space samples +fn Action(a: Dual, at0: Dual, at2: Dual, ax0: Dual, ax2: Dual) -> Dual +{ + //all terms of the action integral that contain a1 + return Ltime(a - at0) + Ltime(at2 - a) + + Lspace(a - ax0) + Lspace(ax2 - a) + + 2.0*Lpotential(a); +} + +#define NEWTON_STEPS 64 +#define STEP_SIZE 0.25 +fn Solve(i: uint) -> float3 +{ + let a = Load(i); + let a0 = a.x - a.y; + let a1 = a.x; + var a2 = a.x; //predicted value + + let f = dV(a1); + let ft0 = dC(a0); + let fx0 = dC(Load(i - 1u).x); + let fx2 = dC(Load(i + 1u).x); + var dA = 0.0; + var dT = STEP_SIZE; + var a2prev = a2; + var A0 = Action(f, ft0, dC(a2), fx0, fx2); + var j = 0; + var K = 0.0; + for(; j float2 +{ + return float2(float(SCREEN_WIDTH) * (float(i) + 0.5)/ float(LENGTH), Load(i).x * float(SCREEN_HEIGHT)/30.0 + float(SCREEN_HEIGHT) * 0.5); +} + +@compute @workgroup_size(16, 16) +fn MainImage(@builtin(global_invocation_id) id: uint3) +{ + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(screen_size.y - id.y) - .5); + + var sdf = 1e10; + var am = 0.0; + for(var i = 0u; i < (uint(LENGTH) - 1u); i++) + { + let sdSeg = sdSegment(fragCoord, GetPoint(i), GetPoint(i+1u)); + if(sdSeg < sdf) + { + sdf = sdSeg; + am = Load(i).z; + } + } + + var col = float3(1.0, 1.0, 1.0)*smoothstep(2.0, 1.0, sdf) + + float3(1.0, 0.0, 0.0)*abs(am)*smoothstep(12.0, 10.0, sdf); + + col = pow(col, float3(2.2)); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/michael0884/lagrangian-wave-equation.wgsl.json b/examples/michael0884/lagrangian-wave-equation.wgsl.json new file mode 100644 index 0000000..8fea107 --- /dev/null +++ b/examples/michael0884/lagrangian-wave-equation.wgsl.json @@ -0,0 +1,29 @@ +{ + "uniforms": [ + { + "name": "CosK", + "value": 0.124 + }, + { + "name": "CosA", + "value": 0.786 + }, + { + "name": "SpeedLimit", + "value": 0.496 + }, + { + "name": "TimeStep", + "value": 0.5 + } + ], + "textures": [ + { + "img": "/textures/blank.png" + }, + { + "img": "/textures/blank.png" + } + ], + "float32Enabled": false +} \ No newline at end of file diff --git a/examples/michael0884/stardust.wgsl b/examples/michael0884/stardust.wgsl new file mode 100644 index 0000000..0934fd7 --- /dev/null +++ b/examples/michael0884/stardust.wgsl @@ -0,0 +1,274 @@ +#storage atomic_storage array> + +const MaxSamples = 256.0; +const FOV = 0.8; +const PI = 3.14159265; +const TWO_PI = 6.28318530718; +//sqrt of particle count +const PARTICLE_COUNT = 600; + +const DEPTH_MIN = 0.2; +const DEPTH_MAX = 5.0; +const DEPTH_BITS = 16u; + +alias float3x3 = mat3x3; + +struct Camera +{ + pos: float3, + cam: float3x3, + fov: float, + size: float2 +} + +struct Particle +{ + position: float4, + velocity: float4, +} + +var camera : Camera; +var state : uint4; +var bokehRad : float; + +fn pcg4d(a: uint4) -> uint4 +{ + var v = a * 1664525u + 1013904223u; + v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z; + v = v ^ ( v >> uint4(16u) ); + v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z; + return v; +} + +fn rand4() -> float4 +{ + state = pcg4d(state); + return float4(state)/float(0xffffffffu); +} + +fn nrand4(sigma: float, mean: float4) -> float4 +{ + let Z = rand4(); + return mean + sigma * sqrt(-2.0 * log(Z.xxyy)) * + float4(cos(TWO_PI * Z.z),sin(TWO_PI * Z.z),cos(TWO_PI * Z.w),sin(TWO_PI * Z.w)); +} + +fn disk(r: float2) -> float2 +{ + return vec2(sin(TWO_PI*r.x), cos(TWO_PI*r.x))*(r.y); +} + +fn GetCameraMatrix(ang: float2) -> float3x3 +{ + let x_dir = float3(cos(ang.x)*sin(ang.y), cos(ang.y), sin(ang.x)*sin(ang.y)); + let y_dir = normalize(cross(x_dir, float3(0.0,1.0,0.0))); + let z_dir = normalize(cross(y_dir, x_dir)); + return float3x3(-x_dir, y_dir, z_dir); +} + +fn SetCamera(ang: float2, fov: float) +{ + camera.fov = fov; + camera.cam = GetCameraMatrix(ang); + camera.pos = - (camera.cam*float3(15.0*custom.Radius+0.5,0.0,0.0)); + camera.size = float2(textureDimensions(screen)); +} + +//project to clip space +fn Project(cam: Camera, p: float3) -> float3 +{ + let td = distance(cam.pos, p); + let dir = (p - cam.pos)/td; + let screen = dir*cam.cam; + return float3(screen.yz*cam.size.y/(cam.fov*screen.x) + 0.5*cam.size,screen.x*td); +} + +fn LoadParticle(pix: int2) -> Particle +{ + var p: Particle; + p.position = textureLoad(pass_in, pix, 0, 0); + p.velocity = textureLoad(pass_in, pix, 1, 0); + return p; +} + +fn SaveParticle(pix: int2, p: Particle) +{ + textureStore(pass_out, pix, 0, p.position); + textureStore(pass_out, pix, 1, p.velocity); +} + +fn ForceField(pos: float3, t: float) -> float4 +{ + let a0 = float3(sin(t),cos(0.4*t),cos(t)); + let d = distance(pos, a0); + let F = (a0 - pos)*(1.0/(d*d*d + 1e-3) - 0.4/(d*d*d*d + 1e-3)) + 1e-3; + return 0.2*float4(F, 0.0); +} + +@compute @workgroup_size(16, 16) +fn SimulateParticles(@builtin(global_invocation_id) id: uint3) +{ + var pix = int2(id.xy); + var p = LoadParticle(pix); + + if(pix.x > PARTICLE_COUNT || pix.y > PARTICLE_COUNT) + { + return; + } + + state = uint4(id.x, id.y, id.z, time.frame); + + if(time.frame == 0u) + { + let rng = rand4(); + p.position = float4(2.0*rng.xyz - 1.0, 0.0); + p.velocity = float4(0.0,0.0,0.0,0.0); + } + let t = fract(custom.Speed*float(time.frame)/800.0)*30.0; + + if(mouse.click == 1) + { + return; + } + + if(t < 0.05) + { + p.velocity -= 0.5 * p.velocity * length(p.velocity); + } + + let dt = custom.Speed * custom.TimeStep; + p.velocity += (ForceField(p.position.xyz, t) - custom.VelocityDecay*p.velocity) * dt; + p.position += p.velocity * dt; + + SaveParticle(pix, p); +} + +@compute @workgroup_size(16, 16) +fn Clear(@builtin(global_invocation_id) id: uint3) { + let screen_size = int2(textureDimensions(screen)); + let idx0 = int(id.x) + int(screen_size.x * int(id.y)); + + atomicStore(&atomic_storage[idx0*4+0], 0); + atomicStore(&atomic_storage[idx0*4+1], 0); + atomicStore(&atomic_storage[idx0*4+2], 0); + atomicStore(&atomic_storage[idx0*4+3], 0); +} + +fn AdditiveBlend(color: float3, depth: float, index: int) +{ + let scaledColor = 256.0 * color/depth; + + atomicAdd(&atomic_storage[index*4+0], int(scaledColor.x)); + atomicAdd(&atomic_storage[index*4+1], int(scaledColor.y)); + atomicAdd(&atomic_storage[index*4+2], int(scaledColor.z)); +} + + +fn RasterizePoint(pos: float3, color: float3) +{ + let screen_size = int2(camera.size); + let projectedPos = Project(camera, pos); + + let screenCoord = int2(projectedPos.xy); + + //outside of our view + if(screenCoord.x < 0 || screenCoord.x >= screen_size.x || + screenCoord.y < 0 || screenCoord.y >= screen_size.y || projectedPos.z < 0.0) + { + return; + } + + let idx = screenCoord.x + screen_size.x * screenCoord.y; + + AdditiveBlend(color, projectedPos.z, idx); +} + +@compute @workgroup_size(16, 16) +fn Rasterize(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = int2(textureDimensions(screen)); + let screen_size_f = float2(screen_size); + + let ang = float2(mouse.pos.xy)*float2(-TWO_PI, PI)/screen_size_f + 1e-4; + + SetCamera(ang, FOV); + + //RNG state + state = uint4(id.x, id.y, id.z, 0u); + + let rng = rand4(); + bokehRad = pow(rng.x, custom.BlurExponent1); + + if(mouse.click == 1 && custom.AnimatedNoise > 0.5) + { + state.w = time.frame; + } + + var pix = int2(id.xy); + + if(pix.x > PARTICLE_COUNT || pix.y > PARTICLE_COUNT) + { + return; + } + + var p = LoadParticle(pix); + + var pos = p.position.xyz; + var col = 5.5*abs(p.velocity.xyz)*dot(p.velocity,p.velocity)+0.1; + col /= (0.1+bokehRad); + let impSample = (col.x + col.y + col.z)*bokehRad; + let sampleCount = clamp(int(impSample*custom.Samples*MaxSamples + 1.0), 1, 1024); + let normalCount = int(custom.Samples*MaxSamples + 1.0); + + col *= float(normalCount)/float(sampleCount); + + for(var i = 0; i < sampleCount; i++) + { + let R = 2.0*custom.BlurRadius*bokehRad; + let rng = rand4(); + let dpos = R*normalize(nrand4(1.0, float4(0.0)).xyz)*pow(rng.x, custom.BlurExponent2); + RasterizePoint(pos + dpos, col); + } +} + +fn Sample(pos: int2) -> float3 +{ + let screen_size = int2(textureDimensions(screen)); + let idx = pos.x + screen_size.x * pos.y; + + var color: float3; + let x = float(atomicLoad(&atomic_storage[idx*4+0]))/(256.0); + let y = float(atomicLoad(&atomic_storage[idx*4+1]))/(256.0); + let z = float(atomicLoad(&atomic_storage[idx*4+2]))/(256.0); + + color = tanh(custom.Exposure*0.03*float(screen_size.x)*float3(x,y,z)/(custom.Samples*MaxSamples + 1.0)); + + return abs(color); +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) +{ + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(id.y) + .5); + + + var color = float4(Sample(int2(id.xy)),1.0); + + let oldColor = textureLoad(pass_in, int2(id.xy), 2, 0); + + if(mouse.click == 1 && custom.AnimatedNoise > 0.5) + { + color += oldColor * custom.Accumulation; + } + + // Output to buffer + textureStore(pass_out, int2(id.xy), 2, color); + + textureStore(screen, int2(id.xy), float4(color.xyz/color.w, 1.)); +} diff --git a/examples/michael0884/stardust.wgsl.json b/examples/michael0884/stardust.wgsl.json new file mode 100644 index 0000000..c2236ce --- /dev/null +++ b/examples/michael0884/stardust.wgsl.json @@ -0,0 +1,56 @@ +{ + "uniforms": [ + { + "name": "Radius", + "value": 0.551 + }, + { + "name": "TimeStep", + "value": 0.313 + }, + { + "name": "Samples", + "value": 0.632 + }, + { + "name": "BlurRadius", + "value": 0.489 + }, + { + "name": "VelocityDecay", + "value": 0.018 + }, + { + "name": "Speed", + "value": 0.697 + }, + { + "name": "BlurExponent1", + "value": 0.621 + }, + { + "name": "BlurExponent2", + "value": 0 + }, + { + "name": "AnimatedNoise", + "value": 1 + }, + { + "name": "Accumulation", + "value": 0.962 + }, + { + "name": "Exposure", + "value": 0.224 + } + ], + "textures": [ + { + "img": "/textures/blank.png" + }, + { + "img": "/textures/blank.png" + } + ] +} \ No newline at end of file diff --git a/examples/michael0884/variational-integrator.wgsl b/examples/michael0884/variational-integrator.wgsl new file mode 100644 index 0000000..e92d302 --- /dev/null +++ b/examples/michael0884/variational-integrator.wgsl @@ -0,0 +1,173 @@ +// Using the lagrangian to solve for the discrete equations of motion. + +#define LENGTH 256 + +struct SimData +{ + data: array,2>, +} + +#storage sim SimData + +#define IN_BUF (time.frame%2u) +#define OUT_BUF ((time.frame+1u)%2u) + +fn Load(i: uint) -> float { return sim.data[IN_BUF][i]; } +fn Store(i: uint, data: float) { sim.data[OUT_BUF][i] = data; } + +fn sdSegment(p: float2, a: float2, b: float2) -> float +{ + let pa = p-a; + let ba = b-a; + let h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); + return length( pa - ba*h ); +} + +alias Dual = vec2; + +fn dC(a: float) -> Dual +{ + return Dual(a, 0.0); +} + +fn dV(a: float) -> Dual +{ + return Dual(a, 1.0); +} + +fn dMul(a: Dual, b: Dual) -> Dual +{ + return Dual( + a.x * b.x, + a.y * b.x + a.x * b.y, + ); +} + +fn dChain(a: Dual, s: float, ds: float) -> Dual +{ + return Dual( + s, + ds * a.y, + ); +} + +fn dSqrt(a: Dual) -> Dual +{ + let s = sqrt(abs(a.x)); + let ds = (0.5 / (s+0.0001)); + return dChain(a, s, ds); +} + +fn dSin(a: Dual) -> Dual +{ + let s = sin(a.x); + let ds = cos(a.x); + return dChain(a, s, ds); +} + +fn dCos(a: Dual) -> Dual +{ + let s = cos(a.x); + let ds = -sin(a.x); + return dChain(a, s, ds); +} + +fn dSqr(a: Dual) -> Dual +{ + let s = a.x*a.x; + let ds = 2.0*a.x; + return dChain(a, s, ds); +} + +//relativistic harmonic oscillator +fn Lagrangian(q: Dual, qt: Dual) -> Dual +{ + return -dSqrt(dC(1.0) - dSqr(qt)) - 0.1*dSqr(q); +} + +fn DLagrangian(a0: Dual, a1: Dual) -> Dual +{ + let Dt = 1.0*(a1 - a0); + return Lagrangian(a0, Dt) + Lagrangian(a1, Dt); +} + +fn Action(a0: Dual, a1: Dual, a2: Dual) -> Dual +{ + return DLagrangian(a0, a1) + DLagrangian(a1, a2); +} + +#define NEWTON_STEPS 16 +#define STEP_SIZE 3e-1 +fn Solve(i: uint) -> float +{ + let a0 = Load(i - 2u); + let a1 = Load(i - 1u); + var a2 = 2.0*a1 - a0; + let f0 = dC(a0); + let f1 = dV(a1); + + for(var i = 0; i= 2u && id.x == time.frame) + { + f = Solve(id.x); + } + + Store(id.x, clamp(f, -35.0, 35.0)); +} + +fn GetPoint(i: uint) -> float2 +{ + return float2(float(SCREEN_WIDTH) * float(i) / float(LENGTH), 10.0*Load(i) + float(SCREEN_HEIGHT) * 0.5); +} + +@compute @workgroup_size(16, 16) +fn MainImage(@builtin(global_invocation_id) id: uint3) +{ + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(screen_size.y - id.y) - .5); + + var sdf = 1e10; + for(var i = 0u; i < (uint(LENGTH) - 1u); i++) + { + sdf = min(sdf, sdSegment(fragCoord, GetPoint(i), GetPoint(i+1u))); + } + + var col = float3(1.0)*smoothstep(1.0, 2.0, sdf); + + col = pow(col, float3(2.2)); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/michael0884/waves-around-a-black-hole.wgsl b/examples/michael0884/waves-around-a-black-hole.wgsl new file mode 100644 index 0000000..d573cc6 --- /dev/null +++ b/examples/michael0884/waves-around-a-black-hole.wgsl @@ -0,0 +1,471 @@ +// Nonlinear waves in a Kerr-Newman metric + +#define LENGTH 1024 +#define WG 64 + +struct SimData +{ + data: array, LENGTH>, LENGTH>,2>, +} + +alias float3x3 = mat3x3; +alias float4x4 = mat4x4; + +#storage sim SimData +#define ITERATIONS 32 +#define SIM_FRAME (uint(ITERATIONS)*time.frame+dispatch.id) +#define IN_BUF (SIM_FRAME%2u) +#define OUT_BUF ((SIM_FRAME+1u)%2u) + +fn Load(i: uint2) -> float4 { return sim.data[IN_BUF][i.x][i.y]; } +fn Store(i: uint2, data: float4) { sim.data[OUT_BUF][i.x][i.y] = data; } + +fn sdSegment(p: float2, a: float2, b: float2) -> float +{ + let pa = p-a; + let ba = b-a; + let h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); + return length( pa - ba*h ); +} + +fn sdBox(p: float2, b: float2) -> float +{ + let d = abs(p)-b; + return length(max(d,float2(0.0))) + min(max(d.x,d.y),0.0); +} + +alias Dual = vec2; + +struct dVec +{ + a: array, +} + +fn dVecMake(t: Dual, x: Dual, y: Dual) -> dVec +{ + var v: dVec; + v.a[0] = t; + v.a[1] = x; + v.a[2] = y; + return v; +} + +fn dC(a: float) -> Dual +{ + return Dual(a, 0.0); +} + +fn dV(a: float) -> Dual +{ + return Dual(a, 1.0); +} + +fn dMul(a: Dual, b: Dual) -> Dual +{ + return Dual( + a.x * b.x, + a.y * b.x + a.x * b.y, + ); +} + +fn dChain(a: Dual, s: float, ds: float) -> Dual +{ + return Dual( + s, + ds * a.y, + ); +} + +fn dSqrt(a: Dual) -> Dual +{ + let s = sqrt(abs(a.x)); + let ds = (0.5 / (s+0.0001)); + return dChain(a, s, ds); +} + +fn dSin(a: Dual) -> Dual +{ + let s = sin(a.x); + let ds = cos(a.x); + return dChain(a, s, ds); +} + +fn dCos(a: Dual) -> Dual +{ + let s = cos(a.x); + let ds = -sin(a.x); + return dChain(a, s, ds); +} + +fn dSqr(a: Dual) -> Dual +{ + let s = a.x*a.x; + let ds = 2.0*a.x; + return dChain(a, s, ds); +} + +fn dAbs(a: Dual) -> Dual +{ + let s = abs(a.x); + let ds = sign(a.x); + return dChain(a, s, ds); +} + +fn dVecLength2(g: float4x4, v: dVec) -> Dual +{ + return g[0][0]*dSqr(v.a[0]) + g[1][1]*dSqr(v.a[1]) + g[2][2]*dSqr(v.a[2]) + + custom.p1*( g[0][1]*dMul(v.a[0],v.a[1]) + g[0][2]*dMul(v.a[0],v.a[2]) + + g[1][2]*dMul(v.a[1],v.a[2]) ); +} + +fn sqr(x: float) -> float +{ + return x*x; +} + +fn diag(a: float4) -> float4x4 +{ + return float4x4( + a.x,0.0,0.0,0.0, + 0.0,a.y,0.0,0.0, + 0.0,0.0,a.z,0.0, + 0.0,0.0,0.0,a.w + ); +} + + +//optimized inverse of symmetric matrix +fn inverse_sym(m: float4x4) -> float4x4 +{ + let n11 = m[0][0]; let n12 = m[1][0]; let n13 = m[2][0]; let n14 = m[3][0]; + let n22 = m[1][1]; let n23 = m[2][1]; let n24 = m[3][1]; + let n33 = m[2][2]; let n34 = m[3][2]; + let n44 = m[3][3]; + + let t11 = 2.0 * n23 * n34 * n24 - n24 * n33 * n24 - n22 * n34 * n34 - n23 * n23 * n44 + n22 * n33 * n44; + let t12 = n14 * n33 * n24 - n13 * n34 * n24 - n14 * n23 * n34 + n12 * n34 * n34 + n13 * n23 * n44 - n12 * n33 * n44; + let t13 = n13 * n24 * n24 - n14 * n23 * n24 + n14 * n22 * n34 - n12 * n24 * n34 - n13 * n22 * n44 + n12 * n23 * n44; + let t14 = n14 * n23 * n23 - n13 * n24 * n23 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + + let det = n11 * t11 + n12 * t12 + n13 * t13 + n14 * t14; + let idet = 1.0 / det; + + var ret: float4x4; + + ret[0][0] = t11 * idet; + ret[0][1] = (n24 * n33 * n14 - n23 * n34 * n14 - n24 * n13 * n34 + n12 * n34 * n34 + n23 * n13 * n44 - n12 * n33 * n44) * idet; + ret[0][2] = (n22 * n34 * n14 - n24 * n23 * n14 + n24 * n13 * n24 - n12 * n34 * n24 - n22 * n13 * n44 + n12 * n23 * n44) * idet; + ret[0][3] = (n23 * n23 * n14 - n22 * n33 * n14 - n23 * n13 * n24 + n12 * n33 * n24 + n22 * n13 * n34 - n12 * n23 * n34) * idet; + + ret[1][0] = ret[0][1]; + ret[1][1] = (2.0 * n13 * n34 * n14 - n14 * n33 * n14 - n11 * n34 * n34 - n13 * n13 * n44 + n11 * n33 * n44) * idet; + ret[1][2] = (n14 * n23 * n14 - n12 * n34 * n14 - n14 * n13 * n24 + n11 * n34 * n24 + n12 * n13 * n44 - n11 * n23 * n44) * idet; + ret[1][3] = (n12 * n33 * n14 - n13 * n23 * n14 + n13 * n13 * n24 - n11 * n33 * n24 - n12 * n13 * n34 + n11 * n23 * n34) * idet; + + ret[2][0] = ret[0][2]; + ret[2][1] = ret[1][2]; + ret[2][2] = (2.0 * n12 * n24 * n14 - n14 * n22 * n14 - n11 * n24 * n24 - n12 * n12 * n44 + n11 * n22 * n44) * idet; + ret[2][3] = (n13 * n22 * n14 - n12 * n23 * n14 - n13 * n12 * n24 + n11 * n23 * n24 + n12 * n12 * n34 - n11 * n22 * n34) * idet; + + ret[3][0] = ret[0][3]; + ret[3][1] = ret[1][3]; + ret[3][2] = ret[2][3]; + ret[3][3] = (2.0 * n12 * n23 * n13 - n13 * n22 * n13 - n11 * n23 * n23 - n12 * n12 * n33 + n11 * n22 * n33) * idet; + + return ret; +} + +fn KerrGetR2(p: float3) -> float +{ + let rho = dot(p,p) - sqr(custom.KerrA); + let r2 = 0.5*(rho + sqrt(sqr(rho) + sqr(2.0*custom.KerrA*p.z))); + return r2; +} + +fn KerrGetK(p: float3) -> float4 +{ + let r2 = KerrGetR2(p); + let r = sqrt(r2); + let invr2 = 1.0 / (r2 + sqr(custom.KerrA) + 1e-6); + let k = float3((r*p.x - custom.KerrA*p.y) * invr2, (r*p.y + custom.KerrA*p.x) * invr2, p.z/(r + 1e-6)); + let f = r2 * (2.0 * custom.KerrM * r - sqr(custom.KerrQ)) / (r2 * r2 + sqr(custom.KerrA * p.z) + 1e-6); + return float4(k, f); +} + +fn G(q: float4) -> float4x4 +{ + //Kerr metric in Kerr-Schild coordinates + let k = KerrGetK(q.yzw); + let kf = k.w*float4(1.0, k.xyz); + return diag(float4(-1.0,1.0,1.0,1.0)) + float4x4(kf, k.x*kf, k.y*kf, k.z*kf); +} + +fn Ginv(q: float4) -> float4x4 +{ + //inverse of Kerr metric in Kerr-Schild coordinates + let k = KerrGetK(q.yzw); + let kf = k.w*vec4(1.0, -k.xyz)/dot(k.xyz, k.xyz); + return diag(float4(-1.0,1.0,1.0,1.0)) + float4x4(-kf, k.x*kf, k.y*kf, k.z*kf); +} + +fn Lpotential(q: Dual) -> Dual +{ + return -custom.CosK*dSqr(q) + + custom.Nonlinear*custom.CosK*dSqr(dSqr(clamp(q,float2(-0.5),float2(0.5)))); + //return custom.CosK*dCos(custom.CosA*q); +} + +fn Pos(p: uint2) -> float4 +{ + let pos = 0.0*float2(1.4, 0.0) + 45.5*(float2(p)/float(LENGTH) - 0.5); + return float4(0.0,pos.x, pos.y,0.0); +} + +#define TIMEK (8.0*custom.TimeK) + +fn LoadA(a0: Dual, i: uint2, i0: uint2) -> Dual +{ + return select(dC(Load(i).x), a0, all(i==i0)); +} + +fn LoadA0(i: uint2) -> Dual +{ + let data = Load(i); + return dC(data.x - data.y); +} + +fn LoadA2(a0: Dual, i: uint2, i0: uint2) -> Dual +{ + return select(dC(Load(i).z), a0, all(i==i0)); +} + +fn LagrangianField(A2: Dual, A1: Dual, p: int2, dp: int2) -> Dual +{ + //load the variables + let i = uint2(p + dp); + let i0 = uint2(p); + + let a0 = LoadA0(i); + let a1 = LoadA(A1, i, i0); + let a2 = LoadA2(A2, i, i0); + let ax0 = LoadA(A1, i - uint2(1u, 0u), i0); + let ax2 = LoadA(A1, i + uint2(1u, 0u), i0); + let ay0 = LoadA(A1, i - uint2(0u, 1u), i0); + let ay2 = LoadA(A1, i + uint2(0u, 1u), i0); + + let a0x0 = LoadA0(i - uint2(1u, 0u)); + let a0x2 = LoadA0(i + uint2(1u, 0u)); + let a0y0 = LoadA0(i - uint2(0u, 1u)); + let a0y2 = LoadA0(i + uint2(0u, 1u)); + + let a2x0 = LoadA2(A2, i - uint2(1u, 0u), i0); + let a2x2 = LoadA2(A2, i + uint2(1u, 0u), i0); + let a2y0 = LoadA2(A2, i - uint2(0u, 1u), i0); + let a2y2 = LoadA2(A2, i + uint2(0u, 1u), i0); + + //compute the metric + let x = Pos(i); + let g = Ginv(x)*diag(float4(TIMEK,1.0,1.0,1.0)); + let vol = 0.5*sqrt(-1.0*determinant(g)); + + let dadt0 = a2 - a1; + let dadx0 = ax2 - a1; + let dady0 = ay2 - a1; + + let dadt1 = a1 - a0; + let dadx1 = a1 - ax0; + let dady1 = a1 - ay0; + + let da0dx0 = a0x2 - a0; + let da0dy0 = a0y2 - a0; + let da0dx1 = a0 - a0x0; + let da0dy1 = a0 - a0y0; + + let da2dx0 = a2x2 - a2; + let da2dy0 = a2y2 - a2; + let da2dx1 = a2 - a2x0; + let da2dy1 = a2 - a2y0; + + let v0 = dVecMake(dadt0, dadx0, dady0); + let v1 = dVecMake(dadt0, dadx0, dady1); + let v2 = dVecMake(dadt0, dadx1, dady0); + let v3 = dVecMake(dadt0, dadx1, dady1); + let v4 = dVecMake(dadt1, dadx0, dady0); + let v5 = dVecMake(dadt1, dadx0, dady1); + let v6 = dVecMake(dadt1, dadx1, dady0); + let v7 = dVecMake(dadt1, dadx1, dady1); + + let v0_1 = dVecMake(dadt1, da0dx0, da0dy0); + let v1_1 = dVecMake(dadt1, da0dx0, da0dy1); + let v2_1 = dVecMake(dadt1, da0dx1, da0dy0); + let v3_1 = dVecMake(dadt1, da0dx1, da0dy1); + let v4_1 = dVecMake(dadt0, da2dx0, da2dy0); + let v5_1 = dVecMake(dadt0, da2dx0, da2dy1); + let v6_1 = dVecMake(dadt0, da2dx1, da2dy0); + let v7_1 = dVecMake(dadt0, da2dx1, da2dy1); + + return vol*(0.5*0.25*( + dVecLength2(g, v0) + dVecLength2(g, v1) + + dVecLength2(g, v2) + dVecLength2(g, v3) + + dVecLength2(g, v4) + dVecLength2(g, v5) + + dVecLength2(g, v6) + dVecLength2(g, v7) + ) + + 0.5*0.25*( + dVecLength2(g, v0_1) + dVecLength2(g, v1_1) + + dVecLength2(g, v2_1) + dVecLength2(g, v3_1) + + dVecLength2(g, v4_1) + dVecLength2(g, v5_1) + + dVecLength2(g, v6_1) + dVecLength2(g, v7_1) + ) - 12.0*Lpotential(a1)); +} + +//integral of the Lagrangian Field (here - just sum) +fn ActionIntegral(A2: Dual, A1: Dual, p: int2) -> Dual +{ + return LagrangianField(A2, A1, p, int2(0,0)) + + LagrangianField(A2, A1, p, int2(1,0)) + + LagrangianField(A2, A1, p, int2(0,1)) + + LagrangianField(A2, A1, p, int2(-1,0)) + + LagrangianField(A2, A1, p, int2(0,-1)) ; +} + + +#define NEWTON_STEPS 1 +#define STEP_SIZE 1.0 +fn Solve(i: uint2) -> float4 +{ + let a = Load(i); + let a0 = a.x - a.y; + let a1 = a.x; + var a2 = a.z; //predicted value + + var dA = 0.0; + var dT = STEP_SIZE; + var j = 0; + for(; j float2 +{ + return float2(cos(x), sin(x)); +} + +#dispatch_count Simulation ITERATIONS +#workgroup_count Simulation WG WG 1 +@compute @workgroup_size(16,16) +fn Simulation(@builtin(global_invocation_id) id: uint3) +{ + if(id.x == 0u || id.x == uint(LENGTH) - 1u || id.y == 0u || id.y == uint(LENGTH) - 1u) + { + Store(id.xy, float4(0.0)); + return; + } + + var f = Solve(id.xy); + if(time.frame == 0u) + { + f *= 0.0; + } + if(time.frame == 0u) + { + let x = Pos(id.xy).yz; + let a =0.0*exp(-dot(x,x)); + let dx = x - float2(16.0*custom.IDis, 0.0); + let vel = float2(0.0,-32.0*custom.IVel); + let b = 2.0*float2(1.0, 0.5) * exp(-8.0*custom.IRad*dot(dx, dx)) * expi(dot(dx, vel)); + f += float4(b,0.0,0.0); + } + + if(time.frame == 60u && id.y > uint(LENGTH/2)) + { + f*=0.0; + } + + Store(id.xy, clamp(f, float4(-35.0), float4(35.0))); +} + +fn Bilinear(p: float2) -> float4 +{ + let pi = uint2(floor(p)); + let pf = fract(p); + + let a00 = Load(pi + uint2(0u,0u)); + let a01 = Load(pi + uint2(0u,1u)); + let a10 = Load(pi + uint2(1u,0u)); + let a11 = Load(pi + uint2(1u,1u)); + + let a0 = mix(a00, a01, pf.y); + let a1 = mix(a10, a11, pf.y); + + return mix(a0, a1, pf.x); +} + +fn Sample(p0: float2) -> float4 +{ + let screen_size = float2(textureDimensions(screen)); + var p = p0; + if(mouse.click == 1) + { + p = 0.25*(p0 - screen_size*0.5) + float2(mouse.pos.xy); + } + let id = float(LENGTH) * ((p - screen_size.xy*0.5)/float(SCREEN_HEIGHT) + 0.5); + let x = Pos(uint2(id)); + let g = G(x); + let K = KerrGetK(x.yzw); + let vol = sqrt(-1.0/determinant(g)); + + let V = 0.5*float4(-0.4,float2(mouse.pos.xy)/screen_size - 0.5,0.); + let V2 = 1.0*dot(g*V,V); + + return Bilinear(id) + 0.*float4(length(K)); +} + +@compute @workgroup_size(16, 16) +fn MainImage(@builtin(global_invocation_id) id: uint3) +{ + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(id.y) + .5); + + var col = float3(2.00)*Sample(fragCoord).w; + + col = pow(col, float3(0.6)); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/michael0884/waves-around-a-black-hole.wgsl.json b/examples/michael0884/waves-around-a-black-hole.wgsl.json new file mode 100644 index 0000000..246e79f --- /dev/null +++ b/examples/michael0884/waves-around-a-black-hole.wgsl.json @@ -0,0 +1,65 @@ +{ + "uniforms": [ + { + "name": "CosK", + "value": 0.114 + }, + { + "name": "CosA", + "value": 1 + }, + { + "name": "TimeStep", + "value": 1 + }, + { + "name": "KerrA", + "value": 1 + }, + { + "name": "KerrQ", + "value": 0 + }, + { + "name": "KerrM", + "value": 1 + }, + { + "name": "p1", + "value": 0.749 + }, + { + "name": "TimeK", + "value": 0.5 + }, + { + "name": "p2", + "value": 0.5 + }, + { + "name": "Nonlinear", + "value": 0.72 + }, + { + "name": "IRad", + "value": 0.02 + }, + { + "name": "IVel", + "value": 0.242 + }, + { + "name": "IDis", + "value": 0.662 + } + ], + "textures": [ + { + "img": "/textures/blank.png" + }, + { + "img": "/textures/blank.png" + } + ], + "float32Enabled": false +} \ No newline at end of file diff --git a/examples/mipmap.wgsl b/examples/mipmap.wgsl new file mode 100644 index 0000000..2924090 --- /dev/null +++ b/examples/mipmap.wgsl @@ -0,0 +1,27 @@ +// WGSL compute shaders don't support automatic gradients or implicit LODs, so you need to do this manually + +fn texCoord(fragCoord: float2) -> float2 { + let screen_size = uint2(textureDimensions(screen)); + let uv = fragCoord / float2(screen_size); + return float2(uv.x - .5, 1.) / -(1. - uv.y); +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(screen_size.y - id.y) - .5); + + let st = texCoord(fragCoord); + let ddx = texCoord(fragCoord + float2(1.,0.)) - texCoord(fragCoord); + let ddy = texCoord(fragCoord + float2(0.,1.)) - texCoord(fragCoord); + let col = textureSampleGrad(channel0, trilinear, fract(st), ddx, ddy).rgb; + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/mipmap.wgsl.json b/examples/mipmap.wgsl.json new file mode 100644 index 0000000..301beff --- /dev/null +++ b/examples/mipmap.wgsl.json @@ -0,0 +1,11 @@ +{ + "uniforms": [], + "textures": [ + { + "img": "/textures/london.jpg" + }, + { + "img": "/textures/blank.png" + } + ] +} \ No newline at end of file diff --git a/examples/parallel-prefix-sum.wgsl b/examples/parallel-prefix-sum.wgsl new file mode 100644 index 0000000..c344d85 --- /dev/null +++ b/examples/parallel-prefix-sum.wgsl @@ -0,0 +1,84 @@ +// A simple demo of inclusive scan, using workgroup shared memory. Put some values into the Input array, and Result[i] will give you the sum Input[0] + Input[1] + ... Input[i] + +#define SCAN_TYPE float +#define SCAN_WORKGROUP_SIZE 256 +#include "davidar/scan" + +#define INPUT_SIZE 2048 +// BUCKET_COUNT = INPUT_SIZE / SCAN_WORKGROUP_SIZE +#define BUCKET_COUNT 8 + +struct Store { + Input: array, + Result: array, + Auxiliary: array, +} + +#storage store Store + +#include "Dave_Hoskins/hash" + +fn GenerateInput(i: uint) { + store.Input[i] = pow(hash12(float2(uint2(i, time.frame / 30u))), 9.); +} + +// scan in each bucket +@compute @workgroup_size(SCAN_WORKGROUP_SIZE) +#workgroup_count ScanInBucket BUCKET_COUNT 1 1 +fn ScanInBucket( + @builtin(global_invocation_id) DTid: uint3, + @builtin(local_invocation_index) GI: uint, +) { + GenerateInput(DTid.x); + store.Result[DTid.x] = scan_pass(GI, store.Input[DTid.x]); +} + +// record and scan the sum of each bucket +@compute @workgroup_size(BUCKET_COUNT) +#workgroup_count ScanBucketResult 1 1 1 +fn ScanBucketResult( + @builtin(global_invocation_id) DTid: uint3, + @builtin(local_invocation_index) GI: uint, +) { + store.Auxiliary[DTid.x] = scan_pass(GI, + select(SCAN_TYPE(0), store.Result[DTid.x * uint(SCAN_WORKGROUP_SIZE - 1)], DTid.x > 0u)); +} + +// add the bucket scanned result to each bucket to get the final result +@compute @workgroup_size(SCAN_WORKGROUP_SIZE) +#workgroup_count ScanAddBucketResult BUCKET_COUNT 1 1 +fn ScanAddBucketResult( + @builtin(workgroup_id) Gid: uint3, + @builtin(global_invocation_id) DTid: uint3, +) { + store.Result[DTid.x] += store.Auxiliary[Gid.x]; +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + var col = float3(0.); + let u = float(screen_size.y - id.y); + if (id.x < uint(INPUT_SIZE)) { + if (float(id.y) < store.Input[id.x] * float(screen_size.y / 10u)) { + col = float3(.1); + } + let y = store.Result[id.x]; + if (u < 5. * y) { + col = float3(1.); + } + let a = store.Auxiliary[id.x / uint(SCAN_WORKGROUP_SIZE)]; + if (u < 5. * a) { + col = float3(.5); + } + } + //assert(0, store.Result[1] == store.Input[0] + store.Input[1]); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/seblague/slime-simulation.wgsl b/examples/seblague/slime-simulation.wgsl new file mode 100644 index 0000000..c44ea7e --- /dev/null +++ b/examples/seblague/slime-simulation.wgsl @@ -0,0 +1,213 @@ +// This is a port of https://playground.babylonjs.com/?webgpu#GXJ3FZ#48 which itself is a port of https://github.com/SebLague/Slime-Simulation (GPLv3) + +#include "Dave_Hoskins/hash" + +#define NUM_AGENTS 256000 + +struct Agent { + position : vec2, + angle : f32, + species : i32, +}; + +struct Store { + agents: array, + trail: array, SCREEN_HEIGHT>, SCREEN_WIDTH>, +} + +#storage store Store + +struct Settings { + turnSpeed : f32, + sensorAngleDegrees : f32, + sensorOffsetDst : f32, +}; + +fn getSettings(i: int) -> Settings { + if (i == 0) { + return Settings( + 100. * custom.TurnSpeed1, + 180. * custom.SensorAngle1, + 50. * custom.SensorOffset1, + ); + } else if (i == 1) { + return Settings( + 100. * custom.TurnSpeed2, + 180. * custom.SensorAngle2, + 50. * custom.SensorOffset2, + ); + } + return Settings(); +} + +const speciesMask = array( + float4(1.,0.,0.,0.), + float4(0.,1.,0.,0.), + float4(0.,0.,1.,0.), + float4(0.,0.,0.,1.), +); + +@compute @workgroup_size(256) +#workgroup_count Init 1000 1 1 +fn Init(@builtin(global_invocation_id) id : vec3) +{ + if(time.frame > 0) { return; } + + let h = hash41(f32(id.x)); + store.agents[id.x] = Agent( + float2(textureDimensions(screen)) * h.xy, + radians(360.) * h.z, + int(2. * h.w), + ); +} + +// Hash function www.cs.ubc.ca/~rbridson/docs/schechter-sca08-turbulence.pdf +fn hash(state0 : u32) -> u32 +{ + var state = state0; + state = state ^ 2747636419u; + state = state * 2654435769u; + state = state ^ (state >> 16u); + state = state * 2654435769u; + state = state ^ (state >> 16u); + state = state * 2654435769u; + return state; +} + +fn scaleToRange01(state : u32) -> f32 +{ + return f32(state) / 4294967295.0; +} + +fn sense(agent : Agent, settings : Settings, sensorAngleOffset : f32) -> f32 +{ + let screen_size = int2(textureDimensions(screen)); + + let sensorAngle = agent.angle + sensorAngleOffset; + let sensorDir = vec2(cos(sensorAngle), sin(sensorAngle)); + + let sensorPos = agent.position + sensorDir * settings.sensorOffsetDst; + let sensorCentreX = i32(sensorPos.x); + let sensorCentreY = i32(sensorPos.y); + + var sum = 0.; + + let senseWeight = speciesMask[agent.species] * (1. + custom.SpeciesAvoidance) - custom.SpeciesAvoidance; + + let sensorSize = 1; + for (var offsetX = -sensorSize; offsetX <= sensorSize; offsetX = offsetX + 1) { + for (var offsetY = -sensorSize; offsetY <= sensorSize; offsetY = offsetY + 1) { + let sampleX = min(screen_size.x - 1, max(0, sensorCentreX + offsetX)); + let sampleY = min(screen_size.y - 1, max(0, sensorCentreY + offsetY)); + sum = sum + dot(vec4(senseWeight), store.trail[sampleX][sampleY]); + } + } + + return sum; +} + +@compute @workgroup_size(256) +#workgroup_count Simulation 1000 1 1 +fn Simulation(@builtin(global_invocation_id) id : vec3) +{ + let screen_size = textureDimensions(screen); + + let agent = store.agents[id.x]; + let settings = getSettings(agent.species); + let pos = agent.position; + + var random = hash(u32(pos.y * f32(screen_size.x) + pos.x) + hash(id.x + u32(time.elapsed * 100000.))); + + // Steer based on sensory data + let sensorAngleRad = settings.sensorAngleDegrees * (3.1415 / 180.); + let weightForward = sense(agent, settings, 0.); + let weightLeft = sense(agent, settings, sensorAngleRad); + let weightRight = sense(agent, settings, -sensorAngleRad); + + + let randomSteerStrength = scaleToRange01(random); + let turnSpeed = settings.turnSpeed * 2. * 3.1415; + + // Continue in same direction + if (weightForward > weightLeft && weightForward > weightRight) { + store.agents[id.x].angle += 0.; + } + else if (weightForward < weightLeft && weightForward < weightRight) { + store.agents[id.x].angle += (randomSteerStrength - 0.5) * 2. * turnSpeed * time.delta; + } + // Turn right + else if (weightRight > weightLeft) { + store.agents[id.x].angle -= randomSteerStrength * turnSpeed * time.delta; + } + // Turn left + else if (weightLeft > weightRight) { + store.agents[id.x].angle += randomSteerStrength * turnSpeed * time.delta; + } + + // Update position + let direction = vec2(cos(agent.angle), sin(agent.angle)); + var newPos = agent.position + direction * time.delta * 200. * custom.MoveSpeed; + + // Clamp position to map boundaries, and pick new random move dir if hit boundary + if (newPos.x < 0. || newPos.x >= f32(screen_size.x) || newPos.y < 0. || newPos.y >= f32(screen_size.y)) { + random = hash(random); + let randomAngle = scaleToRange01(random) * 2. * 3.1415; + + newPos.x = min(f32(screen_size.x - 1), max(0., newPos.x)); + newPos.y = min(f32(screen_size.y - 1), max(0., newPos.y)); + store.agents[id.x].angle = randomAngle; + } else { + let oldTrail = store.trail[i32(newPos.x)][i32(newPos.y)]; + let newVal = min(vec4(1.), oldTrail + speciesMask[agent.species] * 20. * custom.TrailWeight * time.delta); + store.trail[i32(newPos.x)][i32(newPos.y)] = newVal; + } + + store.agents[id.x].position = newPos; +} + +@compute @workgroup_size(16, 16) +fn Diffuse(@builtin(global_invocation_id) id : vec3) +{ + let screen_size = int2(textureDimensions(screen)); + if (id.x >= u32(screen_size.x) || id.y >= u32(screen_size.y)) { + return; + } + + var sum = vec4(0.); + let originalCol = store.trail[id.x][id.y]; + // 3x3 blur + for (var offsetX = -1; offsetX <= 1; offsetX = offsetX + 1) { + for (var offsetY = -1; offsetY <= 1; offsetY = offsetY + 1) { + let sampleX = min(screen_size.x - 1, max(0, i32(id.x) + offsetX)); + let sampleY = min(screen_size.y - 1, max(0, i32(id.y) + offsetY)); + sum = sum + store.trail[sampleX][sampleY]; + } + } + + var blurredCol = sum / vec4(9., 9., 9., 9.); + let diffuseWeight = clamp(5. * custom.DiffuseRate * time.delta, 0., 1.); + + blurredCol = originalCol * (1. - diffuseWeight) + blurredCol * diffuseWeight; + + let p = 2. * custom.DecayRate * time.delta; + let pix = max(vec4(0., 0., 0., 0.), blurredCol - vec4(p)); + + store.trail[id.x][id.y] = pix; +} + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + var col = store.trail[id.x][id.y].rgb; + + // Convert from gamma-encoded to linear colour space + col = pow(col, float3(2.2)); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/examples/seblague/slime-simulation.wgsl.json b/examples/seblague/slime-simulation.wgsl.json new file mode 100644 index 0000000..bc0a6b7 --- /dev/null +++ b/examples/seblague/slime-simulation.wgsl.json @@ -0,0 +1,57 @@ +{ + "uniforms": [ + { + "name": "DecayRate", + "value": 0.101 + }, + { + "name": "TrailWeight", + "value": 0.25 + }, + { + "name": "DiffuseRate", + "value": 0.607 + }, + { + "name": "MoveSpeed", + "value": 0.258 + }, + { + "name": "TurnSpeed1", + "value": 0.307 + }, + { + "name": "SensorAngle1", + "value": 0.25 + }, + { + "name": "SensorOffset1", + "value": 0.411 + }, + { + "name": "TurnSpeed2", + "value": 0.905 + }, + { + "name": "SensorAngle2", + "value": 0.622 + }, + { + "name": "SensorOffset2", + "value": 0.599 + }, + { + "name": "SpeciesAvoidance", + "value": 0 + } + ], + "textures": [ + { + "img": "/textures/blank.png" + }, + { + "img": "/textures/blank.png" + } + ], + "float32Enabled": false +} \ No newline at end of file diff --git a/examples/workgroup-shared-memory.wgsl b/examples/workgroup-shared-memory.wgsl new file mode 100644 index 0000000..cfd2df0 --- /dev/null +++ b/examples/workgroup-shared-memory.wgsl @@ -0,0 +1,58 @@ +// An example of using shared memory to compute gradients across threads, allowing automatic LOD selection without having to duplicate computations + +// Global variables +// private = current thread only +// workgroup = shared with all threads in workgroup +var local_invocation_id_: uint3; +var texCoords: array, 16>; + +fn swap(x: uint) -> uint { + // swap adjacent pairs of indices + // 0 1 2 3 4 5 + // 1 0 3 2 5 4 + return select(x - 1u, x + 1u, x % 2u == 0u); +} + +fn textureSampleMipmap(ch: texture_2d, coords: float2) -> float4 { + let x = local_invocation_id_.x; + let y = local_invocation_id_.y; + + // Share texture coordinates within the workgroup + texCoords[x][y] = coords; + + // Synchronise threads in workgroup + workgroupBarrier(); + + // Read texture coordinates from adjacent threads in quad + let ddx = texCoords[swap(x)][y] - coords; + let ddy = texCoords[x][swap(y)] - coords; + + return textureSampleGrad(ch, trilinear_repeat, coords, ddx, ddy); +} + +@compute @workgroup_size(16, 16) +fn main_image( + @builtin(global_invocation_id) id: uint3, + @builtin(local_invocation_id) local: uint3 +) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(screen_size.y - id.y) - .5); + + // Normalised pixel coordinates (from 0 to 1) + let uv = fragCoord / float2(screen_size); + + // Store global variable + local_invocation_id_ = local; + + // Sample texture with automatically selected mip level + let col = textureSampleMipmap(channel0, float2(uv.x - .5, 1.) / -(1. - uv.y) + .1 * time.elapsed).rgb; + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x < screen_size.x && id.y < screen_size.y) { + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); + } +} diff --git a/examples/workgroup-shared-memory.wgsl.json b/examples/workgroup-shared-memory.wgsl.json new file mode 100644 index 0000000..edfcd0d --- /dev/null +++ b/examples/workgroup-shared-memory.wgsl.json @@ -0,0 +1,13 @@ +{ + "uniforms": [], + "textures": [ + { + "img": "https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/aerial_rocks_02/aerial_rocks_02_diff_1k.jpg", + "url": "https://polyhaven.com/a/aerial_rocks_02" + }, + { + "img": "/textures/blank.png" + } + ], + "float32Enabled": false +} \ No newline at end of file diff --git a/examples/workgroup_count.wgsl b/examples/workgroup_count.wgsl new file mode 100644 index 0000000..f9e5500 --- /dev/null +++ b/examples/workgroup_count.wgsl @@ -0,0 +1,28 @@ +// This is a bit of a contrived example, setting the number of workgroups is more useful for when you want the number of threads dispatched to match up with a buffer that's larger than the viewport. + +// Render a 128x256 pixel rectangle +#workgroup_count main_image 8 16 1 + +@compute @workgroup_size(16, 16) +fn main_image(@builtin(global_invocation_id) id: uint3) { + // Viewport resolution (in pixels) + let screen_size = uint2(textureDimensions(screen)); + + // Prevent overdraw for workgroups on the edge of the viewport + if (id.x >= screen_size.x || id.y >= screen_size.y) { return; } + + // Pixel coordinates (centre of pixel, origin at bottom left) + let fragCoord = float2(float(id.x) + .5, float(screen_size.y - id.y) - .5); + + // Normalised pixel coordinates (from 0 to 1) + let uv = fragCoord / float2(screen_size); + + // Time varying pixel colour + var col = .5 + .5 * cos(time.elapsed + uv.xyx + float3(0.,2.,4.)); + + // Convert from gamma-encoded to linear colour space + col = pow(col, float3(2.2)); + + // Output to screen (linear colour space) + textureStore(screen, int2(id.xy), float4(col, 1.)); +} diff --git a/src/bin/toy.rs b/src/bin/toy.rs new file mode 100644 index 0000000..bb62992 --- /dev/null +++ b/src/bin/toy.rs @@ -0,0 +1,120 @@ +use serde::{Deserialize, Serialize}; +use std::error::Error; +use wgputoy::context::init_wgpu; +use wgputoy::WgpuToyRenderer; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct ShaderMeta { + uniforms: Vec, + textures: Vec, + #[serde(default)] + float32_enabled: bool, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Uniform { + name: String, + value: f32, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Texture { + img: String, +} + +async fn init() -> Result> { + let wgpu = init_wgpu(1280, 720, "").await?; + let mut wgputoy = WgpuToyRenderer::new(wgpu); + + let filename = if std::env::args().len() > 1 { + std::env::args().nth(1).unwrap() + } else { + "examples/default.wgsl".to_string() + }; + let shader = std::fs::read_to_string(&filename)?; + + let client = reqwest_middleware::ClientBuilder::new(reqwest::Client::new()) + .with(reqwest_middleware_cache::Cache { + mode: reqwest_middleware_cache::CacheMode::Default, + cache_manager: reqwest_middleware_cache::managers::CACacheManager::default(), + }) + .build(); + + if let Ok(json) = std::fs::read_to_string(std::format!("{filename}.json")) { + let metadata: ShaderMeta = serde_json::from_str(&json)?; + println!("{:?}", metadata); + + for (i, texture) in metadata.textures.iter().enumerate() { + let img = if texture.img.starts_with("http") { + let resp = client.get(&texture.img).send().await?; + resp.bytes().await?.to_vec() + } else { + std::fs::read(&std::format!("site/public/{}", texture.img))? + }; + if texture.img.ends_with(".hdr") { + wgputoy.load_channel_hdr(i, &img)?; + } else { + wgputoy.load_channel(i, &img); + } + } + + let uniform_names: Vec = metadata.uniforms.iter().map(|u| u.name.clone()).collect(); + let uniform_values: Vec = metadata.uniforms.iter().map(|u| u.value).collect(); + if uniform_names.len() > 0 { + wgputoy.set_custom_floats(uniform_names, uniform_values); + } + + wgputoy.set_pass_f32(metadata.float32_enabled); + } + + if let Some(source) = wgputoy.preprocess_async(&shader).await { + println!("{}", source.source); + wgputoy.compile(source); + } + Ok(wgputoy) +} + +fn main() -> Result<(), Box> { + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build()?; + let mut wgputoy = runtime.block_on(init())?; + let screen_size = wgputoy.wgpu.window.inner_size(); + let start_time = std::time::Instant::now(); + let event_loop = std::mem::take(&mut wgputoy.wgpu.event_loop).unwrap(); + let device_clone = wgputoy.wgpu.device.clone(); + std::thread::spawn(move || loop { + device_clone.poll(wgpu::Maintain::Wait); + }); + event_loop.run(move |event, _, control_flow| { + *control_flow = winit::event_loop::ControlFlow::Poll; + match event { + winit::event::Event::RedrawRequested(_) => { + let time = start_time.elapsed().as_micros() as f32 * 1e-6; + wgputoy.set_time_elapsed(time); + let future = wgputoy.render_async(); + runtime.block_on(future); + } + winit::event::Event::MainEventsCleared => { + wgputoy.wgpu.window.request_redraw(); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::CloseRequested, + .. + } => *control_flow = winit::event_loop::ControlFlow::Exit, + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::CursorMoved { position, .. }, + .. + } => wgputoy.set_mouse_pos( + position.x as f32 / screen_size.width as f32, + position.y as f32 / screen_size.height as f32, + ), + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::MouseInput { state, .. }, + .. + } => wgputoy.set_mouse_click(state == winit::event::ElementState::Pressed), + _ => (), + } + }); +} diff --git a/src/bind.rs b/src/bind.rs index fc9660a..bdf2197 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -194,9 +194,10 @@ impl Bindings { sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, - //view_formats: &[], usage: wgpu::TextureUsages::TEXTURE_BINDING, label: None, + #[cfg(not(target_arch = "wasm32"))] + view_formats: &[], }; let channel_layout = wgpu::BindingType::Texture { multisampled: false, @@ -220,8 +221,9 @@ impl Bindings { sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba16Float, - //view_formats: &[], usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING, + #[cfg(not(target_arch = "wasm32"))] + view_formats: &[], }); let tex_read = wgpu.device.create_texture(&wgpu::TextureDescriptor { label: None, @@ -238,8 +240,9 @@ impl Bindings { } else { wgpu::TextureFormat::Rgba16Float }, - //view_formats: &[], usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING, + #[cfg(not(target_arch = "wasm32"))] + view_formats: &[], }); let tex_write = wgpu.device.create_texture(&wgpu::TextureDescriptor { label: None, @@ -256,8 +259,9 @@ impl Bindings { } else { wgpu::TextureFormat::Rgba16Float }, - //view_formats: &[], usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::STORAGE_BINDING, + #[cfg(not(target_arch = "wasm32"))] + view_formats: &[], }); let channel0 = wgpu.device.create_texture(&blank); let channel1 = wgpu.device.create_texture(&blank); diff --git a/src/blit.rs b/src/blit.rs index 34f3ce8..6dd3da0 100644 --- a/src/blit.rs +++ b/src/blit.rs @@ -137,9 +137,10 @@ impl Blitter { sample_count: 1, dimension: wgpu::TextureDimension::D2, format: self.dest_format, - //view_formats: &[], usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT, label: None, + #[cfg(not(target_arch = "wasm32"))] + view_formats: &[], }); let mut encoder = wgpu.device.create_command_encoder(&Default::default()); let views: Vec = (0..mip_level_count) diff --git a/src/context.rs b/src/context.rs index 17564d8..5b4cb4d 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,9 +1,11 @@ +use std::sync::Arc; + use crate::utils::set_panic_hook; pub struct WgpuContext { pub event_loop: Option>, pub window: winit::window::Window, - pub device: wgpu::Device, + pub device: Arc, pub queue: wgpu::Queue, pub surface: wgpu::Surface, pub surface_format: wgpu::TextureFormat, @@ -44,7 +46,9 @@ fn init_window( event_loop: &winit::event_loop::EventLoop<()>, _: &str, ) -> Result> { - env_logger::init(); + env_logger::init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ); let window = winit::window::WindowBuilder::new() .with_inner_size(size) .build(event_loop)?; @@ -55,8 +59,17 @@ pub async fn init_wgpu(width: u32, height: u32, bind_id: &str) -> Result Result); +#[cfg(target_arch = "wasm32")] impl SuccessCallback { fn call(&self, entry_points: Vec) { match self.0 { @@ -47,6 +49,9 @@ impl SuccessCallback { } } +#[cfg(not(target_arch = "wasm32"))] +struct SuccessCallback(Option<()>); + struct ComputePipeline { name: String, workgroup_size: [u32; 3], @@ -125,8 +130,9 @@ impl WgpuToyRenderer { } } -#[wasm_bindgen] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl WgpuToyRenderer { + #[cfg(target_arch = "wasm32")] pub fn render(&mut self) { match self.wgpu.surface.get_current_texture() { Err(e) => log::error!("Unable to get framebuffer: {e}"), @@ -141,6 +147,22 @@ impl WgpuToyRenderer { } } + #[cfg(not(target_arch = "wasm32"))] + pub async fn render_async(&mut self) { + match self.wgpu.surface.get_current_texture() { + Err(e) => log::error!("Unable to get framebuffer: {e}"), + Ok(f) => { + let staging_buffer = self.render_to(f); + Self::postrender( + staging_buffer, + self.screen_width * self.screen_height, + self.source.assert_map.clone(), + ) + .await + } + } + } + fn render_to(&mut self, frame: wgpu::SurfaceTexture) -> Option { let mut encoder = self.wgpu.device.create_command_encoder(&Default::default()); self.bindings.stage(&self.wgpu.queue); @@ -266,7 +288,10 @@ impl WgpuToyRenderer { if let Some(buf) = staging_buffer { let buffer_slice = buf.slice(..); let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel(); - buffer_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap()); + buffer_slice.map_async(wgpu::MapMode::Read, move |v| match sender.send(v) { + Ok(()) => {} + Err(_) => log::error!("Channel closed unexpectedly"), + }); match receiver.receive().await { None => log::error!("Channel closed unexpectedly"), Some(Err(e)) => log::error!("{e}"), @@ -378,6 +403,7 @@ fn passSampleLevelBilinearRepeat(pass_index: int, uv: float2, lod: float) -> flo self.on_success_cb.call(entry_points); } + #[cfg(target_arch = "wasm32")] pub fn preprocess(&self, shader: &str) -> js_sys::Promise { let shader = shader.to_owned(); let defines = HashMap::from([ @@ -387,6 +413,16 @@ fn passSampleLevelBilinearRepeat(pass_index: int, uv: float2, lod: float) -> flo utils::promise(async move { pp::Preprocessor::new(defines).run(&shader).await }) } + #[cfg(not(target_arch = "wasm32"))] + pub async fn preprocess_async(&self, shader: &str) -> Option { + let shader = shader.to_owned(); + let defines = HashMap::from([ + ("SCREEN_WIDTH".to_owned(), self.screen_width.to_string()), + ("SCREEN_HEIGHT".to_owned(), self.screen_height.to_string()), + ]); + pp::Preprocessor::new(defines).run(&shader).await + } + pub fn compile(&mut self, source: SourceMap) { let now = instant::Instant::now(); let prelude = self.prelude(); // prelude must be generated after preprocessor has run @@ -396,32 +432,34 @@ fn passSampleLevelBilinearRepeat(pass_index: int, uv: float2, lod: float) -> flo let re_parser = regex!(r"(?s):(\d+):(\d+) (.*)"); let re_invalid = regex!(r"\[Invalid \w+\] is invalid."); let sourcemap_clone = source.map.clone(); - self.wgpu.device.on_uncaptured_error(Box::new(move |e: wgpu::Error| { - let err = &e.to_string(); - if re_invalid.is_match(err) { - return; - } - match re_parser.captures(err) { - None => { - log::error!("{e}"); - WGSLError::handler(err, 0, 0); + self.wgpu + .device + .on_uncaptured_error(Box::new(move |e: wgpu::Error| { + let err = &e.to_string(); + if re_invalid.is_match(err) { + return; } - Some(cap) => { - let row = cap[1].parse().unwrap_or(prelude_len); - let col = cap[2].parse().unwrap_or(0); - let summary = &cap[3]; - let mut n = 0; - if row >= prelude_len { - n = row - prelude_len; + match re_parser.captures(err) { + None => { + log::error!("{e}"); + WGSLError::handler(err, 0, 0); } - if n < sourcemap_clone.len() { - n = sourcemap_clone[n]; + Some(cap) => { + let row = cap[1].parse().unwrap_or(prelude_len); + let col = cap[2].parse().unwrap_or(0); + let summary = &cap[3]; + let mut n = 0; + if row >= prelude_len { + n = row - prelude_len; + } + if n < sourcemap_clone.len() { + n = sourcemap_clone[n]; + } + WGSLError::handler(summary, n, col); + SHADER_ERROR.store(true, Ordering::SeqCst); } - WGSLError::handler(summary, n, col); - SHADER_ERROR.store(true, Ordering::SeqCst); } - } - })); + })); let wgsl = &(prelude + &source.source); match wgsl::parse_str(&wgsl) { @@ -516,10 +554,12 @@ fn passSampleLevelBilinearRepeat(pass_index: int, uv: float2, lod: float) -> flo } pub fn set_mouse_pos(&mut self, x: f32, y: f32) { - self.bindings.mouse.host.pos = [ - (x * self.screen_width as f32) as u32, - (y * self.screen_height as f32) as u32, - ]; + if self.bindings.mouse.host.click == 1 { + self.bindings.mouse.host.pos = [ + (x * self.screen_width as f32) as u32, + (y * self.screen_height as f32) as u32, + ]; + } } pub fn set_mouse_click(&mut self, click: bool) { @@ -530,10 +570,16 @@ fn passSampleLevelBilinearRepeat(pass_index: int, uv: float2, lod: float) -> flo self.bindings.keys.host.set(keycode, keydown); } + #[cfg(target_arch = "wasm32")] pub fn set_custom_floats(&mut self, names: Vec, values: Vec) { self.bindings.custom.host = (names.iter().map(From::from).collect(), values); } + #[cfg(not(target_arch = "wasm32"))] + pub fn set_custom_floats(&mut self, names: Vec, values: Vec) { + self.bindings.custom.host = (names, values); + } + pub fn set_pass_f32(&mut self, pass_f32: bool) { self.pass_f32 = pass_f32; self.reset(); @@ -570,6 +616,7 @@ fn passSampleLevelBilinearRepeat(pass_index: int, uv: float2, lod: float) -> flo ); } + #[cfg(target_arch = "wasm32")] pub fn on_success(&mut self, callback: js_sys::Function) { self.on_success_cb = SuccessCallback(Some(callback)); } @@ -663,9 +710,10 @@ fn create_texture_from_image( sample_count: 1, dimension: wgpu::TextureDimension::D2, format, - //view_formats: &[], usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, label: None, + #[cfg(not(target_arch = "wasm32"))] + view_formats: &[], }); wgpu.queue.write_texture( texture.as_image_copy(), diff --git a/src/pp.rs b/src/pp.rs index 87e13b6..e1bb6f8 100644 --- a/src/pp.rs +++ b/src/pp.rs @@ -23,7 +23,10 @@ impl WGSLError { Self { summary, line } } pub fn handler(summary: &str, row: usize, col: usize) { - wgsl_error_handler(summary, row, col) + #[cfg(target_arch = "wasm32")] + wgsl_error_handler(summary, row, col); + #[cfg(not(target_arch = "wasm32"))] + panic!("{}:{}: {}", row, col, summary); } pub fn submit(&self) { Self::handler(&self.summary, self.line, 0) diff --git a/src/utils.rs b/src/utils.rs index 1aa74a8..23fb589 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,5 @@ use crate::WGSLError; use cached::proc_macro::cached; -use gloo_net::http::Request; -use js_sys::Promise; use std::future::Future; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; @@ -33,7 +31,12 @@ pub fn parse_u32(value: &str, line: usize) -> Result { #[cached] pub async fn fetch_include(name: String) -> Option { let url = format!("https://compute-toys.github.io/include/{name}.wgsl"); - let resp = Request::get(&url).send().await.ok()?; + + #[cfg(target_arch = "wasm32")] + let resp = gloo_net::http::Request::get(&url).send().await.ok()?; + #[cfg(not(target_arch = "wasm32"))] + let resp = reqwest::get(&url).await.ok()?; + if resp.status() == 200 { resp.text().await.ok() } else { @@ -41,14 +44,15 @@ pub async fn fetch_include(name: String) -> Option { } } -pub fn promise(future: F) -> Promise +#[cfg(target_arch = "wasm32")] +pub fn promise(future: F) -> js_sys::Promise where F: Future> + 'static, JsValue: From, { let mut future = Some(future); - Promise::new(&mut |resolve, _reject| { + js_sys::Promise::new(&mut |resolve, _reject| { let future = future.take().unwrap_throw(); wasm_bindgen_futures::spawn_local(async move {