From 9cfd9d8b179a9ce15dc2f962dc7f59d6a063d071 Mon Sep 17 00:00:00 2001 From: zack Date: Wed, 26 Mar 2025 20:02:58 -0400 Subject: [PATCH] new architecture --- Cargo.lock | 2345 ++++--------------------- Cargo.toml | 18 +- crates/engine/Cargo.toml | 18 + crates/engine/src/main.rs | 5 + crates/engine/src/scene_data.rs | 67 + crates/gfx_hal/Cargo.toml | 12 + crates/gfx_hal/src/device.rs | 223 +++ crates/gfx_hal/src/error.rs | 64 + crates/gfx_hal/src/instance.rs | 353 ++++ crates/gfx_hal/src/lib.rs | 8 + crates/gfx_hal/src/physical_device.rs | 164 ++ crates/gfx_hal/src/queue.rs | 102 ++ crates/gfx_hal/src/surface.rs | 126 ++ crates/gfx_hal/src/swapchain.rs | 321 ++++ crates/gfx_hal/src/sync.rs | 183 ++ crates/resource_manager/Cargo.toml | 13 + crates/resource_manager/src/error.rs | 48 + crates/resource_manager/src/lib.rs | 609 +++++++ crates/shaders-shared/Cargo.toml | 8 - crates/shaders-shared/src/lib.rs | 31 - crates/vk-rs/Cargo.toml | 33 - crates/vk-rs/build.rs | 71 - crates/vk-rs/src/main.rs | 722 -------- crates/vk-rs/src/renderer.rs | 2207 ----------------------- crates/vk-rs/src/texture_cache.rs | 46 - flake.nix | 13 +- shaders/main.frag.glsl | 134 -- shaders/main.vert.glsl | 32 - 28 files changed, 2625 insertions(+), 5351 deletions(-) create mode 100644 crates/engine/Cargo.toml create mode 100644 crates/engine/src/main.rs create mode 100644 crates/engine/src/scene_data.rs create mode 100644 crates/gfx_hal/Cargo.toml create mode 100644 crates/gfx_hal/src/device.rs create mode 100644 crates/gfx_hal/src/error.rs create mode 100644 crates/gfx_hal/src/instance.rs create mode 100644 crates/gfx_hal/src/lib.rs create mode 100644 crates/gfx_hal/src/physical_device.rs create mode 100644 crates/gfx_hal/src/queue.rs create mode 100644 crates/gfx_hal/src/surface.rs create mode 100644 crates/gfx_hal/src/swapchain.rs create mode 100644 crates/gfx_hal/src/sync.rs create mode 100644 crates/resource_manager/Cargo.toml create mode 100644 crates/resource_manager/src/error.rs create mode 100644 crates/resource_manager/src/lib.rs delete mode 100644 crates/shaders-shared/Cargo.toml delete mode 100644 crates/shaders-shared/src/lib.rs delete mode 100644 crates/vk-rs/Cargo.toml delete mode 100644 crates/vk-rs/build.rs delete mode 100644 crates/vk-rs/src/main.rs delete mode 100644 crates/vk-rs/src/renderer.rs delete mode 100644 crates/vk-rs/src/texture_cache.rs delete mode 100644 shaders/main.frag.glsl delete mode 100644 shaders/main.vert.glsl diff --git a/Cargo.lock b/Cargo.lock index 4146bfc..1bcd1f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,27 +18,6 @@ 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 = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - [[package]] name = "ahash" version = "0.8.11" @@ -52,33 +31,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "aligned-vec" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" - -[[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.6.0", - "cc", - "cesu8", - "jni", - "jni-sys", - "libc", - "log", - "ndk 0.8.0", - "ndk-context", - "ndk-sys 0.5.0+25.2.9519653", - "num_enum", - "thiserror", -] - [[package]] name = "android-activity" version = "0.6.0" @@ -86,18 +38,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.6.0", + "bitflags 2.9.0", "cc", "cesu8", "jni", "jni-sys", "libc", "log", - "ndk 0.9.0", + "ndk", "ndk-context", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -106,53 +58,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" -[[package]] -name = "anyhow" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" - -[[package]] -name = "approx" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" -dependencies = [ - "num-traits", -] - -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" - -[[package]] -name = "arboard" -version = "3.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" -dependencies = [ - "clipboard-win", - "log", - "objc2 0.5.2", - "objc2-app-kit", - "objc2-foundation", - "parking_lot", - "x11rb", -] - -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "arrayref" version = "0.3.9" @@ -173,21 +78,21 @@ checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" [[package]] name = "ash" -version = "0.37.3+1.3.251" +version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "libloading 0.7.4", + "libloading", ] [[package]] name = "ash-window" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b912285a7c29f3a8f87ca6f55afc48768624e5e33ec17dbd2f2075903f5e35ab" +checksum = "52bca67b61cb81e5553babde81b8211f713cb6db79766f80168f3e5f40ea6c82" dependencies = [ "ash", - "raw-window-handle 0.5.2", + "raw-window-handle", "raw-window-metal", ] @@ -203,65 +108,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" -[[package]] -name = "av1-grain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "backtrace" -version = "0.3.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide 0.7.4", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - [[package]] name = "bitflags" version = "1.3.2" @@ -270,15 +116,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "bitstream-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "block" @@ -286,51 +126,26 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[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 0.4.1", -] - [[package]] name = "block2" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "objc2 0.5.2", + "objc2", ] -[[package]] -name = "built" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b" - [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.21.0" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" dependencies = [ "bytemuck_derive", ] @@ -346,37 +161,11 @@ dependencies = [ "syn", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" - -[[package]] -name = "calloop" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" -dependencies = [ - "bitflags 2.6.0", - "log", - "polling", - "rustix", - "slab", - "thiserror", -] +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "calloop" @@ -384,24 +173,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "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 0.12.4", - "rustix", - "wayland-backend", - "wayland-client", + "thiserror 1.0.69", ] [[package]] @@ -410,7 +187,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "calloop 0.13.0", + "calloop", "rustix", "wayland-backend", "wayland-client", @@ -418,9 +195,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.6" +version = "1.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" dependencies = [ "jobserver", "libc", @@ -433,62 +210,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cfg-expr" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" -dependencies = [ - "smallvec", - "target-lexicon", -] - [[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 = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "cgmath" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" -dependencies = [ - "approx", - "num-traits", -] - -[[package]] -name = "clipboard-win" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" -dependencies = [ - "error-code", -] - -[[package]] -name = "cmake" -version = "0.1.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" -dependencies = [ - "cc", -] - [[package]] name = "cocoa" version = "0.25.0" @@ -519,39 +252,6 @@ dependencies = [ "objc", ] -[[package]] -name = "color-eyre" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" -dependencies = [ - "backtrace", - "color-spantrace", - "eyre", - "indenter", - "once_cell", - "owo-colors", - "tracing-error", -] - -[[package]] -name = "color-spantrace" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" -dependencies = [ - "once_cell", - "owo-colors", - "tracing-core", - "tracing-error", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "combine" version = "4.6.7" @@ -611,85 +311,31 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -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-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[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 = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - [[package]] name = "dispatch" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "dlib" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.6", + "libloading", ] [[package]] @@ -705,95 +351,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" [[package]] -name = "ecolor" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57539aabcdbb733b6806ef421b66dec158dc1582107ad6d51913db3600303354" +name = "engine" +version = "0.1.0" dependencies = [ - "bytemuck", -] - -[[package]] -name = "egui" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0bf640ed7f3bf3d14ebf00d73bacc09c886443ee84ca6494bde37953012c9e3" -dependencies = [ - "ahash", - "epaint", - "log", - "nohash-hasher", -] - -[[package]] -name = "egui-ash" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba40c0bd1cb4540c7f688b8898f2c60194dbd9e2ea3400424a33b28b26a0ef16" -dependencies = [ - "anyhow", "ash", - "ash-window", "bytemuck", - "egui", - "egui-winit", + "gfx_hal", + "glam", "gpu-allocator", - "log", - "raw-window-handle 0.5.2", -] - -[[package]] -name = "egui-winit" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d95d9762056c541bd2724de02910d8bccf3af8e37689dc114b21730e64f80a0" -dependencies = [ - "arboard", - "egui", - "log", - "raw-window-handle 0.5.2", - "smithay-clipboard", - "web-time 0.2.4", - "webbrowser", - "winit 0.29.15", -] - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "emath" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee58355767587db7ba3738930d93cad3052cd834c2b48b9ef6ef26fe4823b7e" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "epaint" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e638cb066bff0903bbb6143116cfd134a42279c7d68f19c0352a94f15a402de7" -dependencies = [ - "ab_glyph", - "ahash", - "bytemuck", - "ecolor", - "emath", - "log", - "nohash-hasher", - "parking_lot", + "raw-window-handle", + "resource_manager", + "thiserror 2.0.12", + "tracing", + "tracing-subscriber", + "winit", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -805,56 +383,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "error-code" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" - -[[package]] -name = "exr" -version = "1.73.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" -dependencies = [ - "bit_field", - "half", - "lebe", - "miniz_oxide 0.8.2", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "eyre" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "flate2" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" -dependencies = [ - "crc32fast", - "miniz_oxide 0.8.2", -] - [[package]] name = "foreign-types" version = "0.5.0" @@ -882,15 +410,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - [[package]] name = "gethostname" version = "0.4.3" @@ -913,367 +432,62 @@ dependencies = [ ] [[package]] -name = "gif" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +name = "gfx_hal" +version = "0.1.0" dependencies = [ - "color_quant", - "weezl", + "ash", + "ash-window", + "parking_lot", + "thiserror 2.0.12", + "tracing", + "winit", ] -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - [[package]] name = "glam" -version = "0.29.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677" +checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774" dependencies = [ - "libm", -] - -[[package]] -name = "gltf" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ce1918195723ce6ac74e80542c5a96a40c2b26162c1957a5cd70799b8cacf7" -dependencies = [ - "base64", - "byteorder", - "gltf-json", - "image", - "lazy_static", - "serde_json", - "urlencoding", -] - -[[package]] -name = "gltf-derive" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14070e711538afba5d6c807edb74bcb84e5dbb9211a3bf5dea0dfab5b24f4c51" -dependencies = [ - "inflections", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "gltf-json" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6176f9d60a7eab0a877e8e96548605dedbde9190a7ae1e80bbcc1c9af03ab14" -dependencies = [ - "gltf-derive", - "serde", - "serde_derive", - "serde_json", + "bytemuck", + "num-traits", ] [[package]] name = "gpu-allocator" -version = "0.25.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" dependencies = [ "ash", "log", "presser", - "thiserror", + "thiserror 1.0.69", "windows", ] -[[package]] -name = "gpu-profiler" -version = "0.1.0" -source = "git+https://github.com/zackartz/gpu-profiler#cb5b9d74a9ea25d1f27e4be7949403296e351370" -dependencies = [ - "ash", - "once_cell", - "puffin", -] - -[[package]] -name = "half" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" -dependencies = [ - "cfg-if", - "crunchy", -] - [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "hermit-abi" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" -[[package]] -name = "home" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "icrate" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" -dependencies = [ - "block2 0.3.0", - "dispatch", - "objc2 0.4.1", -] - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "image" -version = "0.25.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" -dependencies = [ - "bytemuck", - "byteorder-lite", - "color_quant", - "exr", - "gif", - "image-webp", - "num-traits", - "png", - "qoi", - "ravif", - "rayon", - "rgb", - "tiff", - "zune-core", - "zune-jpeg", -] - -[[package]] -name = "image-webp" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" -dependencies = [ - "byteorder-lite", - "quick-error", -] - -[[package]] -name = "imgref" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - [[package]] name = "indexmap" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", - "serde", ] -[[package]] -name = "inflections" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" - -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" - [[package]] name = "jni" version = "0.21.1" @@ -1285,7 +499,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -1305,17 +519,11 @@ dependencies = [ "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" - [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -1327,37 +535,11 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "lebe" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" - [[package]] name = "libc" -version = "0.2.169" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" - -[[package]] -name = "libfuzzer-sys" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" -dependencies = [ - "arbitrary", - "cc", -] - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libloading" @@ -1381,22 +563,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.10", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "litemap" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "lock_api" @@ -1410,24 +586,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "loop9" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] - -[[package]] -name = "lz4_flex" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "malloc_buf" @@ -1438,16 +599,6 @@ dependencies = [ "libc", ] -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - [[package]] name = "memchr" version = "2.7.4" @@ -1463,66 +614,19 @@ 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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "natord" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c" - -[[package]] -name = "ndk" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" -dependencies = [ - "bitflags 2.6.0", - "jni-sys", - "log", - "ndk-sys 0.5.0+25.2.9519653", - "num_enum", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", - "thiserror", -] - [[package]] name = "ndk" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "jni-sys", "log", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", - "raw-window-handle 0.6.2", - "thiserror", + "raw-window-handle", + "thiserror 1.0.69", ] [[package]] @@ -1531,15 +635,6 @@ 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 = "ndk-sys" version = "0.6.0+11769913" @@ -1549,34 +644,6 @@ dependencies = [ "jni-sys", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "nohash-hasher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" - -[[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 = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1587,53 +654,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1680,16 +700,6 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" -[[package]] -name = "objc2" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" -dependencies = [ - "objc-sys", - "objc2-encode 3.0.0", -] - [[package]] name = "objc2" version = "0.5.2" @@ -1697,7 +707,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ "objc-sys", - "objc2-encode 4.0.3", + "objc2-encode", ] [[package]] @@ -1706,10 +716,10 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", + "bitflags 2.9.0", + "block2", "libc", - "objc2 0.5.2", + "objc2", "objc2-core-data", "objc2-core-image", "objc2-foundation", @@ -1722,9 +732,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "bitflags 2.9.0", + "block2", + "objc2", "objc2-core-location", "objc2-foundation", ] @@ -1735,8 +745,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", ] @@ -1746,9 +756,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "bitflags 2.9.0", + "block2", + "objc2", "objc2-foundation", ] @@ -1758,8 +768,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", "objc2-metal", ] @@ -1770,23 +780,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-contacts", "objc2-foundation", ] [[package]] name = "objc2-encode" -version = "3.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" - -[[package]] -name = "objc2-encode" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] name = "objc2-foundation" @@ -1794,11 +798,11 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", + "bitflags 2.9.0", + "block2", "dispatch", "libc", - "objc2 0.5.2", + "objc2", ] [[package]] @@ -1807,8 +811,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-app-kit", "objc2-foundation", ] @@ -1819,9 +823,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "bitflags 2.9.0", + "block2", + "objc2", "objc2-foundation", ] @@ -1831,9 +835,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "bitflags 2.9.0", + "block2", + "objc2", "objc2-foundation", "objc2-metal", ] @@ -1844,7 +848,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" dependencies = [ - "objc2 0.5.2", + "objc2", "objc2-foundation", ] @@ -1854,9 +858,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "bitflags 2.9.0", + "block2", + "objc2", "objc2-cloud-kit", "objc2-core-data", "objc2-core-image", @@ -1875,8 +879,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", ] @@ -1886,27 +890,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "bitflags 2.9.0", + "block2", + "objc2", "objc2-core-location", "objc2-foundation", ] -[[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.20.2" +version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" [[package]] name = "orbclient" @@ -1932,12 +927,6 @@ dependencies = [ "ttf-parser", ] -[[package]] -name = "owo-colors" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" - [[package]] name = "parking_lot" version = "0.12.3" @@ -1956,17 +945,11 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.10", "smallvec", "windows-targets 0.52.6", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "percent-encoding" version = "2.3.1" @@ -1975,18 +958,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", @@ -1995,28 +978,15 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide 0.8.2", -] +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "polling" @@ -2033,21 +1003,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - [[package]] name = "presser" version = "0.3.1" @@ -2056,191 +1011,40 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] -[[package]] -name = "profiling" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "puffin" -version = "0.19.0" -source = "git+https://github.com/EmbarkStudios/puffin?rev=5ac4e54164ee89bd68c29f288c2b5613afc2c929#5ac4e54164ee89bd68c29f288c2b5613afc2c929" -dependencies = [ - "anyhow", - "bincode", - "byteorder", - "cfg-if", - "lz4_flex", - "once_cell", - "parking_lot", - "serde", -] - -[[package]] -name = "puffin_egui" -version = "0.25.0" -source = "git+https://github.com/EmbarkStudios/puffin?rev=5ac4e54164ee89bd68c29f288c2b5613afc2c929#5ac4e54164ee89bd68c29f288c2b5613afc2c929" -dependencies = [ - "egui", - "indexmap", - "natord", - "once_cell", - "parking_lot", - "puffin", - "time", - "vec1", - "web-time 0.2.4", -] - -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quick-xml" -version = "0.36.2" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +checksum = "bf763ab1c7a3aa408be466efc86efe35ed1bd3dd74173ed39d6b0d0a6f0ba148" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rav1e" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" -dependencies = [ - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "once_cell", - "paste", - "profiling", - "rand", - "rand_chacha", - "simd_helpers", - "system-deps", - "thiserror", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error", - "rav1e", - "rayon", - "rgb", -] - -[[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.2" @@ -2249,43 +1053,14 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "raw-window-metal" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac4ea493258d54c24cb46aa9345d099e58e2ea3f30dd63667fc54fc892f18e76" +checksum = "76e8caa82e31bb98fee12fa8f051c94a6aa36b07cddb03f0d4fc558988360ff1" dependencies = [ "cocoa", "core-graphics", "objc", - "raw-window-handle 0.5.2", -] - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -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", + "raw-window-handle", ] [[package]] @@ -2299,41 +1074,32 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", ] [[package]] -name = "rgb" -version = "0.8.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" - -[[package]] -name = "roxmltree" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +name = "resource_manager" +version = "0.1.0" dependencies = [ - "xmlparser", + "ash", + "gfx_hal", + "gpu-allocator", + "parking_lot", + "thiserror 2.0.12", + "tracing", ] -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", @@ -2341,10 +1107,10 @@ dependencies = [ ] [[package]] -name = "ryu" -version = "1.0.18" +name = "rustversion" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "same-file" @@ -2367,19 +1133,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sctk-adwaita" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7" -dependencies = [ - "ab_glyph", - "log", - "memmap2", - "smithay-client-toolkit 0.18.1", - "tiny-skia", -] - [[package]] name = "sctk-adwaita" version = "0.10.1" @@ -2389,80 +1142,30 @@ dependencies = [ "ab_glyph", "log", "memmap2", - "smithay-client-toolkit 0.19.2", + "smithay-client-toolkit", "tiny-skia", ] [[package]] name = "serde" -version = "1.0.216" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "serde_json" -version = "1.0.134" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "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 = "shaders-shared" -version = "0.1.0" -dependencies = [ - "bytemuck", - "spirv-std", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -2478,21 +1181,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - [[package]] name = "slab" version = "0.4.9" @@ -2504,34 +1192,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "smithay-client-toolkit" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" -dependencies = [ - "bitflags 2.6.0", - "calloop 0.12.4", - "calloop-wayland-source 0.2.0", - "cursor-icon", - "libc", - "log", - "memmap2", - "rustix", - "thiserror", - "wayland-backend", - "wayland-client", - "wayland-csd-frame", - "wayland-cursor", - "wayland-protocols 0.31.2", - "wayland-protocols-wlr 0.2.0", - "wayland-scanner", - "xkeysym", -] +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "smithay-client-toolkit" @@ -2539,36 +1202,25 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.6.0", - "calloop 0.13.0", - "calloop-wayland-source 0.3.0", + "bitflags 2.9.0", + "calloop", + "calloop-wayland-source", "cursor-icon", "libc", "log", "memmap2", "rustix", - "thiserror", + "thiserror 1.0.69", "wayland-backend", "wayland-client", "wayland-csd-frame", "wayland-cursor", - "wayland-protocols 0.32.5", - "wayland-protocols-wlr 0.3.5", + "wayland-protocols", + "wayland-protocols-wlr", "wayland-scanner", "xkeysym", ] -[[package]] -name = "smithay-clipboard" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" -dependencies = [ - "libc", - "smithay-client-toolkit 0.19.2", - "wayland-backend", -] - [[package]] name = "smol_str" version = "0.2.2" @@ -2578,40 +1230,6 @@ dependencies = [ "serde", ] -[[package]] -name = "spirv-std" -version = "0.9.0" -source = "git+https://github.com/Rust-GPU/rust-gpu.git#bfa63c15a921357b38aa78986670c15f36df76dc" -dependencies = [ - "bitflags 1.3.2", - "glam", - "num-traits", - "spirv-std-macros", - "spirv-std-types", -] - -[[package]] -name = "spirv-std-macros" -version = "0.9.0" -source = "git+https://github.com/Rust-GPU/rust-gpu.git#bfa63c15a921357b38aa78986670c15f36df76dc" -dependencies = [ - "proc-macro2", - "quote", - "spirv-std-types", - "syn", -] - -[[package]] -name = "spirv-std-types" -version = "0.9.0" -source = "git+https://github.com/Rust-GPU/rust-gpu.git#bfa63c15a921357b38aa78986670c15f36df76dc" - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "strict-num" version = "0.1.1" @@ -2620,52 +1238,31 @@ checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] name = "syn" -version = "2.0.92" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "system-deps" -version = "6.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" -dependencies = [ - "cfg-expr", - "heck", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -2679,6 +1276,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -2689,48 +1297,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "tiff" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" -dependencies = [ - "flate2", - "jpeg-decoder", - "weezl", -] - -[[package]] -name = "time" -version = "0.3.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tiny-skia" version = "0.11.4" @@ -2756,55 +1322,19 @@ dependencies = [ "strict-num", ] -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tobj" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bd4ba05f29e4c65b6c0c11a58b6465ffa820bac890d76ad407b4e81d8372e8" -dependencies = [ - "ahash", -] - -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", - "serde", - "serde_spanned", "toml_datetime", "winnow", ] @@ -2841,16 +1371,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-error" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" -dependencies = [ - "tracing", - "tracing-subscriber", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -2884,9 +1404,9 @@ checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-segmentation" @@ -2894,63 +1414,11 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" -[[package]] -name = "url" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "v_frame" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vec1" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab68b56840f69efb0fefbe3ab6661499217ffdc58e2eef7c3f6f69835386322" - -[[package]] -name = "version-compare" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "version_check" @@ -2958,35 +1426,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "vk-rs" -version = "0.1.0" -dependencies = [ - "ash", - "ash-window", - "bytemuck", - "cfg-if", - "cgmath", - "color-eyre", - "egui", - "egui-ash", - "gltf", - "gpu-allocator", - "gpu-profiler", - "image", - "puffin", - "puffin_egui", - "raw-window-handle 0.6.2", - "rayon", - "shaderc", - "shaders-shared", - "spirv-std", - "tobj", - "tracing", - "tracing-subscriber", - "winit 0.30.7", -] - [[package]] name = "walkdir" version = "2.5.0" @@ -3005,20 +1444,21 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", @@ -3030,9 +1470,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -3043,9 +1483,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3053,9 +1493,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -3066,15 +1506,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wayland-backend" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" dependencies = [ "cc", "downcast-rs", @@ -3086,11 +1529,11 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.7" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" +checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "rustix", "wayland-backend", "wayland-scanner", @@ -3102,16 +1545,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.7" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b08bc3aafdb0035e7fe0fdf17ba0c09c268732707dca4ae098f60cb28c9e4c" +checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" dependencies = [ "rustix", "wayland-client", @@ -3120,23 +1563,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.31.2" +version = "0.32.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" dependencies = [ - "bitflags 2.6.0", - "wayland-backend", - "wayland-client", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols" -version = "0.32.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" -dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -3144,61 +1575,35 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.2.0" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "wayland-backend", "wayland-client", - "wayland-protocols 0.31.2", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols-plasma" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd" -dependencies = [ - "bitflags 2.6.0", - "wayland-backend", - "wayland-client", - "wayland-protocols 0.32.5", + "wayland-protocols", "wayland-scanner", ] [[package]] name = "wayland-protocols-wlr" -version = "0.2.0" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "wayland-backend", "wayland-client", - "wayland-protocols 0.31.2", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols-wlr" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" -dependencies = [ - "bitflags 2.6.0", - "wayland-backend", - "wayland-client", - "wayland-protocols 0.32.5", + "wayland-protocols", "wayland-scanner", ] [[package]] name = "wayland-scanner" -version = "0.31.5" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" dependencies = [ "proc-macro2", "quick-xml", @@ -3207,9 +1612,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.5" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" dependencies = [ "dlib", "log", @@ -3219,19 +1624,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" -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" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -3247,29 +1642,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webbrowser" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" -dependencies = [ - "core-foundation", - "home", - "jni", - "log", - "ndk-context", - "objc", - "raw-window-handle 0.5.2", - "url", - "web-sys", -] - -[[package]] -name = "weezl" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" - [[package]] name = "winapi" version = "0.3.9" @@ -3303,9 +1675,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.52.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ "windows-core", "windows-targets 0.52.6", @@ -3313,10 +1685,55 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.52.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", "windows-targets 0.52.6", ] @@ -3329,15 +1746,6 @@ 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" @@ -3536,67 +1944,18 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.29.15" +version = "0.30.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" +checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" dependencies = [ "ahash", - "android-activity 0.5.2", + "android-activity", "atomic-waker", - "bitflags 2.6.0", + "bitflags 2.9.0", + "block2", "bytemuck", - "calloop 0.12.4", - "cfg_aliases 0.1.1", - "core-foundation", - "core-graphics", - "cursor-icon", - "icrate", - "js-sys", - "libc", - "log", - "memmap2", - "ndk 0.8.0", - "ndk-sys 0.5.0+25.2.9519653", - "objc2 0.4.1", - "once_cell", - "orbclient", - "percent-encoding", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", - "redox_syscall 0.3.5", - "rustix", - "sctk-adwaita 0.8.3", - "smithay-client-toolkit 0.18.1", - "smol_str", - "unicode-segmentation", - "wasm-bindgen", - "wasm-bindgen-futures", - "wayland-backend", - "wayland-client", - "wayland-protocols 0.31.2", - "wayland-protocols-plasma 0.2.0", - "web-sys", - "web-time 0.2.4", - "windows-sys 0.48.0", - "x11-dl", - "x11rb", - "xkbcommon-dl", -] - -[[package]] -name = "winit" -version = "0.30.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba50bc8ef4b6f1a75c9274fb95aa9a8f63fbc66c56f391bd85cf68d51e7b1a3" -dependencies = [ - "ahash", - "android-activity 0.6.0", - "atomic-waker", - "bitflags 2.6.0", - "block2 0.5.1", - "bytemuck", - "calloop 0.13.0", - "cfg_aliases 0.2.1", + "calloop", + "cfg_aliases", "concurrent-queue", "core-foundation", "core-graphics", @@ -3605,19 +1964,19 @@ dependencies = [ "js-sys", "libc", "memmap2", - "ndk 0.9.0", - "objc2 0.5.2", + "ndk", + "objc2", "objc2-app-kit", "objc2-foundation", "objc2-ui-kit", "orbclient", "percent-encoding", "pin-project", - "raw-window-handle 0.6.2", + "raw-window-handle", "redox_syscall 0.4.1", "rustix", - "sctk-adwaita 0.10.1", - "smithay-client-toolkit 0.19.2", + "sctk-adwaita", + "smithay-client-toolkit", "smol_str", "tracing", "unicode-segmentation", @@ -3625,10 +1984,10 @@ dependencies = [ "wasm-bindgen-futures", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.5", - "wayland-protocols-plasma 0.3.5", + "wayland-protocols", + "wayland-protocols-plasma", "web-sys", - "web-time 1.1.0", + "web-time", "windows-sys 0.52.0", "x11-dl", "x11rb", @@ -3637,25 +1996,13 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - [[package]] name = "x11-dl" version = "2.21.0" @@ -3676,7 +2023,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "libloading 0.8.6", + "libloading", "once_cell", "rustix", "x11rb-protocol", @@ -3700,7 +2047,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "dlib", "log", "once_cell", @@ -3713,43 +2060,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" -[[package]] -name = "xmlparser" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", "zerocopy-derive", ] @@ -3763,70 +2079,3 @@ dependencies = [ "quote", "syn", ] - -[[package]] -name = "zerofrom" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "zune-jpeg" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" -dependencies = [ - "zune-core", -] diff --git a/Cargo.toml b/Cargo.toml index d675659..a8bfc22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,22 +1,24 @@ [workspace] resolver = "2" -members = ["crates/shaders-shared", "crates/vk-rs"] +members = ["crates/engine", "crates/gfx_hal", "crates/resource_manager"] [workspace.dependencies] -ash = { version = "0.37.3" } +ash = { version = "0.38" } +ash-window = "0.13.0" color-eyre = "0.6.3" winit = { version = "0.30.7", features = ["rwh_06"] } raw-window-handle = "0.6" -gpu-allocator = { version = "0.25.0", features = ["vulkan"] } -glam = { version = "0.22", default-features = false, features = ["libm"] } -bytemuck = "1.21.0" +gpu-allocator = { version = "0.27.0", features = ["vulkan"] } +glam = { version = "0.22", default-features = false, features = [ + "libm", + "bytemuck", +] } +bytemuck = { version = "1.21.0", features = ["derive"] } tracing = "0.1" tracing-subscriber = "0.3" parking_lot = "0.12.3" - -spirv-builder = { git = "https://github.com/Rust-GPU/rust-gpu.git" } -spirv-std = { git = "https://github.com/Rust-GPU/rust-gpu.git" } +thiserror = "2.0.12" # # Enable incremental by default in release mode. diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml new file mode 100644 index 0000000..a632523 --- /dev/null +++ b/crates/engine/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "engine" +version = "0.1.0" +edition = "2021" + +[dependencies] +ash.workspace = true +winit.workspace = true +raw-window-handle.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +glam.workspace = true +bytemuck.workspace = true +thiserror.workspace = true +gpu-allocator.workspace = true + +gfx_hal = { path = "../gfx_hal" } +resource_manager = { path = "../resource_manager" } diff --git a/crates/engine/src/main.rs b/crates/engine/src/main.rs new file mode 100644 index 0000000..dbe8ad8 --- /dev/null +++ b/crates/engine/src/main.rs @@ -0,0 +1,5 @@ +mod scene_data; + +fn main() { + println!("Hello, world!"); +} diff --git a/crates/engine/src/scene_data.rs b/crates/engine/src/scene_data.rs new file mode 100644 index 0000000..0a97760 --- /dev/null +++ b/crates/engine/src/scene_data.rs @@ -0,0 +1,67 @@ +use core::f32; + +use bytemuck::{Pod, Zeroable}; +use glam::{Mat4, Vec3}; + +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod, Zeroable)] +pub struct Vertex { + pub pos: [f32; 3], + pub normal: [f32; 3], +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod, Zeroable)] +pub struct UniformBufferObject { + pub model: Mat4, + pub view: Mat4, + pub proj: Mat4, + pub light_dir: Vec3, + pub _padding: f32, + pub light_color: Vec3, + pub _padding2: f32, +} + +pub fn create_sphere(radius: f32, sectors: u32, stacks: u32) -> (Vec, Vec) { + let mut vertices = Vec::new(); + let mut indices = Vec::new(); + + let sector_step = 2.0 * f32::consts::PI / sectors as f32; + let stack_step = f32::consts::PI / stacks as f32; + + for i in 0..stacks { + let stack_angle = std::f32::consts::PI / 2.0 - (i as f32 * stack_step); + let xy = radius * stack_angle.cos(); + let z = radius * stack_angle.sin(); + + for j in 0..=sectors { + let sector_angle = j as f32 * sector_step; + let x = xy * sector_angle.cos(); + let y = xy * sector_angle.sin(); + + let pos = [x, y, z]; + let normal = Vec3::from(pos).normalize().to_array(); + vertices.push(Vertex { pos, normal }); + } + } + + for i in 0..stacks { + let k1 = i * (sectors + 1); + let k2 = k1 + sectors + 1; + + for j in 0..sectors { + if i != 0 { + indices.push(k1 + j); + indices.push(k2 + j); + indices.push(k1 + j + 1); + } + if i != (stacks - 1) { + indices.push(k1 + j + 1); + indices.push(k2 + j); + indices.push(k2 + j + 1); + } + } + } + + (vertices, indices) +} diff --git a/crates/gfx_hal/Cargo.toml b/crates/gfx_hal/Cargo.toml new file mode 100644 index 0000000..caa74b1 --- /dev/null +++ b/crates/gfx_hal/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "gfx_hal" +version = "0.1.0" +edition = "2021" + +[dependencies] +ash.workspace = true +ash-window.workspace = true +thiserror.workspace = true +tracing.workspace = true +winit.workspace = true +parking_lot.workspace = true diff --git a/crates/gfx_hal/src/device.rs b/crates/gfx_hal/src/device.rs new file mode 100644 index 0000000..b4e087d --- /dev/null +++ b/crates/gfx_hal/src/device.rs @@ -0,0 +1,223 @@ +use ash::vk; +use parking_lot::Mutex; +use std::ffi::CStr; +use std::{collections::HashMap, sync::Arc}; + +use crate::error::{GfxHalError, Result}; +use crate::instance::Instance; +use crate::physical_device::{PhysicalDevice, QueueFamilyIndices}; +use crate::queue::Queue; + +/// Represents the logical Vulkan device, created from a `PhysicalDevice`. +/// +/// Owns the `ash::Device` and provides access to device functions and queues. +pub struct Device { + instance: Arc, + physical_device: vk::PhysicalDevice, + device: ash::Device, + queues: Mutex>>, + graphics_queue_family_index: u32, + compute_queue_family_index: Option, + transfer_queue_family_index: Option, +} + +impl Device { + /// Creates a new logical device. Typically called via `PhysicalDevice::create_logical_device`. + /// + /// # Saftey + /// - `instance` and `physical_device_handle` must be valid. + /// - `queue_family_indicies` must be valid indicies obtained from the `physical_device_handle`. + /// - `required_extensions` must be supported by the `physical_device_handle`. + /// - `enabled_features` and `mesh_features` must be supported by the `physical_device_handle`. + pub(crate) unsafe fn new( + instance: Arc, + physical_device_handle: vk::PhysicalDevice, + queue_family_indicies: &QueueFamilyIndices, + required_extensions: &[&CStr], + enabled_features: &vk::PhysicalDeviceFeatures, + mesh_features: Option<&vk::PhysicalDeviceMeshShaderFeaturesEXT>, + ) -> Result> { + let mut queue_create_infos = Vec::new(); + let mut unique_queue_families = std::collections::HashSet::new(); + + let graphics_family = queue_family_indicies.graphics_family.ok_or_else(|| { + GfxHalError::MissingQueueFamily("Graphics Queue Family Missing".to_string()) + })?; + unique_queue_families.insert(graphics_family); + + if let Some(compute_family) = queue_family_indicies.compute_family { + unique_queue_families.insert(compute_family); + } + if let Some(transfer_family) = queue_family_indicies.transfer_family { + unique_queue_families.insert(transfer_family); + } + + let queue_priorities = [1.0f32]; + for &family_index in &unique_queue_families { + let queue_create_info = vk::DeviceQueueCreateInfo::default() + .queue_family_index(family_index) + .queue_priorities(&queue_priorities); + queue_create_infos.push(queue_create_info); + } + + let extension_names_raw: Vec<*const i8> = + required_extensions.iter().map(|s| s.as_ptr()).collect(); + + let mut features2 = vk::PhysicalDeviceFeatures2::default().features(*enabled_features); + let mut mesh_features_copy; + + if let Some(mesh_feats) = mesh_features { + mesh_features_copy = *mesh_feats; + features2 = features2.push_next(&mut mesh_features_copy); + } + + let device_create_info = vk::DeviceCreateInfo::default() + .queue_create_infos(&queue_create_infos) + .enabled_extension_names(&extension_names_raw) + .push_next(&mut features2); + + tracing::info!( + "Creating logical device with extensions: {:?}", + required_extensions + ); + let device = instance.ash_instance().create_device( + physical_device_handle, + &device_create_info, + None, + )?; + tracing::info!("logical device created successfully."); + + let mut queues_map = HashMap::new(); + let arc_device_placeholder = Arc::new(Self { + instance, + physical_device: physical_device_handle, + device, + queues: Mutex::new(HashMap::new()), + graphics_queue_family_index: graphics_family, + compute_queue_family_index: queue_family_indicies.compute_family, + transfer_queue_family_index: queue_family_indicies.transfer_family, + }); + + for &family_index in &unique_queue_families { + let queue_handler = arc_device_placeholder + .device + .get_device_queue(family_index, 0); + let queue_wrapper = Arc::new(Queue::new( + Arc::clone(&arc_device_placeholder), + queue_handler, + family_index, + )); + queues_map.insert((family_index, 0), queue_wrapper); + } + + let device_handle = unsafe { + arc_device_placeholder + .instance + .ash_instance() + .create_device(physical_device_handle, &device_create_info, None)? + }; + + let final_device = Arc::new(Self { + instance: Arc::clone(&arc_device_placeholder.instance), // Clone from placeholder + physical_device: physical_device_handle, + device: device_handle, // Use the newly created handle + queues: Mutex::new(queues_map), // Use the populated map + graphics_queue_family_index: graphics_family, + compute_queue_family_index: queue_family_indicies.compute_family, + transfer_queue_family_index: queue_family_indicies.transfer_family, + }); + + Ok(final_device) + } + + /// Provides raw access to the underlying `ash::Device`. + /// Use with caution, prefer safe wrappers where possible. + pub fn raw(&self) -> &ash::Device { + &self.device + } + + /// Gets the handle of the physical device this logical device was created from. + pub fn physical_device_handle(&self) -> vk::PhysicalDevice { + self.physical_device + } + + /// Gets the primary graphics queue family index used by this device. + pub fn graphics_queue_family_index(&self) -> u32 { + self.graphics_queue_family_index + } + + /// Gets the compute queue family index, if a distinct one was found/used. + pub fn compute_queue_family_index(&self) -> Option { + self.compute_queue_family_index + } + + /// Gets the transfer queue family index, if a distinct one was found/used. + pub fn transfer_queue_family_index(&self) -> Option { + self.transfer_queue_family_index + } + + /// Gets a wrapped queue handle. + /// Currently only supports queue index 0 for each family. + pub fn get_queue(&self, family_index: u32, queue_index: u32) -> Option> { + if queue_index != 0 { + tracing::warn!("get_queue currently only supports queue_index 0"); + return None; + } + self.queues + .lock() + .get(&(family_index, queue_index)) + .cloned() + } + + /// Gets the primary graphics queue (family index from `graphics_queue_family_index`, queue index 0). + /// Panics if the graphics queue wasn't successfully created. + pub fn get_graphics_queue(&self) -> Arc { + self.get_queue(self.graphics_queue_family_index, 0) + .expect("Graphics queue should always exist") + } + + /// Waits until the logical device becomes idle. + /// This is a heavy operation and should be used sparingly (e.g., before destruction). + pub fn wait_idle(&self) -> Result<()> { + tracing::debug!("Waiting for device idle..."); + unsafe { self.device.device_wait_idle()? }; + tracing::debug!("Device idle."); + Ok(()) + } +} + +impl Drop for Device { + fn drop(&mut self) { + tracing::debug!("Destroying logical device..."); + if let Err(e) = self.wait_idle() { + tracing::error!("Error waiting for device idle during drop: {}", e); + } + unsafe { + self.device.destroy_device(None); + } + tracing::debug!("Logical device destroyed."); + } +} + +impl PhysicalDevice { + /// Creates the logical device (`Device`) from this physical device. + /// + /// # Safety + /// See `Device::new` safety comments. + pub unsafe fn create_logical_device( + &self, + required_extensions: &[&CStr], + queue_family_indices: &QueueFamilyIndices, + enabled_features: &vk::PhysicalDeviceFeatures, + mesh_features: Option<&vk::PhysicalDeviceMeshShaderFeaturesEXT>, + ) -> Result> { + Device::new( + Arc::clone(self.instance()), + self.handle(), + queue_family_indices, + required_extensions, + enabled_features, + mesh_features, + ) + } +} diff --git a/crates/gfx_hal/src/error.rs b/crates/gfx_hal/src/error.rs new file mode 100644 index 0000000..4192b31 --- /dev/null +++ b/crates/gfx_hal/src/error.rs @@ -0,0 +1,64 @@ +use ash::vk; +use thiserror::Error; + +/// Top-level error type for the gfx_hal crate. +#[derive(Error, Debug)] +pub enum GfxHalError { + /// Error originating directly from a Vulkan API call. + #[error("Vulkan API Error: {0}")] + VulkanError(#[from] vk::Result), + + /// Error loading the Vulkan library or resolving function pointers. + #[error("Failed to load Vulkan library: {0}")] + LoadError(String), + + /// No suitable physical device (GPU) could be found that meets requirements. + #[error("No suitable physical device found: {0}")] + NoSuitableGpu(String), + + /// A required Vulkan instance or device extension is not supported. + #[error("Missing required Vulkan extension: {0:?}")] + MissingExtension(String), + + /// A required Vulkan feature is not supported by the physical device. + #[error("Missing required Vulkan feature.")] + MissingFeature, + + /// Failed to find a suitable queue family (e.g., graphics, present). + #[error("Could not find required queue family: {0}")] + MissingQueueFamily(String), + + /// Error related to window system integration surface creation. + #[error("Failed to create Vulkan surface: {0}")] + SurfaceCreationError(vk::Result), + + /// The Vulkan surface became invalid (e.g., window resized, closed). + #[error("Vulkan surface is no longer valid (maybe lost or out of date)")] + SurfaceLost, + + /// Error converting a C-style string. + #[error("Invalid C string: {0}")] + InvalidCString(#[from] std::ffi::NulError), + + /// Error converting C string slice to Rust string slice. + #[error("Invalid UTF-8 sequence in C string: {0}")] + InvalidCStringUtf8(#[from] std::ffi::FromBytesWithNulError), + + /// Generic I/O error. + #[error("I/O error: {0}")] + IoError(#[from] std::io::Error), + + /// Error with winit windowing library. + #[error("Winit Error: {0}")] + WinitHandleError(#[from] winit::raw_window_handle::HandleError), + + /// Ash loader error. + #[error("Error loading the ash entry.")] + AshEntryError(#[from] ash::LoadingError), + + /// Placeholder for other specific errors. + #[error("An unexpected error occurred: {0}")] + Other(String), +} + +pub type Result = std::result::Result; diff --git a/crates/gfx_hal/src/instance.rs b/crates/gfx_hal/src/instance.rs new file mode 100644 index 0000000..b188805 --- /dev/null +++ b/crates/gfx_hal/src/instance.rs @@ -0,0 +1,353 @@ +use std::{ + collections::HashSet, + ffi::{c_char, c_void, CStr, CString}, + sync::Arc, +}; + +use ash::{ext::debug_utils, vk}; +use winit::raw_window_handle::{DisplayHandle, HasDisplayHandle}; + +use crate::error::{GfxHalError, Result}; + +unsafe extern "system" fn vulkan_debug_callback( + message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, + message_type: vk::DebugUtilsMessageTypeFlagsEXT, + p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT, + _p_user_data: *mut c_void, +) -> vk::Bool32 { + let callback_data = *p_callback_data; + let message_id_number: i32 = callback_data.message_id_number; + let message_id_name = if callback_data.p_message_id_name.is_null() { + std::borrow::Cow::from("") + } else { + CStr::from_ptr(callback_data.p_message_id_name).to_string_lossy() + }; + let message = if callback_data.p_message.is_null() { + std::borrow::Cow::from("") + } else { + CStr::from_ptr(callback_data.p_message).to_string_lossy() + }; + + let severity = match message_severity { + vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => "[VERBOSE]", + vk::DebugUtilsMessageSeverityFlagsEXT::INFO => "[INFO]", + vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => "[WARNING]", + vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => "[ERROR]", + _ => "[UNKNOWN SEVERITY]", + }; + let ty = match message_type { + vk::DebugUtilsMessageTypeFlagsEXT::GENERAL => "[GENERAL]", + vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION => "[VALIDATION]", + vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE => "[PERFORMANCE]", + _ => "[UNKNOWN TYPE]", + }; + + // Use the tracing crate for output + match message_severity { + vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => { + tracing::debug!( + "{} {} ({}:{}) {}", + severity, + ty, + message_id_name, + message_id_number, + message + ); + } + vk::DebugUtilsMessageSeverityFlagsEXT::INFO => { + tracing::info!( + "{} {} ({}:{}) {}", + severity, + ty, + message_id_name, + message_id_number, + message + ); + } + vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => { + tracing::warn!( + "{} {} ({}:{}) {}", + severity, + ty, + message_id_name, + message_id_number, + message + ); + } + // Treat ERROR and higher as errors + _ => { + tracing::error!( + "{} {} ({}:{}) {}", + severity, + ty, + message_id_name, + message_id_number, + message + ); + } + } + + vk::FALSE // Standard return value +} + +#[derive(Clone, Debug)] +pub struct InstanceConfig { + pub application_name: String, + pub engine_name: String, + pub application_version: u32, + pub engine_version: u32, + /// Enable Vulkan validation layers + pub enable_validation: bool, + /// Additional required instance extensions beyond surface/debug. + pub required_extensions: Vec<&'static CStr>, +} + +impl Default for InstanceConfig { + fn default() -> Self { + InstanceConfig { + application_name: "Defualt App".to_string(), + engine_name: "Default Engine".to_string(), + application_version: vk::make_api_version(0, 1, 0, 0), + engine_version: vk::make_api_version(0, 1, 0, 0), + enable_validation: cfg!(debug_assertions), + required_extensions: Vec::new(), + } + } +} + +/// Represents the Vulkan API Instance +/// +/// Owns the `ash::Entry`, `ash::Instance` and potentially the debug messenger. +/// This is the starting point for interacting with Vulkan +pub struct Instance { + entry: ash::Entry, + instance: ash::Instance, + debug_utils: Option, + debug_messenger: Option, +} + +impl Instance { + /// Creates a new Vulkan `Instance` + /// + /// # Arguments + /// * `config` - Configuration settings for the instance + /// * `display_handle` - Raw display handle for the windowing system + /// * `external_extensions` - A slice of `CString` representing additional required instance extensions, + /// typically provided by integration libraries like `egui-ash` + pub fn new( + config: &InstanceConfig, + display_handle: &dyn HasDisplayHandle, + external_extentions: &[CString], + ) -> Result> { + let entry = unsafe { ash::Entry::load()? }; + + let app_name = CString::new(config.application_name.clone())?; + let engine_name = CString::new(config.engine_name.clone())?; + let app_info = vk::ApplicationInfo::default() + .application_name(&app_name) + .application_version(config.application_version) + .engine_name(&engine_name) + .engine_version(config.engine_version) + .api_version(vk::API_VERSION_1_3); + + let validation_layers = [c"VK_LAYER_KHRONOS_validation"]; + let enabled_layer_names_raw: Vec<*const c_char> = if config.enable_validation + && Self::check_validation_layer_support(&entry, &validation_layers)? + { + tracing::info!("Validation layers enabled."); + validation_layers.iter().map(|name| name.as_ptr()).collect() + } else { + if config.enable_validation { + tracing::warn!("Validation layers requested but not supported. Disabling."); + } + Vec::new() + }; + + let display_handle: DisplayHandle = display_handle.display_handle()?; + + let surface_extensions_ptrs = + ash_window::enumerate_required_extensions(display_handle.into())?; + + let mut required_cstrs_for_check: Vec<&CStr> = Vec::new(); + + let surface_extensions_cstrs: Vec<&CStr> = surface_extensions_ptrs + .iter() + .map(|&ptr| unsafe { CStr::from_ptr(ptr) }) + .collect(); + required_cstrs_for_check.extend(&surface_extensions_cstrs); + + required_cstrs_for_check.extend(external_extentions.iter().map(|cs| cs.as_c_str())); + + if config.enable_validation { + required_cstrs_for_check.push(ash::ext::debug_utils::NAME); + } + + required_cstrs_for_check.sort_unstable(); + required_cstrs_for_check.dedup(); + + Self::check_instance_extension_support(&entry, &required_cstrs_for_check)?; + tracing::info!( + "Required instance extensions supported: {:?}", + required_cstrs_for_check + ); + + let mut enabled_extension_names_raw: Vec<*const c_char> = Vec::new(); + enabled_extension_names_raw.extend(surface_extensions_ptrs); + enabled_extension_names_raw.extend(external_extentions.iter().map(|cs| cs.as_ptr())); + if config.enable_validation { + enabled_extension_names_raw.push(ash::ext::debug_utils::NAME.as_ptr()); + } + + enabled_extension_names_raw.sort_unstable(); + enabled_extension_names_raw.dedup(); + + let mut debug_create_info = vk::DebugUtilsMessengerCreateInfoEXT::default() + .message_severity( + vk::DebugUtilsMessageSeverityFlagsEXT::WARNING + | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR, + ) + .message_type( + vk::DebugUtilsMessageTypeFlagsEXT::GENERAL + | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION + | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE, + ) + .pfn_user_callback(Some(vulkan_debug_callback)); + + let mut instance_create_info = vk::InstanceCreateInfo::default() + .application_info(&app_info) + .enabled_layer_names(&enabled_layer_names_raw) + .enabled_extension_names(&enabled_extension_names_raw); + + if config.enable_validation { + instance_create_info = instance_create_info.push_next(&mut debug_create_info); + } + + let instance = unsafe { entry.create_instance(&instance_create_info, None)? }; + tracing::info!("Vulkan instance created succesfully."); + + let (debug_utils, debug_messenger) = if config.enable_validation { + let utils = ash::ext::debug_utils::Instance::new(&entry, &instance); + let messenger = + unsafe { utils.create_debug_utils_messenger(&debug_create_info, None)? }; + tracing::debug!("Debug messenger created."); + (Some(utils), Some(messenger)) + } else { + (None, None) + }; + + Ok(Arc::new(Self { + entry, + instance, + debug_utils, + debug_messenger, + })) + } + + /// Provides access to the loaded Vulkan entry points. + pub fn entry(&self) -> &ash::Entry { + &self.entry + } + + /// Provides access to the raw `ash::Instance`. + pub fn ash_instance(&self) -> &ash::Instance { + &self.instance + } + + /// Provides access to the DebugUtils extension loader, if enabled. + pub fn debug_utils(&self) -> Option<&debug_utils::Instance> { + self.debug_utils.as_ref() + } + + /// Checks if the requested validation layers are available. + fn check_validation_layer_support( + entry: &ash::Entry, + required_layers: &[&CStr], + ) -> Result { + let available_layers = unsafe { entry.enumerate_instance_layer_properties()? }; + let available_names: HashSet<&CStr> = available_layers + .iter() + .map(|layer| unsafe { CStr::from_ptr(layer.layer_name.as_ptr()) }) + .collect(); + + for layer in required_layers { + if !available_names.contains(layer) { + tracing::warn!("Required validation layer {:?} not found.", layer); + return Ok(false); + } + } + + Ok(true) + } + + /// Checks if the requested instance extensions are available. + /// Takes a deduplicated list of required extension names (&CStr). + fn check_instance_extension_support( + entry: &ash::Entry, + required_extensions: &[&CStr], + ) -> Result<()> { + let available_extensions = unsafe { entry.enumerate_instance_extension_properties(None)? }; + let available_names: HashSet<&CStr> = available_extensions + .iter() + .map(|ext| unsafe { CStr::from_ptr(ext.extension_name.as_ptr()) }) + .collect(); + tracing::debug!("Available instance extensions: {:?}", available_names); + + for ext in required_extensions { + if !available_names.contains(ext) { + tracing::error!("Missing required instance extension: {:?}", ext); + return Err(GfxHalError::MissingExtension( + ext.to_string_lossy().into_owned(), + )); + } + } + Ok(()) + } + + // /// Enumerates all physical devices available to this instance. + // /// + // /// # Safety + // /// The `Instance` must be kept alive while the returned `PhysicalDevices`s are in use. + // /// This is ensured by returning `PhysicalDevice`s holding an `Arc`. + // pub unsafe fn enumerate_phyiscal_devices(self: &Arc) -> Result> { + // let physical_device_handles = self.instance.enumerate_physical_devices()?; + // + // if physical_device_handles.is_empty() { + // return Err(GfxHalError::NoSuitableGpu( + // "No Vulkan-compatibile GPUs found.".to_string(), + // )); + // } + // + // let devices = physical_device_handles + // .into_iter() + // .map(|handle| PhysicalDevice::new(Arc::clone(self), handle)) + // .collect()?; + // + // Ok(devices) + // } + + // /// Creates a vulkan surface for the given window + // /// + // /// # Safety + // /// The `window_handle_trait_obj` must point to a valid window/display managed by caller. + // /// The `Instance` must be kept alive longer than the returned `Surface` + // pub unsafe fn create_surface( + // self: &Arc, + // window_handle_trait_obj: &(impl HasWindowHandle + HasDisplayHandle), + // ) -> Result> { + // Surface::new(Arc::clone(self), window_handle_trait_obj) + // } +} + +impl Drop for Instance { + fn drop(&mut self) { + unsafe { + if let (Some(utils), Some(messenger)) = (&self.debug_utils, self.debug_messenger) { + tracing::debug!("Destroying debug messenger..."); + utils.destroy_debug_utils_messenger(messenger, None); + } + tracing::debug!("Destroying Vulkan instance..."); + self.instance.destroy_instance(None); + tracing::debug!("Vulkan instance destroyed"); + } + } +} diff --git a/crates/gfx_hal/src/lib.rs b/crates/gfx_hal/src/lib.rs new file mode 100644 index 0000000..e83ecc2 --- /dev/null +++ b/crates/gfx_hal/src/lib.rs @@ -0,0 +1,8 @@ +pub mod device; +pub mod error; +pub mod instance; +pub mod physical_device; +pub mod queue; +pub mod surface; +pub mod swapchain; +pub mod sync; diff --git a/crates/gfx_hal/src/physical_device.rs b/crates/gfx_hal/src/physical_device.rs new file mode 100644 index 0000000..4463dca --- /dev/null +++ b/crates/gfx_hal/src/physical_device.rs @@ -0,0 +1,164 @@ +use ash::vk; + +use crate::{error::GfxHalError, instance::Instance}; + +use std::{ffi::CStr, sync::Arc}; + +/// Represents a physical Vulkan device (GPU). +/// +/// This struct holds a handle to the Vulkan physical device and a +/// reference back to the `Instance` it originated from. It does *not* own +/// the `vk::PhysicalDevice` in the sense that it doesn't destroy it; physical +/// devices are implicitly managed by the `vk::Instance` +/// +/// It's cheap to clone as it only clones the `Arc` and copies the handle +#[derive(Clone)] +pub struct PhysicalDevice { + /// Shared reference to the Vulkan instance + instance: Arc, + /// The raw Vulkan physical device handle. + handle: vk::PhysicalDevice, +} + +/// Holds information about queue families found on a `PhysicalDevice`. +#[derive(Debug, Clone)] +pub struct QueueFamilyIndices { + /// Queue family index supporting graphics operations. + pub graphics_family: Option, + /// Queue family index supporting compute operations. + pub compute_family: Option, + /// Queue family index supporting transfer operations. + pub transfer_family: Option, + /// Queue family index supporting presentaiton to a given surface. + /// This might be the same as the graphics family. + pub present_family: Option, +} + +impl QueueFamilyIndices { + /// Checks if all essential queue families (graphics, present if surface exists) were found. + pub fn is_complete(&self, requires_present: bool) -> bool { + self.graphics_family.is_some() && (!requires_present || self.present_family.is_some()) + } +} + +/// Represents the suitability of a physical device. +#[derive(Debug)] +pub enum Suitability<'a> { + /// The device is suitable and meets requirements. + Suitable { + /// A score indicating preference (higher is better). + score: u32, + /// The indicies of the required queue families. + indicies: QueueFamilyIndices, + /// The properties of the device. + properties: Box, + /// The supported base features of the device. + features: Box, + /// THe supported mesh shader features. + mesh_shader_features: vk::PhysicalDeviceMeshShaderFeaturesEXT<'a>, + }, + /// The device is not suitable. + NotSuitable { + /// The reason why the device is not suitable. + reason: String, + }, +} + +impl PhysicalDevice { + /// Creates a new `PhysicalDevice` wrapper + /// Typically called internally by `Instance::enumerate_physical_devices` + pub(crate) fn new(instance: Arc, handle: vk::PhysicalDevice) -> Self { + Self { instance, handle } + } + + /// Gets the raw `vk::PhysicalDevice` handle. + pub fn handle(&self) -> vk::PhysicalDevice { + self.handle + } + + /// Gets a reference to the `Instance` this device belongs to. + pub fn instance(&self) -> &Arc { + &self.instance + } + + /// Queries the basic properties of the physical device. + /// + /// # Safety + /// Assumes the `PhysicalDevice` handle is valid. + pub unsafe fn get_properties(&self) -> vk::PhysicalDeviceProperties { + self.instance + .ash_instance() + .get_physical_device_properties(self.handle) + } + + /// Queries the supported features, including mesh shaders. + /// + /// # Safety + /// Assumes the `PhysicalDevice` handle is valid. + pub unsafe fn get_features( + &self, + ) -> ( + vk::PhysicalDeviceFeatures, + vk::PhysicalDeviceMeshShaderFeaturesEXT, + ) { + let mut mesh_shader_features = vk::PhysicalDeviceMeshShaderFeaturesEXT::default(); + let mut features2 = + vk::PhysicalDeviceFeatures2::default().push_next(&mut mesh_shader_features); + + self.instance + .ash_instance() + .get_physical_device_features2(self.handle, &mut features2); + + (features2.features, mesh_shader_features) + } + + /// Queries the properties of all queue families available on the device. + /// + /// # Safety + /// Assumes the `PhysicalDevice` handle is valid. + pub unsafe fn get_queue_family_properties(&self) -> Vec { + self.instance + .ash_instance() + .get_physical_device_queue_family_properties(self.handle) + } + + /// Queries the device specific extensions supported by this physical device. + /// + /// # Safety + /// Assumes the `PhysicalDevice` handle is valid. + pub unsafe fn get_supported_extensions( + &self, + ) -> Result, GfxHalError> { + self.instance + .ash_instance() + .enumerate_device_extension_properties(self.handle) + .map_err(GfxHalError::VulkanError) + } + + // /// Finds suitable queue family indicies based on required flags and optional surface. + // /// + // /// # Safety + // /// Assumes the `PhysicalDevice` handle and `Surface` (if provided) are valid. + // pub unsafe fn find_queue_families( + // &self, + // surface: Option<&Surface>, + // ) -> Result { + // } + + // /// Checks if the physical device meets the specified requirements and scores it. + // /// + // /// # Arguments + // /// * `required_extensions` - A slice of C-style strings representing required device extensions (e.g., `ash::extensions::khr::Swapchain::name()`). + // /// * `required_mesh_features` - The minimum mesh shader features required. Check `task_shader` and `mesh_shader` fields. + // /// * `surface` - An optional surface to check for presentation support. + // /// + // /// # Safety + // /// Assumes the `PhysicalDevice` handle and `Surface` (if provided) are valid. + // pub unsafe fn check_suitability( + // &self, + // required_extensions: &[&CStr], + // required_mesh_features: &vk::PhysicalDeviceMeshShaderFeaturesEXT, + // surface: Option<&Surface>, + // ) -> Result { + // } +} diff --git a/crates/gfx_hal/src/queue.rs b/crates/gfx_hal/src/queue.rs new file mode 100644 index 0000000..d10d469 --- /dev/null +++ b/crates/gfx_hal/src/queue.rs @@ -0,0 +1,102 @@ +use std::sync::Arc; + +use ash::vk; +use parking_lot::Mutex; + +use crate::device::Device; +use crate::error::Result; +use crate::sync::Fence; + +/// Represents a Vulkan device queue. +/// +/// Holds a reference to the `Device` and the raw `vk::Queue` handle. +/// Provides methods for submitting command buffers. +pub struct Queue { + device: Arc, + queue: vk::Queue, + family_index: u32, + // Each queue submission must be externally synchronized or locked internally. + // Using a Mutex here provides a simple internal locking per queue. + submit_lock: Mutex<()>, +} + +impl Queue { + /// Creates a new Queue wrapper. Called internally by `Device`. + pub(crate) fn new(device: Arc, queue: vk::Queue, family_index: u32) -> Self { + Self { + device, + queue, + family_index, + submit_lock: Mutex::new(()), + } + } + + /// Gets the raw `vk::Queue` handle. + pub fn handle(&self) -> vk::Queue { + self.queue + } + + /// Gets the queue family index this queue belongs to. + pub fn family_index(&self) -> u32 { + self.family_index + } + + /// Gets a reference to the logical device this queue belongs to. + pub fn device(&self) -> &Arc { + &self.device + } + + /// Submits command buffers to the queue. + /// + /// This method acquires an internal lock for the duration of the submission call + /// to prevent concurrent `vkQueueSubmit` calls on the same queue from this wrapper. + /// + /// # Arguments + /// * `submits` - A slice of `vk::SubmitInfo` describing the work to submit. + /// * `signal_fence` - An optional `Fence` to signal when the submission completes. + /// + /// # Safety + /// - The command buffers and synchronization primitieves within `submits` must be valid. + /// - The `signal_fence`, if provided, must be valid and unsignaled. + pub unsafe fn submit( + &self, + submits: &[vk::SubmitInfo], + signal_fence: Option<&Fence>, + ) -> Result<()> { + let fence_handle = signal_fence.map_or(vk::Fence::null(), |f| f.handle()); + + let _lock = self.submit_lock.lock(); + + tracing::trace!( + "Submitting {} batch(es) to queue family {}", + submits.len(), + self.family_index + ); + self.device + .raw() + .queue_submit(self.queue, submits, fence_handle)?; + tracing::trace!("Submission successful."); + Ok(()) + } + + /// Waits until this queue becomes idle. + /// + /// This is a heavy operation and blocks the current thread. + pub fn wait_idle(&self) -> Result<()> { + tracing::debug!("Waiting for queue idle (family {})...", self.family_index); + // Lock the mutex while waiting to prevent submissions during the wait? + // Or allow submissions and let Vulkan handle it? Let Vulkan handle it. + unsafe { self.device.raw().queue_wait_idle(self.queue)? }; + tracing::debug!("Queue idle (family {}).", self.family_index); + Ok(()) + } + + // Note: vkQueuePresentKHR is intentionally omitted here. + // Presentation is tightly coupled with Swapchain. It's safer to + // have a method like `Swapchain::present(&self, queue: &Queue, ...)` + // which internally calls `queue.device().raw().queue_present_khr(...)` + // using the swapchain's loader. + // If direct access is needed, it can be done with `queue.device().raw()`. +} + +// Queues don't own the vk::Queue handle (the Device does), so no Drop impl. diff --git a/crates/gfx_hal/src/surface.rs b/crates/gfx_hal/src/surface.rs new file mode 100644 index 0000000..8821026 --- /dev/null +++ b/crates/gfx_hal/src/surface.rs @@ -0,0 +1,126 @@ +use crate::{ + error::{GfxHalError, Result}, + instance::Instance, +}; + +use ash::{khr::surface::Instance as SurfaceLoader, vk}; +use std::sync::Arc; +use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; + +/// Represents a Vulkan presentation surface, tied to a window. +/// +/// Owns the `vk::SurfaceKHR` handle and the `ash` Surface loader extension. +pub struct Surface { + instance: Arc, + surface_loader: SurfaceLoader, + surface: vk::SurfaceKHR, +} + +impl Surface { + /// Creates a new Vulkan `Surface` + /// + /// # Safety + /// - The `window_handle_trait_obj` must provide valid window and display handles + /// for the lifetime of the `Surface`. + /// - The `Instance` must outlive the `Surface` + pub unsafe fn new( + instance: Arc, + window_handle_trait_obj: &(impl HasWindowHandle + HasDisplayHandle), + ) -> Result> { + let surface_loader = SurfaceLoader::new(instance.entry(), instance.ash_instance()); + let surface = ash_window::create_surface( + instance.entry(), + instance.ash_instance(), + window_handle_trait_obj.display_handle()?.into(), + window_handle_trait_obj.window_handle()?.into(), + None, + ) + .map_err(GfxHalError::SurfaceCreationError)?; + + tracing::info!("Vulkan surface created successfully."); + + Ok(Arc::new(Self { + instance, + surface_loader, + surface, + })) + } + + /// Gets the raw `vk::SurfaceKHR` handle. + pub fn handle(&self) -> vk::SurfaceKHR { + self.surface + } + + /// Gets a reference to the `ash` Surface loader extension. + pub fn surface_loader(&self) -> &SurfaceLoader { + &self.surface_loader + } + + /// Gets a reference to the `Instance` this surface belongs to + pub fn instance(&self) -> &Arc { + &self.instance + } + + /// Queries surface capabilites for a given physical device. + /// + /// # Safety + /// The `physical_device` handle must be valid and compatible with this surface. + pub unsafe fn get_physical_device_surface_capabilities( + &self, + physical_device: vk::PhysicalDevice, + ) -> Result { + self.surface_loader + .get_physical_device_surface_capabilities(physical_device, self.surface) + .map_err(GfxHalError::VulkanError) + } + + /// Queries supported surface formats for a given physical device. + /// + /// # Safety + /// The `physical_device` handle must be valid and compatible with this surface. + pub unsafe fn get_physical_device_surface_formats( + &self, + physical_device: vk::PhysicalDevice, + ) -> Result> { + self.surface_loader + .get_physical_device_surface_formats(physical_device, self.surface) + .map_err(GfxHalError::VulkanError) + } + + /// Queries supported present modes for a given physical device. + /// + /// # Safety + /// The `physical_device` handle must be valid and compatible with this surface. + pub unsafe fn get_physical_device_surface_present_modes( + &self, + physical_device: vk::PhysicalDevice, + ) -> Result> { + self.surface_loader + .get_physical_device_surface_present_modes(physical_device, self.surface) + .map_err(GfxHalError::VulkanError) + } + + /// Queries surface support for a given queue family index on a physical device. + /// + /// # Safety + /// The `physical_device` handle must be valid and compatible with this surface. + pub unsafe fn get_physical_device_surface_support( + &self, + physical_device: vk::PhysicalDevice, + queue_family_index: u32, + ) -> Result { + self.surface_loader + .get_physical_device_surface_support(physical_device, queue_family_index, self.surface) + .map_err(GfxHalError::VulkanError) + } +} + +impl Drop for Surface { + fn drop(&mut self) { + tracing::debug!("Destroying Vulkan surface..."); + unsafe { + self.surface_loader.destroy_surface(self.surface, None); + } + tracing::debug!("Vulkan surface destroyed."); + } +} diff --git a/crates/gfx_hal/src/swapchain.rs b/crates/gfx_hal/src/swapchain.rs new file mode 100644 index 0000000..b88153a --- /dev/null +++ b/crates/gfx_hal/src/swapchain.rs @@ -0,0 +1,321 @@ +use std::sync::Arc; + +use ash::khr::swapchain::Device as SwapchainLoader; +use ash::vk; + +use crate::device::{self, Device}; +use crate::error::{GfxHalError, Result}; +use crate::surface::{self, Surface}; +use crate::sync::{Fence, Semaphore}; + +/// Configuration for creating or recreating a `Swapchain`. +#[derive(Clone, Debug)] +pub struct SwapchainConfig { + /// Desired number of images in the swapchain (min/max clamped by capabilities). + pub desired_image_count: u32, + /// Preferred surface format (e.g., `vk::Format::B8G8R8A8_SRGB`). + pub desired_format: vk::SurfaceFormatKHR, + /// Preferred presentation mode (e.g., `vk::PresentModeKHR::MAILBOX`). + pub desired_present_mode: vk::PresentModeKHR, + /// Desired usage flags for swapchain images (e.g., `vk::ImageUsageFlags::COLOR_ATTACHMENT`). + pub image_usage: vk::ImageUsageFlags, + /// The dimensions of the surface. + pub extent: vk::Extent2D, + /// Transformation to apply (usually `vk::SurfaceTransformFlagsKHR::IDENTITY`). + pub pre_transform: vk::SurfaceTransformFlagsKHR, + /// Alpha compositing mode (usually `vk::CompositeAlphaFlagsKHR::OPAQUE`). + pub composite_alpha: vk::CompositeAlphaFlagsKHR, +} + +/// Represents the Vulkan swapchain, managing presentation images. +/// +/// Owns the `vk::SwapchainKHR`, the `ash` Swapchain loader, the swapchain images, +/// and their corresponding image views. +pub struct Swapchain { + device: Arc, + swapchain_loader: SwapchainLoader, + swapchain: vk::SwapchainKHR, + images: Vec, + image_views: Vec, + format: vk::SurfaceFormatKHR, + extent: vk::Extent2D, + image_count: u32, +} + +impl Swapchain { + /// Creates a new `Swapchain` or recreates an exisiting one. + /// + /// # Arguments + /// * `device` - The logical device. + /// * `surface` - The surface to present to. + /// * `config` - Desired swapchain configuration. + /// * `old_swapchain` - Optional handle to a previous swapchain for smoother recreation. + /// + /// # Safety + /// - `device` and `surface` must be valid and compatible. + /// - If `old_swapchain` is provided, it must be a valid handle previously created + /// with the same `surface`. + /// - The caller must ensure that the `old_swapchain` (and its associated resources like + /// image views) are no longer in use before calling this function and are properly + /// destroyed *after* the new swapchain is successfully created. + pub unsafe fn new( + device: Arc, + surface: Arc, + config: SwapchainConfig, + old_swapchain: Option, + ) -> Result { + let physical_device = device.physical_device_handle(); + + let capabilities = surface.get_physical_device_surface_capabilities(physical_device)?; + let formats = surface.get_physical_device_surface_formats(physical_device)?; + let present_modes = surface.get_physical_device_surface_present_modes(physical_device)?; + + if formats.is_empty() || present_modes.is_empty() { + return Err(GfxHalError::NoSuitableGpu( + "Swapchain creation failed: No formats or present modes available.".to_string(), + )); + } + + let surface_format = Self::choose_surface_format(&formats, config.desired_format); + let present_mode = Self::choose_present_mode(&present_modes, config.desired_present_mode); + let extent = Self::choose_extent(capabilities, config.extent); + let image_count = Self::choose_image_count(capabilities, config.desired_image_count); + + tracing::info!("Creating swapchain: Format={:?}, ColorSpace={:?}, PresentMode={:?}, Extent={:?}, ImageCount={}", surface_format.format, surface_format.color_space, present_mode, extent, image_count); + + let mut create_info = vk::SwapchainCreateInfoKHR::default() + .surface(surface.handle()) + .min_image_count(image_count) + .image_format(surface_format.format) + .image_color_space(surface_format.color_space) + .image_extent(extent) + .image_array_layers(1) + .image_usage(config.image_usage) + .pre_transform(config.pre_transform) + .composite_alpha(config.composite_alpha) + .present_mode(present_mode) + .clipped(true); + + let queue_family_indicies = [device.graphics_queue_family_index()]; + create_info = create_info + .image_sharing_mode(vk::SharingMode::EXCLUSIVE) + .queue_family_indices(&queue_family_indicies); + + if let Some(old) = old_swapchain { + create_info = create_info.old_swapchain(old); + tracing::debug!("Passing old swapchain handle for recreation."); + } + + let swapchain_loader = + SwapchainLoader::new(surface.instance().ash_instance(), device.raw()); + let swapchain = swapchain_loader.create_swapchain(&create_info, None)?; + tracing::info!("Swapchain created successfully."); + + let images = swapchain_loader.get_swapchain_images(swapchain)?; + tracing::debug!("Retrieved {} swapchain images.", images.len()); + + let image_views = Self::create_image_views(device.raw(), &images, surface_format.format)?; + tracing::debug!("Created {} swapchain image views.", image_views.len()); + + Ok(Self { + device, + swapchain_loader, + swapchain, + images, + image_views, + format: surface_format, + extent, + image_count, + }) + } + + /// Acquires the next available image from the swapchain. + /// + /// Returns the index of the acquired image and a boolean indicating if the + /// swapchain is suboptimal (needs recreation). + /// + /// # Safety + /// - `signal_semaphore` and `signal_fence`, if provided, must be valid handles + /// that are not currently waited on by the GPU. + /// - The caller must ensure proper synchronization before using the returned image index. + pub unsafe fn acquire_next_image( + &self, + timeout_ns: u64, + signal_semaphore: Option<&Semaphore>, + signal_fence: Option<&Fence>, + ) -> Result<(u32, bool)> { + let semaphore_handle = signal_semaphore.map_or(vk::Semaphore::null(), |s| s.handle()); + let fence_handle = signal_fence.map_or(vk::Fence::null(), |f| f.handle()); + + match self.swapchain_loader.acquire_next_image( + self.swapchain, + timeout_ns, + semaphore_handle, + fence_handle, + ) { + Ok((image_index, suboptimal)) => Ok((image_index, suboptimal)), + Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => Err(GfxHalError::SurfaceLost), + Err(e) => Err(GfxHalError::VulkanError(e)), + } + } + + /// Gets the raw `vk::SwapchainKHR` handle. + pub fn handle(&self) -> vk::SwapchainKHR { + self.swapchain + } + + /// Gets a reference to the `ash` Swapchain loader extension + pub fn loader(&self) -> &SwapchainLoader { + &self.swapchain_loader + } + + /// Gets the chosen surface format of the swapchain. + pub fn format(&self) -> vk::SurfaceFormatKHR { + self.format + } + + /// Gets the extent (dimensions) of the swapchain image + pub fn extent(&self) -> vk::Extent2D { + self.extent + } + + /// Gets the actual number of images in the swapchain + pub fn image_count(&self) -> u32 { + self.image_count + } + + /// Gets a slice containing the raw `vk::Image` handles. + pub fn images(&self) -> &[vk::Image] { + &self.images + } + + /// Gets a slice containing the raw `vk::ImageView` handles. + pub fn image_views(&self) -> &[vk::ImageView] { + &self.image_views + } + + fn choose_surface_format( + available_formats: &[vk::SurfaceFormatKHR], + desired_format: vk::SurfaceFormatKHR, + ) -> vk::SurfaceFormatKHR { + for format in available_formats { + if format.format == desired_format.format + && format.color_space == desired_format.color_space + { + return *format; + } + } + + tracing::warn!( + "Desired swapchain format {:?}/{:?} not available. Falling back to {:?}/{:?}.", + desired_format.format, + desired_format.color_space, + available_formats[0].format, + available_formats[0].color_space + ); + available_formats[0] + } + + fn choose_present_mode( + available_modes: &[vk::PresentModeKHR], + desired_mode: vk::PresentModeKHR, + ) -> vk::PresentModeKHR { + if desired_mode == vk::PresentModeKHR::MAILBOX + && available_modes.contains(&vk::PresentModeKHR::MAILBOX) + { + return vk::PresentModeKHR::MAILBOX; + } + + if desired_mode == vk::PresentModeKHR::IMMEDIATE + && available_modes.contains(&vk::PresentModeKHR::IMMEDIATE) + { + return vk::PresentModeKHR::IMMEDIATE; + } + + vk::PresentModeKHR::FIFO + } + + fn choose_extent( + capabilities: vk::SurfaceCapabilitiesKHR, + desired_extent: vk::Extent2D, + ) -> vk::Extent2D { + if capabilities.current_extent.width != u32::MAX { + capabilities.current_extent + } else { + vk::Extent2D { + width: desired_extent.width.clamp( + capabilities.min_image_extent.width, + capabilities.max_image_extent.width, + ), + height: desired_extent.height.clamp( + capabilities.min_image_extent.height, + capabilities.max_image_extent.height, + ), + } + } + } + + fn choose_image_count(capabilities: vk::SurfaceCapabilitiesKHR, desired_count: u32) -> u32 { + let mut count = desired_count.max(capabilities.min_image_count); + if capabilities.max_image_count > 0 { + count = count.min(capabilities.max_image_count); + } + count + } + + unsafe fn create_image_views( + device: &ash::Device, + images: &[vk::Image], + format: vk::Format, + ) -> Result> { + images + .iter() + .map(|image| { + let create_info = vk::ImageViewCreateInfo::default() + .image(*image) + .view_type(vk::ImageViewType::TYPE_2D) + .format(format) + .components(vk::ComponentMapping { + r: vk::ComponentSwizzle::IDENTITY, + g: vk::ComponentSwizzle::IDENTITY, + b: vk::ComponentSwizzle::IDENTITY, + a: vk::ComponentSwizzle::IDENTITY, + }) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }); + device.create_image_view(&create_info, None) + }) + .collect::, _>>() + .map_err(GfxHalError::VulkanError) + } + + /// Destroys the image views associated with this swapchain. + /// Called internally by Drop and potentially during recreation. + unsafe fn destory_image_views(&mut self) { + tracing::debug!( + "Destroying {} swapchain image views...", + self.image_views.len() + ); + for view in self.image_views.drain(..) { + self.device.raw().destroy_image_view(view, None); + } + tracing::debug!("Swapchain image views destroyed.") + } +} + +impl Drop for Swapchain { + fn drop(&mut self) { + tracing::debug!("Destroying swapchain..."); + unsafe { + self.destory_image_views(); + self.swapchain_loader + .destroy_swapchain(self.swapchain, None); + } + tracing::debug!("Swapchain destroyed.") + } +} diff --git a/crates/gfx_hal/src/sync.rs b/crates/gfx_hal/src/sync.rs new file mode 100644 index 0000000..c16d1f5 --- /dev/null +++ b/crates/gfx_hal/src/sync.rs @@ -0,0 +1,183 @@ +use std::{sync::Arc, time::Duration}; + +use ash::vk; + +use crate::{ + device::Device, + error::{GfxHalError, Result}, +}; + +/// Wraps a `vk::Fence`, used for CPU-GPU synchronization. +/// +/// Owns the `vk::Fence` handle. +pub struct Fence { + device: Arc, + fence: vk::Fence, +} + +impl Fence { + /// Creates a new `Fence`. + /// + /// # Arguments + /// * `device` - The logical device. + /// * `signaled` - If true, the fence is created in the signaled state. + pub fn new(device: Arc, signaled: bool) -> Result { + let create_flags = if signaled { + vk::FenceCreateFlags::SIGNALED + } else { + vk::FenceCreateFlags::empty() + }; + let create_info = vk::FenceCreateInfo::default().flags(create_flags); + let fence = unsafe { device.raw().create_fence(&create_info, None)? }; + tracing::trace!("Created Fence (signaled: {})", signaled); + Ok(Self { device, fence }) + } + + /// Waits for the fence to become signaled. + /// + /// # Arguments + /// * `timeout` - Maximum duration to wait. `None` waits indefinitely. + pub fn wait(&self, timeout: Option) -> Result<()> { + let timeout_ns = timeout.map_or(u64::MAX, |d| d.as_nanos() as u64); + tracing::trace!("Waiting for Fence with timeout: {:?}", timeout); + let fences = [self.fence]; + match unsafe { self.device.raw().wait_for_fences(&fences, true, timeout_ns) } { + Ok(_) => { + tracing::trace!("Fence signaled."); + Ok(()) + } + Err(vk::Result::TIMEOUT) => { + tracing::trace!("Fence wait timed out."); + Err(GfxHalError::VulkanError(vk::Result::TIMEOUT)) // Return timeout error + } + Err(e) => Err(GfxHalError::VulkanError(e)), + } + } + + /// Resets the fence to the unsignaled state. + /// Must only be called when the fence is not in use by pending GPU work. + pub fn reset(&self) -> Result<()> { + tracing::trace!("Resetting Fence."); + let fences = [self.fence]; + unsafe { self.device.raw().reset_fences(&fences)? }; + Ok(()) + } + + /// Checks the current status of the fence without waiting. + /// Returns `Ok(true)` if signaled, `Ok(false)` if unsignaled. + pub fn status(&self) -> Result { + match unsafe { self.device.raw().get_fence_status(self.fence) } { + Ok(signaled) => Ok(signaled), + // NOT_READY means unsignaled, not an error in this context + Err(vk::Result::NOT_READY) => Ok(false), + Err(e) => Err(GfxHalError::VulkanError(e)), + } + } + + /// Gets the raw `vk::Fence` handle. + pub fn handle(&self) -> vk::Fence { + self.fence + } +} + +impl Drop for Fence { + fn drop(&mut self) { + tracing::trace!("Destroying fence..."); + unsafe { + self.device.raw().destroy_fence(self.fence, None); + } + tracing::trace!("Fence destroyed.") + } +} + +/// Wraps a `vk::Semaphore`, used for GPU-GPU synchronization (within or across queues). +/// +/// Owns the `vk::Semaphore` handle. +pub struct Semaphore { + device: Arc, + semaphore: vk::Semaphore, +} + +impl Semaphore { + /// Creates a new `Semaphore`. + pub fn new(device: Arc) -> Result { + let create_info = vk::SemaphoreCreateInfo::default(); + let semaphore = unsafe { device.raw().create_semaphore(&create_info, None)? }; + tracing::trace!("Created Semaphore."); + Ok(Self { device, semaphore }) + } + + /// Gets the raw `vk::Semaphore` handle. + pub fn handle(&self) -> vk::Semaphore { + self.semaphore + } +} + +impl Drop for Semaphore { + fn drop(&mut self) { + tracing::trace!("Destroying Semaphore..."); + unsafe { + self.device.raw().destroy_semaphore(self.semaphore, None); + } + tracing::trace!("Semaphore destroyed."); + } +} + +/// Wraps a `vk::Event`, used for fine-grained GPU-GPU or GPU-Host synchronization. +/// +/// Owns the `vk::Event` handle. +pub struct Event { + device: Arc, + event: vk::Event, +} + +impl Event { + /// Creates a new `Event`. + pub fn new(device: Arc) -> Result { + let create_info = vk::EventCreateInfo::default(); + let event = unsafe { device.raw().create_event(&create_info, None)? }; + tracing::trace!("Created Event."); + Ok(Self { device, event }) + } + + /// Sets the event from the host (CPU). + pub fn set(&self) -> Result<()> { + tracing::trace!("Setting Event from host."); + unsafe { self.device.raw().set_event(self.event)? }; + Ok(()) + } + + /// Resets the event from the host (CPU). + pub fn reset(&self) -> Result<()> { + tracing::trace!("Resetting Event from host."); + unsafe { self.device.raw().reset_event(self.event)? }; + Ok(()) + } + + /// Checks the status of the event from the host (CPU). + /// Returns `Ok(true)` if set, `Ok(false)` if reset. + pub fn status(&self) -> Result { + let res = unsafe { self.device.raw().get_event_status(self.event) }?; + Ok(res) + } + + /// Gets the raw `vk::Event` handle. + pub fn handle(&self) -> vk::Event { + self.event + } + + // Note: Setting/resetting/waiting on events from the GPU involves + // vkCmdSetEvent, vkCmdResetEvent, vkCmdWaitEvents within command buffers. + // These are not wrapped here but would be used via device.raw() when + // recording command buffers. +} + +impl Drop for Event { + fn drop(&mut self) { + tracing::trace!("Destroying Event..."); + unsafe { + self.device.raw().destroy_event(self.event, None); + } + tracing::trace!("Event destroyed."); + } +} diff --git a/crates/resource_manager/Cargo.toml b/crates/resource_manager/Cargo.toml new file mode 100644 index 0000000..73c615a --- /dev/null +++ b/crates/resource_manager/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "resource_manager" +version = "0.1.0" +edition = "2021" + +[dependencies] +ash.workspace = true +gpu-allocator.workspace = true +thiserror.workspace = true +parking_lot.workspace = true +tracing.workspace = true + +gfx_hal = { path = "../gfx_hal" } diff --git a/crates/resource_manager/src/error.rs b/crates/resource_manager/src/error.rs new file mode 100644 index 0000000..669427d --- /dev/null +++ b/crates/resource_manager/src/error.rs @@ -0,0 +1,48 @@ +use ash::vk; +use thiserror::Error; + +/// Error type for the resource_manager crate. +#[derive(Error, Debug)] +pub enum ResourceManagerError { + #[error("Vulkan API error: {0}")] + VulkanError(#[from] vk::Result), + + #[error("GPU allocation error: {0}")] + AllocationError(#[from] gpu_allocator::AllocationError), + + #[error("Resource handle {0} not found")] + HandleNotFound(u64), + + #[error("Failed to map buffer memory")] + MappingFailed, + + #[error("Buffer is not CPU visible or mapped")] + NotMapped, + + #[error("Failed to find suitable memory type")] + NoSuitableMemoryType, + + #[error("Failed to find a queue supporting transfer operations")] + NoTransferQueue, + + #[error("Staging transfer failed: {0}")] + TransferFailed(String), + + #[error("Resource lock poisoned: {0}")] + LockPoisoned(String), + + #[error("Error occurred in GfxHal: {0}")] + GfxHalError(#[from] gfx_hal::error::GfxHalError), + + #[error("An unexpected error occurred: {0}")] + Other(String), +} + +// Implement conversion from Lock Poison errors +impl From> for ResourceManagerError { + fn from(e: std::sync::PoisonError) -> Self { + ResourceManagerError::LockPoisoned(e.to_string()) + } +} + +pub type Result = std::result::Result; diff --git a/crates/resource_manager/src/lib.rs b/crates/resource_manager/src/lib.rs new file mode 100644 index 0000000..fe476a0 --- /dev/null +++ b/crates/resource_manager/src/lib.rs @@ -0,0 +1,609 @@ +mod error; + +use std::{ + collections::HashMap, + hash::Hash, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, +}; + +use ash::vk; +use gfx_hal::{device::Device, instance::Instance, queue::Queue}; +use tracing::{debug, error, trace, warn}; + +pub use error::{ResourceManagerError, Result}; +use gpu_allocator::{ + vulkan::{Allocation, AllocationCreateDesc, Allocator, AllocatorCreateDesc}, + MemoryLocation, +}; +use parking_lot::Mutex; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct BufferHandle(u64); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ImageHandle(u64); + +#[derive(Debug, Clone)] +pub struct BufferInfo { + pub handle: BufferHandle, + pub buffer: vk::Buffer, + pub size: vk::DeviceSize, + pub usage: vk::BufferUsageFlags, + pub mapped_ptr: Option<*mut u8>, +} + +#[derive(Debug, Clone)] +pub struct ImageInfo { + pub handle: ImageHandle, + /// Non-owning handle. + pub image: vk::Image, + /// Non-owning handle. + pub view: vk::ImageView, + pub format: vk::Format, + pub extent: vk::Extent3D, + pub usage: vk::ImageUsageFlags, + pub layout: vk::ImageLayout, +} + +struct InternalBufferInfo { + device: Arc, // Keep device alive for Drop + allocator: Arc>, // Needed for Drop + buffer: vk::Buffer, + allocation: Option, // Option because it's taken in Drop + size: vk::DeviceSize, + usage: vk::BufferUsageFlags, + mapped_ptr: Option<*mut u8>, + handle: BufferHandle, +} + +impl Drop for InternalBufferInfo { + fn drop(&mut self) { + trace!("Dropping InternalBufferInfo for handle: {:?}", self.handle); + if let Some(allocation) = self.allocation.take() { + let mut allc = self.allocator.lock(); + if let Err(e) = allc.free(allocation) { + error!( + "Failed to free allocation for buffer handle {:?}, {}", + self.handle, e + ); + } else { + trace!("Freed alloation for buffer handle: {:?}", self.handle); + } + } + unsafe { + self.device.raw().destroy_buffer(self.buffer, None); + } + trace!("Destroyed vk::Buffer for handle {:?}", self.handle); + } +} + +struct InternalImageInfo { + device: Arc, // Keep device alive for Drop + allocator: Arc>, // Needed for Drop + image: vk::Image, + view: vk::ImageView, + allocation: Option, // Option because it's taken in Drop + format: vk::Format, + extent: vk::Extent3D, + usage: vk::ImageUsageFlags, + layout: vk::ImageLayout, + handle: ImageHandle, +} + +impl Drop for InternalImageInfo { + fn drop(&mut self) { + trace!("Dropping InternalImageInfo for handle {:?}", self.handle); + // Destroy view first + unsafe { + self.device.raw().destroy_image_view(self.view, None); + } + // Then free memory + if let Some(allocation) = self.allocation.take() { + let mut allocator = self.allocator.lock(); + if let Err(e) = allocator.free(allocation) { + error!( + "Failed to free allocation for image handle {:?}: {}", + self.handle, e + ); + } else { + trace!("Freed allocation for image handle {:?}", self.handle); + } + } + // Then destroy image + unsafe { + self.device.raw().destroy_image(self.image, None); + } + trace!( + "Destroyed vk::Image/vk::ImageView for handle {:?}", + self.handle + ); + } +} + +struct TransferSetup { + command_pool: vk::CommandPool, + queue: Arc, + fence: vk::Fence, +} + +pub struct ResourceManager { + _instance: Arc, + device: Arc, + allocator: Arc>, + buffers: Mutex>, + images: Mutex>, + next_id: AtomicU64, + transfer_setup: Mutex>, +} + +impl ResourceManager { + /// Creates a new ResourceManager. + pub fn new(instance: Arc, device: Arc) -> Result { + debug!("Initializing ResourceManager..."); + let allocator = Allocator::new(&AllocatorCreateDesc { + instance: instance.ash_instance().clone(), + device: device.raw().clone(), + physical_device: device.physical_device_handle(), + debug_settings: Default::default(), + buffer_device_address: true, + allocation_sizes: Default::default(), + })?; + debug!("GPU Allocator created."); + + Ok(Self { + _instance: instance, + device, + allocator: Arc::new(Mutex::new(allocator)), + buffers: Mutex::new(HashMap::new()), + images: Mutex::new(HashMap::new()), + next_id: AtomicU64::new(1), + transfer_setup: Mutex::new(None), + }) + } + + /// Gets or initializes the TransferSetup resources. + fn get_transfer_setup(&self) -> Result { + let mut setup_guard = self.transfer_setup.lock(); + + if let Some(setup) = setup_guard.as_ref() { + // Simple check: Reset fence before reusing + unsafe { self.device.raw().reset_fences(&[setup.fence])? }; + return Ok(TransferSetup { + // Return a copy/clone + command_pool: setup.command_pool, + queue: setup.queue.clone(), + fence: setup.fence, + }); + } + + debug!("Initializing TransferSetup..."); + // Find a queue that supports transfer (prefer dedicated, fallback to graphics) + let queue_family_index = self + .device + .transfer_queue_family_index() + .or(self.device.compute_queue_family_index()) // Try compute as fallback + .unwrap_or(self.device.graphics_queue_family_index()); // Graphics as last resort + + let queue = self + .device + .get_queue(queue_family_index, 0) + .ok_or(ResourceManagerError::NoTransferQueue)?; + + // Create command pool for transfer commands + let pool_info = vk::CommandPoolCreateInfo::default() + .flags(vk::CommandPoolCreateFlags::TRANSIENT) // Hint that buffers are short-lived + .queue_family_index(queue_family_index); + let command_pool = unsafe { self.device.raw().create_command_pool(&pool_info, None)? }; + + // Create a fence for waiting + let fence_info = vk::FenceCreateInfo::default(); + let fence = unsafe { self.device.raw().create_fence(&fence_info, None)? }; + + let new_setup = TransferSetup { + command_pool, + queue, + fence, + }; + *setup_guard = Some(new_setup); // Store it + debug!("TransferSetup initialized."); + + // Return a new copy for use + Ok(TransferSetup { + command_pool: setup_guard.as_ref().unwrap().command_pool, + queue: setup_guard.as_ref().unwrap().queue.clone(), + fence: setup_guard.as_ref().unwrap().fence, + }) + } + + /// Helper to allocate, begin, end, submit, and wait for a single command buffer. + unsafe fn submit_commands_and_wait( + &self, + transfer_setup: &TransferSetup, + record_fn: F, + ) -> Result<()> + where + F: FnOnce(vk::CommandBuffer) -> Result<()>, + { + let device = self.device.raw(); + + // Allocate command buffer + let alloc_info = vk::CommandBufferAllocateInfo::default() + .command_pool(transfer_setup.command_pool) + .level(vk::CommandBufferLevel::PRIMARY) + .command_buffer_count(1); + let command_buffer = device.allocate_command_buffers(&alloc_info)?[0]; + + // Begin recording + let begin_info = vk::CommandBufferBeginInfo::default() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); + device.begin_command_buffer(command_buffer, &begin_info)?; + + // Record user commands + let record_result = record_fn(command_buffer); + + // End recording (even if user function failed, to allow cleanup) + device.end_command_buffer(command_buffer)?; + + // Check user function result *after* ending buffer + record_result?; + + let binding = [command_buffer]; + // Submit + let submits = [vk::SubmitInfo::default().command_buffers(&binding)]; + // Use the transfer queue and fence + transfer_setup.queue.submit(&submits, None)?; // Submit without fence initially + + // Wait for completion using a separate wait call + // This avoids holding the queue's internal submit lock during the wait. + let fences = [transfer_setup.fence]; + match device.wait_for_fences(&fences, true, u64::MAX) { + Ok(_) => {} + Err(vk::Result::TIMEOUT) => { + // Should not happen with u64::MAX + warn!("Transfer fence wait timed out unexpectedly."); + return Err(ResourceManagerError::TransferFailed( + "Fence wait timeout".to_string(), + )); + } + Err(e) => return Err(e.into()), + } + + // Free command buffer + device.free_command_buffers(transfer_setup.command_pool, &[command_buffer]); + + Ok(()) + } + + /// Creates a Vulkan buffer and allocates/binds memory for it. + pub fn create_buffer( + &self, + size: vk::DeviceSize, + usage: vk::BufferUsageFlags, + location: MemoryLocation, + ) -> Result { + trace!( + "Creating buffer: size={}, usage={:?}, location={:?}", + size, + usage, + location + ); + let buffer_info = vk::BufferCreateInfo::default() + .size(size) + .usage(usage) + .sharing_mode(vk::SharingMode::EXCLUSIVE); // Assuming exclusive access + + let buffer = unsafe { self.device.raw().create_buffer(&buffer_info, None)? }; + + let requirements = unsafe { self.device.raw().get_buffer_memory_requirements(buffer) }; + + let allocation = self.allocator.lock().allocate(&AllocationCreateDesc { + name: &format!("buffer_usage_{:?}_loc_{:?}", usage, location), + requirements, + location, + linear: true, // Buffers are linear + allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, + })?; + + unsafe { + self.device.raw().bind_buffer_memory( + buffer, + allocation.memory(), + allocation.offset(), + )?; + } + trace!("Buffer memory bound."); + + let mapped_ptr = allocation.mapped_ptr().map(|p| p.as_ptr() as *mut u8); + if mapped_ptr.is_some() { + trace!("Buffer memory is mapped."); + } + + let id = self.next_id.fetch_add(1, Ordering::Relaxed); + let handle = BufferHandle(id); + + let internal_info = InternalBufferInfo { + device: self.device.clone(), + allocator: self.allocator.clone(), + buffer, + allocation: Some(allocation), + size, + usage, + mapped_ptr, + handle, + }; + + self.buffers.lock().insert(id, internal_info); + debug!("Buffer created successfully: handle={:?}", handle); + Ok(handle) + } + + /// Creates a buffer, allocates memory, and uploads initial data using a staging buffer. + pub fn create_buffer_init( + &self, + usage: vk::BufferUsageFlags, // Usage for the *final* buffer + location: MemoryLocation, // Memory location for the *final* buffer + data: &[u8], + ) -> Result { + let size = data.len() as vk::DeviceSize; + if size == 0 { + return Err(ResourceManagerError::Other( + "Cannot create buffer with empty data".to_string(), + )); + } + debug!( + "Creating buffer with init data: size={}, usage={:?}, location={:?}", + size, usage, location + ); + + // 1. Create Staging Buffer + let staging_usage = vk::BufferUsageFlags::TRANSFER_SRC; + let staging_location = MemoryLocation::CpuToGpu; // Mapped memory for upload + let staging_handle = self.create_buffer(size, staging_usage, staging_location)?; + + // 2. Map & Copy data to staging buffer + { + // Scope for buffer info and mapping pointer + let staging_info = self.get_buffer_info(staging_handle)?; + let mapping = staging_info + .mapped_ptr + .ok_or(ResourceManagerError::MappingFailed)?; + unsafe { + std::ptr::copy_nonoverlapping(data.as_ptr(), mapping, data.len()); + } + // If memory is not HOST_COHERENT, need to flush here: + // let mem_range = vk::MappedMemoryRange::builder().memory(...).offset(...).size(size); + // unsafe { self.device.raw().flush_mapped_memory_ranges(&[mem_range])? }; + trace!("Data copied to staging buffer."); + } // staging_info goes out of scope + + // 3. Create Destination Buffer + let final_usage = usage | vk::BufferUsageFlags::TRANSFER_DST; // Add transfer dest usage + let dest_handle = self.create_buffer(size, final_usage, location)?; + + // 4. Record and submit transfer command + let transfer_setup = self.get_transfer_setup()?; + let dest_info = self.get_buffer_info(dest_handle)?; // Get info for vk::Buffer handle + let staging_info_for_copy = self.get_buffer_info(staging_handle)?; // Get info again + + trace!("Submitting buffer copy command..."); + unsafe { + self.submit_commands_and_wait(&transfer_setup, |cmd| { + let region = vk::BufferCopy::default() + .src_offset(0) + .dst_offset(0) + .size(size); + self.device.raw().cmd_copy_buffer( + cmd, + staging_info_for_copy.buffer, // Use raw handle from info struct + dest_info.buffer, // Use raw handle from info struct + &[region], + ); + Ok(()) // Return Ok inside the closure + })?; + } + trace!("Buffer copy command finished."); + + // 5. Cleanup staging buffer + self.destroy_buffer(staging_handle)?; // This frees memory and destroys buffer + debug!("Staging buffer destroyed."); + + Ok(dest_handle) + } + + /// Creates a Vulkan image and allocates/binds memory for it. + /// Also creates a default `ImageView`. + /// Does not handle data uploads or layout transitions. + pub fn create_image( + &self, + create_info: &vk::ImageCreateInfo, // User provides image details + location: MemoryLocation, + ) -> Result { + trace!( + "Creating image: format={:?}, extent={:?}, usage={:?}, location={:?}", + create_info.format, + create_info.extent, + create_info.usage, + location + ); + + let image = unsafe { self.device.raw().create_image(create_info, None)? }; + + let requirements = unsafe { self.device.raw().get_image_memory_requirements(image) }; + + let allocation = self.allocator.lock().allocate(&AllocationCreateDesc { + name: &format!( + "image_fmt_{:?}_usage_{:?}", + create_info.format, create_info.usage + ), + requirements, + location, + linear: create_info.tiling == vk::ImageTiling::LINEAR, + allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, + })?; + + unsafe { + self.device + .raw() + .bind_image_memory(image, allocation.memory(), allocation.offset())?; + } + trace!("Image memory bound."); + + // Create a default image view + // TODO: Make view creation more flexible (allow different subresource ranges, types) + let view_info = vk::ImageViewCreateInfo::default() + .image(image) + .view_type(vk::ImageViewType::TYPE_2D) // Assuming 2D, adjust based on create_info + .format(create_info.format) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, // Assuming color, adjust based on usage + base_mip_level: 0, + level_count: create_info.mip_levels, + base_array_layer: 0, + layer_count: create_info.array_layers, + }); + let view = unsafe { self.device.raw().create_image_view(&view_info, None)? }; + trace!("Default image view created."); + + let id = self.next_id.fetch_add(1, Ordering::Relaxed); + let handle = ImageHandle(id); + + let internal_info = InternalImageInfo { + device: self.device.clone(), + allocator: self.allocator.clone(), + image, + view, + allocation: Some(allocation), + format: create_info.format, + extent: create_info.extent, + usage: create_info.usage, + layout: create_info.initial_layout, // Store initial layout + handle, + }; + + self.images.lock().insert(id, internal_info); + debug!("Image created successfully: handle={:?}", handle); + Ok(handle) + } + + // TODO: Implement create_image_init (similar to create_buffer_init but uses vkCmdCopyBufferToImage and layout transitions) + + /// Destroys a buffer and frees its memory. + pub fn destroy_buffer(&self, handle: BufferHandle) -> Result<()> { + debug!("Requesting destroy for buffer handle {:?}", handle); + let mut buffers_map = self.buffers.lock(); + // Remove the entry. The Drop impl of InternalBufferInfo handles the cleanup. + if buffers_map.remove(&handle.0).is_some() { + debug!("Buffer handle {:?} removed for destruction.", handle); + Ok(()) + } else { + warn!( + "Attempted to destroy non-existent buffer handle {:?}", + handle + ); + Err(ResourceManagerError::HandleNotFound(handle.0)) + } + } + + /// Destroys an image, its view, and frees its memory. + pub fn destroy_image(&self, handle: ImageHandle) -> Result<()> { + debug!("Requesting destroy for image handle {:?}", handle); + let mut images_map = self.images.lock(); + // Remove the entry. The Drop impl of InternalImageInfo handles the cleanup. + if images_map.remove(&handle.0).is_some() { + debug!("Image handle {:?} removed for destruction.", handle); + Ok(()) + } else { + warn!( + "Attempted to destroy non-existent image handle {:?}", + handle + ); + Err(ResourceManagerError::HandleNotFound(handle.0)) + } + } + + /// Gets non-owning information about a buffer. + pub fn get_buffer_info(&self, handle: BufferHandle) -> Result { + let buffers_map = self.buffers.lock(); + buffers_map + .get(&handle.0) + .map(|internal| BufferInfo { + handle: internal.handle, + buffer: internal.buffer, + size: internal.size, + usage: internal.usage, + mapped_ptr: internal.mapped_ptr, + }) + .ok_or(ResourceManagerError::HandleNotFound(handle.0)) + } + + /// Gets non-owning information about an image. + pub fn get_image_info(&self, handle: ImageHandle) -> Result { + let images_map = self.images.lock(); + images_map + .get(&handle.0) + .map(|internal| ImageInfo { + handle: internal.handle, + image: internal.image, + view: internal.view, + format: internal.format, + extent: internal.extent, + usage: internal.usage, + layout: internal.layout, // Note: Layout tracking is basic here + }) + .ok_or(ResourceManagerError::HandleNotFound(handle.0)) + } + + /// Explicitly waits for the device to be idle. Useful before shutdown. + pub fn wait_device_idle(&self) -> Result<(), ResourceManagerError> { + self.device + .wait_idle() + .map_err(|e| ResourceManagerError::Other(format!("Device wait idle failed: {}", e))) + } +} + +impl Drop for ResourceManager { + fn drop(&mut self) { + debug!("Destroying ResourceManager..."); + // Ensure all GPU operations are finished before freeing memory/destroying resources + if let Err(e) = self.device.wait_idle() { + error!( + "Failed to wait for device idle during ResourceManager drop: {}", + e + ); + // Proceeding with cleanup, but resources might still be in use! + } + + // Clear resource maps. This triggers the Drop impl for each Internal*Info, + // which frees allocations and destroys Vulkan objects. + let mut buffers_map = self.buffers.lock(); + debug!("Clearing {} buffer entries...", buffers_map.len()); + buffers_map.clear(); + let mut images_map = self.images.lock(); + debug!("Clearing {} image entries...", images_map.len()); + images_map.clear(); + + // Destroy transfer setup resources + let mut setup_guard = self.transfer_setup.lock(); + if let Some(setup) = setup_guard.take() { + // take() removes it from the Option + debug!("Destroying TransferSetup resources..."); + unsafe { + self.device.raw().destroy_fence(setup.fence, None); + self.device + .raw() + .destroy_command_pool(setup.command_pool, None); + } + debug!("TransferSetup resources destroyed."); + } + + // The Allocator is wrapped in an Arc>, so its Drop will be handled + // when the last Arc reference (including those held by Internal*Info) is dropped. + // gpu-allocator's Allocator Drop implementation should be empty, as memory + // is freed via allocator.free(). + + debug!("ResourceManager destroyed."); + } +} diff --git a/crates/shaders-shared/Cargo.toml b/crates/shaders-shared/Cargo.toml deleted file mode 100644 index 8578fdd..0000000 --- a/crates/shaders-shared/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "shaders-shared" -version = "0.1.0" -edition = "2021" - -[dependencies] -spirv-std.workspace = true -bytemuck.workspace = true diff --git a/crates/shaders-shared/src/lib.rs b/crates/shaders-shared/src/lib.rs deleted file mode 100644 index 685527c..0000000 --- a/crates/shaders-shared/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![cfg_attr(target_arch = "spirv", no_std)] - -use spirv_std::glam::{Mat4, Vec3, Vec4}; - -#[derive(Clone, Copy, Debug)] -#[repr(C, align(16))] -pub struct Material { - pub base_color: Vec4, - pub metallic_factor: f32, - pub roughness_factor: f32, - pub _padding: [f32; 2], -} - -#[repr(C, align(16))] -#[derive(Clone)] -pub struct UniformBufferObject { - pub model: Mat4, - pub view: Mat4, - pub proj: Mat4, - pub camera_pos: Vec3, - pub material: Material, -} - -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub struct PushConstants { - pub texture_size: Vec4, -} - -unsafe impl bytemuck::Pod for PushConstants {} -unsafe impl bytemuck::Zeroable for PushConstants {} diff --git a/crates/vk-rs/Cargo.toml b/crates/vk-rs/Cargo.toml deleted file mode 100644 index 5077610..0000000 --- a/crates/vk-rs/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "vk-rs" -version = "0.1.0" -edition = "2021" - -[dependencies] -ash.workspace = true -color-eyre.workspace = true -winit.workspace = true -raw-window-handle.workspace = true -cfg-if = "1.0.0" -cgmath = "0.18.0" -spirv-std.workspace = true -gpu-allocator.workspace = true -egui-ash = { version = "0.4.0", features = ["gpu-allocator"] } -tobj = "4.0.2" -egui = "0.25.0" -ash-window = "0.12.0" -shaders-shared = { path = "../shaders-shared" } -puffin = { git = "https://github.com/EmbarkStudios/puffin", rev = "5ac4e54164ee89bd68c29f288c2b5613afc2c929" } -puffin_egui = { git = "https://github.com/EmbarkStudios/puffin", rev = "5ac4e54164ee89bd68c29f288c2b5613afc2c929" } -gpu-profiler = { git = "https://github.com/zackartz/gpu-profiler", features = [ - "use-ash", -] } -gltf = { version = "1.4.1", features = ["import"] } -image = "0.25.5" -rayon = "1.10.0" -bytemuck.workspace = true -tracing.workspace = true -tracing-subscriber.workspace = true - -[build-dependencies] -shaderc = "0.8" diff --git a/crates/vk-rs/build.rs b/crates/vk-rs/build.rs deleted file mode 100644 index f7cdf2e..0000000 --- a/crates/vk-rs/build.rs +++ /dev/null @@ -1,71 +0,0 @@ -use shaderc::{Compiler, ShaderKind}; -use std::{ - fs::{self, File}, - io::Write, - path::{Path, PathBuf}, -}; - -fn main() -> Result<(), Box> { - // Tell Cargo to rerun if shaders directory changes - println!("cargo:rerun-if-changed=../../shaders"); - - let shader_dir = Path::new("../../shaders"); - let cache_dir = Path::new("../../shader-cache"); - - // Create shader cache directory if it doesn't exist - fs::create_dir_all(cache_dir)?; - - let compiler = Compiler::new().expect("Failed to create shader compiler"); - - // Compile all .vert and .frag files - for entry in fs::read_dir(shader_dir)? { - let entry = entry?; - let path = entry.path(); - - if let Some(extension) = path.extension() { - let kind = match extension.to_str() { - Some("vert") => ShaderKind::Vertex, - Some("frag") => ShaderKind::Fragment, - _ => continue, - }; - - let source = fs::read_to_string(&path)?; - let file_name = path.file_name().unwrap().to_str().unwrap(); - - // Create output path - let spirv_path = cache_dir.join(format!("{}.spv", file_name)); - - // Check if we need to recompile - if should_compile(&path, &spirv_path) { - println!("Compiling shader: {}", file_name); - - let compiled = - compiler.compile_into_spirv(&source, kind, file_name, "main", None)?; - - let mut file = File::create(&spirv_path)?; - file.write_all(compiled.as_binary_u8())?; - } - } - } - - Ok(()) -} - -fn should_compile(source_path: &Path, output_path: &PathBuf) -> bool { - // If output doesn't exist, we need to compile - if !output_path.exists() { - return true; - } - - // Get modification times - let source_modified = fs::metadata(source_path) - .and_then(|m| m.modified()) - .unwrap_or(std::time::SystemTime::UNIX_EPOCH); - - let output_modified = fs::metadata(output_path) - .and_then(|m| m.modified()) - .unwrap_or(std::time::SystemTime::UNIX_EPOCH); - - // Compile if source is newer than output - source_modified > output_modified -} diff --git a/crates/vk-rs/src/main.rs b/crates/vk-rs/src/main.rs deleted file mode 100644 index 686e774..0000000 --- a/crates/vk-rs/src/main.rs +++ /dev/null @@ -1,722 +0,0 @@ -use std::{ - collections::HashSet, - f32::consts::PI, - ffi::CString, - mem::ManuallyDrop, - sync::{Arc, Mutex}, -}; - -use ash::{ - extensions::{ - ext::DebugUtils, - khr::{Surface, Swapchain}, - }, - vk::{self, KhrAccelerationStructureFn, KhrDeferredHostOperationsFn, KhrRayTracingPipelineFn}, - Device, Entry, Instance, -}; -use egui_ash::{ - raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}, - winit, App, AppCreator, AshRenderState, CreationContext, HandleRedraw, RunOption, Theme, -}; -use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc}; -use renderer::Renderer; -use spirv_std::glam::Vec3; - -mod renderer; -mod texture_cache; - -struct Game { - entry: Entry, - instance: Instance, - device: Device, - debug_utils_loader: DebugUtils, - debug_messenger: vk::DebugUtilsMessengerEXT, - physical_device: vk::PhysicalDevice, - surface_loader: Surface, - swapchain_loader: Swapchain, - surface: vk::SurfaceKHR, - queue: vk::Queue, - command_pool: vk::CommandPool, - allocator: ManuallyDrop>>, - - pub renderer: Renderer, - - theme: Theme, - rotate_y: f32, - - camera_position: Vec3, - camera_yaw: f32, - camera_pitch: f32, - camera_fov: f32, - right_mouse_pressed: bool, - last_mouse_pos: Option<(f32, f32)>, - mouse_sensitivity: f32, - - last_fps_update: std::time::Instant, - frame_count_since_last_update: i32, - current_fps: f32, - - bg_color: [f32; 3], - model_color: [f32; 3], - - show_profiler: bool, -} - -impl App for Game { - fn ui(&mut self, ctx: &egui::Context) { - puffin::GlobalProfiler::lock().new_frame(); - let now = std::time::Instant::now(); - self.frame_count_since_last_update += 1; - - if now.duration_since(self.last_fps_update).as_secs_f32() >= 0.1 { - self.current_fps = self.frame_count_since_last_update as f32 - / now.duration_since(self.last_fps_update).as_secs_f32(); - self.frame_count_since_last_update = 0; - self.last_fps_update = now; - } - - if self.show_profiler { - puffin_egui::profiler_window(ctx); - } - - egui::SidePanel::left("my_side_panel").show(ctx, |ui| { - ui.horizontal(|ui| { - ui.label("Theme"); - let id = ui.make_persistent_id("theme_combo_box_side"); - egui::ComboBox::from_id_source(id) - .selected_text(format!("{:?}", self.theme)) - .show_ui(ui, |ui| { - ui.selectable_value(&mut self.theme, Theme::Dark, "Dark"); - ui.selectable_value(&mut self.theme, Theme::Light, "Light"); - }); - }); - - ui.separator(); - - ui.label("Rotate"); - ui.add(egui::widgets::Slider::new( - &mut self.rotate_y, - -180.0..=180.0, - )); - - ui.separator(); - - ui.label("Camera Position"); - ui.horizontal(|ui| { - ui.label("X:"); - ui.add(egui::DragValue::new(&mut self.camera_position.x).speed(0.1)); - }); - ui.horizontal(|ui| { - ui.label("Y:"); - ui.add(egui::DragValue::new(&mut self.camera_position.y).speed(0.1)); - }); - ui.horizontal(|ui| { - ui.label("Z:"); - ui.add(egui::DragValue::new(&mut self.camera_position.z).speed(0.1)); - }); - - ui.separator(); - - ui.label("FOV"); - ui.add(egui::widgets::Slider::new( - &mut self.camera_fov, - 10.0..=150.0, - )); - - ui.separator(); - - ui.label("Mouse Sensitivity"); - ui.add(egui::widgets::Slider::new( - &mut self.mouse_sensitivity, - 0.05..=4.0, - )); - ui.separator(); - - ui.color_edit_button_rgb(&mut self.bg_color); - ui.color_edit_button_rgb(&mut self.model_color); - - ui.separator(); - - if ui.button("Show Profiler").clicked() { - self.show_profiler = !self.show_profiler; - } - ui.label(format!("FPS: {:.1}", self.current_fps)); - }); - - if !ctx.wants_keyboard_input() { - let movement_speed = 0.1; - - let forward = Vec3::new( - self.camera_yaw.sin() * self.camera_pitch.cos(), - self.camera_pitch.sin(), - self.camera_yaw.cos() * self.camera_pitch.cos(), - ) - .normalize(); - - // Calculate right direction - let right = Vec3::new( - (self.camera_yaw + std::f32::consts::FRAC_PI_2).sin(), - 0.0, - (self.camera_yaw + std::f32::consts::FRAC_PI_2).cos(), - ) - .normalize(); - - ctx.input(|i| { - if i.key_down(egui::Key::W) { - self.camera_position += forward * movement_speed; - } - if i.key_down(egui::Key::S) { - self.camera_position -= forward * movement_speed; - } - if i.key_down(egui::Key::A) { - self.camera_position -= right * movement_speed; - } - if i.key_down(egui::Key::D) { - self.camera_position += right * movement_speed; - } - }); - } - - // Handle mouse input for camera rotation - let is_right_mouse_down = ctx.input(|i| i.pointer.secondary_down()); - let is_middle_mouse_down = ctx.input(|i| i.pointer.middle_down()); - let hover_pos = ctx.input(|i| i.pointer.hover_pos()); - - // Set cursor visibility based on right mouse button - // if is_right_mouse_down != self.right_mouse_pressed { - // if is_right_mouse_down { - // ctx.send_viewport_cmd(egui::ViewportCommand::CursorVisible(false)); - // ctx.send_viewport_cmd(egui::ViewportCommand::CursorGrab( - // egui::CursorGrab::Confined, - // )); - // } else { - // ctx.send_viewport_cmd(egui::ViewportCommand::CursorVisible(true)); - // ctx.send_viewport_cmd(egui::ViewportCommand::CursorGrab(egui::CursorGrab::None)); - // } - // } - - self.right_mouse_pressed = is_right_mouse_down; - - match (self.right_mouse_pressed, is_middle_mouse_down) { - (true, false) => { - if let Some(pos) = hover_pos { - if let Some((last_x, last_y)) = self.last_mouse_pos { - let delta_x = pos.x - last_x; - let delta_y = pos.y - last_y; - - // Update camera rotation - let rotation_speed = self.mouse_sensitivity / 100.0; - self.camera_yaw += delta_x * rotation_speed; - self.camera_pitch = (self.camera_pitch - delta_y * rotation_speed) - .clamp(-89.0_f32.to_radians(), 89.0_f32.to_radians()); - } - self.last_mouse_pos = Some((pos.x, pos.y)); - } - } - (false, true) => { - if let Some(pos) = hover_pos { - if let Some((last_x, last_y)) = self.last_mouse_pos { - let delta_x = pos.x - last_x; - let delta_y = pos.y - last_y; - - let move_speed = 0.005; - - self.camera_position.x -= delta_x * move_speed; - self.camera_position.y += delta_y * move_speed; - } - - self.last_mouse_pos = Some((pos.x, pos.y)) - } - } - _ => { - self.last_mouse_pos = None; - } - } - - match self.theme { - Theme::Dark => ctx.set_visuals(egui::style::Visuals::dark()), - Theme::Light => ctx.set_visuals(egui::style::Visuals::light()), - } - } - - fn request_redraw(&mut self, _viewport_id: egui::ViewportId) -> HandleRedraw { - puffin::profile_function!(); - HandleRedraw::Handle(Box::new({ - let renderer = self.renderer.clone(); - let rotate_y = self.rotate_y; - let camera_position = self.camera_position; - let camera_yaw = self.camera_yaw; - let camera_pitch = self.camera_pitch; - let camera_fov = self.camera_fov; - - let bg_color = self.bg_color; - let model_color = self.model_color; - move |size, egui_cmd| { - let mut renderer = renderer.inner.lock().unwrap(); - renderer.update_camera(camera_position, camera_yaw, camera_pitch, camera_fov); - renderer.update_colors(bg_color.into(), model_color.into()); - renderer.render(size.width, size.height, egui_cmd, rotate_y) - } - })) - } -} - -impl Drop for Game { - fn drop(&mut self) { - unsafe { - self.device.device_wait_idle().unwrap(); - self.renderer.destroy(); - self.device.destroy_command_pool(self.command_pool, None); - self.surface_loader.destroy_surface(self.surface, None); - ManuallyDrop::drop(&mut self.allocator); - self.device.destroy_device(None); - if self.debug_messenger != vk::DebugUtilsMessengerEXT::null() { - self.debug_utils_loader - .destroy_debug_utils_messenger(self.debug_messenger, None); - } - self.instance.destroy_instance(None); - } - } -} - -struct MyAppCreator; -impl MyAppCreator { - #[cfg(debug_assertions)] - const ENABLE_VALIDATION_LAYERS: bool = true; - #[cfg(not(debug_assertions))] - const ENABLE_VALIDATION_LAYERS: bool = false; - const VALIDATION: [&'static str; 1] = ["VK_LAYER_KHRONOS_validation"]; - - #[cfg(debug_assertions)] - unsafe extern "system" fn vulkan_debug_utils_callback( - message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, - message_types: vk::DebugUtilsMessageTypeFlagsEXT, - p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT, - _p_user_data: *mut std::ffi::c_void, - ) -> vk::Bool32 { - let types = match message_types { - vk::DebugUtilsMessageTypeFlagsEXT::GENERAL => "[GENERAL]", - vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE => "[PERFORMANCE]", - vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION => "[VALIDATION]", - _ => panic!("[UNKNOWN]"), - }; - - let message = std::ffi::CStr::from_ptr((*p_callback_data).p_message); - match message_severity { - vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => tracing::trace!("{types}{message:?}"), - vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => tracing::warn!("{types}{message:?}"), - vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => tracing::error!("{types}{message:?}"), - vk::DebugUtilsMessageSeverityFlagsEXT::INFO => tracing::info!("{types}{message:?}"), - _ => tracing::debug!("{types}{message:?}"), - }; - - vk::FALSE - } - - #[cfg(not(debug_assertions))] - unsafe extern "system" fn vulkan_debug_utils_callback( - _message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, - _message_types: vk::DebugUtilsMessageTypeFlagsEXT, - _p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT, - _p_user_data: *mut std::ffi::c_void, - ) -> vk::Bool32 { - vk::FALSE - } - - fn create_entry() -> Entry { - unsafe { Entry::load().unwrap() } - } - - fn create_instance( - required_instance_extensions: &[CString], - entry: &Entry, - ) -> (Instance, DebugUtils, vk::DebugUtilsMessengerEXT) { - let mut debug_utils_messenger_create_info = vk::DebugUtilsMessengerCreateInfoEXT::builder() - .flags(vk::DebugUtilsMessengerCreateFlagsEXT::empty()) - .message_severity( - vk::DebugUtilsMessageSeverityFlagsEXT::WARNING - // | vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE - // | vk::DebugUtilsMessageSeverityFlagsEXT::INFO - | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR, - ) - .message_type( - vk::DebugUtilsMessageTypeFlagsEXT::GENERAL - | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE - | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION, - ) - .pfn_user_callback(Some(Self::vulkan_debug_utils_callback)) - .build(); - - let app_name = std::ffi::CString::new("egui-winit-ash example simple").unwrap(); - let app_info = vk::ApplicationInfo::builder() - .application_name(&app_name) - .application_version(vk::make_api_version(1, 0, 0, 0)) - .api_version(vk::API_VERSION_1_2); - let mut extension_names = vec![DebugUtils::name().as_ptr()]; - for ext in required_instance_extensions { - let name = ext.as_ptr(); - extension_names.push(name); - } - let raw_layer_names = Self::VALIDATION - .iter() - .map(|l| std::ffi::CString::new(*l).unwrap()) - .collect::>(); - let layer_names = raw_layer_names - .iter() - .map(|l| l.as_ptr()) - .collect::>(); - let instance_create_info = vk::InstanceCreateInfo::builder() - .push_next(&mut debug_utils_messenger_create_info) - .application_info(&app_info) - .enabled_extension_names(&extension_names); - let instance_create_info = if Self::ENABLE_VALIDATION_LAYERS { - instance_create_info.enabled_layer_names(&layer_names) - } else { - instance_create_info - }; - let instance = unsafe { - entry - .create_instance(&instance_create_info, None) - .expect("Failed to create instance") - }; - - // setup debug utils - let debug_utils_loader = DebugUtils::new(entry, &instance); - let debug_messenger = if Self::ENABLE_VALIDATION_LAYERS { - unsafe { - debug_utils_loader - .create_debug_utils_messenger(&debug_utils_messenger_create_info, None) - .expect("Failed to create debug utils messenger") - } - } else { - vk::DebugUtilsMessengerEXT::null() - }; - - (instance, debug_utils_loader, debug_messenger) - } - - fn create_surface_loader(entry: &Entry, instance: &Instance) -> Surface { - Surface::new(entry, instance) - } - - fn create_swapchain_loader(instance: &Instance, device: &Device) -> Swapchain { - Swapchain::new(instance, device) - } - - fn create_surface( - entry: &Entry, - instance: &Instance, - window: &winit::window::Window, - ) -> vk::SurfaceKHR { - unsafe { - ash_window::create_surface( - entry, - instance, - window.raw_display_handle(), - window.raw_window_handle(), - None, - ) - .expect("Failed to create surface") - } - } - - fn create_physical_device( - instance: &Instance, - surface_loader: &Surface, - surface: vk::SurfaceKHR, - required_device_extensions: &[CString], - ) -> ( - vk::PhysicalDevice, - vk::PhysicalDeviceProperties, - vk::PhysicalDeviceMemoryProperties, - u32, - ) { - let mut queue_family_index: Option = None; - - let (physical_device, phsyical_device_properties, physical_device_memory_properties) = { - let physical_devices = unsafe { - instance - .enumerate_physical_devices() - .expect("Failed to enumerate physical devices") - }; - let physical_device = physical_devices.into_iter().find(|physical_device| { - let queue_families = unsafe { - instance.get_physical_device_queue_family_properties(*physical_device) - }; - for (i, queue_family) in queue_families.iter().enumerate() { - let mut graphics_queue = false; - let mut present_queue = false; - if queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS) { - graphics_queue = true; - } - let present_support = unsafe { - surface_loader - .get_physical_device_surface_support( - *physical_device, - i as u32, - surface, - ) - .unwrap() - }; - if present_support { - present_queue = true; - } - if graphics_queue && present_queue { - queue_family_index = Some(i); - break; - } - } - let is_queue_family_supported = queue_family_index.is_some(); - - // check device extensions - let device_extensions = unsafe { - instance - .enumerate_device_extension_properties(*physical_device) - .unwrap() - }; - let mut device_extensions_name = vec![]; - for device_extension in device_extensions { - let name = unsafe { - std::ffi::CStr::from_ptr(device_extension.extension_name.as_ptr()) - .to_owned() - }; - device_extensions_name.push(name); - } - let mut required_extensions = HashSet::new(); - for extension in required_device_extensions.iter() { - required_extensions.insert(extension.to_owned()); - } - for extension_name in device_extensions_name { - required_extensions.remove(&extension_name); - } - let is_device_extension_supported = required_extensions.is_empty(); - - // check swapchain support - let surface_formats = unsafe { - surface_loader - .get_physical_device_surface_formats(*physical_device, surface) - .unwrap() - }; - let surface_present_modes = unsafe { - surface_loader - .get_physical_device_surface_present_modes(*physical_device, surface) - .unwrap() - }; - let is_swapchain_supported = - !surface_formats.is_empty() && !surface_present_modes.is_empty(); - - is_queue_family_supported && is_swapchain_supported && is_device_extension_supported - }); - let physical_device = physical_device.expect("Failed to get physical device"); - let physical_device_memory_properties = - unsafe { instance.get_physical_device_memory_properties(physical_device) }; - - let physical_device_properties = - unsafe { instance.get_physical_device_properties(physical_device) }; - - ( - physical_device, - physical_device_properties, - physical_device_memory_properties, - ) - }; - - ( - physical_device, - phsyical_device_properties, - physical_device_memory_properties, - queue_family_index.unwrap() as u32, - ) - } - - fn create_device( - instance: &Instance, - physical_device: vk::PhysicalDevice, - queue_family_index: u32, - required_device_extensions: &[CString], - ) -> (Device, vk::Queue) { - let queue_priorities = [1.0_f32]; - let mut queue_create_infos = vec![]; - let queue_create_info = vk::DeviceQueueCreateInfo::builder() - .queue_family_index(queue_family_index) - .queue_priorities(&queue_priorities) - .build(); - queue_create_infos.push(queue_create_info); - - let physical_device_features = vk::PhysicalDeviceFeatures::builder() - .sampler_anisotropy(true) - .build(); - - let enable_extension_names = required_device_extensions - .iter() - .map(|s| s.as_ptr()) - .collect::>(); - - // device create info - let device_create_info = vk::DeviceCreateInfo::builder() - .queue_create_infos(&queue_create_infos) - .enabled_features(&physical_device_features) - .enabled_extension_names(&enable_extension_names); - - // create device - let device = unsafe { - instance - .create_device(physical_device, &device_create_info, None) - .expect("Failed to create device") - }; - - // get device queue - let queue = unsafe { device.get_device_queue(queue_family_index, 0) }; - - (device, queue) - } - - fn create_command_pool(device: &Device, queue_family_index: u32) -> vk::CommandPool { - let command_pool_create_info = vk::CommandPoolCreateInfo::builder() - .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER) - .queue_family_index(queue_family_index); - unsafe { - device - .create_command_pool(&command_pool_create_info, None) - .expect("Failed to create command pool") - } - } -} - -impl AppCreator>> for MyAppCreator { - type App = Game; - - fn create(&self, cc: CreationContext) -> (Self::App, AshRenderState>>) { - // create vk objects - let entry = Self::create_entry(); - let (instance, debug_utils_loader, debug_messenger) = - Self::create_instance(&cc.required_instance_extensions, &entry); - let surface_loader = Self::create_surface_loader(&entry, &instance); - let surface = Self::create_surface(&entry, &instance, cc.main_window); - let mut req_ext = vec![ - KhrDeferredHostOperationsFn::name().to_owned(), - KhrRayTracingPipelineFn::name().to_owned(), - KhrAccelerationStructureFn::name().to_owned(), - ]; - for ext in &cc.required_device_extensions { - req_ext.push(ext.to_owned()); - } - let ( - physical_device, - _physical_device_properties, - _physical_device_memory_properties, - queue_family_index, - ) = Self::create_physical_device(&instance, &surface_loader, surface, &req_ext); - let (device, queue) = Self::create_device( - &instance, - physical_device, - queue_family_index, - &cc.required_device_extensions, - ); - let swapchain_loader = Self::create_swapchain_loader(&instance, &device); - let command_pool = Self::create_command_pool(&device, queue_family_index); - - // create allocator - let allocator = { - Allocator::new(&AllocatorCreateDesc { - instance: instance.clone(), - device: device.clone(), - physical_device, - debug_settings: Default::default(), - buffer_device_address: false, - allocation_sizes: Default::default(), - }) - .expect("Failed to create allocator") - }; - let allocator = Arc::new(Mutex::new(allocator)); - - // setup context - cc.context.set_visuals(egui::style::Visuals::dark()); - - let app = Game { - entry, - instance, - device: device.clone(), - debug_utils_loader, - debug_messenger, - physical_device, - surface_loader: surface_loader.clone(), - swapchain_loader: swapchain_loader.clone(), - surface, - queue, - command_pool, - allocator: ManuallyDrop::new(allocator.clone()), - - show_profiler: false, - - renderer: Renderer::new( - physical_device, - device, - surface_loader, - swapchain_loader, - allocator.clone(), - surface, - queue_family_index, - queue, - command_pool, - 1000, - 800, - ), - - theme: if cc.context.style().visuals.dark_mode { - Theme::Dark - } else { - Theme::Light - }, - rotate_y: 0.0, - - mouse_sensitivity: 0.4, - - camera_position: Vec3::new(0.0, 0.0, -5.0), - camera_pitch: 0., - camera_yaw: 0., - camera_fov: 90., - bg_color: Vec3::splat(0.1).into(), - model_color: Vec3::splat(0.8).into(), - last_mouse_pos: None, - right_mouse_pressed: false, - last_fps_update: std::time::Instant::now(), - frame_count_since_last_update: 0, - current_fps: 0.0, - }; - let ash_render_state = AshRenderState { - entry: app.entry.clone(), - instance: app.instance.clone(), - physical_device: app.physical_device, - device: app.device.clone(), - surface_loader: app.surface_loader.clone(), - swapchain_loader: app.swapchain_loader.clone(), - queue: app.queue, - queue_family_index, - command_pool: app.command_pool, - allocator: allocator.clone(), - }; - - (app, ash_render_state) - } -} - -fn main() -> std::process::ExitCode { - tracing_subscriber::fmt().pretty().init(); - puffin::set_scopes_on(true); - - egui_ash::run( - "vulkan", - MyAppCreator, - RunOption { - viewport_builder: Some( - egui::ViewportBuilder::default() - .with_title("vulkan") - .with_inner_size(egui::vec2(1000.0, 800.0)), - ), - follow_system_theme: false, - default_theme: Theme::Dark, - ..Default::default() - }, - ) -} diff --git a/crates/vk-rs/src/renderer.rs b/crates/vk-rs/src/renderer.rs deleted file mode 100644 index d0098a4..0000000 --- a/crates/vk-rs/src/renderer.rs +++ /dev/null @@ -1,2207 +0,0 @@ -use ash::{ - extensions::khr::{Surface, Swapchain}, - vk::{self, Buffer, DescriptorType}, - Device, -}; -use egui::Vec2; -use egui_ash::EguiCommand; -use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, Allocator}; -use shaders_shared::{Material, PushConstants, UniformBufferObject}; -use spirv_std::glam::{Mat4, Vec3, Vec4}; -use std::{ - ffi::CString, - mem::ManuallyDrop, - panic, - path::Path, - sync::{Arc, Mutex}, - time::Instant, -}; - -use crate::texture_cache::TextureCache; - -macro_rules! include_spirv { - ($file:literal) => {{ - let bytes = include_bytes!($file); - bytes - .chunks_exact(4) - .map(|x| x.try_into().unwrap()) - .map(match bytes[0] { - 0x03 => u32::from_le_bytes, - 0x07 => u32::from_be_bytes, - _ => panic!("Unknown endianness"), - }) - .collect::>() - }}; -} - -pub struct DefaultTextures { - white: Texture, // Default albedo (white) - metallic_roughness: Texture, // Default metallic-roughness (black metallic, 0.5 roughness) - normal: Texture, // Default normal map (flat normal) - sampler: vk::Sampler, // Common sampler for all textures -} - -#[repr(C)] -#[derive(Debug, Clone)] -struct Vertex { - position: Vec3, - normal: Vec3, - tex_coords: Vec2, -} -impl Vertex { - fn get_binding_descriptions() -> [vk::VertexInputBindingDescription; 1] { - [vk::VertexInputBindingDescription::builder() - .binding(0) - .stride(std::mem::size_of::() as u32) - .input_rate(vk::VertexInputRate::VERTEX) - .build()] - } - - fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 3] { - [ - vk::VertexInputAttributeDescription::builder() - .binding(0) - .location(0) - .format(vk::Format::R32G32B32_SFLOAT) - .offset(0) - .build(), - vk::VertexInputAttributeDescription::builder() - .binding(0) - .location(1) - .format(vk::Format::R32G32B32_SFLOAT) - .offset(4 * 3) - .build(), - vk::VertexInputAttributeDescription::builder() - .binding(0) - .location(2) - .format(vk::Format::R32G32_SFLOAT) - .offset(24) - .build(), - ] - } -} - -pub struct Texture { - image: vk::Image, - image_allocation: Option, - image_view: vk::ImageView, - sampler: vk::Sampler, -} - -impl Texture { - fn new( - device: &Device, - allocator: Arc>, - command_pool: vk::CommandPool, - queue: vk::Queue, - width: u32, - height: u32, - data: &[u8], - ) -> Self { - let mut allocator = allocator.lock().unwrap(); - - let buffer_size = data.len() as u64; - - tracing::info!("DATA LEN: {}", buffer_size); - let staging_buffer = unsafe { - device - .create_buffer( - &vk::BufferCreateInfo::builder() - .size(buffer_size) - .usage(vk::BufferUsageFlags::TRANSFER_SRC), - None, - ) - .expect("failed to create_buffer") - }; - - let staging_allocation = allocator - .allocate(&gpu_allocator::vulkan::AllocationCreateDesc { - name: "staging_buffer", - requirements: unsafe { device.get_buffer_memory_requirements(staging_buffer) }, - location: gpu_allocator::MemoryLocation::CpuToGpu, - linear: true, - allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, - }) - .expect("failed to allocate memory"); - - unsafe { - device - .bind_buffer_memory( - staging_buffer, - staging_allocation.memory(), - staging_allocation.offset(), - ) - .expect("failed to bind_buffer_memory"); - - let ptr = staging_allocation.mapped_ptr().unwrap().as_ptr() as *mut u8; - ptr.copy_from_nonoverlapping(data.as_ptr(), data.len()); - } - - let image = unsafe { - device - .create_image( - &vk::ImageCreateInfo::builder() - .image_type(vk::ImageType::TYPE_2D) - .format(vk::Format::R8G8B8A8_SRGB) - .extent(vk::Extent3D { - width, - height, - depth: 1, - }) - .mip_levels(1) - .array_layers(1) - .samples(vk::SampleCountFlags::TYPE_1) - .tiling(vk::ImageTiling::OPTIMAL) - .usage(vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED) - .initial_layout(vk::ImageLayout::UNDEFINED), - None, - ) - .expect("failed to create image") - }; - - let image_allocation = allocator - .allocate(&AllocationCreateDesc { - name: "texture image", - requirements: unsafe { device.get_image_memory_requirements(image) }, - location: gpu_allocator::MemoryLocation::GpuOnly, - linear: true, - allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, - }) - .expect("failed to allocate memory"); - - unsafe { - device - .bind_image_memory(image, image_allocation.memory(), image_allocation.offset()) - .expect("failed to bind image memory") - }; - - unsafe { - let command_buffer = device - .allocate_command_buffers( - &vk::CommandBufferAllocateInfo::builder() - .command_pool(command_pool) - .level(vk::CommandBufferLevel::PRIMARY) - .command_buffer_count(1), - ) - .expect("failed to allocate command_buffer")[0]; - - device - .begin_command_buffer( - command_buffer, - &vk::CommandBufferBeginInfo::builder() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), - ) - .expect("failed to begin_command_buffer"); - - let barrier = vk::ImageMemoryBarrier::builder() - .old_layout(vk::ImageLayout::UNDEFINED) - .new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) - .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED) - .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED) - .image(image) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }) - .src_access_mask(vk::AccessFlags::empty()) - .dst_access_mask(vk::AccessFlags::TRANSFER_WRITE); - - device.cmd_pipeline_barrier( - command_buffer, - vk::PipelineStageFlags::TOP_OF_PIPE, - vk::PipelineStageFlags::TRANSFER, - vk::DependencyFlags::empty(), - &[], - &[], - &[barrier.build()], - ); - - let region = vk::BufferImageCopy::builder() - .image_subresource(vk::ImageSubresourceLayers { - aspect_mask: vk::ImageAspectFlags::COLOR, - mip_level: 0, - base_array_layer: 0, - layer_count: 1, - }) - .image_extent(vk::Extent3D { - width, - height, - depth: 1, - }) - .buffer_offset(0) - .buffer_row_length(width) - .buffer_image_height(height); - - device.cmd_copy_buffer_to_image( - command_buffer, - staging_buffer, - image, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &[region.build()], - ); - - let barrier = vk::ImageMemoryBarrier::builder() - .old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) - .new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) - .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED) - .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED) - .image(image) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }) - .src_access_mask(vk::AccessFlags::TRANSFER_WRITE) - .dst_access_mask(vk::AccessFlags::SHADER_READ); - - device.cmd_pipeline_barrier( - command_buffer, - vk::PipelineStageFlags::TRANSFER, - vk::PipelineStageFlags::FRAGMENT_SHADER, - vk::DependencyFlags::empty(), - &[], - &[], - &[barrier.build()], - ); - - device - .end_command_buffer(command_buffer) - .expect("failed to end the command buffer"); - - device - .queue_submit( - queue, - &[vk::SubmitInfo::builder() - .command_buffers(&[command_buffer]) - .build()], - vk::Fence::null(), - ) - .expect("failed to submit queue"); - - device.queue_wait_idle(queue).expect("failed to wait queue"); - - device.free_command_buffers(command_pool, &[command_buffer]); - - device.destroy_buffer(staging_buffer, None); - allocator - .free(staging_allocation) - .expect("failed to free memory"); - }; - - let image_view = unsafe { - device - .create_image_view( - &vk::ImageViewCreateInfo::builder() - .image(image) - .view_type(vk::ImageViewType::TYPE_2D) - .format(vk::Format::R8G8B8A8_SRGB) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }), - None, - ) - .expect("failed to create image view") - }; - - let sampler = unsafe { - device - .create_sampler( - &vk::SamplerCreateInfo::builder() - .mag_filter(vk::Filter::LINEAR) - .min_filter(vk::Filter::LINEAR) - .address_mode_u(vk::SamplerAddressMode::REPEAT) - .address_mode_v(vk::SamplerAddressMode::REPEAT) - .address_mode_w(vk::SamplerAddressMode::REPEAT) - .anisotropy_enable(true) - .max_anisotropy(16.0) - .border_color(vk::BorderColor::INT_OPAQUE_BLACK) - .unnormalized_coordinates(false) - .compare_enable(false) - .compare_op(vk::CompareOp::ALWAYS) - .mipmap_mode(vk::SamplerMipmapMode::LINEAR) - .mip_lod_bias(0.0) - .min_lod(0.0) - .max_lod(0.0), - None, - ) - .expect("failed to create sampler") - }; - - Self { - image, - image_allocation: Some(image_allocation), - image_view, - sampler, - } - } - - pub fn destroy(&mut self, device: &Device, allocator: &mut Allocator) { - unsafe { - device.destroy_sampler(self.sampler, None); - device.destroy_image_view(self.image_view, None); - device.destroy_image(self.image, None); - } - if let Some(allocation) = self.image_allocation.take() { - allocator.free(allocation).expect("failed to free memory"); - } - } -} - -pub struct Mesh { - vertex_buffer: Buffer, - vertex_buffer_allocation: Option, - vertex_count: u32, - transform: Mat4, - albedo_texture: Option>, - metallic_roughness_texture: Option>, - normal_texture: Option>, - metallic_factor: f32, - roughness_factor: f32, - base_color: Vec4, - descriptor_sets: Vec, -} - -pub struct Model { - meshes: Vec, - texture_cache: TextureCache, -} - -impl Model { - fn destroy(&mut self, device: &Device, allocator: &mut Allocator) { - for mesh in &mut self.meshes { - unsafe { - device.destroy_buffer(mesh.vertex_buffer, None); - if let Some(vertex_buffer_allocation) = mesh.vertex_buffer_allocation.take() { - allocator - .free(vertex_buffer_allocation) - .expect("Failed to free memory"); - } - self.texture_cache.cleanup(device, allocator); - } - } - } -} - -fn resize_texture(image: &image::DynamicImage, max_dimension: u32) -> image::DynamicImage { - let width = image.width(); - let height = image.height(); - - if width <= max_dimension && height <= max_dimension { - return image.clone(); - } - - let aspect_ratio = width as f32 / height as f32; - let (new_width, new_height) = if width > height { - let w = max_dimension; - let h = (w as f32 / aspect_ratio) as u32; - (w, h) - } else { - let h = max_dimension; - let w = (h as f32 * aspect_ratio) as u32; - (w, h) - }; - - image.resize(new_width, new_height, image::imageops::FilterType::Lanczos3) -} - -fn load_texture_from_gltf( - device: &Device, - allocator: Arc>, - command_pool: vk::CommandPool, - queue: vk::Queue, - texture: &gltf::Texture, - buffers: &[gltf::buffer::Data], - path: &Path, -) -> Option { - let img_data = - gltf::image::Data::from_source(texture.source().source(), Some(path), buffers).ok()?; - - tracing::info!("WIDTH: {}", img_data.width); - tracing::info!("HEIGHT: {}", img_data.height); - - // Resize texture if needed - let image = image::DynamicImage::ImageRgb8( - image::RgbImage::from_raw(img_data.width, img_data.height, img_data.pixels).unwrap(), - ); - - const MAX_TEXTURE_DIMENSION: u32 = 2048; // Adjust this value based on your needs - let resized = resize_texture(&image, MAX_TEXTURE_DIMENSION); - - let pixels_rgba = resized.to_rgba8().into_raw(); - - Some(Texture::new( - device, - allocator, - command_pool, - queue, - resized.width(), - resized.height(), - &pixels_rgba, - )) -} - -impl Model { - fn load( - device: &Device, - allocator: Arc>, - command_pool: vk::CommandPool, - queue: vk::Queue, - path: &str, - ) -> Self { - let path = Path::new(path); - let base_path = path.parent(); - let (document, buffers, _) = gltf::import(path).expect("failed to load GLTF"); - let mut meshes = Vec::new(); - let mut texture_cache = TextureCache::new(); - - for scene in document.scenes() { - for node in scene.nodes() { - meshes.extend(process_node( - node, - Mat4::IDENTITY, - &buffers, - device, - allocator.clone(), - command_pool, - queue, - base_path.unwrap(), - &mut texture_cache, - )); - } - } - - Self { - meshes, - texture_cache, - } - } -} - -fn process_node( - node: gltf::Node, - parent_transform: Mat4, - buffers: &[gltf::buffer::Data], - device: &Device, - allocator: Arc>, - command_pool: vk::CommandPool, - queue: vk::Queue, - path: &Path, - texture_cache: &mut TextureCache, -) -> Vec { - let mut meshes = Vec::new(); - - let local_transform = { - let (translation, rotation, scale) = node.transform().decomposed(); - Mat4::from_scale_rotation_translation( - Vec3::from(scale), - spirv_std::glam::Quat::from_array(rotation), - Vec3::from(translation), - ) - }; - let world_transform = parent_transform * local_transform; - - if let Some(mesh) = node.mesh() { - for primitive in mesh.primitives() { - let material = primitive.material(); - let pbr = material.pbr_metallic_roughness(); - - let base_color = pbr.base_color_factor(); - let metallic_factor = pbr.metallic_factor(); - let roughness_factor = pbr.roughness_factor(); - - let albedo_texture = pbr.base_color_texture().and_then(|tex| { - texture_cache.get_or_load_texture( - format!("albedo_{:?}", tex.texture().source().source()), - || { - load_texture_from_gltf( - device, - allocator.clone(), - command_pool, - queue, - &tex.texture(), - buffers, - path, - ) - }, - ) - }); - - let metallic_roughness_texture = pbr.base_color_texture().and_then(|tex| { - texture_cache.get_or_load_texture( - format!("mr_{:?}", tex.texture().source().source()), - || { - load_texture_from_gltf( - device, - allocator.clone(), - command_pool, - queue, - &tex.texture(), - buffers, - path, - ) - }, - ) - }); - - let normal_texture = pbr.base_color_texture().and_then(|tex| { - texture_cache.get_or_load_texture( - format!("norm_{:?}", tex.texture().source().source()), - || { - load_texture_from_gltf( - device, - allocator.clone(), - command_pool, - queue, - &tex.texture(), - buffers, - path, - ) - }, - ) - }); - - let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); - - if let (Some(positions), Some(normals), Some(tex_coords)) = ( - reader.read_positions(), - reader.read_normals(), - reader.read_tex_coords(0), - ) { - let mut vertices = Vec::new(); - - let indices = reader - .read_indices() - .map(|indices| indices.into_u32().collect::>()) - .unwrap_or_else(|| (0..positions.len() as u32).collect()); - - let positions: Vec<_> = positions.collect(); - let normals: Vec<_> = normals.collect(); - let tex_coords: Vec<_> = tex_coords.into_f32().collect(); - - for &index in indices.iter() { - let i = index as usize; - // Apply world transform to position - let position = world_transform.transform_point3(Vec3::new( - positions[i][0], - positions[i][1], - positions[i][2], - )); - let normal = world_transform.transform_vector3(Vec3::new( - normals[i][0], - normals[i][1], - normals[i][2], - )); - - let vertex = Vertex { - position, - normal, - tex_coords: Vec2::new(tex_coords[i][0], tex_coords[i][1]), - }; - vertices.push(vertex); - } - - let (vertex_buffer, vertex_buffer_allocation) = - create_vertex_buffer(device, allocator.clone(), command_pool, queue, &vertices); - - meshes.push(Mesh { - vertex_buffer, - vertex_buffer_allocation: Some(vertex_buffer_allocation), - vertex_count: vertices.len() as u32, - albedo_texture, - metallic_roughness_texture, - normal_texture, - metallic_factor, - roughness_factor, - base_color: base_color.into(), - transform: Mat4::IDENTITY, - descriptor_sets: Vec::new(), - }); - } - } - } - - for child in node.children() { - meshes.extend(process_node( - child, - world_transform, - buffers, - device, - allocator.clone(), - command_pool, - queue, - path, - texture_cache, - )); - } - - meshes -} - -fn create_vertex_buffer( - device: &Device, - allocator: Arc>, - command_pool: vk::CommandPool, - queue: vk::Queue, - vertices: &[Vertex], -) -> (Buffer, Allocation) { - let mut allocator = allocator.lock().unwrap(); - - let vertex_buffer_size = std::mem::size_of_val(vertices) as u64; - - let staging_buffer = unsafe { - device - .create_buffer( - &vk::BufferCreateInfo::builder() - .size(vertex_buffer_size) - .usage(vk::BufferUsageFlags::TRANSFER_SRC), - None, - ) - .expect("failed to create buffer") - }; - - let staging_allocation = allocator - .allocate(&AllocationCreateDesc { - name: "Staging vertex buffer", - requirements: unsafe { device.get_buffer_memory_requirements(staging_buffer) }, - location: gpu_allocator::MemoryLocation::CpuToGpu, - linear: true, - allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, - }) - .expect("failed to allocate memory"); - - unsafe { - device - .bind_buffer_memory( - staging_buffer, - staging_allocation.memory(), - staging_allocation.offset(), - ) - .expect("failed to bind buffer memory"); - - let ptr = staging_allocation.mapped_ptr().unwrap().as_ptr() as *mut Vertex; - ptr.copy_from_nonoverlapping(vertices.as_ptr(), vertices.len()); - } - - let vertex_buffer = unsafe { - device - .create_buffer( - &vk::BufferCreateInfo::builder() - .size(vertex_buffer_size) - .usage( - vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, - ), - None, - ) - .expect("failed to create buffer") - }; - - let vertex_allocation = allocator - .allocate(&AllocationCreateDesc { - name: "Vertex Buffer", - requirements: unsafe { device.get_buffer_memory_requirements(vertex_buffer) }, - location: gpu_allocator::MemoryLocation::GpuOnly, - linear: true, - allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, - }) - .expect("failed to allocate memory"); - - unsafe { - device - .bind_buffer_memory( - vertex_buffer, - vertex_allocation.memory(), - vertex_allocation.offset(), - ) - .expect("failed to bind buffer memory"); - } - - let command_buffer = unsafe { - device - .allocate_command_buffers( - &vk::CommandBufferAllocateInfo::builder() - .command_pool(command_pool) - .level(vk::CommandBufferLevel::PRIMARY) - .command_buffer_count(1), - ) - .expect("failed to allocate command buffer")[0] - }; - - unsafe { - device - .begin_command_buffer( - command_buffer, - &vk::CommandBufferBeginInfo::builder() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT) - .build(), - ) - .expect("failed to begin command_buffer"); - - device.cmd_copy_buffer( - command_buffer, - staging_buffer, - vertex_buffer, - &[vk::BufferCopy::builder().size(vertex_buffer_size).build()], - ); - - device - .end_command_buffer(command_buffer) - .expect("Failed to end command buffer"); - - device - .queue_submit( - queue, - &[vk::SubmitInfo::builder() - .command_buffers(&[command_buffer]) - .build()], - vk::Fence::null(), - ) - .expect("failed to submit queue"); - - device.queue_wait_idle(queue).expect("failed to wait queue"); - device.free_command_buffers(command_pool, &[command_buffer]); - } - - unsafe { - device.destroy_buffer(staging_buffer, None); - } - allocator - .free(staging_allocation) - .expect("failed to free memory"); - - (vertex_buffer, vertex_allocation) -} - -pub struct RendererInner { - width: u32, - height: u32, - - physical_device: vk::PhysicalDevice, - device: Device, - surface_loader: Surface, - swapchain_loader: Swapchain, - allocator: ManuallyDrop>>, - surface: vk::SurfaceKHR, - queue: vk::Queue, - - swapchain: vk::SwapchainKHR, - surface_format: vk::SurfaceFormatKHR, - surface_extent: vk::Extent2D, - swapchain_images: Vec, - uniform_buffers: Vec, - uniform_buffer_allocations: Vec, - descriptor_pool: vk::DescriptorPool, - descriptor_set_layouts: Vec, - render_pass: vk::RenderPass, - framebuffers: Vec, - depth_images_and_allocations: Vec<(vk::Image, Allocation)>, - color_image_views: Vec, - depth_image_views: Vec, - pipeline: vk::Pipeline, - pipeline_layout: vk::PipelineLayout, - - model: Model, - - command_buffers: Vec, - in_flight_fences: Vec, - image_available_semaphores: Vec, - render_finished_semaphores: Vec, - current_frame: usize, - dirty_swapchain: bool, - - frame_counter: u32, - camera_position: Vec3, - camera_fov: f32, - camera_yaw: f32, - camera_pitch: f32, - bg_color: Vec3, - model_color: Vec3, - accumulation_reset_needed: bool, -} - -struct CreateFramebuffersResult( - Vec, - Vec<(vk::Image, Allocation)>, - Vec, - Vec, -); - -impl RendererInner { - fn create_swapchain( - physical_device: vk::PhysicalDevice, - surface_loader: &ash::extensions::khr::Surface, - swapchain_loader: &ash::extensions::khr::Swapchain, - surface: vk::SurfaceKHR, - queue_family_index: u32, - width: u32, - height: u32, - ) -> ( - vk::SwapchainKHR, - vk::SurfaceFormatKHR, - vk::Extent2D, - Vec, - ) { - let surface_formats = unsafe { - surface_loader - .get_physical_device_surface_formats(physical_device, surface) - .expect("Failed to get physical device surface formats") - }; - let surface_format = *surface_formats - .iter() - .find(|format| { - format.format == vk::Format::B8G8R8A8_UNORM - || format.format == vk::Format::R8G8B8A8_UNORM - }) - .unwrap_or(&surface_formats[0]); - let surface_capabilities = unsafe { - surface_loader - .get_physical_device_surface_capabilities(physical_device, surface) - .expect("Failed to get physical device surface capabilities") - }; - let surface_extent = if surface_capabilities.current_extent.width != u32::MAX { - surface_capabilities.current_extent - } else { - vk::Extent2D { - width: width - .max(surface_capabilities.min_image_extent.width) - .min(surface_capabilities.max_image_extent.width), - height: height - .max(surface_capabilities.min_image_extent.height) - .min(surface_capabilities.max_image_extent.height), - } - }; - - let image_count = surface_capabilities.min_image_count + 1; - let image_count = if surface_capabilities.max_image_count != 0 { - image_count.min(surface_capabilities.max_image_count) - } else { - image_count - }; - - let image_sharing_mode = vk::SharingMode::EXCLUSIVE; - let queue_family_indices = [queue_family_index]; - - let swapchain_create_info = vk::SwapchainCreateInfoKHR::builder() - .surface(surface) - .min_image_count(image_count) - .image_format(surface_format.format) - .image_color_space(surface_format.color_space) - .image_extent(surface_extent) - .image_array_layers(1) - .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) - .image_sharing_mode(image_sharing_mode) - .queue_family_indices(&queue_family_indices) - .pre_transform(surface_capabilities.current_transform) - .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) - .present_mode(vk::PresentModeKHR::MAILBOX) - .clipped(true); - - let swapchain = unsafe { - swapchain_loader - .create_swapchain(&swapchain_create_info, None) - .expect("Failed to create swapchain") - }; - - let swapchain_images = unsafe { - swapchain_loader - .get_swapchain_images(swapchain) - .expect("Failed to get swapchain images") - }; - - (swapchain, surface_format, surface_extent, swapchain_images) - } - - fn create_uniform_buffers( - device: &Device, - allocator: Arc>, - swapchain_count: usize, - ) -> (Vec, Vec) { - let buffer_size = std::mem::size_of::() as u64; - - let buffer_usage = vk::BufferUsageFlags::UNIFORM_BUFFER; - let buffer_create_info = vk::BufferCreateInfo::builder() - .size(buffer_size) - .usage(buffer_usage) - .sharing_mode(vk::SharingMode::EXCLUSIVE); - let buffers = (0..swapchain_count) - .map(|_| unsafe { - device - .create_buffer(&buffer_create_info, None) - .expect("Failed to create buffer") - }) - .collect::>(); - let buffer_memory_requirements = - unsafe { device.get_buffer_memory_requirements(buffers[0]) }; - let buffer_alloc_info = gpu_allocator::vulkan::AllocationCreateDesc { - name: "Uniform Buffer", - requirements: buffer_memory_requirements, - location: gpu_allocator::MemoryLocation::CpuToGpu, - linear: true, - allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, - }; - let buffer_allocations = buffers - .iter() - .map(|_| { - allocator - .lock() - .unwrap() - .allocate(&buffer_alloc_info) - .expect("Failed to allocate memory") - }) - .collect::>(); - for (&buffer, buffer_memory) in buffers.iter().zip(buffer_allocations.iter()) { - unsafe { - device - .bind_buffer_memory(buffer, buffer_memory.memory(), buffer_memory.offset()) - .expect("Failed to bind buffer memory") - } - } - - (buffers, buffer_allocations) - } - - fn create_descriptor_pool(device: &Device, total_sets_needed: usize) -> vk::DescriptorPool { - let pool_sizes = [ - vk::DescriptorPoolSize::builder() - .ty(vk::DescriptorType::UNIFORM_BUFFER) - .descriptor_count(total_sets_needed as u32) - .build(), - vk::DescriptorPoolSize::builder() - .ty(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .descriptor_count(total_sets_needed as u32) - .build(), - ]; - - let descriptor_pool_create_info = vk::DescriptorPoolCreateInfo::builder() - .pool_sizes(&pool_sizes) - .max_sets(total_sets_needed as u32); - - unsafe { - device - .create_descriptor_pool(&descriptor_pool_create_info, None) - .expect("failed to create descriptor pool") - } - } - - fn create_descriptor_set_layouts( - device: &Device, - swapchain_count: usize, - ) -> Vec { - let bindings = [ - vk::DescriptorSetLayoutBinding::builder() - .binding(0) - .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) - .descriptor_count(1) - .stage_flags(vk::ShaderStageFlags::VERTEX | vk::ShaderStageFlags::FRAGMENT) - .build(), - vk::DescriptorSetLayoutBinding::builder() - .binding(1) - .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .descriptor_count(1) - .stage_flags(vk::ShaderStageFlags::FRAGMENT) - .build(), - vk::DescriptorSetLayoutBinding::builder() - .binding(2) - .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .descriptor_count(1) - .stage_flags(vk::ShaderStageFlags::FRAGMENT) - .build(), - vk::DescriptorSetLayoutBinding::builder() - .binding(3) - .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .descriptor_count(1) - .stage_flags(vk::ShaderStageFlags::FRAGMENT) - .build(), - ]; - - let layout_create_info = vk::DescriptorSetLayoutCreateInfo::builder().bindings(&bindings); - - (0..swapchain_count) - .map(|_| unsafe { - device - .create_descriptor_set_layout(&layout_create_info, None) - .expect("Failed to create descriptor set layout") - }) - .collect() - } - - fn create_descriptor_sets( - device: &Device, - descriptor_pool: vk::DescriptorPool, - descriptor_set_layouts: &[vk::DescriptorSetLayout], - uniform_buffers: &[Buffer], - allocator: Arc>, - command_pool: vk::CommandPool, - queue: vk::Queue, - mesh: &Mesh, - ) -> Vec { - let descriptor_sets = unsafe { - device - .allocate_descriptor_sets( - &vk::DescriptorSetAllocateInfo::builder() - .descriptor_pool(descriptor_pool) - .set_layouts(descriptor_set_layouts), - ) - .expect("failed to allocate descriptor sets") - }; - - let default_texture = - create_default_texture(device, allocator.clone(), command_pool, queue); - let default_metallic_roughness = create_default_metallic_roughness_texture( - device, - allocator.clone(), - command_pool, - queue, - ); - let default_normal = - create_default_normal_texture(device, allocator.clone(), command_pool, queue); - - for (index, &descriptor_set) in descriptor_sets.iter().enumerate() { - let buffer_info = vk::DescriptorBufferInfo::builder() - .buffer(uniform_buffers[index]) - .offset(0) - .range(vk::WHOLE_SIZE) - .build(); - - let albedo_texture = mesh.albedo_texture.as_ref().unwrap_or(&default_texture); - let albedo_info = vk::DescriptorImageInfo::builder() - .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) - .image_view(albedo_texture.image_view) - .sampler(albedo_texture.sampler) - .build(); - - let metallic_roughness_texture = mesh - .metallic_roughness_texture - .as_ref() - .unwrap_or(&default_metallic_roughness); - let metallic_roughness_info = vk::DescriptorImageInfo::builder() - .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) - .image_view(metallic_roughness_texture.image_view) - .sampler(metallic_roughness_texture.sampler) - .build(); - - let normal_texture = mesh.normal_texture.as_ref().unwrap_or(&default_normal); - let normal_info = vk::DescriptorImageInfo::builder() - .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) - .image_view(normal_texture.image_view) - .sampler(normal_texture.sampler) - .build(); - - let descriptor_writes = [ - vk::WriteDescriptorSet::builder() - .dst_set(descriptor_set) - .dst_binding(0) - .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) - .buffer_info(std::slice::from_ref(&buffer_info)) - .build(), - // albedo - vk::WriteDescriptorSet::builder() - .dst_set(descriptor_set) - .dst_binding(1) - .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .image_info(std::slice::from_ref(&albedo_info)) - .build(), - // metallic - vk::WriteDescriptorSet::builder() - .dst_set(descriptor_set) - .dst_binding(2) - .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .image_info(std::slice::from_ref(&metallic_roughness_info)) - .build(), - // norm - vk::WriteDescriptorSet::builder() - .dst_set(descriptor_set) - .dst_binding(3) - .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .image_info(std::slice::from_ref(&normal_info)) - .build(), - ]; - - unsafe { - device.update_descriptor_sets(&descriptor_writes, &[]); - } - } - - descriptor_sets - } - - fn create_render_pass(device: &Device, surface_format: vk::SurfaceFormatKHR) -> vk::RenderPass { - let attachments = [ - vk::AttachmentDescription::builder() - .format(surface_format.format) - .samples(vk::SampleCountFlags::TYPE_1) - .load_op(vk::AttachmentLoadOp::CLEAR) - .store_op(vk::AttachmentStoreOp::STORE) - .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) - .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) - .initial_layout(vk::ImageLayout::UNDEFINED) - .final_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) - .build(), - vk::AttachmentDescription::builder() - .format(vk::Format::D32_SFLOAT) - .samples(vk::SampleCountFlags::TYPE_1) - .load_op(vk::AttachmentLoadOp::CLEAR) - .store_op(vk::AttachmentStoreOp::DONT_CARE) - .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) - .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) - .initial_layout(vk::ImageLayout::UNDEFINED) - .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL) - .build(), - ]; - let color_reference = [vk::AttachmentReference::builder() - .attachment(0) - .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) - .build()]; - let depth_reference = vk::AttachmentReference::builder() - .attachment(1) - .layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - let subpasses = [vk::SubpassDescription::builder() - .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) - .color_attachments(&color_reference) - .depth_stencil_attachment(&depth_reference) - .build()]; - let render_pass_create_info = vk::RenderPassCreateInfo::builder() - .attachments(&attachments) - .subpasses(&subpasses); - unsafe { - device - .create_render_pass(&render_pass_create_info, None) - .expect("Failed to create render pass") - } - } - - fn create_framebuffers( - device: &Device, - allocator: Arc>, - render_pass: vk::RenderPass, - format: vk::SurfaceFormatKHR, - extent: vk::Extent2D, - swapchain_images: &[vk::Image], - ) -> CreateFramebuffersResult { - let mut framebuffers = vec![]; - let mut depth_images_and_allocations = vec![]; - let mut color_image_views = vec![]; - let mut depth_image_views = vec![]; - for &image in swapchain_images.iter() { - let mut attachments = vec![]; - - let color_attachment = unsafe { - device - .create_image_view( - &vk::ImageViewCreateInfo::builder() - .image(image) - .view_type(vk::ImageViewType::TYPE_2D) - .format(format.format) - .subresource_range( - vk::ImageSubresourceRange::builder() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .base_mip_level(0) - .level_count(1) - .base_array_layer(0) - .layer_count(1) - .build(), - ), - None, - ) - .expect("Failed to create image view") - }; - attachments.push(color_attachment); - color_image_views.push(color_attachment); - let depth_image_create_info = vk::ImageCreateInfo::builder() - .format(vk::Format::D32_SFLOAT) - .samples(vk::SampleCountFlags::TYPE_1) - .mip_levels(1) - .array_layers(1) - .usage(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT) - .initial_layout(vk::ImageLayout::UNDEFINED) - .image_type(vk::ImageType::TYPE_2D) - .extent(vk::Extent3D { - width: extent.width, - height: extent.height, - depth: 1, - }); - let depth_image = unsafe { - device - .create_image(&depth_image_create_info, None) - .expect("Failed to create image") - }; - let depth_allocation = allocator - .lock() - .unwrap() - .allocate(&gpu_allocator::vulkan::AllocationCreateDesc { - name: "Depth Image", - requirements: unsafe { device.get_image_memory_requirements(depth_image) }, - location: gpu_allocator::MemoryLocation::GpuOnly, - linear: true, - allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, - }) - .expect("Failed to allocate memory"); - unsafe { - device - .bind_image_memory( - depth_image, - depth_allocation.memory(), - depth_allocation.offset(), - ) - .expect("Failed to bind image memory") - }; - let depth_attachment = unsafe { - device - .create_image_view( - &vk::ImageViewCreateInfo::builder() - .image(depth_image) - .view_type(vk::ImageViewType::TYPE_2D) - .format(vk::Format::D32_SFLOAT) - .subresource_range( - vk::ImageSubresourceRange::builder() - .aspect_mask(vk::ImageAspectFlags::DEPTH) - .base_mip_level(0) - .level_count(1) - .base_array_layer(0) - .layer_count(1) - .build(), - ), - None, - ) - .expect("Failed to create depth image view") - }; - attachments.push(depth_attachment); - depth_image_views.push(depth_attachment); - framebuffers.push(unsafe { - device - .create_framebuffer( - &vk::FramebufferCreateInfo::builder() - .render_pass(render_pass) - .attachments(attachments.as_slice()) - .width(extent.width) - .height(extent.height) - .layers(1), - None, - ) - .expect("Failed to create framebuffer") - }); - depth_images_and_allocations.push((depth_image, depth_allocation)); - } - CreateFramebuffersResult( - framebuffers, - depth_images_and_allocations, - color_image_views, - depth_image_views, - ) - } - - fn create_graphics_pipeline( - device: &Device, - descriptor_set_layouts: &[vk::DescriptorSetLayout], - render_pass: vk::RenderPass, - ) -> (vk::Pipeline, vk::PipelineLayout) { - let vertex_shader_module = { - let spirv = include_spirv!("../../../shader-cache/main.vert.spv"); - let shader_module_create_info = vk::ShaderModuleCreateInfo::builder().code(&spirv); - unsafe { - device - .create_shader_module(&shader_module_create_info, None) - .expect("Failed to create shader module") - } - }; - let fragment_shader_module = { - let spirv = include_spirv!("../../../shader-cache/main.frag.spv"); - let shader_module_create_info = vk::ShaderModuleCreateInfo::builder().code(&spirv); - unsafe { - device - .create_shader_module(&shader_module_create_info, None) - .expect("Failed to create shader module") - } - }; - let main_function_name_fs = CString::new("main").unwrap(); - let main_function_name_vs = CString::new("main").unwrap(); - let pipeline_shader_stages = [ - vk::PipelineShaderStageCreateInfo::builder() - .stage(vk::ShaderStageFlags::VERTEX) - .module(vertex_shader_module) - .name(&main_function_name_vs) - .build(), - vk::PipelineShaderStageCreateInfo::builder() - .stage(vk::ShaderStageFlags::FRAGMENT) - .module(fragment_shader_module) - .name(&main_function_name_fs) - .build(), - ]; - - let push_constant_range = vk::PushConstantRange::builder() - .stage_flags(vk::ShaderStageFlags::FRAGMENT) - .offset(0) - .size(std::mem::size_of::() as u32) - .build(); - - let pipeline_layout = unsafe { - device - .create_pipeline_layout( - &vk::PipelineLayoutCreateInfo::builder() - .set_layouts(descriptor_set_layouts) - .push_constant_ranges(&[push_constant_range]), - None, - ) - .expect("Failed to create pipeline layout") - }; - let vertex_input_binding = Vertex::get_binding_descriptions(); - let vertex_input_attribute = Vertex::get_attribute_descriptions(); - let input_assembly_info = vk::PipelineInputAssemblyStateCreateInfo::builder() - .topology(vk::PrimitiveTopology::TRIANGLE_LIST); - let viewport_info = vk::PipelineViewportStateCreateInfo::builder() - .viewport_count(1) - .scissor_count(1); - let rasterization_info = vk::PipelineRasterizationStateCreateInfo::builder() - .depth_clamp_enable(false) - .rasterizer_discard_enable(false) - .polygon_mode(vk::PolygonMode::FILL) - .cull_mode(vk::CullModeFlags::BACK) - .front_face(vk::FrontFace::CLOCKWISE) - .depth_bias_enable(false) - .line_width(1.0); - let stencil_op = vk::StencilOpState::builder() - .fail_op(vk::StencilOp::KEEP) - .pass_op(vk::StencilOp::KEEP) - .compare_op(vk::CompareOp::ALWAYS); - let depth_stencil_info = vk::PipelineDepthStencilStateCreateInfo::builder() - .depth_test_enable(true) - .depth_write_enable(true) - .depth_compare_op(vk::CompareOp::LESS) - .depth_bounds_test_enable(false) - .stencil_test_enable(false) - .front(*stencil_op) - .back(*stencil_op); - let color_blend_attachment = vk::PipelineColorBlendAttachmentState::builder() - .color_write_mask( - vk::ColorComponentFlags::R - | vk::ColorComponentFlags::G - | vk::ColorComponentFlags::B - | vk::ColorComponentFlags::A, - ); - let color_blend_info = vk::PipelineColorBlendStateCreateInfo::builder() - .attachments(std::slice::from_ref(&color_blend_attachment)); - let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]; - let dynamic_state_info = - vk::PipelineDynamicStateCreateInfo::builder().dynamic_states(&dynamic_states); - let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::builder() - .vertex_attribute_descriptions(&vertex_input_attribute) - .vertex_binding_descriptions(&vertex_input_binding); - let multisample_info = vk::PipelineMultisampleStateCreateInfo::builder() - .rasterization_samples(vk::SampleCountFlags::TYPE_1); - let pipeline_create_info = vk::GraphicsPipelineCreateInfo::builder() - .stages(&pipeline_shader_stages) - .vertex_input_state(&vertex_input_state) - .input_assembly_state(&input_assembly_info) - .viewport_state(&viewport_info) - .rasterization_state(&rasterization_info) - .multisample_state(&multisample_info) - .depth_stencil_state(&depth_stencil_info) - .color_blend_state(&color_blend_info) - .dynamic_state(&dynamic_state_info) - .layout(pipeline_layout) - .render_pass(render_pass) - .subpass(0); - let graphics_pipeline = unsafe { - device - .create_graphics_pipelines( - vk::PipelineCache::null(), - std::slice::from_ref(&pipeline_create_info), - None, - ) - .unwrap()[0] - }; - unsafe { - device.destroy_shader_module(vertex_shader_module, None); - device.destroy_shader_module(fragment_shader_module, None); - } - - (graphics_pipeline, pipeline_layout) - } - - fn create_command_buffers( - device: &Device, - command_pool: vk::CommandPool, - swapchain_count: usize, - ) -> Vec { - unsafe { - device - .allocate_command_buffers( - &vk::CommandBufferAllocateInfo::builder() - .command_pool(command_pool) - .level(vk::CommandBufferLevel::PRIMARY) - .command_buffer_count(swapchain_count as u32), - ) - .expect("Failed to allocate command buffers") - } - } - - fn create_sync_objects( - device: &Device, - swapchain_count: usize, - ) -> (Vec, Vec, Vec) { - let fence_create_info = - vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED); // Start signaled - - let semaphore_create_info = vk::SemaphoreCreateInfo::builder(); - - let mut in_flight_fences = Vec::with_capacity(swapchain_count); - let mut image_available_semaphores = Vec::with_capacity(swapchain_count); - let mut render_finished_semaphores = Vec::with_capacity(swapchain_count); - - for _ in 0..swapchain_count { - unsafe { - let fence = device - .create_fence(&fence_create_info, None) - .expect("Failed to create fence"); - let image_available = device - .create_semaphore(&semaphore_create_info, None) - .expect("Failed to create semaphore"); - let render_finished = device - .create_semaphore(&semaphore_create_info, None) - .expect("Failed to create semaphore"); - - in_flight_fences.push(fence); - image_available_semaphores.push(image_available); - render_finished_semaphores.push(render_finished); - } - } - - ( - in_flight_fences, - image_available_semaphores, - render_finished_semaphores, - ) - } - - fn recreate_swapchain(&mut self, width: u32, height: u32, egui_cmd: &mut EguiCommand) { - unsafe { - self.device - .device_wait_idle() - .expect("Failed to wait device idle") - }; - - unsafe { - let mut allocator = self.allocator.lock().unwrap(); - for &fence in self.in_flight_fences.iter() { - self.device.destroy_fence(fence, None); - } - for &semaphore in self.image_available_semaphores.iter() { - self.device.destroy_semaphore(semaphore, None); - } - for &semaphore in self.render_finished_semaphores.iter() { - self.device.destroy_semaphore(semaphore, None); - } - for &framebuffer in self.framebuffers.iter() { - self.device.destroy_framebuffer(framebuffer, None); - } - for &image_view in self.color_image_views.iter() { - self.device.destroy_image_view(image_view, None); - } - for &image_view in self.depth_image_views.iter() { - self.device.destroy_image_view(image_view, None); - } - for (image, allocation) in self.depth_images_and_allocations.drain(..) { - self.device.destroy_image(image, None); - allocator.free(allocation).expect("Failed to free memory"); - } - self.device.destroy_render_pass(self.render_pass, None); - self.swapchain_loader - .destroy_swapchain(self.swapchain, None); - } - - self.width = width; - self.height = height; - - let (swapchain, swapchain_images, surface_format, surface_extent) = { - let surface_capabilities = unsafe { - self.surface_loader - .get_physical_device_surface_capabilities(self.physical_device, self.surface) - .expect("Failed to get physical device surface capabilities") - }; - let surface_formats = unsafe { - self.surface_loader - .get_physical_device_surface_formats(self.physical_device, self.surface) - .expect("Failed to get physical device surface formats") - }; - - let surface_format = *surface_formats - .iter() - .find(|surface_format| { - surface_format.format == vk::Format::B8G8R8A8_UNORM - || surface_format.format == vk::Format::R8G8B8A8_UNORM - }) - .unwrap_or(&surface_formats[0]); - - let surface_present_mode = vk::PresentModeKHR::FIFO; - - let surface_extent = if surface_capabilities.current_extent.width != u32::MAX { - surface_capabilities.current_extent - } else { - vk::Extent2D { - width: self.width.clamp( - surface_capabilities.min_image_extent.width, - surface_capabilities.max_image_extent.width, - ), - height: self.height.clamp( - surface_capabilities.min_image_extent.height, - surface_capabilities.max_image_extent.height, - ), - } - }; - - let image_count = surface_capabilities.min_image_count + 1; - let image_count = if surface_capabilities.max_image_count != 0 { - image_count.min(surface_capabilities.max_image_count) - } else { - image_count - }; - - let swapchain_create_info = vk::SwapchainCreateInfoKHR::builder() - .surface(self.surface) - .min_image_count(image_count) - .image_color_space(surface_format.color_space) - .image_format(surface_format.format) - .image_extent(surface_extent) - .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) - .image_sharing_mode(vk::SharingMode::EXCLUSIVE) - .pre_transform(surface_capabilities.current_transform) - .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) - .present_mode(surface_present_mode) - .image_array_layers(1) - .clipped(true); - let swapchain = unsafe { - self.swapchain_loader - .create_swapchain(&swapchain_create_info, None) - .expect("Failed to create swapchain") - }; - - let swapchain_images = unsafe { - self.swapchain_loader - .get_swapchain_images(swapchain) - .expect("Failed to get swapchain images") - }; - - (swapchain, swapchain_images, surface_format, surface_extent) - }; - self.swapchain = swapchain; - self.swapchain_images = swapchain_images; - self.surface_format = surface_format; - self.surface_extent = surface_extent; - - egui_cmd.update_swapchain(egui_ash::SwapchainUpdateInfo { - swapchain_images: self.swapchain_images.clone(), - surface_format: self.surface_format.format, - width: self.width, - height: self.height, - }); - - self.render_pass = Self::create_render_pass(&self.device, self.surface_format); - - let CreateFramebuffersResult( - framebuffers, - depth_images_and_allocations, - color_image_views, - depth_image_views, - ) = Self::create_framebuffers( - &self.device, - Arc::clone(&self.allocator), - self.render_pass, - self.surface_format, - self.surface_extent, - &self.swapchain_images, - ); - self.framebuffers = framebuffers; - self.depth_images_and_allocations = depth_images_and_allocations; - self.color_image_views = color_image_views; - self.depth_image_views = depth_image_views; - - let (in_flight_fences, image_available_semaphores, render_finished_semaphores) = - Self::create_sync_objects(&self.device, self.swapchain_images.len()); - self.in_flight_fences = in_flight_fences; - self.image_available_semaphores = image_available_semaphores; - self.render_finished_semaphores = render_finished_semaphores; - - self.current_frame = 0; - self.dirty_swapchain = false; - } - - fn new( - physical_device: vk::PhysicalDevice, - device: Device, - surface_loader: Surface, - swapchain_loader: Swapchain, - allocator: Arc>, - surface: vk::SurfaceKHR, - queue_family_index: u32, - queue: vk::Queue, - command_pool: vk::CommandPool, - width: u32, - height: u32, - ) -> Self { - let (swapchain, surface_format, surface_extent, swapchain_images) = Self::create_swapchain( - physical_device, - &surface_loader, - &swapchain_loader, - surface, - queue_family_index, - width, - height, - ); - let (uniform_buffers, uniform_buffer_allocations) = - Self::create_uniform_buffers::( - &device, - allocator.clone(), - swapchain_images.len(), - ); - - let t = Instant::now(); - tracing::info!("loading model!"); - let mut model = Model::load( - &device, - allocator.clone(), - command_pool, - queue, - "./sponza/NewSponza_Main_glTF_003.gltf", - ); - tracing::info!( - "loaded {} meshes in {:.2} seconds!", - model.meshes.len(), - t.elapsed().as_secs_f32() - ); - - let descriptor_pool = - Self::create_descriptor_pool(&device, model.meshes.len() * swapchain_images.len()); - let descriptor_set_layouts = - Self::create_descriptor_set_layouts(&device, swapchain_images.len()); - - let render_pass = Self::create_render_pass(&device, surface_format); - let CreateFramebuffersResult( - framebuffers, - depth_images_and_allocations, - color_image_views, - depth_image_views, - ) = Self::create_framebuffers( - &device, - allocator.clone(), - render_pass, - surface_format, - surface_extent, - &swapchain_images, - ); - let (pipeline, pipeline_layout) = - Self::create_graphics_pipeline(&device, &descriptor_set_layouts, render_pass); - let command_buffers = - Self::create_command_buffers(&device, command_pool, swapchain_images.len()); - let (in_flight_fences, image_available_semaphores, render_finished_semaphores) = - Self::create_sync_objects(&device, swapchain_images.len()); - - for mesh in &mut model.meshes { - mesh.descriptor_sets = Self::create_descriptor_sets( - &device, - descriptor_pool, - &descriptor_set_layouts, - &uniform_buffers, - allocator.clone(), - command_pool, - queue, - &mesh, - ); - } - - Self { - width, - height, - physical_device, - device, - surface_loader, - swapchain_loader, - allocator: ManuallyDrop::new(allocator), - surface, - queue, - swapchain, - surface_format, - surface_extent, - swapchain_images, - uniform_buffers, - uniform_buffer_allocations, - descriptor_pool, - descriptor_set_layouts, - render_pass, - framebuffers, - depth_images_and_allocations, - color_image_views, - depth_image_views, - pipeline, - pipeline_layout, - model, - command_buffers, - in_flight_fences, - image_available_semaphores, - render_finished_semaphores, - current_frame: 0, - dirty_swapchain: true, - - frame_counter: 0, - camera_position: Vec3::new(0.0, 0.0, -5.0), - camera_yaw: 0., - camera_pitch: 0., - camera_fov: 90., - bg_color: Vec3::splat(0.1), - model_color: Vec3::splat(0.8), - accumulation_reset_needed: true, - } - } - - pub fn update_colors(&mut self, bg_color: Vec3, model_color: Vec3) { - let bg_color = clamped_color(bg_color); - let model_color = clamped_color(model_color); - - if bg_color != self.bg_color { - self.bg_color = bg_color; - self.accumulation_reset_needed = true; - } - - if model_color != self.model_color { - self.model_color = model_color; - self.accumulation_reset_needed = true; - } - } - - pub fn update_camera(&mut self, new_position: Vec3, yaw: f32, pitch: f32, fov: f32) { - if (new_position - self.camera_position).length() > 0.0001 - || (yaw - self.camera_yaw).abs() > 0.001 - || (pitch - self.camera_pitch).abs() > 0.001 - || (fov - self.camera_fov).abs() > 0.001 - { - self.camera_position = new_position; - self.camera_yaw = yaw; - self.camera_pitch = pitch; - self.camera_fov = fov; - self.accumulation_reset_needed = true; - } - } - - pub fn render(&mut self, width: u32, height: u32, mut egui_cmd: EguiCommand, rotate_y: f32) { - puffin::profile_function!(); - if width == 0 || height == 0 { - return; - } - - if self.dirty_swapchain || self.accumulation_reset_needed { - self.frame_counter = 0; - self.accumulation_reset_needed = false; - } - - if self.dirty_swapchain - || width != self.width - || height != self.height - || egui_cmd.swapchain_recreate_required() - { - puffin::profile_scope!("recreate_swapchain"); - self.recreate_swapchain(width, height, &mut egui_cmd); - } - - unsafe { - puffin::profile_scope!("wait_for_fences"); - self.device.wait_for_fences( - std::slice::from_ref(&self.in_flight_fences[self.current_frame]), - true, - u64::MAX, - ) - } - .expect("Failed to wait for fences"); - - unsafe { - self.device.reset_fences(std::slice::from_ref( - &self.in_flight_fences[self.current_frame], - )) - } - .expect("Failed to reset fences"); - - let result = unsafe { - puffin::profile_scope!("acquire_next_image"); - self.swapchain_loader.acquire_next_image( - self.swapchain, - u64::MAX, - self.image_available_semaphores[self.current_frame], - vk::Fence::null(), - ) - }; - let index = match result { - Ok((index, _)) => index as usize, - Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => { - self.dirty_swapchain = true; - return; - } - Err(_) => return, - }; - - let view = { - puffin::profile_scope!("calculate_view"); - let (sin_pitch, cos_pitch) = self.camera_pitch.sin_cos(); - let (sin_yaw, cos_yaw) = self.camera_yaw.sin_cos(); - - let look_dir = - Vec3::new(cos_pitch * sin_yaw, sin_pitch, cos_pitch * cos_yaw).normalize(); - - Mat4::look_at_rh( - self.camera_position, - self.camera_position + look_dir, - Vec3::new(0.0, -1.0, 0.0), - ) - }; - - let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); - unsafe { - puffin::profile_scope!("render"); - self.device - .begin_command_buffer( - self.command_buffers[self.current_frame], - &command_buffer_begin_info, - ) - .expect("Failed to begin command buffer"); - - self.device.cmd_begin_render_pass( - self.command_buffers[self.current_frame], - &vk::RenderPassBeginInfo::builder() - .render_pass(self.render_pass) - .framebuffer(self.framebuffers[index]) - .render_area( - vk::Rect2D::builder() - .offset(vk::Offset2D::builder().x(0).y(0).build()) - .extent(self.surface_extent) - .build(), - ) - .clear_values(&[ - vk::ClearValue { - color: vk::ClearColorValue { - float32: [self.bg_color.x, self.bg_color.y, self.bg_color.z, 1.0], - }, - }, - vk::ClearValue { - depth_stencil: vk::ClearDepthStencilValue { - depth: 1.0, - stencil: 0, - }, - }, - ]), - vk::SubpassContents::INLINE, - ); - self.device.cmd_bind_pipeline( - self.command_buffers[self.current_frame], - vk::PipelineBindPoint::GRAPHICS, - self.pipeline, - ); - - self.device.cmd_set_viewport( - self.command_buffers[self.current_frame], - 0, - std::slice::from_ref( - &vk::Viewport::builder() - .width(width as f32) - .height(height as f32) - .min_depth(0.0) - .max_depth(1.0), - ), - ); - self.device.cmd_set_scissor( - self.command_buffers[self.current_frame], - 0, - std::slice::from_ref( - &vk::Rect2D::builder() - .offset(vk::Offset2D::builder().build()) - .extent(self.surface_extent), - ), - ); - - for mesh in &self.model.meshes { - let push_constants = PushConstants { - texture_size: Vec4::new(1024.0, 1024.0, 1.0 / 1024.0, 1.0 / 1024.0), - }; - unsafe { - self.device.cmd_push_constants( - self.command_buffers[self.current_frame], - self.pipeline_layout, - vk::ShaderStageFlags::FRAGMENT, - 0, - bytemuck::cast_slice(&[push_constants]), - ); - } - let model_matrix = Mat4::from_rotation_y(rotate_y.to_radians()) * mesh.transform; - - let ubo = UniformBufferObject { - model: model_matrix, - view, - proj: Mat4::perspective_rh( - self.camera_fov.to_radians(), - width as f32 / height as f32, - 0.1, - 1000.0, - ), - camera_pos: self.camera_position, - material: Material { - base_color: mesh.base_color, - metallic_factor: mesh.metallic_factor, - roughness_factor: mesh.roughness_factor, - _padding: [0.0, 0.0], - }, - }; - - let ptr = self.uniform_buffer_allocations[self.current_frame] - .mapped_ptr() - .unwrap() - .as_ptr() as *mut UniformBufferObject; - ptr.copy_from_nonoverlapping(&ubo, 1); - - self.device.cmd_bind_descriptor_sets( - self.command_buffers[self.current_frame], - vk::PipelineBindPoint::GRAPHICS, - self.pipeline_layout, - 0, - &[mesh.descriptor_sets[self.current_frame]], - &[], - ); - - self.device.cmd_bind_vertex_buffers( - self.command_buffers[self.current_frame], - 0, - &[mesh.vertex_buffer], - &[0], - ); - self.device.cmd_draw( - self.command_buffers[self.current_frame], - mesh.vertex_count, - 1, - 0, - 0, - ); - } - - self.device - .cmd_end_render_pass(self.command_buffers[self.current_frame]); - - egui_cmd.record(self.command_buffers[self.current_frame], index); - - self.device - .end_command_buffer(self.command_buffers[self.current_frame]) - .expect("Failed to end command buffer"); - } - - let buffers_to_submit = [self.command_buffers[self.current_frame]]; - let submit_info = vk::SubmitInfo::builder() - .command_buffers(&buffers_to_submit) - .wait_semaphores(std::slice::from_ref( - &self.image_available_semaphores[self.current_frame], - )) - .wait_dst_stage_mask(&[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]) - .signal_semaphores(std::slice::from_ref( - &self.render_finished_semaphores[self.current_frame], - )); - unsafe { - puffin::profile_scope!("queue_submit"); - self.device - .queue_submit( - self.queue, - std::slice::from_ref(&submit_info), - self.in_flight_fences[self.current_frame], - ) - .expect("Failed to submit queue"); - }; - - let image_indices = [index as u32]; - let present_info = vk::PresentInfoKHR::builder() - .wait_semaphores(std::slice::from_ref( - &self.render_finished_semaphores[self.current_frame], - )) - .swapchains(std::slice::from_ref(&self.swapchain)) - .image_indices(&image_indices); - let result = unsafe { - self.swapchain_loader - .queue_present(self.queue, &present_info) - }; - let is_dirty_swapchain = match result { - Ok(true) | Err(vk::Result::ERROR_OUT_OF_DATE_KHR | vk::Result::SUBOPTIMAL_KHR) => true, - Err(error) => panic!("Failed to present queue. Cause: {}", error), - _ => false, - }; - self.dirty_swapchain = is_dirty_swapchain; - - self.current_frame = (self.current_frame + 1) % self.in_flight_fences.len(); - - self.frame_counter += 1; - - static mut LAST_ROTATE_Y: f32 = 0.0; - unsafe { - if (LAST_ROTATE_Y - rotate_y).abs() > 0.001 { - self.accumulation_reset_needed = true; - LAST_ROTATE_Y = rotate_y; - } - } - } - - fn destroy(&mut self) { - unsafe { - self.device - .device_wait_idle() - .expect("Failed to wait device idle"); - - let mut allocator = self.allocator.lock().unwrap(); - for fence in self.in_flight_fences.drain(..) { - self.device.destroy_fence(fence, None); - } - for semaphore in self.image_available_semaphores.drain(..) { - self.device.destroy_semaphore(semaphore, None); - } - for semaphore in self.render_finished_semaphores.drain(..) { - self.device.destroy_semaphore(semaphore, None); - } - self.model.destroy(&self.device, &mut allocator); - self.device.destroy_pipeline(self.pipeline, None); - self.device - .destroy_pipeline_layout(self.pipeline_layout, None); - for &framebuffer in self.framebuffers.iter() { - self.device.destroy_framebuffer(framebuffer, None); - } - for &color_image_view in self.color_image_views.iter() { - self.device.destroy_image_view(color_image_view, None); - } - for &depth_image_view in self.depth_image_views.iter() { - self.device.destroy_image_view(depth_image_view, None); - } - for (depth_image, allocation) in self.depth_images_and_allocations.drain(..) { - self.device.destroy_image(depth_image, None); - allocator.free(allocation).expect("Failed to free memory"); - } - self.device.destroy_render_pass(self.render_pass, None); - for &descriptor_set_layout in self.descriptor_set_layouts.iter() { - self.device - .destroy_descriptor_set_layout(descriptor_set_layout, None); - } - self.device - .destroy_descriptor_pool(self.descriptor_pool, None); - for &uniform_buffer in self.uniform_buffers.iter() { - self.device.destroy_buffer(uniform_buffer, None); - } - for allocation in self.uniform_buffer_allocations.drain(..) { - allocator.free(allocation).expect("Failed to free memory"); - } - - self.swapchain_loader - .destroy_swapchain(self.swapchain, None); - } - unsafe { - ManuallyDrop::drop(&mut self.allocator); - } - } -} - -#[derive(Clone)] -pub struct Renderer { - pub inner: Arc>, -} -impl Renderer { - pub fn new( - physical_device: vk::PhysicalDevice, - device: Device, - surface_loader: Surface, - swapchain_loader: Swapchain, - allocator: Arc>, - surface: vk::SurfaceKHR, - queue_family_index: u32, - queue: vk::Queue, - command_pool: vk::CommandPool, - width: u32, - height: u32, - ) -> Self { - Self { - inner: Arc::new(Mutex::new(RendererInner::new( - physical_device, - device, - surface_loader, - swapchain_loader, - allocator, - surface, - queue_family_index, - queue, - command_pool, - width, - height, - ))), - } - } - - pub fn destroy(&mut self) { - self.inner.lock().unwrap().destroy(); - } -} - -fn clamped_color(color: Vec3) -> Vec3 { - Vec3::new( - color.x.clamp(0.0, 1.0), - color.y.clamp(0.0, 1.0), - color.z.clamp(0.0, 1.0), - ) -} - -fn create_default_texture( - device: &Device, - allocator: Arc>, - command_pool: vk::CommandPool, - queue: vk::Queue, -) -> Arc { - let white_pixel = vec![255u8, 255, 255, 255]; - Arc::new(Texture::new( - device, - allocator, - command_pool, - queue, - 1, - 1, - &white_pixel, - )) -} - -fn create_default_metallic_roughness_texture( - device: &Device, - allocator: Arc>, - command_pool: vk::CommandPool, - queue: vk::Queue, -) -> Arc { - let pixel = vec![0u8, 0, 255, 255]; // Non-metallic (0), rough (1.0) - Arc::new(Texture::new( - device, - allocator, - command_pool, - queue, - 1, - 1, - &pixel, - )) -} - -fn create_default_normal_texture( - device: &Device, - allocator: Arc>, - command_pool: vk::CommandPool, - queue: vk::Queue, -) -> Arc { - let pixel = vec![128u8, 128, 255, 255]; // Default normal pointing up - Arc::new(Texture::new( - device, - allocator, - command_pool, - queue, - 1, - 1, - &pixel, - )) -} diff --git a/crates/vk-rs/src/texture_cache.rs b/crates/vk-rs/src/texture_cache.rs deleted file mode 100644 index 171589b..0000000 --- a/crates/vk-rs/src/texture_cache.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::{ - collections::HashMap, - sync::{Arc, Mutex}, -}; - -use ash::Device; -use gpu_allocator::vulkan::Allocator; - -use super::renderer::Texture; - -pub struct TextureCache { - cache: HashMap>, -} - -impl TextureCache { - pub fn new() -> Self { - Self { - cache: HashMap::new(), - } - } - - pub fn get_or_load_texture( - &mut self, - key: String, - load_fn: impl FnOnce() -> Option, - ) -> Option> { - if let Some(texture) = self.cache.get(&key) { - Some(Arc::clone(texture)) - } else { - load_fn().map(|texture| { - let texture = Arc::new(texture); - self.cache.insert(key, Arc::clone(&texture)); - texture - }) - } - } - - pub fn cleanup(&mut self, device: &Device, allocator: &mut Allocator) { - for (_, texture) in self.cache.drain() { - if let Ok(texture) = Arc::try_unwrap(texture) { - let mut texture = texture; - texture.destroy(device, allocator); - } - } - } -} diff --git a/flake.nix b/flake.nix index 076005f..162a4ff 100644 --- a/flake.nix +++ b/flake.nix @@ -18,17 +18,7 @@ nixpkgs, flake-parts, ... - }: let - # For details on these options, See - # https://github.com/oxalica/rust-overlay?tab=readme-ov-file#cheat-sheet-common-usage-of-rust-bin - # - # Channel of the Rust toolchain (stable or beta). - rustChannel = "nightly"; - # Version (latest or specific date/semantic version) - rustVersion = "latest"; - # Profile (default or minimal) - rustProfile = "default"; - in + }: flake-parts.lib.mkFlake {inherit inputs;} { systems = import inputs.systems; @@ -77,6 +67,7 @@ # WINIT_UNIX_BACKEND=wayland wayland spirv-tools + spirv-cross vulkan-loader ]; }; diff --git a/shaders/main.frag.glsl b/shaders/main.frag.glsl deleted file mode 100644 index 4a3e1c6..0000000 --- a/shaders/main.frag.glsl +++ /dev/null @@ -1,134 +0,0 @@ -#version 450 - -layout(location = 0) in vec3 frag_world_position; -layout(location = 1) in vec3 frag_world_normal; -layout(location = 2) in vec2 frag_tex_coord; - -struct Material { - vec4 base_color; - float metallic_factor; - float roughness_factor; - vec2 _padding; -}; - -layout(set = 0, binding = 0) uniform UniformBufferObject { - mat4 model; - mat4 view; - mat4 proj; - vec3 camera_pos; - float _padding; - Material material; -} ubo; - -layout(set = 0, binding = 1) uniform sampler2D albedo_map; -layout(set = 0, binding = 2) uniform sampler2D metallic_roughness_map; -layout(set = 0, binding = 3) uniform sampler2D normal_map; - -layout(location = 0) out vec4 out_color; - -const float PI = 3.14159265359; - -// PBR functions -vec3 fresnelSchlick(float cosTheta, vec3 F0) { - return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); -} - -float DistributionGGX(vec3 N, vec3 H, float roughness) { - float a = roughness*roughness; - float a2 = a*a; - float NdotH = max(dot(N, H), 0.0); - float NdotH2 = NdotH*NdotH; - - float nom = a2; - float denom = (NdotH2 * (a2 - 1.0) + 1.0); - denom = PI * denom * denom; - - return nom / max(denom, 0.0000001); -} - -float GeometrySchlickGGX(float NdotV, float roughness) { - float r = (roughness + 1.0); - float k = (r*r) / 8.0; - - float nom = NdotV; - float denom = NdotV * (1.0 - k) + k; - - return nom / denom; -} - -float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { - float NdotV = max(dot(N, V), 0.0); - float NdotL = max(dot(N, L), 0.0); - float ggx2 = GeometrySchlickGGX(NdotV, roughness); - float ggx1 = GeometrySchlickGGX(NdotL, roughness); - - return ggx1 * ggx2; -} - -void main() { - // Sample textures - vec4 albedo = texture(albedo_map, frag_tex_coord); - vec2 metallic_roughness = texture(metallic_roughness_map, frag_tex_coord).bg; - vec3 normal = normalize(2.0 * texture(normal_map, frag_tex_coord).rgb - 1.0); - - float metallic = metallic_roughness.x * ubo.material.metallic_factor; - float roughness = metallic_roughness.y * ubo.material.roughness_factor; - - vec3 N = normalize(normal); - vec3 V = normalize(ubo.camera_pos - frag_world_position); - - // Calculate reflectance at normal incidence - vec3 F0 = vec3(0.04); - F0 = mix(F0, albedo.rgb, metallic); - - // Light parameters - vec3 light_positions[4] = vec3[]( - vec3(5.0, 5.0, 5.0), - vec3(-5.0, 5.0, 5.0), - vec3(5.0, -5.0, 5.0), - vec3(-5.0, -5.0, 5.0) - ); - vec3 light_colors[4] = vec3[]( - vec3(23.47, 21.31, 20.79), - vec3(23.47, 21.31, 20.79), - vec3(23.47, 21.31, 20.79), - vec3(23.47, 21.31, 20.79) - ); - - // Reflectance equation - vec3 Lo = vec3(0.0); - for(int i = 0; i < 4; ++i) { - vec3 L = normalize(light_positions[i] - frag_world_position); - vec3 H = normalize(V + L); - float distance = length(light_positions[i] - frag_world_position); - float attenuation = 1.0 / (distance * distance); - vec3 radiance = light_colors[i] * attenuation; - - // Cook-Torrance BRDF - float NDF = DistributionGGX(N, H, roughness); - float G = GeometrySmith(N, V, L, roughness); - vec3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0); - - vec3 numerator = NDF * G * F; - float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); - vec3 specular = numerator / max(denominator, 0.001); - - vec3 kS = F; - vec3 kD = vec3(1.0) - kS; - kD *= 1.0 - metallic; - - float NdotL = max(dot(N, L), 0.0); - - Lo += (kD * albedo.rgb / PI + specular) * radiance * NdotL; - } - - vec3 ambient = vec3(0.03) * albedo.rgb; - vec3 color = ambient + Lo; - - // HDR tonemapping - color = color / (color + vec3(1.0)); - // gamma correction - color = pow(color, vec3(1.0/2.2)); - - out_color = vec4(color, albedo.a); -} diff --git a/shaders/main.vert.glsl b/shaders/main.vert.glsl deleted file mode 100644 index 7448a91..0000000 --- a/shaders/main.vert.glsl +++ /dev/null @@ -1,32 +0,0 @@ -#version 450 - -// Vertex inputs -layout(location = 0) in vec3 in_pos; -layout(location = 1) in vec3 in_normal; -layout(location = 2) in vec2 in_tex_coord; - -// Uniform buffer -layout(set = 0, binding = 0) uniform UniformBufferObject { - mat4 model; - mat4 view; - mat4 proj; -} ubo; - -// Vertex outputs -layout(location = 0) out vec3 out_world_position; -layout(location = 1) out vec3 out_world_normal; -layout(location = 2) out vec2 out_tex_coord; - -void main() { - // Transform position to world space - vec4 pos = ubo.model * vec4(in_pos, 1.0); - out_world_position = (pos / pos.w).xyz; - - // Transform normal to world space - mat3 normal_matrix = transpose(inverse(mat3(ubo.model))); - out_world_normal = normal_matrix * in_normal; - - // Calculate clip space position - gl_Position = ubo.proj * ubo.view * pos; - out_tex_coord = in_tex_coord; -}