From 68843293ffa9386227b07f34a1b20791f2f530e6 Mon Sep 17 00:00:00 2001 From: Bradlee Speice Date: Wed, 19 Feb 2025 22:14:06 -0500 Subject: [PATCH] Put in a GUI, provide the viewport information --- Cargo.lock | 1427 +++++++++++++++++++++++++++++++- crates/flare-shader/src/lib.rs | 142 ++-- crates/flare/Cargo.toml | 3 + crates/flare/src/main.rs | 907 ++++++++++---------- crates/flare/src/main_winit.rs | 581 +++++++++++++ 5 files changed, 2537 insertions(+), 523 deletions(-) create mode 100644 crates/flare/src/main_winit.rs diff --git a/Cargo.lock b/Cargo.lock index 51fa1ba..4dbd694 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,98 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" +[[package]] +name = "accesskit" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3d3b8f9bae46a948369bc4a03e815d4ed6d616bd00de4051133a5019dc31c5a" + +[[package]] +name = "accesskit_atspi_common" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c5dd55e6e94949498698daf4d48fb5659e824d7abec0d394089656ceaf99d4f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "atspi-common", + "serde", + "thiserror 1.0.69", + "zvariant", +] + +[[package]] +name = "accesskit_consumer" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47983a1084940ba9a39c077a8c63e55c619388be5476ac04c804cfbd1e63459" +dependencies = [ + "accesskit", + "hashbrown 0.15.2", + "immutable-chunkmap", +] + +[[package]] +name = "accesskit_macos" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7329821f3bd1101e03a7d2e03bd339e3ac0dc64c70b4c9f9ae1949e3ba8dece1" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.15.2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "accesskit_unix" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcee751cc20d88678c33edaf9c07e8b693cd02819fe89053776f5313492273f5" +dependencies = [ + "accesskit", + "accesskit_atspi_common", + "async-channel", + "async-executor", + "async-task", + "atspi", + "futures-lite", + "futures-util", + "serde", + "zbus", +] + +[[package]] +name = "accesskit_windows" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24fcd5d23d70670992b823e735e859374d694a3d12bfd8dd32bd3bd8bedb5d81" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.15.2", + "paste", + "static_assertions", + "windows", + "windows-core", +] + +[[package]] +name = "accesskit_winit" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6a48dad5530b6deb9fc7a52cc6c3bf72cdd9eb8157ac9d32d69f2427a5e879" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "raw-window-handle", + "winit", +] + [[package]] name = "adler2" version = "2.0.0" @@ -150,6 +242,24 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" +[[package]] +name = "arboard" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" +dependencies = [ + "clipboard-win", + "core-graphics", + "image", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "parking_lot", + "windows-sys 0.48.0", + "x11rb", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -177,12 +287,206 @@ dependencies = [ "libloading", ] +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atspi" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be534b16650e35237bb1ed189ba2aab86ce65e88cc84c66f4935ba38575cecbf" +dependencies = [ + "atspi-common", + "atspi-connection", + "atspi-proxies", +] + +[[package]] +name = "atspi-common" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1909ed2dc01d0a17505d89311d192518507e8a056a48148e3598fef5e7bb6ba7" +dependencies = [ + "enumflags2", + "serde", + "static_assertions", + "zbus", + "zbus-lockstep", + "zbus-lockstep-macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "atspi-connection" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "430c5960624a4baaa511c9c0fcc2218e3b58f5dbcc47e6190cafee344b873333" +dependencies = [ + "atspi-common", + "atspi-proxies", + "futures-lite", + "zbus", +] + +[[package]] +name = "atspi-proxies" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e6c5de3e524cf967569722446bcd458d5032348554d9a17d7d72b041ab7496" +dependencies = [ + "atspi-common", + "serde", + "zbus", + "zvariant", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -225,6 +529,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "block2" version = "0.5.1" @@ -234,6 +547,19 @@ dependencies = [ "objc2", ] +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -266,6 +592,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.9.0" @@ -327,6 +659,24 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + +[[package]] +name = "clipboard-win" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +dependencies = [ + "error-code", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -378,6 +728,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -391,7 +751,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "core-graphics-types", "foreign-types", "libc", @@ -404,7 +764,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ "libc", ] @@ -423,6 +792,16 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "cursor-icon" version = "1.1.0" @@ -442,12 +821,33 @@ dependencies = [ "syn", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dispatch" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dlib" version = "0.5.2" @@ -478,6 +878,128 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +[[package]] +name = "ecolor" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "878e9005799dd739e5d5d89ff7480491c12d0af571d44399bcaefa1ee172dd76" +dependencies = [ + "bytemuck", + "emath", +] + +[[package]] +name = "eframe" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba4c50d905804fe9ec4e159fde06b9d38f9440228617ab64a03d7a2091ece63" +dependencies = [ + "ahash", + "bytemuck", + "document-features", + "egui", + "egui-wgpu", + "egui-winit", + "egui_glow", + "glow", + "glutin", + "glutin-winit", + "image", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "pollster", + "profiling", + "raw-window-handle", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "wgpu", + "winapi", + "windows-sys 0.59.0", + "winit", +] + +[[package]] +name = "egui" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d2768eaa6d5c80a6e2a008da1f0e062dff3c83eb2b28605ea2d0732d46e74d6" +dependencies = [ + "accesskit", + "ahash", + "bitflags 2.8.0", + "emath", + "epaint", + "log", + "nohash-hasher", + "profiling", +] + +[[package]] +name = "egui-wgpu" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d8151704bcef6271bec1806c51544d70e79ef20e8616e5eac01facfd9c8c54a" +dependencies = [ + "ahash", + "bytemuck", + "document-features", + "egui", + "epaint", + "log", + "profiling", + "thiserror 1.0.69", + "type-map", + "web-time", + "wgpu", + "winit", +] + +[[package]] +name = "egui-winit" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace791b367c1f63e6044aef2f3834904509d1d1a6912fd23ebf3f6a9af92cd84" +dependencies = [ + "accesskit_winit", + "ahash", + "arboard", + "bytemuck", + "egui", + "log", + "profiling", + "raw-window-handle", + "smithay-clipboard", + "web-time", + "webbrowser", + "winit", +] + +[[package]] +name = "egui_glow" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a53e2374a964c3c793cb0b8ead81bca631f24974bc0b747d1a5622f4e39fdd0" +dependencies = [ + "ahash", + "bytemuck", + "egui", + "glow", + "log", + "memoffset", + "profiling", + "wasm-bindgen", + "web-sys", + "winit", +] + [[package]] name = "either" version = "1.13.0" @@ -494,6 +1016,42 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "emath" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55b7b6be5ad1d247f11738b0e4699d9c20005ed366f2c29f5ec1f8e1de180bc2" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "env_filter" version = "0.1.3" @@ -517,6 +1075,30 @@ dependencies = [ "log", ] +[[package]] +name = "epaint" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "275b665a7b9611d8317485187e5458750850f9e64604d3c58434bb3fc1d22915" +dependencies = [ + "ab_glyph", + "ahash", + "bytemuck", + "ecolor", + "emath", + "epaint_default_fonts", + "log", + "nohash-hasher", + "parking_lot", + "profiling", +] + +[[package]] +name = "epaint_default_fonts" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9343d356d7cac894dacafc161b4654e0881301097bdf32a122ed503d97cb94b6" + [[package]] name = "equivalent" version = "1.0.1" @@ -533,6 +1115,33 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "error-code" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -545,6 +1154,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "fixedbitset" version = "0.5.7" @@ -557,6 +1175,9 @@ version = "0.1.0" dependencies = [ "anyhow", "bytemuck", + "eframe", + "egui", + "egui-wgpu", "env_logger", "flare-shader", "futures-executor", @@ -622,6 +1243,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures-core" version = "0.3.31" @@ -639,6 +1269,42 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + [[package]] name = "futures-task" version = "0.3.31" @@ -652,12 +1318,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "gethostname" version = "0.4.3" @@ -723,6 +1403,63 @@ dependencies = [ "web-sys", ] +[[package]] +name = "glutin" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03642b8b0cce622392deb0ee3e88511f75df2daac806102597905c3ea1974848" +dependencies = [ + "bitflags 2.8.0", + "cfg_aliases", + "cgl", + "core-foundation 0.9.4", + "dispatch", + "glutin_egl_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "libloading", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", + "raw-window-handle", + "wayland-sys", + "windows-sys 0.52.0", + "x11-dl", +] + +[[package]] +name = "glutin-winit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f" +dependencies = [ + "cfg_aliases", + "glutin", + "raw-window-handle", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4680ba6195f424febdc3ba46e7a42a0e58743f2edb115297b86d7f8ecc02d2" +dependencies = [ + "gl_generator", + "windows-sys 0.52.0", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7bb2938045a88b612499fbcba375a77198e01306f52272e692f8c1f3751185" +dependencies = [ + "gl_generator", + "x11-dl", +] + [[package]] name = "glutin_wgl_sys" version = "0.6.1" @@ -814,18 +1551,194 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[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 = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", + "png", + "tiff", +] + +[[package]] +name = "immutable-chunkmap" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f97096f508d54f8f8ab8957862eee2ccd628847b6217af1a335e1c44dee578" +dependencies = [ + "arrayvec", +] + [[package]] name = "indexmap" version = "2.7.1" @@ -894,6 +1807,12 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.77" @@ -966,6 +1885,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "litrs" version = "0.4.1" @@ -1018,6 +1943,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "metal" version = "0.31.0" @@ -1040,6 +1974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1104,6 +2039,25 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.8.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "num-traits" version = "0.2.19" @@ -1386,6 +2340,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "owned_ttf_parser" version = "0.25.0" @@ -1395,6 +2359,12 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1472,12 +2442,36 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "3.7.4" @@ -1493,6 +2487,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -1532,6 +2532,16 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quick-xml" version = "0.36.2" @@ -1846,12 +2856,49 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "slab" version = "0.4.9" @@ -1904,6 +2951,17 @@ dependencies = [ "xkeysym", ] +[[package]] +name = "smithay-clipboard" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" +dependencies = [ + "libc", + "smithay-client-toolkit", + "wayland-backend", +] + [[package]] name = "smol_str" version = "0.2.2" @@ -2055,6 +3113,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tempfile" version = "3.15.0" @@ -2130,6 +3199,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "tiny-skia" version = "0.11.4" @@ -2155,6 +3235,16 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "toml_datetime" version = "0.6.8" @@ -2219,6 +3309,32 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "type-map" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + [[package]] name = "unicode-ident" version = "1.0.14" @@ -2243,6 +3359,29 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -2444,7 +3583,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.36.2", "quote", ] @@ -2480,6 +3619,30 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webbrowser" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea9fe1ebb156110ff855242c1101df158b822487e4957b0556d9ffce9db0f535" +dependencies = [ + "block2", + "core-foundation 0.10.0", + "home", + "jni", + "log", + "ndk-context", + "objc2", + "objc2-foundation", + "url", + "web-sys", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "wgpu" version = "24.0.0" @@ -2590,6 +3753,22 @@ dependencies = [ "web-sys", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.9" @@ -2599,6 +3778,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows" version = "0.58.0" @@ -2672,6 +3857,15 @@ dependencies = [ "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.5", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -2883,7 +4077,7 @@ dependencies = [ "calloop", "cfg_aliases", "concurrent-queue", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "cursor-icon", "dpi", @@ -2929,6 +4123,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "x11-dl" version = "2.21.0" @@ -2967,6 +4173,16 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "xkbcommon-dl" version = "0.4.2" @@ -2992,6 +4208,129 @@ version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus-lockstep" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca2c5dceb099bddaade154055c926bb8ae507a18756ba1d8963fd7b51d8ed1d" +dependencies = [ + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus-lockstep-macros" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "zbus-lockstep", + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zbus_xml" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f374552b954f6abb4bd6ce979e6c9b38fb9d0cd7cc68a7d796e70c9f3a233" +dependencies = [ + "quick-xml 0.30.0", + "serde", + "static_assertions", + "zbus_names", + "zvariant", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -3012,3 +4351,83 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/crates/flare-shader/src/lib.rs b/crates/flare-shader/src/lib.rs index 95e1bb0..f39b709 100644 --- a/crates/flare-shader/src/lib.rs +++ b/crates/flare-shader/src/lib.rs @@ -5,48 +5,47 @@ use rand::Rng; use rand::distributions::Standard; use rand::prelude::Distribution; use rand_xoshiro::Xoshiro128Plus; -use spirv_std::spirv; use spirv_std::num_traits::Float; +use spirv_std::spirv; #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[repr(C)] -pub struct ImageConstants { - accum_width: i32, - accum_height: i32, - viewport_width: i32, - viewport_height: i32, - background_color: Vec4, +pub struct ViewportConstants { + offset_x: i32, + offset_y: i32, + width: i32, + height: i32, } -impl ImageConstants { +impl ViewportConstants { pub fn new( - accum_width: i32, - accum_height: i32, - viewport_width: i32, - viewport_height: i32, - background_color: Vec4, + offset_x: i32, + offset_y: i32, + width: i32, + height: i32, ) -> Self { Self { - accum_width, - accum_height, - viewport_width, - viewport_height, - background_color, + offset_x, offset_y, width, height + } + } +} + +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +pub struct AccumConstants { + pub width: i32, + pub height: i32, +} + +impl AccumConstants { + pub fn new(width: i32, height: i32) -> Self { + Self { + width, height } } - pub fn viewport_dimensions(&self) -> glam::IVec2 { - glam::ivec2(self.viewport_width, self.viewport_height) - } - - pub fn with_accumulate(&mut self, width: i32, height: i32) { - self.accum_width = width; - self.accum_height = height; - } - - pub fn with_viewport(&mut self, width: i32, height: i32) { - self.viewport_width = width; - self.viewport_height = height; + pub fn min_dimension(&self) -> i32 { + if self.width < self.height { self.width } else { self.height } } } @@ -61,6 +60,16 @@ pub struct CameraConstants { } impl CameraConstants { + pub fn new(scale: f32) -> Self { + Self { + scale, + zoom: 0.0, + rotate: 0.0, + offset_x: 0.0, + offset_y: 0.0, + } + } + pub fn camera(&self, image_width: i32, image_height: i32) -> glam::Affine2 { let zoom_factor = 2f32.powf(self.zoom); let zoom_rotate_offset = glam::Affine2::from_scale_angle_translation( @@ -120,13 +129,23 @@ impl ThreadState { unsafe { core::mem::transmute::<_, Xoshiro128Plus>(*rng_state) } } - pub fn new(rng: &mut Xoshiro128Plus) -> Self { + pub fn new(rng: &mut R) -> Self { + let mut rng_state: [u32; 4] = [0; 4]; + rng.fill(&mut rng_state); let point = vec2(rng.sample(BiUnit), rng.sample(BiUnit)); - let rng_state = Self::xoshiro_to_state(rng); - rng.jump(); Self { rng_state, point } } + pub fn jump(&self) -> Self { + let mut rng = self.get_rng(); + rng.jump(); + let point = vec2(rng.sample(BiUnit), rng.sample(BiUnit)); + Self { + rng_state: Self::xoshiro_to_state(&rng), + point, + } + } + pub fn get_rng(&self) -> Xoshiro128Plus { Self::state_to_xoshiro(&self.rng_state) } @@ -238,11 +257,13 @@ fn next_transform( #[spirv(compute(threads(1)))] pub fn main_cs( - #[spirv(push_constant)] image_constants: &ImageConstants, - #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] thread_state: &mut [ThreadState], - #[spirv(storage_buffer, descriptor_set = 0, binding = 1)] transforms: &[Transform], - #[spirv(storage_buffer, descriptor_set = 0, binding = 2)] variations: &[Variation], + #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] _viewport_constants: &ViewportConstants, + #[spirv(storage_buffer, descriptor_set = 0, binding = 1)] accum_constants: &AccumConstants, + #[spirv(storage_buffer, descriptor_set = 0, binding = 2)] _camera_constants: &CameraConstants, #[spirv(storage_buffer, descriptor_set = 0, binding = 3)] accum_image: &mut [Vec4], + #[spirv(storage_buffer, descriptor_set = 1, binding = 0)] thread_state: &mut [ThreadState], + #[spirv(storage_buffer, descriptor_set = 1, binding = 1)] transforms: &[Transform], + #[spirv(storage_buffer, descriptor_set = 1, binding = 2)] variations: &[Variation], ) { let mut rng = thread_state[0].get_rng(); let mut point = thread_state[0].point; @@ -258,7 +279,11 @@ pub fn main_cs( } // ...because `.min()` has compilation issues. - let min_dimension = if image_constants.accum_width < image_constants.accum_height { image_constants.accum_width } else { image_constants.accum_height }; + let min_dimension = if accum_constants.width < accum_constants.height { + accum_constants.width + } else { + accum_constants.height + }; // Fixed camera, should be provided by a uniform in the future let camera = CameraConstants { @@ -268,7 +293,8 @@ pub fn main_cs( offset_x: 0.5, offset_y: 0.5, } - .camera(image_constants.accum_width, image_constants.accum_height); + .camera(accum_constants.width, accum_constants.height); + // let camera = camera_constants.camera(image_constants.accum_width, image_constants.accum_height); // Iterate 100,000, should be provided by a uniform in the future for _i in 0..100_000 { @@ -276,9 +302,9 @@ pub fn main_cs( let pixel_coordinates = camera.transform_point2(point).as_ivec2(); if pixel_coordinates.x < 0 - || pixel_coordinates.x >= image_constants.accum_width + || pixel_coordinates.x >= accum_constants.width || pixel_coordinates.y < 0 - || pixel_coordinates.y >= image_constants.accum_height + || pixel_coordinates.y >= accum_constants.height { continue; } @@ -286,7 +312,7 @@ pub fn main_cs( let ii = image_index( pixel_coordinates.x, pixel_coordinates.y, - image_constants.accum_width, + accum_constants.width, ); accum_image[ii as usize] = Color::WHITE; } @@ -311,18 +337,24 @@ pub fn main_vs( #[spirv(fragment)] pub fn main_fs( #[spirv(frag_coord)] frag_coord: Vec4, - #[spirv(push_constant)] ifs_constants: &ImageConstants, - #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] accum_image: &[Vec4], + #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] viewport_constants: &ViewportConstants, + #[spirv(storage_buffer, descriptor_set = 0, binding = 1)] accum_constants: &AccumConstants, + #[spirv(storage_buffer, descriptor_set = 0, binding = 2)] _accum_image: &mut [Vec4], output: &mut Vec4, ) { // Bootleg texture sampling; map from viewport image pixel coordinates to accumulator image // pixel coordinates - let viewport_coordinate = frag_coord.xy().as_uvec2(); + let viewport_offset = glam::ivec2( + viewport_constants.offset_x, + viewport_constants.offset_y, + ) + .as_uvec2(); + let viewport_coordinate = frag_coord.xy().as_uvec2() - viewport_offset; - let a_width = ifs_constants.accum_width as f32; - let a_height = ifs_constants.accum_height as f32; - let v_width = ifs_constants.viewport_width as f32; - let v_height = ifs_constants.viewport_height as f32; + let a_width = accum_constants.width as f32; + let a_height = accum_constants.height as f32; + let v_width = viewport_constants.width as f32; + let v_height = viewport_constants.height as f32; // Scale both width and height by the same factor; preserves aspect ratio let scale_width = a_width / v_width; @@ -333,8 +365,9 @@ pub fn main_fs( let offset_x = (v_width * scale - a_width) / 2.0; let offset_y = (v_height * scale - a_height) / 2.0; - let accum_coordinate = viewport_coordinate.as_vec2() * scale - vec2(offset_x, offset_y); + let _accum_coordinate = viewport_coordinate.as_vec2() * scale - vec2(offset_x, offset_y); + /* if accum_coordinate.x < 0.0 || accum_coordinate.x >= ifs_constants.accum_width as f32 || accum_coordinate.y < 0.0 @@ -348,4 +381,13 @@ pub fn main_fs( ifs_constants.accum_width, ) as usize]; } + */ + + if viewport_coordinate.x == 0 + || viewport_coordinate.x == viewport_constants.width as u32 - 1 + || viewport_coordinate.y == 0 + || viewport_coordinate.y == viewport_constants.height as u32 - 1 + { + *output = glam::Vec4::splat(1.); + } } diff --git a/crates/flare/Cargo.toml b/crates/flare/Cargo.toml index 0c48e2b..31b686b 100644 --- a/crates/flare/Cargo.toml +++ b/crates/flare/Cargo.toml @@ -8,6 +8,9 @@ license.workspace = true [dependencies] anyhow.workspace = true bytemuck.workspace = true +eframe = { version = "0.31", features = ["wgpu"]} +egui = "0.31" +egui-wgpu = "0.31" env_logger.workspace = true flare-shader = { path = "../flare-shader" } futures-executor.workspace = true diff --git a/crates/flare/src/main.rs b/crates/flare/src/main.rs index bd5395a..98700b7 100644 --- a/crates/flare/src/main.rs +++ b/crates/flare/src/main.rs @@ -1,38 +1,219 @@ -use flare_shader::{Coefs, Color, ImageConstants, ThreadState, Transform, Variation, VariationKind}; -use futures_executor::block_on; -use glam::Vec4; -use rand::SeedableRng; -use rand_xoshiro::Xoshiro128Plus; -use std::sync::Arc; +use eframe::Frame; +use eframe::epaint::PaintCallbackInfo; +use egui::Context; +use egui_wgpu::{CallbackResources, CallbackTrait, ScreenDescriptor}; +use flare_shader::{CameraConstants, Coefs, ViewportConstants, ThreadState, Transform, Variation, VariationKind, AccumConstants}; +use rand::thread_rng; use wgpu::util::DeviceExt; -use wgpu::{ - Adapter, Device, Features, Instance, Queue, ShaderModule, Surface, SurfaceConfiguration, -}; -use winit::event::MouseButton; -use winit::{ - application::ApplicationHandler, - dpi::LogicalSize, - event::WindowEvent, - event_loop::{ActiveEventLoop, EventLoop}, - window::{Window, WindowAttributes, WindowId}, -}; +use wgpu::{CommandBuffer, CommandEncoder, Device, Queue, RenderPass}; -struct AccumulatePipeline { - device: Device, +struct RenderGroup { + device: wgpu::Device, + queue: wgpu::Queue, bind_group_layout: wgpu::BindGroupLayout, - pipeline: wgpu::ComputePipeline, + bind_group: wgpu::BindGroup, + viewport_constants_buffer: wgpu::Buffer, + accum_constants: AccumConstants, + accum_constants_buffer: wgpu::Buffer, + camera_constants_buffer: wgpu::Buffer, + image_accum_buffer: wgpu::Buffer, +} - thread_state: Option, - transforms: Option, - variations: Option, - accum_image: Option, +impl RenderGroup { + pub fn bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("render"), + entries: &[ + // image_constants + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // accum_constants + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // camera_constants + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // accum_image + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: false }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + ], + }) + } + + fn build_image_accum_buffer(device: &wgpu::Device, width: i32, height: i32) -> wgpu::Buffer { + let pixel_count = (width * height) as u64; + let size = pixel_count * size_of::() as u64; + device.create_buffer(&wgpu::BufferDescriptor { + label: Some("accum_image"), + size, + usage: wgpu::BufferUsages::STORAGE, + mapped_at_creation: false, + }) + } + + fn build_bind_group( + device: &wgpu::Device, + layout: &wgpu::BindGroupLayout, + image_constants_buffer: &wgpu::Buffer, + accum_constants_buffer: &wgpu::Buffer, + camera_constants_buffer: &wgpu::Buffer, + image_accum_buffer: &wgpu::Buffer, + ) -> wgpu::BindGroup { + device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("render"), + layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: image_constants_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: accum_constants_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: camera_constants_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: image_accum_buffer.as_entire_binding(), + }, + ], + }) + } + + pub fn new( + device: &wgpu::Device, + queue: &wgpu::Queue, + width: i32, + height: i32, + ) -> Self { + let bind_group_layout = Self::bind_group_layout(device); + + let viewport_constants_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("viewport_constants"), + size: size_of::() as u64, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let accum_constants = AccumConstants::new(width, height); + let accum_constants_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("accum_constants"), + contents: bytemuck::cast_slice(&[accum_constants.clone()]), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); + + let min_dimension = width.min(height); + let initial_camera = CameraConstants::new(min_dimension as f32); + let camera_constants_buffer = + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("camera_constants"), + contents: bytemuck::cast_slice(&[initial_camera]), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); + + let image_accum_buffer = + Self::build_image_accum_buffer(device, width, height); + + let bind_group = Self::build_bind_group( + device, + &bind_group_layout, + &viewport_constants_buffer, + &accum_constants_buffer, + &camera_constants_buffer, + &image_accum_buffer, + ); + + Self { + device: device.clone(), + queue: queue.clone(), + bind_group_layout, + viewport_constants_buffer, + accum_constants, + accum_constants_buffer, + camera_constants_buffer, + image_accum_buffer, + bind_group, + } + } + + pub fn set_accum_dimensions(&mut self, width: i32, height: i32) { + self.accum_constants = AccumConstants::new(width, height); + self.queue.write_buffer( + &self.accum_constants_buffer, + 0, + bytemuck::cast_slice(&[self.accum_constants]), + ); + + self.image_accum_buffer = Self::build_image_accum_buffer(&self.device, width, height); + self.bind_group = Self::build_bind_group( + &self.device, + &self.bind_group_layout, + &self.viewport_constants_buffer, + &self.accum_constants_buffer, + &self.camera_constants_buffer, + &self.image_accum_buffer, + ); + } + + pub fn set_viewport_dimensions(&mut self, rect: egui::Rect) { + let offset = rect.left_top(); + let viewport_constants = ViewportConstants::new(offset.x as i32, offset.y as i32, rect.width() as i32, rect.height() as i32); + self.queue.write_buffer(&self.viewport_constants_buffer, 0, bytemuck::cast_slice(&[viewport_constants])) + } + + pub fn bind_group(&self) -> &wgpu::BindGroup { + &self.bind_group + } +} + +struct IfsGroup { + device: wgpu::Device, + bind_group_layout: wgpu::BindGroupLayout, + thread_state_buffer: wgpu::Buffer, + transforms_buffer: Option, + variations_buffer: Option, bind_group: Option, } -impl AccumulatePipeline { - const BIND_GROUP_LAYOUT_DESCRIPTOR: wgpu::BindGroupLayoutDescriptor<'static> = - wgpu::BindGroupLayoutDescriptor { - label: Some("accumulate"), +impl IfsGroup { + pub fn bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("accum"), entries: &[ // thread_state wgpu::BindGroupLayoutEntry { @@ -67,35 +248,105 @@ impl AccumulatePipeline { }, count: None, }, - // accum_image - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStages::COMPUTE, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: false }, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, ], - }; + }) + } - const N_THREADS: usize = 1; + pub fn new(device: &wgpu::Device, queue: &wgpu::Queue) -> Self { + let mut seed_rng = thread_rng(); + let mut thread_states = vec![ThreadState::new(&mut seed_rng)]; + while thread_states.len() < 64 { + thread_states.push(thread_states[thread_states.len() - 1].jump()); + } - pub fn new(device: &Device, module: &ShaderModule) -> Self { - let bind_group_layout = - device.create_bind_group_layout(&Self::BIND_GROUP_LAYOUT_DESCRIPTOR); - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("accumulate"), - bind_group_layouts: &[&bind_group_layout], - push_constant_ranges: &[wgpu::PushConstantRange { - stages: wgpu::ShaderStages::COMPUTE, - range: 0..size_of::() as u32, - }], + let thread_state_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("thread_state"), + contents: bytemuck::cast_slice(thread_states.as_slice()), + usage: wgpu::BufferUsages::STORAGE, }); + + Self { + device: device.clone(), + bind_group_layout: Self::bind_group_layout(device), + thread_state_buffer, + transforms_buffer: None, + variations_buffer: None, + bind_group: None, + } + } + + pub fn set_transforms(&mut self, transforms: &[Transform]) { + let transforms_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("transforms"), + contents: bytemuck::cast_slice(transforms), + usage: wgpu::BufferUsages::STORAGE, + }); + let _ = self.transforms_buffer.insert(transforms_buffer); + } + + pub fn set_variations(&mut self, variations: &[Variation]) { + let variations_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("variations"), + contents: bytemuck::cast_slice(variations), + usage: wgpu::BufferUsages::STORAGE, + }); + let _ = self.variations_buffer.insert(variations_buffer); + } + + pub fn bind_group(&mut self) -> &wgpu::BindGroup { + if self.bind_group.is_none() { + let transforms_buffer = self + .transforms_buffer + .as_ref() + .expect("No transforms available"); + let variations_buffer = self + .variations_buffer + .as_ref() + .expect("No variations available"); + + let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("ifs"), + layout: &self.bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: self.thread_state_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: transforms_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: variations_buffer.as_entire_binding(), + }, + ], + }); + + let _ = self.bind_group.insert(bind_group); + } + + self.bind_group.as_ref().unwrap() + } +} + +struct AccumPipeline { + pipeline: wgpu::ComputePipeline, +} + +impl AccumPipeline { + pub fn new(device: &wgpu::Device, module: &wgpu::ShaderModule) -> Self { + let render_layout = RenderGroup::bind_group_layout(device); + let ifs_layout = IfsGroup::bind_group_layout(device); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("accum"), + bind_group_layouts: &[&render_layout, &ifs_layout], + push_constant_ranges: &[], + }); + let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { - label: Some("accumulate"), + label: Some("accum"), layout: Some(&pipeline_layout), module, entry_point: Some("main_cs"), @@ -103,157 +354,25 @@ impl AccumulatePipeline { cache: None, }); - let rng = rand::thread_rng(); - let mut rng_xoshiro = Xoshiro128Plus::from_rng(rng).expect("Unable to seed thread_state"); - let mut thread_state_elements = vec![]; - for _i in 0..Self::N_THREADS { - thread_state_elements.push(ThreadState::new(&mut rng_xoshiro)); - } - let thread_state = Some( - device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("accumulate/thread_state"), - contents: bytemuck::cast_slice(&thread_state_elements), - usage: wgpu::BufferUsages::STORAGE, - }), - ); - - Self { - device: device.clone(), - bind_group_layout, - pipeline, - thread_state, - transforms: None, - variations: None, - accum_image: None, - bind_group: None, - } - } - - pub fn set_transforms(&mut self, transforms: &[Transform]) { - // Should be smarter about allocation in the future - self.transforms = Some( - self.device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("accumulate/transforms"), - contents: bytemuck::cast_slice(transforms), - usage: wgpu::BufferUsages::STORAGE, - }), - ); - - // Setting a new buffer invalidates the existing bindings - self.bind_group.take(); - } - - pub fn set_variations(&mut self, variations: &[Variation]) { - self.variations = Some( - self.device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("accumulate/variations"), - contents: bytemuck::cast_slice(variations), - usage: wgpu::BufferUsages::STORAGE, - }), - ); - - self.bind_group.take(); - } - - pub fn set_accum_image(&mut self, accum_image: &wgpu::Buffer) { - self.accum_image = Some(accum_image.clone()); - - // Setting a new buffer invalidates the existing bindings - self.bind_group.take(); - } - - fn fetch_bind_group(&mut self) -> &wgpu::BindGroup { - self.bind_group.get_or_insert_with(|| { - self.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("accumulate"), - layout: &self.bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: self - .thread_state - .as_ref() - .expect("thread_state missing") - .as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: self - .transforms - .as_ref() - .expect("transforms missing") - .as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: self - .variations - .as_ref() - .expect("variations missing") - .as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: self - .accum_image - .as_ref() - .expect("accum_image missing") - .as_entire_binding(), - }, - ], - }) - }) - } - - pub fn run(&mut self, encoder: &mut wgpu::CommandEncoder, constants: &ImageConstants) { - let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { - label: Some("accumulate"), - timestamp_writes: None, - }); - pass.set_pipeline(&self.pipeline); - pass.set_push_constants(0, bytemuck::cast_slice(&[*constants])); - pass.set_bind_group(0, self.fetch_bind_group(), &[]); - pass.dispatch_workgroups(1, 1, 1); + Self { pipeline } } } struct RenderPipeline { - device: Device, - bind_group_layout: wgpu::BindGroupLayout, pipeline: wgpu::RenderPipeline, - - accum_image: Option, - bind_group: Option, } impl RenderPipeline { - const BIND_GROUP_LAYOUT_DESCRIPTOR: wgpu::BindGroupLayoutDescriptor<'static> = - wgpu::BindGroupLayoutDescriptor { - label: Some("render"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: true }, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - }; - - pub fn new(device: &Device, module: &ShaderModule, format: wgpu::TextureFormat) -> Self { - let bind_group_layout = - device.create_bind_group_layout(&Self::BIND_GROUP_LAYOUT_DESCRIPTOR); + pub fn new( + device: &wgpu::Device, + module: &wgpu::ShaderModule, + target: &wgpu::TextureFormat, + ) -> Self { + let render_layout = RenderGroup::bind_group_layout(device); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("render"), - bind_group_layouts: &[&bind_group_layout], - push_constant_ranges: &[wgpu::PushConstantRange { - stages: wgpu::ShaderStages::FRAGMENT, - range: 0..size_of::() as u32, - }], + bind_group_layouts: &[&render_layout], + push_constant_ranges: &[], }); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("render"), @@ -271,311 +390,161 @@ impl RenderPipeline { module, entry_point: Some("main_fs"), compilation_options: Default::default(), - targets: &[Some(wgpu::ColorTargetState { - format, - blend: None, - write_mask: Default::default(), - })], + targets: &[Some((*target).into())], }), multiview: None, cache: None, }); - Self { - device: device.clone(), - bind_group_layout, - pipeline, - accum_image: None, - bind_group: None, - } + Self { pipeline } } +} - pub fn set_accum_image(&mut self, accum_image: &wgpu::Buffer) { - self.accum_image = Some(accum_image.clone()); - self.bind_group.take(); - } - - fn fetch_bind_group(&mut self) -> &wgpu::BindGroup { - self.bind_group.get_or_insert_with(|| { - self.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("render"), - layout: &self.bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: self - .accum_image - .as_ref() - .expect("accum_image missing") - .as_entire_binding(), - }], - }) - }) - } - - pub fn run( - &mut self, - encoder: &mut wgpu::CommandEncoder, - output_view: &wgpu::TextureView, - constants: &ImageConstants, - ) { - let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("render"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: output_view, - resolve_target: None, - ops: Default::default(), - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - pass.set_pipeline(&self.pipeline); - pass.set_push_constants( - wgpu::ShaderStages::FRAGMENT, - 0, - bytemuck::cast_slice(&[*constants]), - ); - pass.set_bind_group(0, self.fetch_bind_group(), &[]); - pass.draw(0..3, 0..1); - } +struct FlareAssets { + render_group: RenderGroup, + ifs_group: IfsGroup, + accum_pipeline: AccumPipeline, + render_pipeline: RenderPipeline, } struct Flare { - instance: Instance, - adapter: Adapter, - device: Device, - queue: Queue, - module: ShaderModule, + first_draw: bool, } impl Flare { - pub fn new() -> Self { - let backends = wgpu::Backends::from_env().unwrap_or_default(); - let instance = Instance::new(&wgpu::InstanceDescriptor { - backends, - ..Default::default() - }); + pub fn new(cc: &eframe::CreationContext<'_>, width: i32, height: i32) -> Self { + let cc_wgpu = cc + .wgpu_render_state + .as_ref() + .expect("Unable to acquire wgpu render state"); + let device = cc_wgpu.device.clone(); + let queue = cc_wgpu.queue.clone(); - let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - force_fallback_adapter: false, - compatible_surface: None, - }); - let adapter = block_on(adapter).expect("Failed to find GPU adapter"); + let module_spirv = wgpu::include_spirv!(concat!(env!("OUT_DIR"), "/flare.spv")); + let module = device.create_shader_module(module_spirv); - let required_limits = wgpu::Limits { - max_push_constant_size: size_of::() as u32, - ..Default::default() - }; - - let device = adapter.request_device( - &wgpu::DeviceDescriptor { - label: Some("flare"), - required_features: Features::TIMESTAMP_QUERY | Features::PUSH_CONSTANTS, - required_limits, - memory_hints: Default::default(), - }, - None, - ); - let (device, queue) = block_on(device).expect("Failed to find GPU device"); - let module = device - .create_shader_module(wgpu::include_spirv!(concat!(env!("OUT_DIR"), "/flare.spv"))); - - Flare { - instance, - adapter, - device, - queue, - module, - } - } -} - -struct FlareRender<'window> { - flare: Arc, - surface: Surface<'window>, - surface_configuration: SurfaceConfiguration, - accumulate_pipeline: AccumulatePipeline, - render_pipeline: RenderPipeline, - ifs_constants: ImageConstants, - accum_image: wgpu::Buffer, -} - -impl FlareRender<'_> { - fn create_accum_image(device: &Device, width: u32, height: u32) -> wgpu::Buffer { - let pixels = (width * height) as u64; - let pixel_size = 4u64 * size_of::() as u64; - let size = pixels * pixel_size; - device.create_buffer(&wgpu::BufferDescriptor { - label: Some("accum_image"), - size, - usage: wgpu::BufferUsages::STORAGE, - mapped_at_creation: false, - }) - } - - pub fn new(flare: Arc, window: Arc) -> Self { - let window_size = window.inner_size(); - - let surface = flare - .instance - .create_surface(window.clone()) - .expect("Unable to create surface"); - let mut surface_configuration = surface - .get_default_config(&flare.adapter, window_size.width, window_size.height) - .expect("Unable to get surface config"); - surface_configuration.present_mode = wgpu::PresentMode::AutoVsync; - surface.configure(&flare.device, &surface_configuration); - - let mut accumulate_pipeline = AccumulatePipeline::new(&flare.device, &flare.module); - let mut render_pipeline = - RenderPipeline::new(&flare.device, &flare.module, surface_configuration.format); - - let ifs_constants = ImageConstants::new( - window_size.width as i32, - window_size.height as i32, - window_size.width as i32, - window_size.height as i32, - Vec4::BLACK, - ); - - let accum_image = - Self::create_accum_image(&flare.device, window_size.width, window_size.height); - - accumulate_pipeline.set_accum_image(&accum_image); - render_pipeline.set_accum_image(&accum_image); - - Self { - flare, - surface, - surface_configuration, - accumulate_pipeline, - render_pipeline, - ifs_constants, - accum_image, - } - } - - pub fn render(&mut self, run_accumulate: bool) { - let output = self - .surface - .get_current_texture() - .expect("Failed to get current texture"); - let output_view = output.texture.create_view(&Default::default()); - let mut encoder = self - .flare - .device - .create_command_encoder(&Default::default()); - - if run_accumulate { - self.accumulate_pipeline - .run(&mut encoder, &self.ifs_constants); - } - self.render_pipeline - .run(&mut encoder, &output_view, &self.ifs_constants); - - self.flare.queue.submit(Some(encoder.finish())); - output.present(); - } - - pub fn resize_accumulate(&mut self) { - let vp_dimensions = self.ifs_constants.viewport_dimensions(); - self.ifs_constants - .with_accumulate(vp_dimensions.x, vp_dimensions.y); - - let accum_image = - Self::create_accum_image(&self.flare.device, vp_dimensions.x as u32, vp_dimensions.y as u32); - self.accumulate_pipeline.set_accum_image(&accum_image); - self.render_pipeline.set_accum_image(&accum_image); - self.accum_image = accum_image; - } - - pub fn resize_viewport(&mut self, width: u32, height: u32) { - self.surface_configuration.width = width; - self.surface_configuration.height = height; - self.surface - .configure(&self.flare.device, &self.surface_configuration); - self.ifs_constants.with_viewport(width as i32, height as i32); - } -} - -struct Application<'window> { - flare: Arc, - window: Option>, - flare_render: Option>, -} - -impl Application<'_> { - pub fn new() -> Self { - Self { - flare: Arc::new(Flare::new()), - window: None, - flare_render: None, - } - } -} - -impl ApplicationHandler for Application<'_> { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let attributes = WindowAttributes::default() - .with_title("Flare") - .with_inner_size(LogicalSize::new(1024, 768)); - - let window = Arc::new( - event_loop - .create_window(attributes) - .expect("Failed to create window"), - ); - - self.window = Some(window.clone()); - - let mut flare_render = FlareRender::new(self.flare.clone(), window); + // Draw a gasket + let mut ifs_group = IfsGroup::new(&device, &queue); let f0 = Transform::new(Coefs::new(0.5, 0., 0., 0., 0.5, 0.), 0, 1, 1.0); let f1 = Transform::new(Coefs::new(0.5, 0., 0.5, 0., 0.5, 0.), 0, 1, 1.0); let f2 = Transform::new(Coefs::new(0.5, 0., 0., 0., 0.5, 0.5), 0, 1, 1.0); let variation = Variation::new(VariationKind::Linear, 1.0); - flare_render.accumulate_pipeline.set_transforms(&[f0, f1, f2]); - flare_render.accumulate_pipeline.set_variations(&[variation]); + ifs_group.set_transforms(&[f0, f1, f2]); + ifs_group.set_variations(&[variation]); - flare_render.render(true); + cc_wgpu + .renderer + .write() + .callback_resources + .insert(FlareAssets { + render_group: RenderGroup::new(&device, &queue, width, height), + ifs_group, + accum_pipeline: AccumPipeline::new(&device, &module), + render_pipeline: RenderPipeline::new(&device, &module, &cc_wgpu.target_format), + }); - self.flare_render = Some(flare_render); + Self { first_draw: true } } +} - fn window_event( - &mut self, - event_loop: &ActiveEventLoop, - _window_id: WindowId, - event: WindowEvent, - ) { - match event { - WindowEvent::Resized(size) => { - let flare_render = self.flare_render.as_mut().unwrap(); - flare_render.resize_viewport(size.width, size.height); - flare_render.render(false); - } - WindowEvent::MouseInput { - button: MouseButton::Left, - .. - } => { - let flare_render = self.flare_render.as_mut().unwrap(); - flare_render.resize_accumulate(); - flare_render.render(true); - } - WindowEvent::CloseRequested => event_loop.exit(), - _ => (), +impl eframe::App for Flare { + fn update(&mut self, ctx: &Context, _frame: &mut Frame) { + egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { + egui::menu::bar(ui, |ui| { + ui.menu_button("File", |ui| { + if ui.button("Quit").clicked() { + ctx.send_viewport_cmd(egui::ViewportCommand::Close); + } + }); + }); + }); + + egui::CentralPanel::default().show(ctx, |ui| { + egui::Frame::canvas(ui.style()).show(ui, |ui| { + let rect = ui.available_rect_before_wrap(); + let response = ui.allocate_rect(rect, egui::Sense::click()); + let painter = PaintIfs { + run_accumulate: self.first_draw || response.clicked(), + viewport: rect, + }; + ui.painter() + .add(egui_wgpu::Callback::new_paint_callback(rect, painter)); + }); + }); + + self.first_draw = false + } +} + +struct PaintIfs { + run_accumulate: bool, + viewport: egui::Rect, +} + +impl CallbackTrait for PaintIfs { + fn prepare( + &self, + _device: &Device, + _queue: &Queue, + _screen_descriptor: &ScreenDescriptor, + egui_encoder: &mut CommandEncoder, + callback_resources: &mut CallbackResources, + ) -> Vec { + if self.run_accumulate { + let flare_assets = callback_resources + .get_mut::() + .expect("Missing assets"); + + flare_assets + .render_group + .set_accum_dimensions(self.viewport.width() as i32, self.viewport.height() as i32); + + let mut pass = egui_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { + label: Some("accum"), + timestamp_writes: None, + }); + pass.set_pipeline(&flare_assets.accum_pipeline.pipeline); + pass.set_bind_group(0, flare_assets.render_group.bind_group(), &[]); + pass.set_bind_group(1, flare_assets.ifs_group.bind_group(), &[]); + pass.dispatch_workgroups(1, 1, 1); } + + let flare_assets = callback_resources.get_mut::().expect("Missing assets"); + flare_assets.render_group.set_viewport_dimensions(self.viewport); + + vec![] + } + + fn paint( + &self, + _info: PaintCallbackInfo, + render_pass: &mut RenderPass<'static>, + callback_resources: &CallbackResources, + ) { + let flare_assets = callback_resources + .get::() + .expect("Missing assets"); + + render_pass.set_pipeline(&flare_assets.render_pipeline.pipeline); + render_pass.set_bind_group(0, flare_assets.render_group.bind_group(), &[]); + render_pass.draw(0..3, 0..1); } } -pub fn main() -> anyhow::Result<()> { +fn main() -> eframe::Result { env_logger::init(); - let _module = wgpu::include_spirv!(concat!(env!("OUT_DIR"), "/flare.spv")); - let event_loop = EventLoop::new()?; - let mut application = Application::new(); - event_loop.run_app(&mut application)?; + let initial_dimensions = egui::vec2(800., 600.); + let native_options = eframe::NativeOptions { + viewport: egui::ViewportBuilder::default().with_inner_size(initial_dimensions), + renderer: eframe::Renderer::Wgpu, + ..Default::default() + }; - Ok(()) + eframe::run_native( + "flare", + native_options, + Box::new(|cc| Ok(Box::new(Flare::new(cc, initial_dimensions.x as i32, initial_dimensions.y as i32)))), + ) } diff --git a/crates/flare/src/main_winit.rs b/crates/flare/src/main_winit.rs new file mode 100644 index 0000000..bd5395a --- /dev/null +++ b/crates/flare/src/main_winit.rs @@ -0,0 +1,581 @@ +use flare_shader::{Coefs, Color, ImageConstants, ThreadState, Transform, Variation, VariationKind}; +use futures_executor::block_on; +use glam::Vec4; +use rand::SeedableRng; +use rand_xoshiro::Xoshiro128Plus; +use std::sync::Arc; +use wgpu::util::DeviceExt; +use wgpu::{ + Adapter, Device, Features, Instance, Queue, ShaderModule, Surface, SurfaceConfiguration, +}; +use winit::event::MouseButton; +use winit::{ + application::ApplicationHandler, + dpi::LogicalSize, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowAttributes, WindowId}, +}; + +struct AccumulatePipeline { + device: Device, + bind_group_layout: wgpu::BindGroupLayout, + pipeline: wgpu::ComputePipeline, + + thread_state: Option, + transforms: Option, + variations: Option, + accum_image: Option, + bind_group: Option, +} + +impl AccumulatePipeline { + const BIND_GROUP_LAYOUT_DESCRIPTOR: wgpu::BindGroupLayoutDescriptor<'static> = + wgpu::BindGroupLayoutDescriptor { + label: Some("accumulate"), + entries: &[ + // thread_state + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: false }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // transforms + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // variations + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // accum_image + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: false }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + ], + }; + + const N_THREADS: usize = 1; + + pub fn new(device: &Device, module: &ShaderModule) -> Self { + let bind_group_layout = + device.create_bind_group_layout(&Self::BIND_GROUP_LAYOUT_DESCRIPTOR); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("accumulate"), + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[wgpu::PushConstantRange { + stages: wgpu::ShaderStages::COMPUTE, + range: 0..size_of::() as u32, + }], + }); + let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: Some("accumulate"), + layout: Some(&pipeline_layout), + module, + entry_point: Some("main_cs"), + compilation_options: Default::default(), + cache: None, + }); + + let rng = rand::thread_rng(); + let mut rng_xoshiro = Xoshiro128Plus::from_rng(rng).expect("Unable to seed thread_state"); + let mut thread_state_elements = vec![]; + for _i in 0..Self::N_THREADS { + thread_state_elements.push(ThreadState::new(&mut rng_xoshiro)); + } + let thread_state = Some( + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("accumulate/thread_state"), + contents: bytemuck::cast_slice(&thread_state_elements), + usage: wgpu::BufferUsages::STORAGE, + }), + ); + + Self { + device: device.clone(), + bind_group_layout, + pipeline, + thread_state, + transforms: None, + variations: None, + accum_image: None, + bind_group: None, + } + } + + pub fn set_transforms(&mut self, transforms: &[Transform]) { + // Should be smarter about allocation in the future + self.transforms = Some( + self.device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("accumulate/transforms"), + contents: bytemuck::cast_slice(transforms), + usage: wgpu::BufferUsages::STORAGE, + }), + ); + + // Setting a new buffer invalidates the existing bindings + self.bind_group.take(); + } + + pub fn set_variations(&mut self, variations: &[Variation]) { + self.variations = Some( + self.device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("accumulate/variations"), + contents: bytemuck::cast_slice(variations), + usage: wgpu::BufferUsages::STORAGE, + }), + ); + + self.bind_group.take(); + } + + pub fn set_accum_image(&mut self, accum_image: &wgpu::Buffer) { + self.accum_image = Some(accum_image.clone()); + + // Setting a new buffer invalidates the existing bindings + self.bind_group.take(); + } + + fn fetch_bind_group(&mut self) -> &wgpu::BindGroup { + self.bind_group.get_or_insert_with(|| { + self.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("accumulate"), + layout: &self.bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: self + .thread_state + .as_ref() + .expect("thread_state missing") + .as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: self + .transforms + .as_ref() + .expect("transforms missing") + .as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: self + .variations + .as_ref() + .expect("variations missing") + .as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: self + .accum_image + .as_ref() + .expect("accum_image missing") + .as_entire_binding(), + }, + ], + }) + }) + } + + pub fn run(&mut self, encoder: &mut wgpu::CommandEncoder, constants: &ImageConstants) { + let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { + label: Some("accumulate"), + timestamp_writes: None, + }); + pass.set_pipeline(&self.pipeline); + pass.set_push_constants(0, bytemuck::cast_slice(&[*constants])); + pass.set_bind_group(0, self.fetch_bind_group(), &[]); + pass.dispatch_workgroups(1, 1, 1); + } +} + +struct RenderPipeline { + device: Device, + bind_group_layout: wgpu::BindGroupLayout, + pipeline: wgpu::RenderPipeline, + + accum_image: Option, + bind_group: Option, +} + +impl RenderPipeline { + const BIND_GROUP_LAYOUT_DESCRIPTOR: wgpu::BindGroupLayoutDescriptor<'static> = + wgpu::BindGroupLayoutDescriptor { + label: Some("render"), + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + }; + + pub fn new(device: &Device, module: &ShaderModule, format: wgpu::TextureFormat) -> Self { + let bind_group_layout = + device.create_bind_group_layout(&Self::BIND_GROUP_LAYOUT_DESCRIPTOR); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("render"), + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[wgpu::PushConstantRange { + stages: wgpu::ShaderStages::FRAGMENT, + range: 0..size_of::() as u32, + }], + }); + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("render"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module, + entry_point: Some("main_vs"), + compilation_options: Default::default(), + buffers: &[], + }, + primitive: Default::default(), + depth_stencil: None, + multisample: Default::default(), + fragment: Some(wgpu::FragmentState { + module, + entry_point: Some("main_fs"), + compilation_options: Default::default(), + targets: &[Some(wgpu::ColorTargetState { + format, + blend: None, + write_mask: Default::default(), + })], + }), + multiview: None, + cache: None, + }); + Self { + device: device.clone(), + bind_group_layout, + pipeline, + accum_image: None, + bind_group: None, + } + } + + pub fn set_accum_image(&mut self, accum_image: &wgpu::Buffer) { + self.accum_image = Some(accum_image.clone()); + self.bind_group.take(); + } + + fn fetch_bind_group(&mut self) -> &wgpu::BindGroup { + self.bind_group.get_or_insert_with(|| { + self.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("render"), + layout: &self.bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: self + .accum_image + .as_ref() + .expect("accum_image missing") + .as_entire_binding(), + }], + }) + }) + } + + pub fn run( + &mut self, + encoder: &mut wgpu::CommandEncoder, + output_view: &wgpu::TextureView, + constants: &ImageConstants, + ) { + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("render"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: output_view, + resolve_target: None, + ops: Default::default(), + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + pass.set_pipeline(&self.pipeline); + pass.set_push_constants( + wgpu::ShaderStages::FRAGMENT, + 0, + bytemuck::cast_slice(&[*constants]), + ); + pass.set_bind_group(0, self.fetch_bind_group(), &[]); + pass.draw(0..3, 0..1); + } +} + +struct Flare { + instance: Instance, + adapter: Adapter, + device: Device, + queue: Queue, + module: ShaderModule, +} + +impl Flare { + pub fn new() -> Self { + let backends = wgpu::Backends::from_env().unwrap_or_default(); + let instance = Instance::new(&wgpu::InstanceDescriptor { + backends, + ..Default::default() + }); + + let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + force_fallback_adapter: false, + compatible_surface: None, + }); + let adapter = block_on(adapter).expect("Failed to find GPU adapter"); + + let required_limits = wgpu::Limits { + max_push_constant_size: size_of::() as u32, + ..Default::default() + }; + + let device = adapter.request_device( + &wgpu::DeviceDescriptor { + label: Some("flare"), + required_features: Features::TIMESTAMP_QUERY | Features::PUSH_CONSTANTS, + required_limits, + memory_hints: Default::default(), + }, + None, + ); + let (device, queue) = block_on(device).expect("Failed to find GPU device"); + let module = device + .create_shader_module(wgpu::include_spirv!(concat!(env!("OUT_DIR"), "/flare.spv"))); + + Flare { + instance, + adapter, + device, + queue, + module, + } + } +} + +struct FlareRender<'window> { + flare: Arc, + surface: Surface<'window>, + surface_configuration: SurfaceConfiguration, + accumulate_pipeline: AccumulatePipeline, + render_pipeline: RenderPipeline, + ifs_constants: ImageConstants, + accum_image: wgpu::Buffer, +} + +impl FlareRender<'_> { + fn create_accum_image(device: &Device, width: u32, height: u32) -> wgpu::Buffer { + let pixels = (width * height) as u64; + let pixel_size = 4u64 * size_of::() as u64; + let size = pixels * pixel_size; + device.create_buffer(&wgpu::BufferDescriptor { + label: Some("accum_image"), + size, + usage: wgpu::BufferUsages::STORAGE, + mapped_at_creation: false, + }) + } + + pub fn new(flare: Arc, window: Arc) -> Self { + let window_size = window.inner_size(); + + let surface = flare + .instance + .create_surface(window.clone()) + .expect("Unable to create surface"); + let mut surface_configuration = surface + .get_default_config(&flare.adapter, window_size.width, window_size.height) + .expect("Unable to get surface config"); + surface_configuration.present_mode = wgpu::PresentMode::AutoVsync; + surface.configure(&flare.device, &surface_configuration); + + let mut accumulate_pipeline = AccumulatePipeline::new(&flare.device, &flare.module); + let mut render_pipeline = + RenderPipeline::new(&flare.device, &flare.module, surface_configuration.format); + + let ifs_constants = ImageConstants::new( + window_size.width as i32, + window_size.height as i32, + window_size.width as i32, + window_size.height as i32, + Vec4::BLACK, + ); + + let accum_image = + Self::create_accum_image(&flare.device, window_size.width, window_size.height); + + accumulate_pipeline.set_accum_image(&accum_image); + render_pipeline.set_accum_image(&accum_image); + + Self { + flare, + surface, + surface_configuration, + accumulate_pipeline, + render_pipeline, + ifs_constants, + accum_image, + } + } + + pub fn render(&mut self, run_accumulate: bool) { + let output = self + .surface + .get_current_texture() + .expect("Failed to get current texture"); + let output_view = output.texture.create_view(&Default::default()); + let mut encoder = self + .flare + .device + .create_command_encoder(&Default::default()); + + if run_accumulate { + self.accumulate_pipeline + .run(&mut encoder, &self.ifs_constants); + } + self.render_pipeline + .run(&mut encoder, &output_view, &self.ifs_constants); + + self.flare.queue.submit(Some(encoder.finish())); + output.present(); + } + + pub fn resize_accumulate(&mut self) { + let vp_dimensions = self.ifs_constants.viewport_dimensions(); + self.ifs_constants + .with_accumulate(vp_dimensions.x, vp_dimensions.y); + + let accum_image = + Self::create_accum_image(&self.flare.device, vp_dimensions.x as u32, vp_dimensions.y as u32); + self.accumulate_pipeline.set_accum_image(&accum_image); + self.render_pipeline.set_accum_image(&accum_image); + self.accum_image = accum_image; + } + + pub fn resize_viewport(&mut self, width: u32, height: u32) { + self.surface_configuration.width = width; + self.surface_configuration.height = height; + self.surface + .configure(&self.flare.device, &self.surface_configuration); + self.ifs_constants.with_viewport(width as i32, height as i32); + } +} + +struct Application<'window> { + flare: Arc, + window: Option>, + flare_render: Option>, +} + +impl Application<'_> { + pub fn new() -> Self { + Self { + flare: Arc::new(Flare::new()), + window: None, + flare_render: None, + } + } +} + +impl ApplicationHandler for Application<'_> { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let attributes = WindowAttributes::default() + .with_title("Flare") + .with_inner_size(LogicalSize::new(1024, 768)); + + let window = Arc::new( + event_loop + .create_window(attributes) + .expect("Failed to create window"), + ); + + self.window = Some(window.clone()); + + let mut flare_render = FlareRender::new(self.flare.clone(), window); + + let f0 = Transform::new(Coefs::new(0.5, 0., 0., 0., 0.5, 0.), 0, 1, 1.0); + let f1 = Transform::new(Coefs::new(0.5, 0., 0.5, 0., 0.5, 0.), 0, 1, 1.0); + let f2 = Transform::new(Coefs::new(0.5, 0., 0., 0., 0.5, 0.5), 0, 1, 1.0); + let variation = Variation::new(VariationKind::Linear, 1.0); + + flare_render.accumulate_pipeline.set_transforms(&[f0, f1, f2]); + flare_render.accumulate_pipeline.set_variations(&[variation]); + + flare_render.render(true); + + self.flare_render = Some(flare_render); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + match event { + WindowEvent::Resized(size) => { + let flare_render = self.flare_render.as_mut().unwrap(); + flare_render.resize_viewport(size.width, size.height); + flare_render.render(false); + } + WindowEvent::MouseInput { + button: MouseButton::Left, + .. + } => { + let flare_render = self.flare_render.as_mut().unwrap(); + flare_render.resize_accumulate(); + flare_render.render(true); + } + WindowEvent::CloseRequested => event_loop.exit(), + _ => (), + } + } +} + +pub fn main() -> anyhow::Result<()> { + env_logger::init(); + let _module = wgpu::include_spirv!(concat!(env!("OUT_DIR"), "/flare.spv")); + + let event_loop = EventLoop::new()?; + let mut application = Application::new(); + event_loop.run_app(&mut application)?; + + Ok(()) +}