From 3e76d7c24891fe36d3f87dbc7802b5b5d816a1d7 Mon Sep 17 00:00:00 2001 From: Tony Klink Date: Tue, 26 Mar 2024 22:44:34 -0600 Subject: [PATCH] Add config reload system --- .envrc | 1 + .gitignore | 1 + Cargo.lock | 2264 +++++++++++++++++++++++++++++++++ Cargo.toml | 23 + engine_config.scm | 1 + flake.lock | 93 ++ flake.nix | 73 ++ src/app.rs | 146 +++ src/comp.rs | 42 + src/components/mod.rs | 10 + src/config/components.rs | 9 + src/config/mod.rs | 68 + src/config/systems.rs | 51 + src/core/events/dispatcher.rs | 183 +++ src/core/events/mod.rs | 208 +++ src/core/mod.rs | 2 + src/core/render/mod.rs | 1 + src/main.rs | 112 ++ src/module/mod.rs | 64 + src/render/mod.rs | 19 + src/systems/mod.rs | 694 ++++++++++ 21 files changed, 4065 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 engine_config.scm create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 src/app.rs create mode 100644 src/comp.rs create mode 100644 src/components/mod.rs create mode 100644 src/config/components.rs create mode 100644 src/config/mod.rs create mode 100644 src/config/systems.rs create mode 100644 src/core/events/dispatcher.rs create mode 100644 src/core/events/mod.rs create mode 100644 src/core/mod.rs create mode 100644 src/core/render/mod.rs create mode 100644 src/main.rs create mode 100644 src/module/mod.rs create mode 100644 src/render/mod.rs create mode 100644 src/systems/mod.rs diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8fd68ea --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2264 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ab_glyph" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80179d7dd5d7e8c285d67c4a1e652972a92de7475beddfb92028c76463b13225" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "android-activity" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" +dependencies = [ + "android-properties", + "bitflags 2.4.2", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atomic_refcell" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95d8e92cac0961e91dbd517496b00f7e9b92363dbe6d42c3198268323798860c" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "block-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" +dependencies = [ + "block-sys", + "objc2", +] + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "calloop" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" +dependencies = [ + "bitflags 2.4.2", + "log", + "polling", + "rustix", + "slab", + "thiserror", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.3", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erased-serde" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" +dependencies = [ + "serde", +] + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "flax" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e93fc013ea9e562aadd71884c5ebfb84f7cac39e691759e281bbc14552709d" +dependencies = [ + "anyhow", + "atomic_refcell", + "bitflags 2.4.2", + "erased-serde", + "flax-derive", + "flume", + "itertools", + "once_cell", + "rayon", + "serde", + "smallvec", + "tokio", + "tracing", + "tynm", +] + +[[package]] +name = "flax-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d141b9bb793ce817c1ec513e93b72e4e2a6d9494b18d5c1e454fa08905586d6" +dependencies = [ + "itertools", + "proc-macro-crate 2.0.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "bytemuck", + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "icrate" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +dependencies = [ + "block2", + "dispatch", + "objc2", +] + +[[package]] +name = "indexmap" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "lexpr" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a84de6a9df442363b08f5dbf0cd5b92edc70097b89c4ce4bfea4679fe48bc67" +dependencies = [ + "itoa", + "lexpr-macros", + "ryu", +] + +[[package]] +name = "lexpr-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36b5cb8bb985c81a8ac1a0f8b5c4865214f574ddd64397ef7a99c236e21f35bb" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.4", +] + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.4.2", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.4.2", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "notify-debouncer-mini" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d40b221972a1fc5ef4d858a2f671fb34c75983eb385463dff3780eeff6a9d43" +dependencies = [ + "crossbeam-channel", + "log", + "notify", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c71324e4180d0899963fc83d9d241ac39e699609fc1025a850aadac8257459" + +[[package]] +name = "objc2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "orbclient" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" +dependencies = [ + "libredox", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "polling" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24f040dee2588b4963afb4e420540439d126f73fdacf4a9c486a96d840bac3c9" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + +[[package]] +name = "rayon" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b2eaf3a5b264a521b988b2e73042e742df700c4f962cde845d1541adb46550" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-lexpr" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4cda13396159f59e7946118cdac0beadeecfb7cf76b197f4147e546f4ead6f" +dependencies = [ + "lexpr", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shaderc" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e07913ada18607bb60d12431cbe3358d3bbebbe95948e1618851dc01e63b7b" +dependencies = [ + "libc", + "shaderc-sys", +] + +[[package]] +name = "shaderc-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73120d240fe22196300f39ca8547ca2d014960f27b19b47b21288b396272f7f7" +dependencies = [ + "cmake", + "libc", + "roxmltree", +] + +[[package]] +name = "shrev" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ea33232fdcf1bf691ca33450e5a94dde13e1a8cbb8caabc5e4f9d761e10b1a" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slabbin" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1040fc353ab4658e2094706abb1ee035506f6a4ba35af2cf16c80437866c6d96" + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "smithay-client-toolkit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" +dependencies = [ + "bitflags 2.4.2", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smol_str" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "syn" +version = "2.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "tynm" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd30d05e69d1478e13fe3e7a853409cfec82cebc2cf9b8d613b3c6b0081781ed" +dependencies = [ + "nom", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vk-parse" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81086c28be67a8759cd80cbb3c8f7b520e0874605fc5eb74d5a1c9c2d1878e79" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "vulkano" +version = "0.34.0" +source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#7cbf3a7f2694f75e44c778878b5520a45c6a2d1c" +dependencies = [ + "ahash", + "ash", + "bytemuck", + "core-graphics-types", + "crossbeam-queue", + "half", + "heck", + "indexmap", + "libloading 0.8.3", + "nom", + "objc", + "once_cell", + "parking_lot", + "proc-macro2", + "quote", + "raw-window-handle 0.6.0", + "serde", + "serde_json", + "slabbin", + "smallvec", + "thread_local", + "vk-parse", + "vulkano-macros", +] + +[[package]] +name = "vulkano-macros" +version = "0.34.0" +source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#7cbf3a7f2694f75e44c778878b5520a45c6a2d1c" +dependencies = [ + "proc-macro-crate 2.0.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "vulkano-shaders" +version = "0.34.0" +source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#7cbf3a7f2694f75e44c778878b5520a45c6a2d1c" +dependencies = [ + "ahash", + "heck", + "proc-macro2", + "quote", + "shaderc", + "syn", + "vulkano", +] + +[[package]] +name = "vulkano-util" +version = "0.34.0" +source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#7cbf3a7f2694f75e44c778878b5520a45c6a2d1c" +dependencies = [ + "ahash", + "vulkano", + "winit", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wayland-backend" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" +dependencies = [ + "bitflags 2.4.2", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.4.2", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags 2.4.2", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +dependencies = [ + "bitflags 2.4.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.4.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winit" +version = "0.29.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.4.2", + "bytemuck", + "calloop", + "cfg_aliases", + "core-foundation", + "core-graphics", + "cursor-icon", + "icrate", + "js-sys", + "libc", + "log", + "memmap2", + "ndk", + "ndk-sys", + "objc2", + "once_cell", + "orbclient", + "percent-encoding", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", + "redox_syscall 0.3.5", + "rustix", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.48.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading 0.8.3", + "once_cell", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" + +[[package]] +name = "xcursor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.4.2", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" + +[[package]] +name = "xml-rs" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ztest" +version = "0.1.0" +dependencies = [ + "anyhow", + "downcast-rs", + "flax", + "flume", + "notify", + "notify-debouncer-mini", + "parking_lot", + "serde", + "serde-lexpr", + "shrev", + "tokio", + "vulkano", + "vulkano-shaders", + "vulkano-util", + "winit", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0c9b5e1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "khors" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.80" +shrev = "1.1.3" +winit = { version = "0.29.15",features = ["rwh_05"] } +vulkano = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" } +vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" } +vulkano-util = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" } +flax = { version = "0.6.2", features = ["derive", "serde", "tokio", "tracing"] } +flume = "0.11.0" +parking_lot = "0.12.1" +downcast-rs = "1.2.0" +serde = { version = "1.0.197", features = ["derive"] } +serde-lexpr = "0.1.3" +tokio = { version = "1.36.0", features = ["full"] } +notify = "6.1.1" +notify-debouncer-mini = "0.4.1" diff --git a/engine_config.scm b/engine_config.scm new file mode 100644 index 0000000..0a3d94c --- /dev/null +++ b/engine_config.scm @@ -0,0 +1 @@ +((asset_path . "/assets")) diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..0f47cc2 --- /dev/null +++ b/flake.lock @@ -0,0 +1,93 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "naersk": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1698420672, + "narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=", + "owner": "nix-community", + "repo": "naersk", + "rev": "aeb58d5e8faead8980a807c840232697982d47b9", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "naersk", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1710272261, + "narHash": "sha256-g0bDwXFmTE7uGDOs9HcJsfLFhH7fOsASbAuOzDC+fhQ=", + "path": "/nix/store/k5l01g2zwhysjyl5zjvg5zxnj0lyxpp1-source", + "rev": "0ad13a6833440b8e238947e47bea7f11071dc2b2", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1710637405, + "narHash": "sha256-w/woLwnFyhOeJWPjSWFtMNI2/RZTaAtHySIfm43Chos=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "299d4668ba61600311553920d9fd9c102145b2cb", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "naersk": "naersk", + "nixpkgs": "nixpkgs_2" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..f7edf02 --- /dev/null +++ b/flake.nix @@ -0,0 +1,73 @@ +{ + inputs = { + flake-utils.url = "github:numtide/flake-utils"; + naersk.url = "github:nix-community/naersk"; + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; + + outputs = { self, flake-utils, naersk, nixpkgs }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = (import nixpkgs) { inherit system; }; + naersk' = pkgs.callPackage naersk { }; + libPath = with pkgs; + lib.makeLibraryPath [ + libGL + libxkbcommon + wayland + glibc + vulkan-loader + xorg.libX11 + xorg.libXcursor + xorg.libXi + xorg.libXrandr + alsa-lib + vulkan-tools + ]; + + in rec { + # For `nix build` & `nix run`: + packages.default = naersk'.buildPackage { + src = ./.; + pname = "khors"; + nativeBuildInputs = with pkgs; [ + makeWrapper + pkg-config + openssl + xorg.libxcb + ]; + GIT_HASH = "000000000000000000000000000000"; + postInstall = '' + wrapProgram "$out/bin/${packages.default.pname}" --prefix LD_LIBRARY_PATH : "${libPath}" + ''; + }; + + # For `nix develop`: + devShells.default = pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + rustc + cargo + cargo-watch + clippy + rustfmt + rust-analyzer + cmake + vulkan-tools + python3 + + vulkan-tools-lunarg + + pkg-config + openssl + xorg.libxcb + alsa-lib + ]; + LD_LIBRARY_PATH = libPath; + env = { + VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d"; + RUST_BACKTRACE = 1; + RUST_LOG = "debug"; + }; + }; + }); +} diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..44183fb --- /dev/null +++ b/src/app.rs @@ -0,0 +1,146 @@ +#![warn(dead_code)] + +use flax::{Schedule, World}; +use anyhow::Result; +use crate::{ + core::events::Events, + module::{Module, ModulesStack}, +}; + +#[allow(dead_code)] +pub struct App { + name: String, + modules: ModulesStack, + world: World, + schedule: Schedule, + events: Events, + rx: flume::Receiver, + running: bool, + event_cleanup_time: std::time::Duration, +} + +impl App { + pub fn new() -> Self { + let mut events = Events::new(); + + let (tx, rx) = flume::unbounded(); + events.subscribe_custom(tx); + + Self { + name: "ZTest".into(), + modules: ModulesStack::new(), + world: World::new(), + schedule: Schedule::default(), + events, + rx, + running: false, + event_cleanup_time: std::time::Duration::from_secs(60), + } + } + + pub fn run(&mut self) -> Result<()> { + self.running = true; + + // self.schedule.execute_par(&mut self.world).unwrap(); + + let world = &mut self.world; + let events = &mut self.events; + let frame_time = std::time::Duration::from_millis(16); + + for module in self.modules.iter_mut() { + module.on_update(world, events, frame_time)?; + } + + self.handle_events(); + + Ok(()) + } + + pub fn handle_events(&mut self) { + for event in self.rx.try_iter() { + match event { + AppEvent::Exit => self.running = false, + } + } + } + + pub fn set_schedule(&mut self, schedule: Schedule) { + self.schedule = schedule; + } + + pub fn world(&self) -> &World { + &self.world + } + + pub fn world_mut(&mut self) -> &mut World { + &mut self.world + } + + pub fn events(&self) -> &Events { + &self.events + } + + pub fn events_mut(&mut self) -> &mut Events { + &mut self.events + } + + /// Pushes a layer from the provided init closure to to the top of the layer stack. The provided + /// closure to construct the layer takes in the world and events. + pub fn push_module(&mut self, func: F) + where + F: FnOnce(&mut World, &mut Events) -> T, + T: 'static + Module, + { + let module = func(&mut self.world, &mut self.events); + self.modules.push(module); + } + + /// Pushes a module from the provided init closure to to the top of the module stack. The provided + /// closure to construct the module takes in the world and events, and may return an error which + /// is propagated to the callee. + pub fn try_push_module(&mut self, func: F) -> Result<(), E> + where + F: FnOnce(&mut World, &mut Events) -> Result, + T: 'static + Module, + { + let module = func(&mut self.world, &mut self.events)?; + self.modules.push(module); + Ok(()) + } + + /// Inserts a module from the provided init closure to to the top of the module stack. The provided + /// closure to construct the module takes in the world and events. + pub fn insert_module(&mut self, index: usize, func: F) + where + F: FnOnce(&mut World, &mut Events) -> T, + T: 'static + Module, + { + let module = func(&mut self.world, &mut self.events); + self.modules.insert(index, module); + } + + /// Pushes a module from the provided init closure to to the top of the module stack. The provided + /// closure to construct the module takes in the world and events, and may return an error which + /// is propagated to the callee. + pub fn try_insert_module(&mut self, index: usize, func: F) -> Result<(), E> + where + F: FnOnce(&mut World, &mut Events) -> Result, + T: 'static + Module, + { + let module = func(&mut self.world, &mut self.events)?; + self.modules.insert(index, module); + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +#[allow(dead_code)] +pub enum AppEvent { + Exit, +} + +impl Default for App { + fn default() -> Self { + Self::new() + } +} diff --git a/src/comp.rs b/src/comp.rs new file mode 100644 index 0000000..8b2efaf --- /dev/null +++ b/src/comp.rs @@ -0,0 +1,42 @@ +use flax::{component, BoxedSystem, EntityBorrow, Query, System}; +use winit::window::Window; + +component! { + pub window_width: f32, + pub window: Window, + pub counter: i32, + + pub resources, +} + +pub fn update_distance_system() -> BoxedSystem { + System::builder() + .with_name("update_distance") + .with_query( + Query::new((window_width().as_mut(), window(), counter().as_mut())).entity(resources()), + ) + .build(|mut query: EntityBorrow<_>| { + if let Ok((window_width, _window, counter)) = query.get() { + // println!("Win width: {window_width}"); + *(window_width as &mut f32) = *(counter as &mut i32) as f32; + *(counter as &mut i32) += 1; + } + }) + .boxed() +} + +pub fn log_window_system() -> BoxedSystem { + let query = Query::new((window_width(), window())).entity(resources()); + + System::builder() + .with_query(query) + .build(|mut q: EntityBorrow<_>| { + if let Ok((width, wind)) = q.get() { + println!("window id: {:?}", (wind as &Window).id()); + println!("Config changed width: {width}"); + } else { + println!("No config change"); + } + }) + .boxed() +} diff --git a/src/components/mod.rs b/src/components/mod.rs new file mode 100644 index 0000000..ff0a229 --- /dev/null +++ b/src/components/mod.rs @@ -0,0 +1,10 @@ +use std::sync::Arc; + +use specs::{Component, VecStorage}; +use winit::window::Window; + +#[derive(Component, Debug)] +#[storage(VecStorage)] +pub struct EntityWindow { + pub window: Arc, +} diff --git a/src/config/components.rs b/src/config/components.rs new file mode 100644 index 0000000..a1892ae --- /dev/null +++ b/src/config/components.rs @@ -0,0 +1,9 @@ +use flax::component; + +use super::Config; + +component! { + pub config: Config, + pub notify_file_event: notify::Event, + pub resources, +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..548e6e4 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,68 @@ +use flax::{Schedule, World}; +use notify::{Config as NotifyConfig, INotifyWatcher, RecommendedWatcher, RecursiveMode, Watcher}; +use serde::{Deserialize, Serialize}; +use std::env::current_dir; + +use crate::module::Module; + +use self::{components::{notify_file_event, resources}, systems::{read_config_system, read_notify_events_system}}; + +pub mod components; +pub mod systems; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub struct Config { + pub asset_path: String, +} + + +#[allow(dead_code)] +pub struct ConfigModule { + schedule: Schedule, + watcher: INotifyWatcher, + watcher_rx: std::sync::mpsc::Receiver>, +} + +impl ConfigModule { + pub fn new(_world: &mut World, _events: &mut crate::core::events::Events) -> Self { + let (tx, rx) = std::sync::mpsc::channel(); + let mut watcher = RecommendedWatcher::new(tx, NotifyConfig::default().with_poll_interval(std::time::Duration::from_secs(2))).unwrap(); + + watcher + .watch(¤t_dir().unwrap(), RecursiveMode::NonRecursive) + .unwrap(); + + let schedule = Schedule::builder() + .with_system(read_config_system()) + .with_system(read_notify_events_system()) + .build(); + + Self { + schedule, + watcher, + watcher_rx: rx, + } + } +} + +impl Module for ConfigModule { + fn on_update( + &mut self, + world: &mut World, + _events: &mut crate::core::events::Events, + _frame_time: std::time::Duration, + ) -> anyhow::Result<()> { + self.schedule.execute_par(world).unwrap(); + + if let Ok(event) = self.watcher_rx.recv() { + match event { + Ok(e) => { + world.set(resources(), notify_file_event(), e.clone()).unwrap(); + } + Err(e) => println!("Watcher error. {}", e), + } + } + + Ok(()) + } +} diff --git a/src/config/systems.rs b/src/config/systems.rs new file mode 100644 index 0000000..780a220 --- /dev/null +++ b/src/config/systems.rs @@ -0,0 +1,51 @@ +use std::{fs, path::Path}; + +use flax::{BoxedSystem, CommandBuffer, EntityBorrow, Query, System}; +use serde_lexpr::from_str; + +use super::{components::{config, notify_file_event, resources}, Config}; + +pub fn read_config_system() -> BoxedSystem { + let query = Query::new(notify_file_event()).entity(resources()); + System::builder() + .with_name("read_config") + .with_cmd_mut() + .with_query(query) + .build(|cmd: &mut CommandBuffer, mut q: EntityBorrow<_>| { + if let Ok(n_event) = q.get() { + if (n_event as ¬ify::Event).kind.is_modify() { + println!("file modified: {:?}", (n_event as ¬ify::Event).paths); + cmd.set(resources(), config(), read_engine_config()); + } + } + }) + .boxed() +} + +fn read_engine_config() -> Config { + let config_path = Path::new("engine_config.scm"); + + let config_file = fs::read_to_string(config_path).unwrap(); + let config: Config = from_str::(&config_file).expect("Failed to parse config file"); + + config +} + +pub fn read_notify_events_system() -> BoxedSystem { + let query = Query::new(config().as_mut()).entity(resources()); + System::builder() + .with_name("first_read_config") + .with_cmd_mut() + .with_query(query) + .build(|cmd: &mut CommandBuffer, mut q: EntityBorrow<_>| { + if let Ok(_config) = q.get() { + return; + } else { + println!("read_notify_events_system: config read"); + cmd.set(resources(), config(), read_engine_config()); + } + + std::thread::sleep(std::time::Duration::from_secs(3)); + }) + .boxed() +} diff --git a/src/core/events/dispatcher.rs b/src/core/events/dispatcher.rs new file mode 100644 index 0000000..dbf0cd8 --- /dev/null +++ b/src/core/events/dispatcher.rs @@ -0,0 +1,183 @@ +use std::sync::mpsc; + +use downcast_rs::{impl_downcast, Downcast}; +use parking_lot::Mutex; + +use super::Event; + +pub trait AnyEventDispatcher: 'static + Send + Sync + Downcast { + fn cleanup(&mut self); +} + +impl_downcast!(AnyEventDispatcher); + +pub trait AnyEventSender: 'static + Send + Sync + Downcast {} +impl_downcast!(AnyEventSender); + +/// Handles event dispatching for a single type of event +pub struct EventDispatcher { + subscribers: Vec>, + pub blocked: bool, +} + +impl Default for EventDispatcher +where + T: Event + Clone, +{ + fn default() -> Self { + EventDispatcher::new() + } +} + +impl EventDispatcher +where + T: Event + Clone, +{ + pub fn new() -> Self { + Self { + subscribers: Vec::new(), + blocked: false, + } + } + + /// Sends an event to all subscribed subscriber. Event is cloned for each registered subscriber. Requires mutable access to cleanup no longer active subscribers. + pub fn send(&self, event: T) { + if self.blocked { + return; + } + + for subscriber in &self.subscribers { + if (subscriber.filter)(&event) { + subscriber.send(event.clone()); + } + } + } + + /// Subscribes to events using sender to send events. The subscriber is automatically cleaned + /// up when the receiving end is dropped. + pub fn subscribe(&mut self, sender: S, filter: fn(&T) -> bool) + where + S: 'static + EventSender + Send, + { + self.subscribers.push(Subscriber::new(sender, filter)); + } +} + +impl AnyEventDispatcher for EventDispatcher { + fn cleanup(&mut self) { + self.subscribers.retain(|val| !val.sender.is_disconnected()) + } +} + +struct Subscriber { + sender: Box + Send>, + filter: fn(&T) -> bool, +} + +impl Subscriber { + pub fn new(sender: S, filter: fn(&T) -> bool) -> Self + where + S: 'static + EventSender + Send, + { + Self { + sender: Box::new(sender), + filter, + } + } + pub fn send(&self, event: T) { + self.sender.send(event) + } +} + +/// Describes a type which can send events. Implemented for mpsc::channel and crossbeam channel. +pub trait EventSender: 'static + Send + Sync { + /// Send an event + fn send(&self, event: T); + /// Returns true if the sender has been disconnected + fn is_disconnected(&self) -> bool; +} + +/// Wrapper for thread safe sender +pub struct MpscSender { + inner: Mutex<(bool, mpsc::Sender)>, +} + +impl From> for MpscSender { + fn from(val: mpsc::Sender) -> Self { + Self::new(val) + } +} + +impl MpscSender { + pub fn new(inner: mpsc::Sender) -> Self { + Self { + inner: Mutex::new((false, inner)), + } + } +} + +impl EventSender for MpscSender { + fn send(&self, event: T) { + let mut inner = self.inner.lock(); + match inner.1.send(event) { + Ok(_) => {} + Err(_) => inner.0 = true, + } + } + + fn is_disconnected(&self) -> bool { + // TODO + self.inner.lock().0 + // self.inner.is_disconnected() + } +} + +#[cfg(feature = "crossbeam-channel")] +impl EventSender for crossbeam_channel::Sender { + fn send(&self, event: T) -> bool { + let _ = self.send(event); + } + + fn is_disconnected(&self) -> bool { + self.is_disconnected + } +} + +impl EventSender for flume::Sender { + fn send(&self, event: T) { + let _ = self.send(event); + } + + fn is_disconnected(&self) -> bool { + self.is_disconnected() + } +} + +pub fn new_event_dispatcher() -> Box { + let dispatcher: EventDispatcher = EventDispatcher::new(); + Box::new(dispatcher) +} + +pub struct ConcreteSender { + inner: Box>, +} + +impl ConcreteSender { + pub fn new>(sender: S) -> Self { + Self { + inner: Box::new(sender), + } + } +} + +impl EventSender for ConcreteSender { + fn send(&self, event: T) { + self.inner.send(event) + } + + fn is_disconnected(&self) -> bool { + self.inner.is_disconnected() + } +} + +impl AnyEventSender for ConcreteSender {} diff --git a/src/core/events/mod.rs b/src/core/events/mod.rs new file mode 100644 index 0000000..df8a4c3 --- /dev/null +++ b/src/core/events/mod.rs @@ -0,0 +1,208 @@ +mod dispatcher; +pub use dispatcher::EventSender; + +use std::{ + any::{type_name, TypeId}, + collections::HashMap, + error::Error, + fmt::Display, +}; + +use self::dispatcher::{ + new_event_dispatcher, AnyEventDispatcher, AnyEventSender, ConcreteSender, EventDispatcher, +}; + +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct AlreadyIntercepted { + ty: &'static str, +} + +impl Display for AlreadyIntercepted { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Events of type {:?} have already been intercepted", + self.ty + ) + } +} + +impl Error for AlreadyIntercepted {} + +/// Manages event broadcasting for different types of events. +/// Sending an event will send a clone of the event to all subscribed listeners. +/// +/// The event listeners can be anything implementing `EventSender`. Implemented by `std::sync::mpsc::Sender`, +/// `flume::Sender`, `crossbeam_channel::Sender`. +/// +/// # Example +/// ``` +/// use ivy_base::Events; +/// use std::sync::mpsc; +/// let mut events = Events::new(); +/// +/// let (tx1, rx1) = mpsc::channel::<&'static str>(); +/// events.subscribe(tx1); +/// +/// let (tx2, rx2) = mpsc::channel::<&'static str>(); +/// events.subscribe(tx2); +/// +/// events.send("Hello"); +/// +/// if let Ok(e) = rx1.try_recv() { +/// println!("1 Received: {}", e); +/// } +/// +/// if let Ok(e) = rx2.try_recv() { +/// println!("2 Received: {}", e); +/// } +/// ``` +pub struct Events { + dispatchers: HashMap>, + // A single receiver to intercept events + intercepts: HashMap>, +} + +impl Events { + pub fn new() -> Events { + Self { + dispatchers: HashMap::new(), + intercepts: HashMap::new(), + } + } + + /// Returns the internal dispatcher for the specified event type. + pub fn dispatcher(&self) -> Option<&EventDispatcher> { + self.dispatchers.get(&TypeId::of::()).map(|val| { + val.downcast_ref::>() + .expect("Failed to downcast") + }) + } + + /// Returns the internal dispatcher for the specified event type. + pub fn dispatcher_mut(&mut self) -> &mut EventDispatcher { + self.dispatchers + .entry(TypeId::of::()) + .or_insert_with(new_event_dispatcher::) + .downcast_mut::>() + .expect("Failed to downcast") + } + + /// Sends an event of type `T` to all subscribed listeners. + /// If no dispatcher exists for event `T`, a new one will be created. + pub fn send(&self, event: T) { + if let Some(intercept) = self.intercepts.get(&TypeId::of::()) { + intercept + .downcast_ref::>() + .unwrap() + .send(event); + } else if let Some(dispatcher) = self.dispatcher() { + dispatcher.send(event) + } + } + + /// Send an event after intercept, this function avoids intercepts. + /// It can also be useful if the message is not supposed to be intercepted + pub fn intercepted_send(&self, event: T) { + if let Some(dispatcher) = self.dispatcher() { + dispatcher.send(event) + } + } + + /// Intercept an event before it is broadcasted. Use + /// `Events::intercepted_send` to send. + pub fn intercept>( + &mut self, + sender: S, + ) -> Result<(), AlreadyIntercepted> { + match self.intercepts.entry(TypeId::of::()) { + std::collections::hash_map::Entry::Occupied(_) => Err(AlreadyIntercepted { + ty: type_name::(), + }), + std::collections::hash_map::Entry::Vacant(entry) => { + entry.insert(Box::new(ConcreteSender::new(sender))); + Ok(()) + } + } + } + + /// Shorthand to subscribe using a flume channel. + pub fn subscribe(&mut self) -> flume::Receiver { + let (tx, rx) = flume::unbounded(); + + self.dispatcher_mut().subscribe(tx, |_| true); + dbg!(self.dispatchers.len()); + rx + } + /// Subscribes to an event of type T by sending events to the provided + /// channel + pub fn subscribe_custom(&mut self, sender: S) + where + S: 'static + EventSender + Send, + { + self.dispatcher_mut().subscribe(sender, |_| true) + } + + /// Subscribes to an event of type T by sending events to teh provided + /// channel + pub fn subscribe_filter(&mut self, sender: S, filter: fn(&T) -> bool) + where + S: EventSender, + { + self.dispatcher_mut().subscribe(sender, filter) + } + + /// Blocks all events of a certain type. All events sent will be silently + /// ignored. + pub fn block(&mut self, block: bool) { + self.dispatcher_mut::().blocked = block + } + + /// Return true if events of type T are blocked + pub fn is_blocked(&mut self) -> bool { + self.dispatcher_mut::().blocked + } + + /// Remove disconnected subscribers + pub fn cleanup(&mut self) { + for (_, dispatcher) in self.dispatchers.iter_mut() { + dispatcher.cleanup() + } + } +} + +impl Default for Events { + fn default() -> Self { + Self::new() + } +} + +// Blanket type for events. +pub trait Event: Send + Sync + 'static + Clone {} +impl Event for T {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn event_broadcast() { + let mut events = Events::new(); + + let (tx1, rx1) = flume::unbounded::<&'static str>(); + events.subscribe_custom(tx1); + + let (tx2, rx2) = flume::unbounded::<&'static str>(); + events.subscribe_custom(tx2); + + events.send("Hello"); + + if let Ok(e) = rx1.try_recv() { + assert_eq!(e, "Hello") + } + + if let Ok(e) = rx2.try_recv() { + assert_eq!(e, "Hello") + } + } +} diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 0000000..1d84135 --- /dev/null +++ b/src/core/mod.rs @@ -0,0 +1,2 @@ +pub mod events; +// pub mod render; diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/core/render/mod.rs @@ -0,0 +1 @@ + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..2c4d5eb --- /dev/null +++ b/src/main.rs @@ -0,0 +1,112 @@ +use app::App; +use config::ConfigModule; +use tokio::runtime::Builder; +use vulkano_util::{ + context::{VulkanoConfig, VulkanoContext}, + renderer::VulkanoWindowRenderer, + window::{VulkanoWindows, WindowDescriptor}, +}; +use winit::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoopBuilder}, +}; + +mod app; +mod config; +mod core; +mod module; + +fn main() { + let event_loop = EventLoopBuilder::new().build().unwrap(); + let context = VulkanoContext::new(VulkanoConfig::default()); + let mut windows = VulkanoWindows::default(); + + let runtime = Builder::new_multi_thread().enable_all().build().unwrap(); + let (event_tx, event_rx) = flume::unbounded(); + + runtime.block_on(async { + runtime.spawn(async move { + loop { + let _event = event_rx.recv_async().await.unwrap(); + // println!( + // "Tokio got event: {:?} on thread: {:?}", + // event, + // std::thread::current().id() + // ); + std::thread::sleep(std::time::Duration::from_secs(1)); + } + }); + }); + + let _id = windows.create_window( + &event_loop, + &context, + &WindowDescriptor { + title: "ztest".into(), + present_mode: vulkano::swapchain::PresentMode::Fifo, + ..Default::default() + }, + |_| {}, + ); + + let primary_window_renderer = windows.get_primary_renderer_mut().unwrap(); + let _gfx_queue = context.graphics_queue(); + + let mut app = App::new(); + app.push_module(ConfigModule::new); + + event_loop + .run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Poll); + if process_event(primary_window_renderer, &event, &mut app) { + elwt.exit(); + } + + event_tx.send(event.clone()).unwrap(); + }) + .unwrap(); +} + +pub fn process_event( + renderer: &mut VulkanoWindowRenderer, + event: &Event<()>, + app: &mut App, +) -> bool { + match &event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => { + return true; + } + Event::WindowEvent { + event: WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. }, + .. + } => renderer.resize(), + Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } => 'redraw: { + app.run().unwrap(); + + // Tasks for redrawing: + // 1. Update state based on events + // 2. Compute & Render + // 3. Reset input state + // 4. Update time & title + + // The rendering part goes here: + match renderer.window_size() { + [w, h] => { + // Skip this frame when minimized. + if w == 0.0 || h == 0.0 { + break 'redraw; + } + } + } + } + Event::AboutToWait => renderer.window().request_redraw(), + _ => (), + } + false +} diff --git a/src/module/mod.rs b/src/module/mod.rs new file mode 100644 index 0000000..230d662 --- /dev/null +++ b/src/module/mod.rs @@ -0,0 +1,64 @@ +use std::time::Duration; + +use anyhow::Result; +use flax::World; + +use crate::core::events::Events; + +pub trait Module { + fn on_update(&mut self, world: &mut World, events: &mut Events, frame_time: Duration) -> Result<()>; +} + +pub struct ModulesStack { + modules: Vec>, +} + +impl ModulesStack { + pub fn new() -> Self { + Self { modules: Vec::new() } + } + + pub fn iter(&self) -> std::slice::Iter> { + self.modules.iter() + } + + pub fn iter_mut(&mut self) -> std::slice::IterMut> { + self.modules.iter_mut() + } + + pub fn push(&mut self, layer: T) { + let layer = Box::new(layer); + self.modules.push(layer); + } + + pub fn insert(&mut self, index: usize, layer: T) { + let layer = Box::new(layer); + self.modules.insert(index, layer); + } +} + +impl Default for ModulesStack { + fn default() -> Self { + Self::new() + } +} + +impl<'a> IntoIterator for &'a ModulesStack { + type Item = &'a Box; + + type IntoIter = std::slice::Iter<'a, Box>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a> IntoIterator for &'a mut ModulesStack { + type Item = &'a mut Box; + + type IntoIter = std::slice::IterMut<'a, Box>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} diff --git a/src/render/mod.rs b/src/render/mod.rs new file mode 100644 index 0000000..c590599 --- /dev/null +++ b/src/render/mod.rs @@ -0,0 +1,19 @@ +use vulkano::device::DeviceFeatures; +use vulkano_util::context::{VulkanoConfig, VulkanoContext}; + +pub fn make_render_config() -> VulkanoConfig { + let device_features: DeviceFeatures = DeviceFeatures { + dynamic_rendering: true, + ..DeviceFeatures::empty() + }; + + VulkanoConfig { + device_features, + print_device_name: true, + ..Default::default() + } +} + +pub fn make_render_context() -> VulkanoContext { + VulkanoContext::new(make_render_config()) +} diff --git a/src/systems/mod.rs b/src/systems/mod.rs new file mode 100644 index 0000000..8ff4bbb --- /dev/null +++ b/src/systems/mod.rs @@ -0,0 +1,694 @@ +use std::{collections::HashMap, sync::Arc}; + +use super::components::EntityWindow; +use specs::prelude::*; +use vulkano::{ + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, + command_buffer::{ + allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, + CommandBufferUsage, RecordingCommandBuffer, RenderingAttachmentInfo, RenderingInfo, + }, + device::{ + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, + Queue, QueueCreateInfo, QueueFlags, + }, + image::{view::ImageView, Image, ImageUsage}, + instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, + memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, + pipeline::{ + graphics::{ + color_blend::{ColorBlendAttachmentState, ColorBlendState}, + input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, + subpass::PipelineRenderingCreateInfo, + vertex_input::{Vertex, VertexDefinition}, + viewport::{Viewport, ViewportState}, + GraphicsPipelineCreateInfo, + }, + layout::PipelineDescriptorSetLayoutCreateInfo, + DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, + }, + render_pass::{AttachmentLoadOp, AttachmentStoreOp}, + swapchain::{ + acquire_next_image, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, + }, + sync::{self, GpuFuture}, + Validated, Version, VulkanError, VulkanLibrary, +}; + +use winit::window::{Window, WindowId}; + +pub struct Render { + renderers: HashMap, + library: Arc, +} + +impl<'a> System<'a> for Render { + type SystemData = (Entities<'a>, ReadStorage<'a, EntityWindow>); + + fn run(&mut self, data: Self::SystemData) { + let (entities, windows) = data; + (&entities, &windows).join().for_each(|(_entity, window)| { + self.renderers + .entry(window.window.id()) + .or_insert_with(|| VkRender::new(self.library.clone(), window.window.clone())); + + self.renderers.values_mut().for_each(|rend| rend.render()); + window.window.request_redraw(); + }); + } + + fn setup(&mut self, world: &mut World) { + Self::SystemData::setup(world); + } +} + +impl Default for Render { + fn default() -> Self { + Self { + renderers: HashMap::new(), + library: VulkanLibrary::new().unwrap(), + } + } +} + +struct VkRender { + window: Arc, + device: Arc, + queue: Arc, + command_buffer_allocator: Arc, + viewport: Viewport, + vertex_buffer: Subbuffer<[MyVertex]>, + recreate_swapchain: bool, + swapchain: Arc, + previous_frame_end: Option>, + attachment_image_views: Vec>, + pipeline: Arc, +} + +impl VkRender { + pub fn new(library: Arc, window: Arc) -> Self { + println!("Created new renderer for window: {:?}", window.id()); + let required_extensions = Surface::required_extensions(&window).unwrap(); + + // Now creating the instance. + let instance = Instance::new( + library, + InstanceCreateInfo { + // Enable enumerating devices that use non-conformant Vulkan implementations. + // (e.g. MoltenVK) + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) + .unwrap(); + + let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); + + // Choose device extensions that we're going to use. In order to present images to a surface, + // we need a `Swapchain`, which is provided by the `khr_swapchain` extension. + let mut device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + + // We then choose which physical device to use. First, we enumerate all the available physical + // devices, then apply filters to narrow them down to those that can support our needs. + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| { + // For this example, we require at least Vulkan 1.3, or a device that has the + // `khr_dynamic_rendering` extension available. + p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering + }) + .filter(|p| { + // Some devices may not support the extensions or features that your application, or + // report properties and limits that are not sufficient for your application. These + // should be filtered out here. + p.supported_extensions().contains(&device_extensions) + }) + .filter_map(|p| { + // For each physical device, we try to find a suitable queue family that will execute + // our draw commands. + // + // Devices can provide multiple queues to run commands in parallel (for example a draw + // queue and a compute queue), similar to CPU threads. This is something you have to + // have to manage manually in Vulkan. Queues of the same type belong to the same queue + // family. + // + // Here, we look for a single queue family that is suitable for our purposes. In a + // real-world application, you may want to use a separate dedicated transfer queue to + // handle data transfers in parallel with graphics operations. You may also need a + // separate queue for compute operations, if your application uses those. + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + // We select a queue family that supports graphics operations. When drawing to + // a window surface, as we do in this example, we also need to check that + // queues in this queue family are capable of presenting images to the surface. + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.surface_support(i as u32, &surface).unwrap_or(false) + }) + // The code here searches for the first queue family that is suitable. If none is + // found, `None` is returned to `filter_map`, which disqualifies this physical + // device. + .map(|i| (p, i as u32)) + }) + // All the physical devices that pass the filters above are suitable for the application. + // However, not every device is equal, some are preferred over others. Now, we assign each + // physical device a score, and pick the device with the lowest ("best") score. + // + // In this example, we simply select the best-scoring device to use in the application. + // In a real-world setting, you may want to use the best-scoring device only as a "default" + // or "recommended" device, and let the user choose the device themself. + .min_by_key(|(p, _)| { + // We assign a lower score to device types that are likely to be faster/better. + match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + } + }) + .expect("no suitable physical device found"); + + if physical_device.api_version() < Version::V1_3 { + device_extensions.khr_dynamic_rendering = true; + } + + // Now initializing the device. This is probably the most important object of Vulkan. + // + // An iterator of created queues is returned by the function alongside the device. + let (device, mut queues) = Device::new( + // Which physical device to connect to. + physical_device, + DeviceCreateInfo { + // The list of queues that we are going to use. Here we only use one queue, from the + // previously chosen queue family. + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + + // A list of optional features and extensions that our program needs to work correctly. + // Some parts of the Vulkan specs are optional and must be enabled manually at device + // creation. In this example the only things we are going to need are the + // `khr_swapchain` extension that allows us to draw to a window, and + // `khr_dynamic_rendering` if we don't have Vulkan 1.3 available. + enabled_extensions: device_extensions, + + // In order to render with Vulkan 1.3's dynamic rendering, we need to enable it here. + // Otherwise, we are only allowed to render with a render pass object, as in the + // standard triangle example. The feature is required to be supported by the device if + // it supports Vulkan 1.3 and higher, or if the `khr_dynamic_rendering` extension is + // available, so we don't need to check for support. + enabled_features: DeviceFeatures { + dynamic_rendering: true, + ..DeviceFeatures::empty() + }, + + ..Default::default() + }, + ) + .unwrap(); + + let queue = queues.next().unwrap(); + + // Before we can draw on the surface, we have to create what is called a swapchain. Creating a + // swapchain allocates the color buffers that will contain the image that will ultimately be + // visible on the screen. These images are returned alongside the swapchain. + let (mut swapchain, images) = { + // Querying the capabilities of the surface. When we create the swapchain we can only pass + // values that are allowed by the capabilities. + let surface_capabilities = device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + + // Choosing the internal format that the images will have. + let image_format = device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0; + + // Please take a look at the docs for the meaning of the parameters we didn't mention. + Swapchain::new( + device.clone(), + surface, + SwapchainCreateInfo { + // Some drivers report an `min_image_count` of 1, but fullscreen mode requires at + // least 2. Therefore we must ensure the count is at least 2, otherwise the program + // would crash when entering fullscreen mode on those drivers. + min_image_count: surface_capabilities.min_image_count.max(2), + + image_format, + + // The size of the window, only used to initially setup the swapchain. + // + // NOTE: + // On some drivers the swapchain extent is specified by + // `surface_capabilities.current_extent` and the swapchain size must use this + // extent. This extent is always the same as the window size. + // + // However, other drivers don't specify a value, i.e. + // `surface_capabilities.current_extent` is `None`. These drivers will allow + // anything, but the only sensible value is the window size. + // + // Both of these cases need the swapchain to use the window size, so we just + // use that. + image_extent: window.inner_size().into(), + + image_usage: ImageUsage::COLOR_ATTACHMENT, + + // The alpha mode indicates how the alpha value of the final image will behave. For + // example, you can choose whether the window will be opaque or transparent. + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + + ..Default::default() + }, + ) + .unwrap() + }; + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + + let vertices = [ + MyVertex { + position: [-0.5, -0.25, 0.1], + }, + MyVertex { + position: [0.0, 0.5, 0.1], + }, + MyVertex { + position: [0.25, -0.1, 0.1], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + layout(location = 0) in vec3 position; + + void main() { + gl_Position = vec4(position, 1.0); + } + ", + } + } + + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 + + layout(location = 0) out vec4 f_color; + + void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); + } + ", + } + } + + // At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL + // implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this + // manually. + + // Before we draw, we have to create what is called a **pipeline**. A pipeline describes how + // a GPU operation is to be performed. It is similar to an OpenGL program, but it also contains + // many settings for customization, all baked into a single object. For drawing, we create + // a **graphics** pipeline, but there are also other types of pipeline. + let pipeline = { + // First, we load the shaders that the pipeline will use: + // the vertex shader and the fragment shader. + // + // A Vulkan shader can in theory contain multiple entry points, so we have to specify which + // one. + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + + // Automatically generate a vertex input state from the vertex shader's input interface, + // that takes a single vertex buffer containing `Vertex` structs. + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + + // Make a list of the shader stages that the pipeline will have. + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + + // We must now create a **pipeline layout** object, which describes the locations and types of + // descriptor sets and push constants used by the shaders in the pipeline. + // + // Multiple pipelines can share a common layout object, which is more efficient. + // The shaders in a pipeline must use a subset of the resources described in its pipeline + // layout, but the pipeline layout is allowed to contain resources that are not present in the + // shaders; they can be used by shaders in other pipelines that share the same layout. + // Thus, it is a good idea to design shaders so that many pipelines have common resource + // locations, which allows them to share pipeline layouts. + let layout = PipelineLayout::new( + device.clone(), + // Since we only have one pipeline in this example, and thus one pipeline layout, + // we automatically generate the creation info for it from the resources used in the + // shaders. In a real application, you would specify this information manually so that you + // can re-use one layout in multiple pipelines. + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(device.clone()) + .unwrap(), + ) + .unwrap(); + + // We describe the formats of attachment images where the colors, depth and/or stencil + // information will be written. The pipeline will only be usable with this particular + // configuration of the attachment images. + let subpass = PipelineRenderingCreateInfo { + // We specify a single color attachment that will be rendered to. When we begin + // rendering, we will specify a swapchain image to be used as this attachment, so here + // we set its format to be the same format as the swapchain. + color_attachment_formats: vec![Some(swapchain.image_format())], + ..Default::default() + }; + + // Finally, create the pipeline. + GraphicsPipeline::new( + device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + // How vertex data is read from the vertex buffers into the vertex shader. + vertex_input_state: Some(vertex_input_state), + // How vertices are arranged into primitive shapes. + // The default primitive shape is a triangle. + input_assembly_state: Some(InputAssemblyState::default()), + // How primitives are transformed and clipped to fit the framebuffer. + // We use a resizable viewport, set to draw over the entire window. + viewport_state: Some(ViewportState::default()), + // How polygons are culled and converted into a raster of pixels. + // The default value does not perform any culling. + rasterization_state: Some(RasterizationState::default()), + // How multiple fragment shader samples are converted to a single pixel value. + // The default value does not perform any multisampling. + multisample_state: Some(MultisampleState::default()), + // How pixel values are combined with the values already present in the framebuffer. + // The default value overwrites the old value with the new one, without any blending. + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.color_attachment_formats.len() as u32, + ColorBlendAttachmentState::default(), + )), + // Dynamic states allows us to specify parts of the pipeline settings when + // recording the command buffer, before we perform drawing. + // Here, we specify that the viewport should be dynamic. + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap() + }; + + // Dynamic viewports allow us to recreate just the viewport when the window is resized. + // Otherwise we would have to recreate the whole pipeline. + let mut viewport = Viewport { + offset: [0.0, 0.0], + extent: [0.0, 0.0], + depth_range: 0.0..=1.0, + }; + + // When creating the swapchain, we only created plain images. To use them as an attachment for + // rendering, we must wrap then in an image view. + // + // Since we need to draw to multiple images, we are going to create a different image view for + // each image. + let mut attachment_image_views = window_size_dependent_setup(&images, &mut viewport); + + // Before we can start creating and recording command buffers, we need a way of allocating + // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools + // underneath and provides a safe interface for them. + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + // Initialization is finally finished! + + // In some situations, the swapchain will become invalid by itself. This includes for example + // when the window is resized (as the images of the swapchain will no longer match the + // window's) or, on Android, when the application went to the background and goes back to the + // foreground. + // + // In this situation, acquiring a swapchain image or presenting it will return an error. + // Rendering to an image of that swapchain will not produce any error, but may or may not work. + // To continue rendering, we need to recreate the swapchain by creating a new swapchain. Here, + // we remember that we need to do this for the next loop iteration. + let mut recreate_swapchain = false; + + // In the loop below we are going to submit commands to the GPU. Submitting a command produces + // an object that implements the `GpuFuture` trait, which holds the resources for as long as + // they are in use by the GPU. + // + // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid + // that, we store the submission of the previous frame here. + let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); + + Self { + window, + device, + queue, + command_buffer_allocator, + viewport, + vertex_buffer, + recreate_swapchain, + swapchain, + previous_frame_end, + attachment_image_views, + pipeline, + } + } + + pub fn render(&mut self) { + // Do not draw the frame when the screen size is zero. On Windows, this can + // occur when minimizing the application. + let image_extent: [u32; 2] = self.window.inner_size().into(); + + if image_extent.contains(&0) { + return; + } + + // It is important to call this function from time to time, otherwise resources + // will keep accumulating and you will eventually reach an out of memory error. + // Calling this function polls various fences in order to determine what the GPU + // has already processed, and frees the resources that are no longer needed. + self.previous_frame_end.as_mut().unwrap().cleanup_finished(); + + // Whenever the window resizes we need to recreate everything dependent on the + // window size. In this example that includes the swapchain, the framebuffers and + // the dynamic state viewport. + if self.recreate_swapchain { + let (new_swapchain, new_images) = self + .swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..self.swapchain.create_info() + }) + .expect("failed to recreate swapchain"); + + self.swapchain = new_swapchain; + + // Now that we have new swapchain images, we must create new image views from + // them as well. + self.attachment_image_views = + window_size_dependent_setup(&new_images, &mut self.viewport); + + self.recreate_swapchain = false; + } + + // Before we can draw on the output, we have to *acquire* an image from the + // swapchain. If no image is available (which happens if you submit draw commands + // too quickly), then the function will block. This operation returns the index of + // the image that we are allowed to draw upon. + // + // This function can block if no image is available. The parameter is an optional + // timeout after which the function call will return an error. + let (image_index, suboptimal, acquire_future) = + match acquire_next_image(self.swapchain.clone(), None).map_err(Validated::unwrap) { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + self.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; + + // `acquire_next_image` can be successful, but suboptimal. This means that the + // swapchain image will still work, but it may not display correctly. With some + // drivers this can be when the window resizes, but it may not cause the swapchain + // to become out of date. + if suboptimal { + self.recreate_swapchain = true; + } + + // In order to draw, we have to build a *command buffer*. The command buffer object + // holds the list of commands that are going to be executed. + // + // Building a command buffer is an expensive operation (usually a few hundred + // microseconds), but it is known to be a hot path in the driver and is expected to + // be optimized. + // + // Note that we have to pass a queue family when we create the command buffer. The + // command buffer will only be executable on that given queue family. + let mut builder = RecordingCommandBuffer::new( + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), + CommandBufferLevel::Primary, + CommandBufferBeginInfo { + usage: CommandBufferUsage::OneTimeSubmit, + ..Default::default() + }, + ) + .unwrap(); + + builder + // Before we can draw, we have to *enter a render pass*. We specify which + // attachments we are going to use for rendering here, which needs to match + // what was previously specified when creating the pipeline. + .begin_rendering(RenderingInfo { + // As before, we specify one color attachment, but now we specify the image + // view to use as well as how it should be used. + color_attachments: vec![Some(RenderingAttachmentInfo { + // `Clear` means that we ask the GPU to clear the content of this + // attachment at the start of rendering. + load_op: AttachmentLoadOp::Clear, + // `Store` means that we ask the GPU to store the rendered output in + // the attachment image. We could also ask it to discard the result. + store_op: AttachmentStoreOp::Store, + // The value to clear the attachment with. Here we clear it with a blue + // color. + // + // Only attachments that have `AttachmentLoadOp::Clear` are provided + // with clear values, any others should use `None` as the clear value. + clear_value: Some([0.0, 0.0, 1.0, 1.0].into()), + ..RenderingAttachmentInfo::image_view( + // We specify image view corresponding to the currently acquired + // swapchain image, to use for this attachment. + self.attachment_image_views[image_index as usize].clone(), + ) + })], + ..Default::default() + }) + .unwrap() + // We are now inside the first subpass of the render pass. + // + // TODO: Document state setting and how it affects subsequent draw commands. + .set_viewport(0, [self.viewport.clone()].into_iter().collect()) + .unwrap() + .bind_pipeline_graphics(self.pipeline.clone()) + .unwrap() + .bind_vertex_buffers(0, self.vertex_buffer.clone()) + .unwrap(); + + unsafe { + builder + // We add a draw command. + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) + .unwrap(); + } + + builder + // We leave the render pass. + .end_rendering() + .unwrap(); + + // Finish recording the command buffer by calling `end`. + let command_buffer = builder.end().unwrap(); + + let future = self + .previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(self.queue.clone(), command_buffer) + .unwrap() + // The color output is now expected to contain our triangle. But in order to + // show it on the screen, we have to *present* the image by calling + // `then_swapchain_present`. + // + // This function does not actually present the image immediately. Instead it + // submits a present command at the end of the queue. This means that it will + // only be presented once the GPU has finished executing the command buffer + // that draws the triangle. + .then_swapchain_present( + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(self.swapchain.clone(), image_index), + ) + .then_signal_fence_and_flush(); + + match future.map_err(Validated::unwrap) { + Ok(future) => { + self.previous_frame_end = Some(future.boxed()); + } + Err(VulkanError::OutOfDate) => { + self.recreate_swapchain = true; + self.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + } + Err(e) => { + println!("failed to flush future: {e}"); + self.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + } + } + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32B32_SFLOAT)] + position: [f32; 3], +} + +fn window_size_dependent_setup( + images: &[Arc], + viewport: &mut Viewport, +) -> Vec> { + let extent = images[0].extent(); + viewport.extent = [extent[0] as f32, extent[1] as f32]; + + images + .iter() + .map(|image| ImageView::new_default(image.clone()).unwrap()) + .collect::>() +}